1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-06-13 00:09:51 +02:00

common: Rework the simple password query module.

* common/simple-pwquery.c (writen, readline): Drop.
(agent_send_option, agent_send_all_options, agent_open): Just use
libassuan.
(simple_pw_set_socket): Simplify.
(default_inq_cb): New function.
(simple_pwquery, simple_query): Just use libassuan.
* agent/Makefile.am (gpg_preset_passphrase_LDADD): Add libassuan.
* tools/Makefile.am (symcryptrun_LDADD): Likewise.

Signed-off-by: Justus Winter <justus@g10code.com>
This commit is contained in:
Justus Winter 2016-08-11 12:26:09 +02:00
parent 9e6503b7ce
commit 14479e2515
3 changed files with 105 additions and 323 deletions

View File

@ -85,7 +85,7 @@ gpg_preset_passphrase_SOURCES = \
# Needs $(NETLIBS) for libsimple-pwquery.la. # Needs $(NETLIBS) for libsimple-pwquery.la.
gpg_preset_passphrase_LDADD = \ gpg_preset_passphrase_LDADD = \
$(pwquery_libs) $(common_libs) \ $(pwquery_libs) $(common_libs) $(LIBASSUAN_LIBS) \
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV)

View File

@ -17,10 +17,10 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/ */
/* This module is intended as a standalone client implementation to /* This module is intended as a simple client implementation to
gpg-agent's GET_PASSPHRASE command. In particular it does not use gpg-agent's GET_PASSPHRASE command. It can only cope with an
the Assuan library and can only cope with an already running already running gpg-agent. Some stuff is configurable in the
gpg-agent. Some stuff is configurable in the header file. */ header file. */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
@ -30,6 +30,7 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <assuan.h>
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
#include <winsock2.h> #include <winsock2.h>
#else #else
@ -42,9 +43,8 @@
#define GNUPG_COMMON_NEED_AFLOCAL #define GNUPG_COMMON_NEED_AFLOCAL
#include "../common/mischelp.h" #include "../common/mischelp.h"
#ifdef HAVE_W32_SYSTEM #include "sysutils.h"
#include "../common/w32-afunix.h" #include "membuf.h"
#endif
#define SIMPLE_PWQUERY_IMPLEMENTATION 1 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
@ -96,88 +96,11 @@ my_stpcpy(char *a,const char *b)
#endif #endif
/* 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)
{
#ifdef HAVE_W32_SYSTEM
nwritten = send (fd, buf, nleft, 0);
#else
nwritten = write (fd, buf, nleft);
#endif
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)
{
#ifdef HAVE_W32_SYSTEM
int n = recv (fd, buf, nleft, 0);
#else
int n = read (fd, buf, nleft);
#endif
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)
{
break; /* At least one full line available - that's enough.
This function is just a simple implementation, so
it is okay to forget about pending bytes. */
}
}
return nread;
}
/* Send an option to the agent */ /* Send an option to the agent */
static int static int
agent_send_option (int fd, const char *name, const char *value) agent_send_option (assuan_context_t ctx, const char *name, const char *value)
{ {
int err;
char buf[200]; char buf[200];
int nread; int nread;
char *line; char *line;
@ -188,28 +111,17 @@ agent_send_option (int fd, const char *name, const char *value)
return SPWQ_OUT_OF_CORE; return SPWQ_OUT_OF_CORE;
strcpy (stpcpy (stpcpy (stpcpy ( strcpy (stpcpy (stpcpy (stpcpy (
stpcpy (line, "OPTION "), name), "="), value), "\n"); stpcpy (line, "OPTION "), name), "="), value), "\n");
i = writen (fd, line, strlen (line));
err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
spwq_free (line); spwq_free (line);
if (i) return err;
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. */ /* Send all available options to the agent. */
static int static int
agent_send_all_options (int fd) agent_send_all_options (assuan_context_t ctx)
{ {
char *dft_display = NULL; char *dft_display = NULL;
char *dft_ttyname = NULL; char *dft_ttyname = NULL;
@ -221,7 +133,7 @@ agent_send_all_options (int fd)
dft_display = getenv ("DISPLAY"); dft_display = getenv ("DISPLAY");
if (dft_display) if (dft_display)
{ {
if ((rc = agent_send_option (fd, "display", dft_display))) if ((rc = agent_send_option (ctx, "display", dft_display)))
return rc; return rc;
} }
@ -232,14 +144,14 @@ agent_send_all_options (int fd)
#endif #endif
if (dft_ttyname && *dft_ttyname) if (dft_ttyname && *dft_ttyname)
{ {
if ((rc=agent_send_option (fd, "ttyname", dft_ttyname))) if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname)))
return rc; return rc;
} }
dft_ttytype = getenv ("TERM"); dft_ttytype = getenv ("TERM");
if (dft_ttyname && dft_ttytype) if (dft_ttyname && dft_ttytype)
{ {
if ((rc = agent_send_option (fd, "ttytype", dft_ttytype))) if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype)))
return rc; return rc;
} }
@ -260,7 +172,7 @@ agent_send_all_options (int fd)
} }
dft_lc = setlocale (LC_CTYPE, ""); dft_lc = setlocale (LC_CTYPE, "");
if (dft_ttyname && dft_lc) if (dft_ttyname && dft_lc)
rc = agent_send_option (fd, "lc-ctype", dft_lc); rc = agent_send_option (ctx, "lc-ctype", dft_lc);
if (old_lc) if (old_lc)
{ {
setlocale (LC_CTYPE, old_lc); setlocale (LC_CTYPE, old_lc);
@ -282,7 +194,7 @@ agent_send_all_options (int fd)
} }
dft_lc = setlocale (LC_MESSAGES, ""); dft_lc = setlocale (LC_MESSAGES, "");
if (dft_ttyname && dft_lc) if (dft_ttyname && dft_lc)
rc = agent_send_option (fd, "lc-messages", dft_lc); rc = agent_send_option (ctx, "lc-messages", dft_lc);
if (old_lc) if (old_lc)
{ {
setlocale (LC_MESSAGES, old_lc); setlocale (LC_MESSAGES, old_lc);
@ -300,7 +212,7 @@ agent_send_all_options (int fd)
{ {
/* We ignore errors here because older gpg-agents don't support /* We ignore errors here because older gpg-agents don't support
this option. */ this option. */
agent_send_option (fd, "xauthority", dft_xauthority); agent_send_option (ctx, "xauthority", dft_xauthority);
} }
/* Send the PINENTRY_USER_DATA variable. */ /* Send the PINENTRY_USER_DATA variable. */
@ -309,9 +221,14 @@ agent_send_all_options (int fd)
{ {
/* We ignore errors here because older gpg-agents don't support /* We ignore errors here because older gpg-agents don't support
this option. */ this option. */
agent_send_option (fd, "pinentry-user-data", dft_pinentry_user_data); agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data);
} }
/* Tell the agent that we support Pinentry notifications. No
error checking so that it will work with older agents. */
assuan_transact (ctx, "OPTION allow-pinentry-notify",
NULL, NULL, NULL, NULL, NULL, NULL);
return 0; return 0;
} }
@ -321,7 +238,7 @@ agent_send_all_options (int fd)
the file descriptor for the connection. Return -1 in case of the file descriptor for the connection. Return -1 in case of
error. */ error. */
static int static int
agent_open (int *rfd) agent_open (assuan_context_t *ctx)
{ {
int rc; int rc;
int fd; int fd;
@ -331,7 +248,6 @@ agent_open (int *rfd)
char line[200]; char line[200];
int nread; int nread;
*rfd = -1;
infostr = default_gpg_agent_info; infostr = default_gpg_agent_info;
if ( !infostr || !*infostr ) if ( !infostr || !*infostr )
{ {
@ -340,81 +256,35 @@ agent_open (int *rfd)
#endif #endif
return SPWQ_NO_AGENT; return SPWQ_NO_AGENT;
} }
p = spwq_malloc (strlen (infostr)+1);
if (!p)
return SPWQ_OUT_OF_CORE;
strcpy (p, infostr);
infostr = p;
if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr rc = assuan_new (ctx);
|| (p-infostr)+1 >= sizeof client_addr.sun_path ) if (rc)
{ return rc;
spwq_free (infostr);
return SPWQ_NO_AGENT;
}
*p++ = 0;
while (*p && *p != PATHSEP_C) rc = assuan_socket_connect (*ctx, infostr, 0, 0);
p++; if (rc)
#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)
{
#ifdef SPWQ_USE_LOGGING
log_error ("can't create socket: %s\n", strerror(errno) );
#endif
spwq_free (infostr);
return SPWQ_SYS_ERROR;
}
memset (&client_addr, 0, sizeof client_addr);
client_addr.sun_family = AF_UNIX;
strcpy (client_addr.sun_path, infostr);
spwq_free (infostr);
len = SUN_LEN (&client_addr);
#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)
{ {
#ifdef SPWQ_USE_LOGGING #ifdef SPWQ_USE_LOGGING
log_error (_("can't connect to '%s': %s\n"), log_error (_("can't connect to '%s': %s\n"),
client_addr.sun_path, strerror (errno)); infostr, gpg_strerror (rc));
#endif #endif
close (fd ); goto errout;
return SPWQ_IO_ERROR;
} }
nread = readline (fd, line, DIM(line)); rc = agent_send_all_options (*ctx);
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) if (rc)
{ {
#ifdef SPWQ_USE_LOGGING #ifdef SPWQ_USE_LOGGING
log_error (_("problem setting the gpg-agent options\n")); log_error (_("problem setting the gpg-agent options\n"));
#endif #endif
close (fd); goto errout;
return rc;
} }
*rfd = fd;
return 0; return 0;
errout:
assuan_release (*ctx);
*ctx = NULL;
} }
@ -451,17 +321,37 @@ int
simple_pw_set_socket (const char *name) simple_pw_set_socket (const char *name)
{ {
spwq_free (default_gpg_agent_info); spwq_free (default_gpg_agent_info);
default_gpg_agent_info = NULL;
if (name) if (name)
{ {
default_gpg_agent_info = spwq_malloc (strlen (name) + 4 + 1); default_gpg_agent_info = spwq_malloc (strlen (name) + 1);
if (!default_gpg_agent_info) if (!default_gpg_agent_info)
return SPWQ_OUT_OF_CORE; return SPWQ_OUT_OF_CORE;
/* We don't know the PID thus we use 0. */ strcpy (default_gpg_agent_info, name);
strcpy (stpcpy (default_gpg_agent_info, name), }
PATHSEP_S "0" PATHSEP_S "1");
return 0;
}
/* This is the default inquiry callback. It merely handles the
Pinentry notification. */
static gpg_error_t
default_inq_cb (void *opaque, const char *line)
{
(void)opaque;
if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
{
gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
/* We do not return errors to avoid breaking other code. */
} }
else else
default_gpg_agent_info = NULL; {
#ifdef SPWQ_USE_LOGGING
log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
#endif
}
return 0; return 0;
} }
@ -483,14 +373,15 @@ simple_pwquery (const char *cacheid,
int opt_check, int opt_check,
int *errorcode) int *errorcode)
{ {
int fd = -1; assuan_context_t ctx;
membuf_t data;
int nread; int nread;
char *result = NULL; char *result = NULL;
char *pw = NULL; char *pw = NULL;
char *p; char *p;
int rc, i; int rc, i;
rc = agent_open (&fd); rc = agent_open (&ctx);
if (rc) if (rc)
goto leave; goto leave;
@ -530,73 +421,43 @@ simple_pwquery (const char *cacheid,
*p++ = ' '; *p++ = ' ';
p = copy_and_escape (p, description); p = copy_and_escape (p, description);
*p++ = '\n'; *p++ = '\n';
rc = writen (fd, line, p - line);
init_membuf_secure (&data, 64);
rc = assuan_transact (ctx, line, put_membuf_cb, &data,
default_inq_cb, NULL, NULL, NULL);
spwq_free (line); spwq_free (line);
/* Older Pinentries return the old assuan error code for canceled
which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
not to the code for a user cancel. Fix this here. */
if (rc && gpg_err_source (rc)
&& gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
if (rc) if (rc)
goto leave; {
} void *p;
size_t n;
/* get response */ p = get_membuf (&data, &n);
pw = spwq_secure_malloc (500); if (p)
nread = readline (fd, pw, 499); wipememory (p, n);
if (nread < 0) spwq_free (p);
{
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;
}
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)) )
{
/* 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
*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;
}
} }
else else
{ {
#ifdef SPWQ_USE_LOGGING put_membuf (&data, "", 1);
log_error (_("problem with the agent\n")); result = get_membuf (&data, NULL);
#endif if (pw == NULL)
rc = SPWQ_ERR_RESPONSE; rc = gpg_error_from_syserror ();
}
} }
leave: leave:
if (errorcode) if (errorcode)
*errorcode = rc; *errorcode = rc;
if (fd != -1) assuan_release (ctx);
close (fd);
if (pw)
spwq_secure_free (pw);
return result; return result;
} }
@ -628,96 +489,17 @@ simple_pwclear (const char *cacheid)
int int
simple_query (const char *query) simple_query (const char *query)
{ {
int fd = -1; assuan_context_t ctx;
int nread;
char response[500]; char response[500];
int have = 0; int have = 0;
int rc; int rc;
rc = agent_open (&fd); rc = agent_open (&ctx);
if (rc) if (rc)
goto leave; return rc;
rc = writen (fd, query, strlen (query)); rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL);
if (rc)
goto leave;
while (1) assuan_release (ctx);
{
if (! have || ! strchr (response, '\n'))
/* get response */
{
nread = readline (fd, &response[have],
sizeof (response) - 1 /* NUL */ - have);
if (nread < 0)
{
rc = -nread;
goto leave;
}
have += nread;
if (have < 3)
{
rc = SPWQ_PROTOCOL_ERROR;
goto leave;
}
response[have] = 0;
}
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 if (response[0] == 'S' && response[1] == ' ')
{
char *nextline;
int consumed;
nextline = strchr (response, '\n');
if (! nextline)
/* Point to the NUL. */
nextline = &response[have];
else
/* Move past the \n. */
nextline ++;
consumed = (size_t) nextline - (size_t) response;
/* Skip any additional newlines. */
while (consumed < have && response[consumed] == '\n')
consumed ++;
have -= consumed;
if (have)
memmove (response, &response[consumed], have + 1);
continue;
}
else
{
#ifdef SPWQ_USE_LOGGING
log_error (_("problem with the agent (unexpected response \"%s\")\n"),
response);
#endif
rc = SPWQ_ERR_RESPONSE;
}
break;
}
leave:
if (fd != -1)
close (fd);
return rc; return rc;
} }

View File

@ -113,7 +113,7 @@ gpgparsemail_LDADD =
symcryptrun_SOURCES = symcryptrun.c symcryptrun_SOURCES = symcryptrun.c
symcryptrun_LDADD = $(LIBUTIL_LIBS) $(common_libs) $(pwquery_libs) \ symcryptrun_LDADD = $(LIBUTIL_LIBS) $(common_libs) $(pwquery_libs) \
$(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) \ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL) \
$(LIBICONV) $(NETLIBS) $(W32SOCKLIBS) $(LIBICONV) $(NETLIBS) $(W32SOCKLIBS) $(LIBASSUAN_LIBS)
watchgnupg_SOURCES = watchgnupg.c watchgnupg_SOURCES = watchgnupg.c
watchgnupg_LDADD = $(NETLIBS) watchgnupg_LDADD = $(NETLIBS)