1
/* simple-pwquery.c - A simple password query cleint for gpg-agent
2
* Copyright (C) 2002, 2004 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* GnuPG is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
21
/* This module is intended as a standalone client implementation to
22
gpg-agent's GET_PASSPHRASE command. In particular it does not use
23
the Assuan library and can only cope with an already running
24
gpg-agent. Some stuff is configurable in the header file. */
34
#ifdef HAVE_W32_SYSTEM
37
#include <sys/socket.h>
43
#ifdef HAVE_W32_SYSTEM
44
#include "../jnlib/w32-afunix.h"
48
#define SIMPLE_PWQUERY_IMPLEMENTATION 1
49
#include "simple-pwquery.h"
51
#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
52
# undef SPWQ_USE_LOGGING
59
#if !defined (hexdigitp) && !defined (xtoi_2)
60
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
61
#define hexdigitp(a) (digitp (a) \
62
|| (*(a) >= 'A' && *(a) <= 'F') \
63
|| (*(a) >= 'a' && *(a) <= 'f'))
64
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
65
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
66
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
76
my_stpcpy(char *a,const char *b)
84
#define stpcpy(a,b) my_stpcpy((a), (b))
89
/* Write NBYTES of BUF to file descriptor FD. */
91
writen (int fd, const void *buf, size_t nbytes)
93
size_t nleft = nbytes;
98
#ifdef HAVE_W32_SYSTEM
99
nwritten = send (fd, buf, nleft, 0);
101
nwritten = write (fd, buf, nleft);
108
#ifdef SPWQ_USE_LOGGING
109
log_error ("write failed: %s\n", strerror (errno));
111
return SPWQ_IO_ERROR;
115
buf = (const char*)buf + nwritten;
122
/* Read an entire line and return number of bytes read. */
124
readline (int fd, char *buf, size_t buflen)
126
size_t nleft = buflen;
132
#ifdef HAVE_W32_SYSTEM
133
int n = recv (fd, buf, nleft, 0);
135
int n = read (fd, buf, nleft);
141
return -(SPWQ_IO_ERROR);
145
return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
152
for (; n && *p != '\n'; n--, p++)
156
break; /* at least one full line available - that's enough.
157
This function is just a simple implementation, so
158
it is okay to forget about pending bytes */
166
/* Send an option to the agent */
168
agent_send_option (int fd, const char *name, const char *value)
175
line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
177
return SPWQ_OUT_OF_CORE;
178
strcpy (stpcpy (stpcpy (stpcpy (
179
stpcpy (line, "OPTION "), name), "="), value), "\n");
180
i = writen (fd, line, strlen (line));
186
nread = readline (fd, buf, DIM(buf)-1);
190
return SPWQ_PROTOCOL_ERROR;
192
if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n'))
195
return SPWQ_ERR_RESPONSE;
199
/* Send all available options to the agent. */
201
agent_send_all_options (int fd)
203
char *dft_display = NULL;
204
char *dft_ttyname = NULL;
205
char *dft_ttytype = NULL;
208
dft_display = getenv ("DISPLAY");
211
if ((rc = agent_send_option (fd, "display", dft_display)))
215
dft_ttyname = getenv ("GPG_TTY");
216
#ifndef HAVE_W32_SYSTEM
217
if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
218
dft_ttyname = ttyname (0);
220
if (dft_ttyname && *dft_ttyname)
222
if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
226
dft_ttytype = getenv ("TERM");
227
if (dft_ttyname && dft_ttytype)
229
if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
233
#if defined(HAVE_SETLOCALE)
238
#if defined(LC_CTYPE)
239
old_lc = setlocale (LC_CTYPE, NULL);
242
char *p = spwq_malloc (strlen (old_lc)+1);
244
return SPWQ_OUT_OF_CORE;
248
dft_lc = setlocale (LC_CTYPE, "");
249
if (dft_ttyname && dft_lc)
250
rc = agent_send_option (fd, "lc-ctype", dft_lc);
253
setlocale (LC_CTYPE, old_lc);
260
#if defined(LC_MESSAGES)
261
old_lc = setlocale (LC_MESSAGES, NULL);
264
char *p = spwq_malloc (strlen (old_lc)+1);
266
return SPWQ_OUT_OF_CORE;
270
dft_lc = setlocale (LC_MESSAGES, "");
271
if (dft_ttyname && dft_lc)
272
rc = agent_send_option (fd, "lc-messages", dft_lc);
275
setlocale (LC_MESSAGES, old_lc);
282
#endif /*HAVE_SETLOCALE*/
289
/* Try to open a connection to the agent, send all options and return
290
the file descriptor for the connection. Return -1 in case of
293
agent_open (int *rfd)
298
struct sockaddr_un client_addr;
305
infostr = getenv ( "GPG_AGENT_INFO" );
306
if ( !infostr || !*infostr )
308
#ifdef SPWQ_USE_LOGGING
309
log_error (_("gpg-agent is not available in this session\n"));
311
return SPWQ_NO_AGENT;
313
p = spwq_malloc (strlen (infostr)+1);
315
return SPWQ_OUT_OF_CORE;
319
if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr
320
|| (p-infostr)+1 >= sizeof client_addr.sun_path )
322
#ifdef SPWQ_USE_LOGGING
323
log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
325
return SPWQ_NO_AGENT;
329
while (*p && *p != PATHSEP_C)
331
prot = *p? atoi (p+1) : 0;
334
#ifdef SPWQ_USE_LOGGING
335
log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
337
return SPWQ_PROTOCOL_ERROR;
340
#ifdef HAVE_W32_SYSTEM
341
fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
343
fd = socket (AF_UNIX, SOCK_STREAM, 0);
347
#ifdef SPWQ_USE_LOGGING
348
log_error ("can't create socket: %s\n", strerror(errno) );
350
return SPWQ_SYS_ERROR;
353
memset (&client_addr, 0, sizeof client_addr);
354
client_addr.sun_family = AF_UNIX;
355
strcpy (client_addr.sun_path, infostr);
356
len = (offsetof (struct sockaddr_un, sun_path)
357
+ strlen(client_addr.sun_path) + 1);
359
#ifdef HAVE_W32_SYSTEM
360
rc = _w32_sock_connect (fd, (struct sockaddr*)&client_addr, len );
362
rc = connect (fd, (struct sockaddr*)&client_addr, len );
366
#ifdef SPWQ_USE_LOGGING
367
log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
370
return SPWQ_IO_ERROR;
373
nread = readline (fd, line, DIM(line));
374
if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
375
&& (line[2] == '\n' || line[2] == ' ')) )
377
#ifdef SPWQ_USE_LOGGING
378
log_error ( _("communication problem with gpg-agent\n"));
381
return SPWQ_PROTOCOL_ERROR;
384
rc = agent_send_all_options (fd);
387
#ifdef SPWQ_USE_LOGGING
388
log_error (_("problem setting the gpg-agent options\n"));
399
/* Copy text to BUFFER and escape as required. Return a pointer to
400
the end of the new buffer. Note that BUFFER must be large enough
401
to keep the entire text; allocataing it 3 times the size of TEXT
404
copy_and_escape (char *buffer, const char *text)
407
const unsigned char *s = text;
413
if (s[i] < ' ' || s[i] == '+')
415
sprintf (p, "%%%02X", s[i]);
418
else if (s[i] == ' ')
427
/* Ask the gpg-agent for a passphrase and present the user with a
428
DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
429
If a CACHEID is not NULL it is used to locate the passphrase in in
430
the cache and store it under this ID. If ERRORCODE is not NULL it
431
should point a variable receiving an errorcode; thsi errocode might
432
be 0 if the user canceled the operation. The function returns NULL
433
to indicate an error. */
435
simple_pwquery (const char *cacheid,
436
const char *tryagain,
438
const char *description,
448
rc = agent_open (&fd);
463
/* We allocate 3 times the needed space so that there is enough
464
space for escaping. */
465
line = spwq_malloc (15
466
+ 3*strlen (cacheid) + 1
467
+ 3*strlen (tryagain) + 1
468
+ 3*strlen (prompt) + 1
469
+ 3*strlen (description) + 1
473
rc = SPWQ_OUT_OF_CORE;
476
strcpy (line, "GET_PASSPHRASE ");
478
p = copy_and_escape (p, cacheid);
480
p = copy_and_escape (p, tryagain);
482
p = copy_and_escape (p, prompt);
484
p = copy_and_escape (p, description);
486
rc = writen (fd, line, p - line);
493
pw = spwq_secure_malloc (500);
494
nread = readline (fd, pw, 499);
502
rc = SPWQ_PROTOCOL_ERROR;
506
if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ')
507
{ /* we got a passphrase - convert it back from hex */
510
for (i=3; i < nread && hexdigitp (pw+i); i+=2)
511
pw[pwlen++] = xtoi_2 (pw+i);
512
pw[pwlen] = 0; /* make a C String */
516
else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
517
&& (pw[7] == ' ' || pw[7] == '\n') )
518
|| ((nread > 4 && !memcmp (pw, "ERR ", 4)
519
&& (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) )
521
/* 111 is the old Assuan code for canceled which might still
522
be in use by old installations. 99 is GPG_ERR_CANCELED as
523
used by modern gpg-agents; 0xffff is used to mask out the
525
#ifdef SPWQ_USE_LOGGING
526
log_info (_("canceled by user\n") );
528
*errorcode = 0; /* canceled */
532
#ifdef SPWQ_USE_LOGGING
533
log_error (_("problem with the agent\n"));
535
rc = SPWQ_ERR_RESPONSE;
549
/* Perform the simple query QUERY (which must be new-line and 0
550
terminated) and return the error code. */
552
simple_query (const char *query)
559
rc = agent_open (&fd);
563
rc = writen (fd, query, strlen (query));
568
nread = readline (fd, response, 499);
576
rc = SPWQ_PROTOCOL_ERROR;
580
if (response[0] == 'O' && response[1] == 'K')
581
/* OK, do nothing. */;
582
else if ((nread > 7 && !memcmp (response, "ERR 111", 7)
583
&& (response[7] == ' ' || response[7] == '\n') )
584
|| ((nread > 4 && !memcmp (response, "ERR ", 4)
585
&& (strtoul (response+4, NULL, 0) & 0xffff) == 99)) )
587
/* 111 is the old Assuan code for canceled which might still
588
be in use by old installations. 99 is GPG_ERR_CANCELED as
589
used by modern gpg-agents; 0xffff is used to mask out the
591
#ifdef SPWQ_USE_LOGGING
592
log_info (_("canceled by user\n") );
597
#ifdef SPWQ_USE_LOGGING
598
log_error (_("problem with the agent\n"));
600
rc = SPWQ_ERR_RESPONSE;