2005-02-24 18:36:11 +01:00
|
|
|
|
/* simple-pwquery.c - A simple password query client for gpg-agent
|
2007-06-14 19:05:07 +02:00
|
|
|
|
* Copyright (C) 2002, 2004, 2007 Free Software Foundation, Inc.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2007-07-04 21:49:40 +02:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-08-05 19:11:04 +02:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
2007-07-04 21:49:40 +02:00
|
|
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* This module is intended as a standalone client implementation to
|
|
|
|
|
gpg-agent's GET_PASSPHRASE command. In particular it does not use
|
|
|
|
|
the Assuan library and can only cope with an already running
|
|
|
|
|
gpg-agent. Some stuff is configurable in the header file. */
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <unistd.h>
|
2004-12-15 15:15:54 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
2004-12-02 08:48:09 +01:00
|
|
|
|
#include <winsock2.h>
|
|
|
|
|
#else
|
2003-08-05 19:11:04 +02:00
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/un.h>
|
2004-12-02 08:48:09 +01:00
|
|
|
|
#endif
|
2003-08-05 19:11:04 +02:00
|
|
|
|
#ifdef HAVE_LOCALE_H
|
|
|
|
|
#include <locale.h>
|
|
|
|
|
#endif
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
#include "../jnlib/w32-afunix.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
|
|
|
|
#define SIMPLE_PWQUERY_IMPLEMENTATION 1
|
|
|
|
|
#include "simple-pwquery.h"
|
|
|
|
|
|
|
|
|
|
#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
|
|
|
|
|
# undef SPWQ_USE_LOGGING
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef _
|
|
|
|
|
#define _(a) (a)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if !defined (hexdigitp) && !defined (xtoi_2)
|
|
|
|
|
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
|
|
|
|
|
#define hexdigitp(a) (digitp (a) \
|
|
|
|
|
|| (*(a) >= 'A' && *(a) <= 'F') \
|
|
|
|
|
|| (*(a) >= 'a' && *(a) <= 'f'))
|
|
|
|
|
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
|
|
|
|
|
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
|
|
|
|
|
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2007-06-14 19:05:07 +02:00
|
|
|
|
/* Name of the socket to be used if GPG_AGENT_INFO has not been
|
|
|
|
|
set. No default socket is used if this is NULL. */
|
|
|
|
|
static char *default_gpg_agent_info;
|
|
|
|
|
|
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef HAVE_STPCPY
|
|
|
|
|
static char *
|
|
|
|
|
my_stpcpy(char *a,const char *b)
|
|
|
|
|
{
|
|
|
|
|
while( *b )
|
|
|
|
|
*a++ = *b++;
|
|
|
|
|
*a = 0;
|
|
|
|
|
|
|
|
|
|
return (char*)a;
|
|
|
|
|
}
|
|
|
|
|
#define stpcpy(a,b) my_stpcpy((a), (b))
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
/* Write NBYTES of BUF to file descriptor FD. */
|
|
|
|
|
static int
|
|
|
|
|
writen (int fd, const void *buf, size_t nbytes)
|
|
|
|
|
{
|
|
|
|
|
size_t nleft = nbytes;
|
|
|
|
|
int nwritten;
|
|
|
|
|
|
|
|
|
|
while (nleft > 0)
|
|
|
|
|
{
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
nwritten = send (fd, buf, nleft, 0);
|
|
|
|
|
#else
|
|
|
|
|
nwritten = write (fd, buf, nleft);
|
|
|
|
|
#endif
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (nwritten < 0)
|
|
|
|
|
{
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
nwritten = 0;
|
|
|
|
|
else {
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error ("write failed: %s\n", strerror (errno));
|
|
|
|
|
#endif
|
|
|
|
|
return SPWQ_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nleft -= nwritten;
|
|
|
|
|
buf = (const char*)buf + nwritten;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read an entire line and return number of bytes read. */
|
|
|
|
|
static int
|
|
|
|
|
readline (int fd, char *buf, size_t buflen)
|
|
|
|
|
{
|
|
|
|
|
size_t nleft = buflen;
|
|
|
|
|
char *p;
|
|
|
|
|
int nread = 0;
|
|
|
|
|
|
|
|
|
|
while (nleft > 0)
|
|
|
|
|
{
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
int n = recv (fd, buf, nleft, 0);
|
|
|
|
|
#else
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int n = read (fd, buf, nleft);
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#endif
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (n < 0)
|
|
|
|
|
{
|
|
|
|
|
if (errno == EINTR)
|
|
|
|
|
continue;
|
|
|
|
|
return -(SPWQ_IO_ERROR);
|
|
|
|
|
}
|
|
|
|
|
else if (!n)
|
|
|
|
|
{
|
|
|
|
|
return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
|
|
|
|
|
}
|
|
|
|
|
p = buf;
|
|
|
|
|
nleft -= n;
|
|
|
|
|
buf += n;
|
|
|
|
|
nread += n;
|
|
|
|
|
|
|
|
|
|
for (; n && *p != '\n'; n--, p++)
|
|
|
|
|
;
|
|
|
|
|
if (n)
|
|
|
|
|
{
|
2007-06-14 19:05:07 +02:00
|
|
|
|
break; /* At least one full line available - that's enough.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
This function is just a simple implementation, so
|
2007-06-14 19:05:07 +02:00
|
|
|
|
it is okay to forget about pending bytes. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nread;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send an option to the agent */
|
|
|
|
|
static int
|
|
|
|
|
agent_send_option (int fd, const char *name, const char *value)
|
|
|
|
|
{
|
|
|
|
|
char buf[200];
|
|
|
|
|
int nread;
|
|
|
|
|
char *line;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
|
|
|
|
|
if (!line)
|
|
|
|
|
return SPWQ_OUT_OF_CORE;
|
|
|
|
|
strcpy (stpcpy (stpcpy (stpcpy (
|
|
|
|
|
stpcpy (line, "OPTION "), name), "="), value), "\n");
|
|
|
|
|
i = writen (fd, line, strlen (line));
|
|
|
|
|
spwq_free (line);
|
|
|
|
|
if (i)
|
|
|
|
|
return i;
|
|
|
|
|
|
|
|
|
|
/* get response */
|
|
|
|
|
nread = readline (fd, buf, DIM(buf)-1);
|
|
|
|
|
if (nread < 0)
|
|
|
|
|
return -nread;
|
|
|
|
|
if (nread < 3)
|
|
|
|
|
return SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
|
|
|
|
|
if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n'))
|
|
|
|
|
return 0; /* okay */
|
|
|
|
|
|
|
|
|
|
return SPWQ_ERR_RESPONSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Send all available options to the agent. */
|
|
|
|
|
static int
|
|
|
|
|
agent_send_all_options (int fd)
|
|
|
|
|
{
|
|
|
|
|
char *dft_display = NULL;
|
|
|
|
|
char *dft_ttyname = NULL;
|
|
|
|
|
char *dft_ttytype = NULL;
|
2008-03-06 19:28:47 +01:00
|
|
|
|
char *dft_xauthority = NULL;
|
|
|
|
|
char *dft_pinentry_user_data = NULL;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
|
|
dft_display = getenv ("DISPLAY");
|
|
|
|
|
if (dft_display)
|
|
|
|
|
{
|
|
|
|
|
if ((rc = agent_send_option (fd, "display", dft_display)))
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dft_ttyname = getenv ("GPG_TTY");
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#ifndef HAVE_W32_SYSTEM
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
|
|
|
|
|
dft_ttyname = ttyname (0);
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#endif
|
2003-08-05 19:11:04 +02:00
|
|
|
|
if (dft_ttyname && *dft_ttyname)
|
|
|
|
|
{
|
|
|
|
|
if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dft_ttytype = getenv ("TERM");
|
|
|
|
|
if (dft_ttyname && dft_ttytype)
|
|
|
|
|
{
|
|
|
|
|
if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_SETLOCALE)
|
|
|
|
|
{
|
|
|
|
|
char *old_lc = NULL;
|
|
|
|
|
char *dft_lc = NULL;
|
|
|
|
|
|
|
|
|
|
#if defined(LC_CTYPE)
|
|
|
|
|
old_lc = setlocale (LC_CTYPE, NULL);
|
|
|
|
|
if (old_lc)
|
|
|
|
|
{
|
|
|
|
|
char *p = spwq_malloc (strlen (old_lc)+1);
|
|
|
|
|
if (!p)
|
|
|
|
|
return SPWQ_OUT_OF_CORE;
|
|
|
|
|
strcpy (p, old_lc);
|
|
|
|
|
old_lc = p;
|
|
|
|
|
}
|
|
|
|
|
dft_lc = setlocale (LC_CTYPE, "");
|
|
|
|
|
if (dft_ttyname && dft_lc)
|
|
|
|
|
rc = agent_send_option (fd, "lc-ctype", dft_lc);
|
|
|
|
|
if (old_lc)
|
|
|
|
|
{
|
|
|
|
|
setlocale (LC_CTYPE, old_lc);
|
|
|
|
|
spwq_free (old_lc);
|
|
|
|
|
}
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(LC_MESSAGES)
|
|
|
|
|
old_lc = setlocale (LC_MESSAGES, NULL);
|
|
|
|
|
if (old_lc)
|
|
|
|
|
{
|
|
|
|
|
char *p = spwq_malloc (strlen (old_lc)+1);
|
|
|
|
|
if (!p)
|
|
|
|
|
return SPWQ_OUT_OF_CORE;
|
|
|
|
|
strcpy (p, old_lc);
|
|
|
|
|
old_lc = p;
|
|
|
|
|
}
|
|
|
|
|
dft_lc = setlocale (LC_MESSAGES, "");
|
|
|
|
|
if (dft_ttyname && dft_lc)
|
|
|
|
|
rc = agent_send_option (fd, "lc-messages", dft_lc);
|
|
|
|
|
if (old_lc)
|
|
|
|
|
{
|
|
|
|
|
setlocale (LC_MESSAGES, old_lc);
|
|
|
|
|
spwq_free (old_lc);
|
|
|
|
|
}
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_SETLOCALE*/
|
|
|
|
|
|
2008-03-06 19:28:47 +01:00
|
|
|
|
/* Send the XAUTHORITY variable. */
|
|
|
|
|
dft_xauthority = getenv ("XAUTHORITY");
|
|
|
|
|
if (dft_xauthority)
|
|
|
|
|
{
|
|
|
|
|
/* We ignore errors here because older gpg-agents don't support
|
|
|
|
|
this option. */
|
2008-03-20 16:31:43 +01:00
|
|
|
|
agent_send_option (fd, "xauthority", dft_xauthority);
|
2008-03-06 19:28:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Send the PINENTRY_USER_DATA variable. */
|
|
|
|
|
dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
|
|
|
|
|
if (dft_pinentry_user_data)
|
|
|
|
|
{
|
|
|
|
|
/* We ignore errors here because older gpg-agents don't support
|
|
|
|
|
this option. */
|
2008-03-20 16:31:43 +01:00
|
|
|
|
agent_send_option (fd, "pinentry-user-data", dft_pinentry_user_data);
|
2008-03-06 19:28:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Try to open a connection to the agent, send all options and return
|
|
|
|
|
the file descriptor for the connection. Return -1 in case of
|
|
|
|
|
error. */
|
|
|
|
|
static int
|
|
|
|
|
agent_open (int *rfd)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
int fd;
|
|
|
|
|
char *infostr, *p;
|
|
|
|
|
struct sockaddr_un client_addr;
|
|
|
|
|
size_t len;
|
|
|
|
|
int prot;
|
|
|
|
|
char line[200];
|
|
|
|
|
int nread;
|
|
|
|
|
|
|
|
|
|
*rfd = -1;
|
|
|
|
|
infostr = getenv ( "GPG_AGENT_INFO" );
|
2007-06-14 19:05:07 +02:00
|
|
|
|
if ( !infostr || !*infostr )
|
|
|
|
|
infostr = default_gpg_agent_info;
|
2004-02-18 17:58:00 +01:00
|
|
|
|
if ( !infostr || !*infostr )
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error (_("gpg-agent is not available in this session\n"));
|
|
|
|
|
#endif
|
|
|
|
|
return SPWQ_NO_AGENT;
|
|
|
|
|
}
|
2004-02-19 17:26:32 +01:00
|
|
|
|
p = spwq_malloc (strlen (infostr)+1);
|
|
|
|
|
if (!p)
|
|
|
|
|
return SPWQ_OUT_OF_CORE;
|
|
|
|
|
strcpy (p, infostr);
|
|
|
|
|
infostr = p;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|| (p-infostr)+1 >= sizeof client_addr.sun_path )
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
|
|
|
|
|
#endif
|
|
|
|
|
return SPWQ_NO_AGENT;
|
|
|
|
|
}
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
while (*p && *p != PATHSEP_C)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
p++;
|
|
|
|
|
prot = *p? atoi (p+1) : 0;
|
|
|
|
|
if ( prot != 1)
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
|
|
|
|
|
#endif
|
|
|
|
|
return SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
}
|
2004-12-21 20:05:15 +01:00
|
|
|
|
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
#else
|
|
|
|
|
fd = socket (AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
|
#endif
|
|
|
|
|
if (fd == -1)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error ("can't create socket: %s\n", strerror(errno) );
|
|
|
|
|
#endif
|
|
|
|
|
return SPWQ_SYS_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memset (&client_addr, 0, sizeof client_addr);
|
|
|
|
|
client_addr.sun_family = AF_UNIX;
|
|
|
|
|
strcpy (client_addr.sun_path, infostr);
|
|
|
|
|
len = (offsetof (struct sockaddr_un, sun_path)
|
|
|
|
|
+ strlen(client_addr.sun_path) + 1);
|
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
rc = _w32_sock_connect (fd, (struct sockaddr*)&client_addr, len );
|
|
|
|
|
#else
|
|
|
|
|
rc = connect (fd, (struct sockaddr*)&client_addr, len );
|
|
|
|
|
#endif
|
|
|
|
|
if (rc == -1)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
|
|
|
|
|
#endif
|
|
|
|
|
close (fd );
|
|
|
|
|
return SPWQ_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nread = readline (fd, line, DIM(line));
|
|
|
|
|
if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
|
|
|
|
|
&& (line[2] == '\n' || line[2] == ' ')) )
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error ( _("communication problem with gpg-agent\n"));
|
|
|
|
|
#endif
|
|
|
|
|
close (fd );
|
|
|
|
|
return SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = agent_send_all_options (fd);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error (_("problem setting the gpg-agent options\n"));
|
|
|
|
|
#endif
|
|
|
|
|
close (fd);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*rfd = fd;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-02-19 17:26:32 +01:00
|
|
|
|
/* Copy text to BUFFER and escape as required. Return a pointer to
|
2004-12-21 20:05:15 +01:00
|
|
|
|
the end of the new buffer. Note that BUFFER must be large enough
|
2003-08-05 19:11:04 +02:00
|
|
|
|
to keep the entire text; allocataing it 3 times the size of TEXT
|
|
|
|
|
is sufficient. */
|
|
|
|
|
static char *
|
|
|
|
|
copy_and_escape (char *buffer, const char *text)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
2005-06-16 10:12:03 +02:00
|
|
|
|
const unsigned char *s = (unsigned char *)text;
|
2003-08-05 19:11:04 +02:00
|
|
|
|
char *p = buffer;
|
2004-04-13 11:45:23 +02:00
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
|
2004-04-13 11:45:23 +02:00
|
|
|
|
for (i=0; s[i]; i++)
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2004-04-13 11:45:23 +02:00
|
|
|
|
if (s[i] < ' ' || s[i] == '+')
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2004-04-13 11:45:23 +02:00
|
|
|
|
sprintf (p, "%%%02X", s[i]);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
p += 3;
|
|
|
|
|
}
|
2004-04-13 11:45:23 +02:00
|
|
|
|
else if (s[i] == ' ')
|
2003-08-05 19:11:04 +02:00
|
|
|
|
*p++ = '+';
|
|
|
|
|
else
|
2004-04-13 11:45:23 +02:00
|
|
|
|
*p++ = s[i];
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2007-06-14 19:05:07 +02:00
|
|
|
|
/* Set the name of the default socket to NAME. */
|
|
|
|
|
int
|
|
|
|
|
simple_pw_set_socket (const char *name)
|
|
|
|
|
{
|
|
|
|
|
spwq_free (default_gpg_agent_info);
|
|
|
|
|
if (name)
|
|
|
|
|
{
|
|
|
|
|
default_gpg_agent_info = spwq_malloc (strlen (name) + 4 + 1);
|
|
|
|
|
if (!default_gpg_agent_info)
|
|
|
|
|
return SPWQ_OUT_OF_CORE;
|
|
|
|
|
/* We don't know the PID thus we use 0. */
|
|
|
|
|
strcpy (stpcpy (default_gpg_agent_info, name),
|
|
|
|
|
PATHSEP_S "0" PATHSEP_S "1");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
default_gpg_agent_info = NULL;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-08-05 19:11:04 +02:00
|
|
|
|
/* Ask the gpg-agent for a passphrase and present the user with a
|
2007-01-25 10:15:10 +01:00
|
|
|
|
DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
|
2003-08-05 19:11:04 +02:00
|
|
|
|
If a CACHEID is not NULL it is used to locate the passphrase in in
|
2007-01-25 10:15:10 +01:00
|
|
|
|
the cache and store it under this ID. If OPT_CHECK is true
|
|
|
|
|
gpg-agent is asked to apply some checks on the passphrase security.
|
|
|
|
|
If ERRORCODE is not NULL it should point a variable receiving an
|
2007-06-14 19:05:07 +02:00
|
|
|
|
errorcode; this error code might be 0 if the user canceled the
|
2007-01-25 10:15:10 +01:00
|
|
|
|
operation. The function returns NULL to indicate an error. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
char *
|
|
|
|
|
simple_pwquery (const char *cacheid,
|
|
|
|
|
const char *tryagain,
|
|
|
|
|
const char *prompt,
|
|
|
|
|
const char *description,
|
2007-01-25 10:15:10 +01:00
|
|
|
|
int opt_check,
|
2003-08-05 19:11:04 +02:00
|
|
|
|
int *errorcode)
|
|
|
|
|
{
|
|
|
|
|
int fd = -1;
|
|
|
|
|
int nread;
|
|
|
|
|
char *result = NULL;
|
|
|
|
|
char *pw = NULL;
|
|
|
|
|
char *p;
|
|
|
|
|
int rc, i;
|
|
|
|
|
|
|
|
|
|
rc = agent_open (&fd);
|
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
if (!cacheid)
|
|
|
|
|
cacheid = "X";
|
|
|
|
|
if (!tryagain)
|
|
|
|
|
tryagain = "X";
|
|
|
|
|
if (!prompt)
|
|
|
|
|
prompt = "X";
|
|
|
|
|
if (!description)
|
|
|
|
|
description = "X";
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
char *line;
|
|
|
|
|
/* We allocate 3 times the needed space so that there is enough
|
|
|
|
|
space for escaping. */
|
2007-01-25 10:15:10 +01:00
|
|
|
|
line = spwq_malloc (15 + 10
|
2003-08-05 19:11:04 +02:00
|
|
|
|
+ 3*strlen (cacheid) + 1
|
|
|
|
|
+ 3*strlen (tryagain) + 1
|
|
|
|
|
+ 3*strlen (prompt) + 1
|
|
|
|
|
+ 3*strlen (description) + 1
|
|
|
|
|
+ 2);
|
|
|
|
|
if (!line)
|
|
|
|
|
{
|
|
|
|
|
rc = SPWQ_OUT_OF_CORE;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
strcpy (line, "GET_PASSPHRASE ");
|
|
|
|
|
p = line+15;
|
2007-01-25 10:15:10 +01:00
|
|
|
|
if (opt_check)
|
|
|
|
|
p = stpcpy (p, "--check ");
|
2003-08-05 19:11:04 +02:00
|
|
|
|
p = copy_and_escape (p, cacheid);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = copy_and_escape (p, tryagain);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = copy_and_escape (p, prompt);
|
|
|
|
|
*p++ = ' ';
|
|
|
|
|
p = copy_and_escape (p, description);
|
|
|
|
|
*p++ = '\n';
|
|
|
|
|
rc = writen (fd, line, p - line);
|
|
|
|
|
spwq_free (line);
|
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get response */
|
|
|
|
|
pw = spwq_secure_malloc (500);
|
|
|
|
|
nread = readline (fd, pw, 499);
|
|
|
|
|
if (nread < 0)
|
|
|
|
|
{
|
|
|
|
|
rc = -nread;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (nread < 3)
|
|
|
|
|
{
|
|
|
|
|
rc = SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ')
|
|
|
|
|
{ /* we got a passphrase - convert it back from hex */
|
|
|
|
|
size_t pwlen = 0;
|
|
|
|
|
|
|
|
|
|
for (i=3; i < nread && hexdigitp (pw+i); i+=2)
|
|
|
|
|
pw[pwlen++] = xtoi_2 (pw+i);
|
|
|
|
|
pw[pwlen] = 0; /* make a C String */
|
|
|
|
|
result = pw;
|
|
|
|
|
pw = NULL;
|
|
|
|
|
}
|
2004-08-18 16:37:22 +02:00
|
|
|
|
else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
|
|
|
|
|
&& (pw[7] == ' ' || pw[7] == '\n') )
|
|
|
|
|
|| ((nread > 4 && !memcmp (pw, "ERR ", 4)
|
|
|
|
|
&& (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) )
|
2003-08-05 19:11:04 +02:00
|
|
|
|
{
|
2004-08-18 16:37:22 +02:00
|
|
|
|
/* 111 is the old Assuan code for canceled which might still
|
|
|
|
|
be in use by old installations. 99 is GPG_ERR_CANCELED as
|
|
|
|
|
used by modern gpg-agents; 0xffff is used to mask out the
|
|
|
|
|
error source. */
|
2003-08-05 19:11:04 +02:00
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_info (_("canceled by user\n") );
|
|
|
|
|
#endif
|
2007-06-14 19:05:07 +02:00
|
|
|
|
*errorcode = 0; /* Special error code to indicate Cancel. */
|
|
|
|
|
}
|
|
|
|
|
else if (nread > 4 && !memcmp (pw, "ERR ", 4))
|
|
|
|
|
{
|
|
|
|
|
switch ( (strtoul (pw+4, NULL, 0) & 0xffff) )
|
|
|
|
|
{
|
|
|
|
|
case 85: rc = SPWQ_NO_PIN_ENTRY; break;
|
|
|
|
|
default: rc = SPWQ_GENERAL_ERROR; break;
|
|
|
|
|
}
|
2003-08-05 19:11:04 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error (_("problem with the agent\n"));
|
|
|
|
|
#endif
|
|
|
|
|
rc = SPWQ_ERR_RESPONSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (errorcode)
|
|
|
|
|
*errorcode = rc;
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close (fd);
|
|
|
|
|
if (pw)
|
2005-04-11 00:12:31 +02:00
|
|
|
|
spwq_secure_free (pw);
|
2003-08-05 19:11:04 +02:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2004-12-21 20:05:15 +01:00
|
|
|
|
|
|
|
|
|
|
2005-07-04 06:55:48 +02:00
|
|
|
|
/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */
|
|
|
|
|
int
|
|
|
|
|
simple_pwclear (const char *cacheid)
|
|
|
|
|
{
|
|
|
|
|
char line[500];
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
/* We need not more than 50 characters for the command and the
|
|
|
|
|
terminating nul. */
|
|
|
|
|
if (strlen (cacheid) * 3 > sizeof (line) - 50)
|
|
|
|
|
return SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
|
|
|
|
|
strcpy (line, "CLEAR_PASSPHRASE ");
|
|
|
|
|
p = line + 17;
|
|
|
|
|
p = copy_and_escape (p, cacheid);
|
|
|
|
|
*p++ = '\n';
|
|
|
|
|
*p++ = '\0';
|
|
|
|
|
|
|
|
|
|
return simple_query (line);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
/* Perform the simple query QUERY (which must be new-line and 0
|
|
|
|
|
terminated) and return the error code. */
|
|
|
|
|
int
|
|
|
|
|
simple_query (const char *query)
|
|
|
|
|
{
|
|
|
|
|
int fd = -1;
|
|
|
|
|
int nread;
|
|
|
|
|
char response[500];
|
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
|
|
rc = agent_open (&fd);
|
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
rc = writen (fd, query, strlen (query));
|
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
/* get response */
|
|
|
|
|
nread = readline (fd, response, 499);
|
|
|
|
|
if (nread < 0)
|
|
|
|
|
{
|
|
|
|
|
rc = -nread;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
if (nread < 3)
|
|
|
|
|
{
|
|
|
|
|
rc = SPWQ_PROTOCOL_ERROR;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2005-07-04 06:55:48 +02:00
|
|
|
|
|
2004-12-21 20:05:15 +01:00
|
|
|
|
if (response[0] == 'O' && response[1] == 'K')
|
|
|
|
|
/* OK, do nothing. */;
|
|
|
|
|
else if ((nread > 7 && !memcmp (response, "ERR 111", 7)
|
|
|
|
|
&& (response[7] == ' ' || response[7] == '\n') )
|
|
|
|
|
|| ((nread > 4 && !memcmp (response, "ERR ", 4)
|
|
|
|
|
&& (strtoul (response+4, NULL, 0) & 0xffff) == 99)) )
|
|
|
|
|
{
|
|
|
|
|
/* 111 is the old Assuan code for canceled which might still
|
|
|
|
|
be in use by old installations. 99 is GPG_ERR_CANCELED as
|
|
|
|
|
used by modern gpg-agents; 0xffff is used to mask out the
|
|
|
|
|
error source. */
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_info (_("canceled by user\n") );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#ifdef SPWQ_USE_LOGGING
|
|
|
|
|
log_error (_("problem with the agent\n"));
|
|
|
|
|
#endif
|
|
|
|
|
rc = SPWQ_ERR_RESPONSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (fd != -1)
|
|
|
|
|
close (fd);
|
|
|
|
|
return rc;
|
|
|
|
|
}
|