agent/
[gnupg.git] / common / simple-pwquery.c
1 /* simple-pwquery.c - A simple password query client for gpg-agent
2  *      Copyright (C) 2002, 2004 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 /* This module is intended as a standalone client implementation to
23    gpg-agent's GET_PASSPHRASE command.  In particular it does not use
24    the Assuan library and can only cope with an already running
25    gpg-agent.  Some stuff is configurable in the header file. */
26
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 #include <stdlib.h>
31 #include <stddef.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #ifdef HAVE_W32_SYSTEM
36 #include <winsock2.h>
37 #else
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #endif
41 #ifdef HAVE_LOCALE_H
42 #include <locale.h>
43 #endif
44 #ifdef HAVE_W32_SYSTEM
45 #include "../jnlib/w32-afunix.h"
46 #endif
47
48
49 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
50 #include "simple-pwquery.h"
51
52 #if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
53 # undef SPWQ_USE_LOGGING
54 #endif
55
56 #ifndef _
57 #define _(a) (a)
58 #endif
59
60 #if !defined (hexdigitp) && !defined (xtoi_2)
61 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
62 #define hexdigitp(a) (digitp (a)                     \
63                       || (*(a) >= 'A' && *(a) <= 'F')  \
64                       || (*(a) >= 'a' && *(a) <= 'f'))
65 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
66                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
67 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
68 #endif
69
70
71
72
73 \f
74
75 #ifndef HAVE_STPCPY
76 static char *
77 my_stpcpy(char *a,const char *b)
78 {
79     while( *b )
80         *a++ = *b++;
81     *a = 0;
82
83     return (char*)a;
84 }
85 #define stpcpy(a,b)  my_stpcpy((a), (b))
86 #endif
87
88
89
90 /* Write NBYTES of BUF to file descriptor FD. */
91 static int
92 writen (int fd, const void *buf, size_t nbytes)
93 {
94   size_t nleft = nbytes;
95   int nwritten;
96   
97   while (nleft > 0)
98     {
99 #ifdef HAVE_W32_SYSTEM
100       nwritten = send (fd, buf, nleft, 0);
101 #else
102       nwritten = write (fd, buf, nleft);
103 #endif
104       if (nwritten < 0)
105         {
106           if (errno == EINTR)
107             nwritten = 0;
108           else {
109 #ifdef SPWQ_USE_LOGGING
110             log_error ("write failed: %s\n", strerror (errno));
111 #endif
112             return SPWQ_IO_ERROR;
113           }
114         }
115       nleft -= nwritten;
116       buf = (const char*)buf + nwritten;
117     }
118     
119   return 0;
120 }
121
122
123 /* Read an entire line and return number of bytes read. */
124 static int
125 readline (int fd, char *buf, size_t buflen)
126 {
127   size_t nleft = buflen;
128   char *p;
129   int nread = 0;
130
131   while (nleft > 0)
132     {
133 #ifdef HAVE_W32_SYSTEM
134       int n = recv (fd, buf, nleft, 0);
135 #else
136       int n = read (fd, buf, nleft);
137 #endif
138       if (n < 0)
139         {
140           if (errno == EINTR)
141             continue;
142           return -(SPWQ_IO_ERROR);
143         }
144       else if (!n)
145         {
146           return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
147         }
148       p = buf;
149       nleft -= n;
150       buf += n;
151       nread += n;
152       
153       for (; n && *p != '\n'; n--, p++)
154         ;
155       if (n)
156         {
157           break; /* at least one full line available - that's enough.
158                     This function is just a simple implementation, so
159                     it is okay to forget about pending bytes */
160         }
161     }
162
163   return nread; 
164 }
165
166
167 /* Send an option to the agent */
168 static int
169 agent_send_option (int fd, const char *name, const char *value)
170 {
171   char buf[200];
172   int nread;
173   char *line;
174   int i; 
175   
176   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
177   if (!line)
178     return SPWQ_OUT_OF_CORE;
179   strcpy (stpcpy (stpcpy (stpcpy (
180                      stpcpy (line, "OPTION "), name), "="), value), "\n");
181   i = writen (fd, line, strlen (line));
182   spwq_free (line);
183   if (i)
184     return i;
185   
186   /* get response */
187   nread = readline (fd, buf, DIM(buf)-1);
188   if (nread < 0)
189     return -nread;
190   if (nread < 3)
191     return SPWQ_PROTOCOL_ERROR;
192   
193   if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) 
194     return 0; /* okay */
195
196   return SPWQ_ERR_RESPONSE;
197 }
198
199
200 /* Send all available options to the agent. */
201 static int 
202 agent_send_all_options (int fd)
203 {
204   char *dft_display = NULL;
205   char *dft_ttyname = NULL;
206   char *dft_ttytype = NULL;
207   int rc = 0;
208
209   dft_display = getenv ("DISPLAY");
210   if (dft_display)
211     {
212       if ((rc = agent_send_option (fd, "display", dft_display)))
213         return rc;
214     }
215
216   dft_ttyname = getenv ("GPG_TTY");
217 #ifndef HAVE_W32_SYSTEM
218   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
219     dft_ttyname = ttyname (0);
220 #endif
221   if (dft_ttyname && *dft_ttyname)
222     {
223       if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
224         return rc;
225     }
226
227   dft_ttytype = getenv ("TERM");
228   if (dft_ttyname && dft_ttytype)
229     {
230       if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
231         return rc;
232     }
233
234 #if defined(HAVE_SETLOCALE) 
235   {
236     char *old_lc = NULL;
237     char *dft_lc = NULL;
238
239 #if defined(LC_CTYPE)
240     old_lc = setlocale (LC_CTYPE, NULL);
241     if (old_lc)
242       {
243         char *p = spwq_malloc (strlen (old_lc)+1);
244         if (!p)
245           return SPWQ_OUT_OF_CORE;
246         strcpy (p, old_lc);
247         old_lc = p;
248       }
249     dft_lc = setlocale (LC_CTYPE, "");
250     if (dft_ttyname && dft_lc)
251       rc = agent_send_option (fd, "lc-ctype", dft_lc);
252     if (old_lc)
253       {
254         setlocale (LC_CTYPE, old_lc);
255         spwq_free (old_lc);
256       }
257     if (rc)
258       return rc;
259 #endif
260
261 #if defined(LC_MESSAGES)
262     old_lc = setlocale (LC_MESSAGES, NULL);
263     if (old_lc)
264       {
265         char *p = spwq_malloc (strlen (old_lc)+1);
266         if (!p)
267           return SPWQ_OUT_OF_CORE;
268         strcpy (p, old_lc);
269         old_lc = p;
270       }
271     dft_lc = setlocale (LC_MESSAGES, "");
272     if (dft_ttyname && dft_lc)
273       rc = agent_send_option (fd, "lc-messages", dft_lc);
274     if (old_lc)
275       {
276         setlocale (LC_MESSAGES, old_lc);
277         spwq_free (old_lc);
278       }
279     if (rc)
280       return rc;
281 #endif
282   }
283 #endif /*HAVE_SETLOCALE*/
284
285   return 0;
286 }
287
288
289
290 /* Try to open a connection to the agent, send all options and return
291    the file descriptor for the connection.  Return -1 in case of
292    error. */
293 static int
294 agent_open (int *rfd)
295 {
296   int rc;
297   int fd;
298   char *infostr, *p;
299   struct sockaddr_un client_addr;
300   size_t len;
301   int prot;
302   char line[200];
303   int nread;
304
305   *rfd = -1;
306   infostr = getenv ( "GPG_AGENT_INFO" );
307   if ( !infostr || !*infostr ) 
308     {
309 #ifdef SPWQ_USE_LOGGING
310       log_error (_("gpg-agent is not available in this session\n"));
311 #endif
312       return SPWQ_NO_AGENT;
313     }
314   p = spwq_malloc (strlen (infostr)+1);
315   if (!p)
316     return SPWQ_OUT_OF_CORE;
317   strcpy (p, infostr);
318   infostr = p;
319
320   if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr
321        || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
322     {
323 #ifdef SPWQ_USE_LOGGING
324       log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
325 #endif
326       return SPWQ_NO_AGENT;
327     }
328   *p++ = 0;
329
330   while (*p && *p != PATHSEP_C)
331     p++;
332   prot = *p? atoi (p+1) : 0;
333   if ( prot != 1)
334     {
335 #ifdef SPWQ_USE_LOGGING
336       log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
337 #endif
338       return SPWQ_PROTOCOL_ERROR;
339     }
340
341 #ifdef HAVE_W32_SYSTEM       
342   fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
343 #else
344   fd = socket (AF_UNIX, SOCK_STREAM, 0);
345 #endif
346   if (fd == -1) 
347     {
348 #ifdef SPWQ_USE_LOGGING
349       log_error ("can't create socket: %s\n", strerror(errno) );
350 #endif
351       return SPWQ_SYS_ERROR;
352     }
353     
354   memset (&client_addr, 0, sizeof client_addr);
355   client_addr.sun_family = AF_UNIX;
356   strcpy (client_addr.sun_path, infostr);
357   len = (offsetof (struct sockaddr_un, sun_path)
358          + strlen(client_addr.sun_path) + 1);
359     
360 #ifdef HAVE_W32_SYSTEM       
361   rc = _w32_sock_connect (fd, (struct sockaddr*)&client_addr, len );
362 #else
363   rc = connect (fd, (struct sockaddr*)&client_addr, len );
364 #endif
365   if (rc == -1)
366     {
367 #ifdef SPWQ_USE_LOGGING
368       log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
369 #endif
370       close (fd );
371       return SPWQ_IO_ERROR;
372     }
373
374   nread = readline (fd, line, DIM(line));
375   if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
376                      && (line[2] == '\n' || line[2] == ' ')) ) 
377     {
378 #ifdef SPWQ_USE_LOGGING
379       log_error ( _("communication problem with gpg-agent\n"));
380 #endif
381       close (fd );
382       return SPWQ_PROTOCOL_ERROR;
383     }
384
385   rc = agent_send_all_options (fd);
386   if (rc)
387     {
388 #ifdef SPWQ_USE_LOGGING
389       log_error (_("problem setting the gpg-agent options\n"));
390 #endif
391       close (fd);
392       return rc;
393     }
394
395   *rfd = fd;
396   return 0;
397 }
398
399
400 /* Copy text to BUFFER and escape as required.  Return a pointer to
401    the end of the new buffer.  Note that BUFFER must be large enough
402    to keep the entire text; allocataing it 3 times the size of TEXT
403    is sufficient. */
404 static char *
405 copy_and_escape (char *buffer, const char *text)
406 {
407   int i;
408   const unsigned char *s = (unsigned char *)text;
409   char *p = buffer;
410   
411
412   for (i=0; s[i]; i++)
413     {
414       if (s[i] < ' ' || s[i] == '+')
415         {
416           sprintf (p, "%%%02X", s[i]);
417           p += 3;
418         }
419       else if (s[i] == ' ')
420         *p++ = '+';
421       else
422         *p++ = s[i];
423     }
424   return p;
425 }
426
427
428 /* Ask the gpg-agent for a passphrase and present the user with a
429    DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
430    If a CACHEID is not NULL it is used to locate the passphrase in in
431    the cache and store it under this ID.  If OPT_CHECK is true
432    gpg-agent is asked to apply some checks on the passphrase security.
433    If ERRORCODE is not NULL it should point a variable receiving an
434    errorcode; this errocode might be 0 if the user canceled the
435    operation.  The function returns NULL to indicate an error.  */
436 char *
437 simple_pwquery (const char *cacheid, 
438                 const char *tryagain,
439                 const char *prompt,
440                 const char *description,
441                 int opt_check,
442                 int *errorcode)
443 {
444   int fd = -1;
445   int nread;
446   char *result = NULL;
447   char *pw = NULL;
448   char *p;
449   int rc, i; 
450
451   rc = agent_open (&fd);
452   if (rc)
453     goto leave;
454
455   if (!cacheid)
456     cacheid = "X";
457   if (!tryagain)
458     tryagain = "X";
459   if (!prompt)
460     prompt = "X";
461   if (!description)
462     description = "X";
463
464   {
465     char *line;
466     /* We allocate 3 times the needed space so that there is enough
467        space for escaping. */
468     line = spwq_malloc (15 + 10
469                         + 3*strlen (cacheid) + 1
470                         + 3*strlen (tryagain) + 1
471                         + 3*strlen (prompt) + 1
472                         + 3*strlen (description) + 1
473                         + 2);
474     if (!line)
475       {
476         rc = SPWQ_OUT_OF_CORE;
477         goto leave;
478       }
479     strcpy (line, "GET_PASSPHRASE ");
480     p = line+15;
481     if (opt_check)
482       p = stpcpy (p, "--check ");
483     p = copy_and_escape (p, cacheid);
484     *p++ = ' ';
485     p = copy_and_escape (p, tryagain);
486     *p++ = ' ';
487     p = copy_and_escape (p, prompt);
488     *p++ = ' ';
489     p = copy_and_escape (p, description);
490     *p++ = '\n';
491     rc = writen (fd, line, p - line);
492     spwq_free (line);
493     if (rc)
494       goto leave;
495   }
496
497   /* get response */
498   pw = spwq_secure_malloc (500);
499   nread = readline (fd, pw, 499);
500   if (nread < 0)
501     {
502       rc = -nread;
503       goto leave;
504     }
505   if (nread < 3)
506     {
507       rc = SPWQ_PROTOCOL_ERROR;
508       goto leave;
509     }
510       
511   if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
512     { /* we got a passphrase - convert it back from hex */
513       size_t pwlen = 0;
514       
515       for (i=3; i < nread && hexdigitp (pw+i); i+=2)
516         pw[pwlen++] = xtoi_2 (pw+i);
517       pw[pwlen] = 0; /* make a C String */
518       result = pw;
519       pw = NULL;
520     }
521   else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
522             && (pw[7] == ' ' || pw[7] == '\n') )
523            || ((nread > 4 && !memcmp (pw, "ERR ", 4)
524                 && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) ) 
525     {
526       /* 111 is the old Assuan code for canceled which might still
527          be in use by old installations. 99 is GPG_ERR_CANCELED as
528          used by modern gpg-agents; 0xffff is used to mask out the
529          error source.  */
530 #ifdef SPWQ_USE_LOGGING
531       log_info (_("canceled by user\n") );
532 #endif
533       *errorcode = 0; /* canceled */
534     }
535   else 
536     {
537 #ifdef SPWQ_USE_LOGGING
538       log_error (_("problem with the agent\n"));
539 #endif
540       rc = SPWQ_ERR_RESPONSE;
541     }
542         
543  leave:
544   if (errorcode)
545     *errorcode = rc;
546   if (fd != -1)
547     close (fd);
548   if (pw)
549     spwq_secure_free (pw);
550   return result;
551 }
552
553
554 /* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID.  */
555 int
556 simple_pwclear (const char *cacheid)
557 {
558   char line[500];
559   char *p;
560
561   /* We need not more than 50 characters for the command and the
562      terminating nul.  */
563   if (strlen (cacheid) * 3 > sizeof (line) - 50)
564     return SPWQ_PROTOCOL_ERROR;
565
566   strcpy (line, "CLEAR_PASSPHRASE ");
567   p = line + 17;
568   p = copy_and_escape (p, cacheid);
569   *p++ = '\n';
570   *p++ = '\0';
571
572   return simple_query (line);
573 }
574
575
576 /* Perform the simple query QUERY (which must be new-line and 0
577    terminated) and return the error code.  */
578 int
579 simple_query (const char *query)
580 {
581   int fd = -1;
582   int nread;
583   char response[500];
584   int rc;
585
586   rc = agent_open (&fd);
587   if (rc)
588     goto leave;
589
590   rc = writen (fd, query, strlen (query));
591   if (rc)
592     goto leave;
593
594   /* get response */
595   nread = readline (fd, response, 499);
596   if (nread < 0)
597     {
598       rc = -nread;
599       goto leave;
600     }
601   if (nread < 3)
602     {
603       rc = SPWQ_PROTOCOL_ERROR;
604       goto leave;
605     }
606   
607   if (response[0] == 'O' && response[1] == 'K') 
608     /* OK, do nothing.  */;
609   else if ((nread > 7 && !memcmp (response, "ERR 111", 7)
610             && (response[7] == ' ' || response[7] == '\n') )
611            || ((nread > 4 && !memcmp (response, "ERR ", 4)
612                 && (strtoul (response+4, NULL, 0) & 0xffff) == 99)) ) 
613     {
614       /* 111 is the old Assuan code for canceled which might still
615          be in use by old installations. 99 is GPG_ERR_CANCELED as
616          used by modern gpg-agents; 0xffff is used to mask out the
617          error source.  */
618 #ifdef SPWQ_USE_LOGGING
619       log_info (_("canceled by user\n") );
620 #endif
621     }
622   else 
623     {
624 #ifdef SPWQ_USE_LOGGING
625       log_error (_("problem with the agent\n"));
626 #endif
627       rc = SPWQ_ERR_RESPONSE;
628     }
629         
630  leave:
631   if (fd != -1)
632     close (fd);
633   return rc;
634 }