A whole bunch of changes to allow building for W32.
[gnupg.git] / common / simple-pwquery.c
1 /* simple-pwquery.c - A simple password query cleint for gpg-agent
2  *      Copyright (C) 2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
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. */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29 #include <stdlib.h>
30 #include <stddef.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #ifdef HAVE_W32_SYSTEM
35 #include <winsock2.h>
36 #else
37 #include <sys/socket.h>
38 #include <sys/un.h>
39 #endif
40 #ifdef HAVE_LOCALE_H
41 #include <locale.h>
42 #endif
43
44 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
45 #include "simple-pwquery.h"
46
47 #if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
48 # undef SPWQ_USE_LOGGING
49 #endif
50
51 #ifndef _
52 #define _(a) (a)
53 #endif
54
55 #if !defined (hexdigitp) && !defined (xtoi_2)
56 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
57 #define hexdigitp(a) (digitp (a)                     \
58                       || (*(a) >= 'A' && *(a) <= 'F')  \
59                       || (*(a) >= 'a' && *(a) <= 'f'))
60 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
61                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
62 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
63 #endif
64
65
66 /* Write NBYTES of BUF to file descriptor FD. */
67 static int
68 writen (int fd, const void *buf, size_t nbytes)
69 {
70   size_t nleft = nbytes;
71   int nwritten;
72   
73   while (nleft > 0)
74     {
75       nwritten = write( fd, buf, nleft );
76       if (nwritten < 0)
77         {
78           if (errno == EINTR)
79             nwritten = 0;
80           else {
81 #ifdef SPWQ_USE_LOGGING
82             log_error ("write failed: %s\n", strerror (errno));
83 #endif
84             return SPWQ_IO_ERROR;
85           }
86         }
87       nleft -= nwritten;
88       buf = (const char*)buf + nwritten;
89     }
90     
91   return 0;
92 }
93
94
95 /* Read an entire line and return number of bytes read. */
96 static int
97 readline (int fd, char *buf, size_t buflen)
98 {
99   size_t nleft = buflen;
100   char *p;
101   int nread = 0;
102
103   while (nleft > 0)
104     {
105       int n = read (fd, buf, nleft);
106       if (n < 0)
107         {
108           if (errno == EINTR)
109             continue;
110           return -(SPWQ_IO_ERROR);
111         }
112       else if (!n)
113         {
114           return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
115         }
116       p = buf;
117       nleft -= n;
118       buf += n;
119       nread += n;
120       
121       for (; n && *p != '\n'; n--, p++)
122         ;
123       if (n)
124         {
125           break; /* at least one full line available - that's enough.
126                     This function is just a simple implementation, so
127                     it is okay to forget about pending bytes */
128         }
129     }
130
131   return nread; 
132 }
133
134
135 /* Send an option to the agent */
136 static int
137 agent_send_option (int fd, const char *name, const char *value)
138 {
139   char buf[200];
140   int nread;
141   char *line;
142   int i; 
143   
144   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
145   if (!line)
146     return SPWQ_OUT_OF_CORE;
147   strcpy (stpcpy (stpcpy (stpcpy (
148                      stpcpy (line, "OPTION "), name), "="), value), "\n");
149   i = writen (fd, line, strlen (line));
150   spwq_free (line);
151   if (i)
152     return i;
153   
154   /* get response */
155   nread = readline (fd, buf, DIM(buf)-1);
156   if (nread < 0)
157     return -nread;
158   if (nread < 3)
159     return SPWQ_PROTOCOL_ERROR;
160   
161   if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n')) 
162     return 0; /* okay */
163
164   return SPWQ_ERR_RESPONSE;
165 }
166
167
168 /* Send all available options to the agent. */
169 static int 
170 agent_send_all_options (int fd)
171 {
172   char *dft_display = NULL;
173   char *dft_ttyname = NULL;
174   char *dft_ttytype = NULL;
175   int rc = 0;
176
177   dft_display = getenv ("DISPLAY");
178   if (dft_display)
179     {
180       if ((rc = agent_send_option (fd, "display", dft_display)))
181         return rc;
182     }
183
184   dft_ttyname = getenv ("GPG_TTY");
185   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
186     dft_ttyname = ttyname (0);
187   if (dft_ttyname && *dft_ttyname)
188     {
189       if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
190         return rc;
191     }
192
193   dft_ttytype = getenv ("TERM");
194   if (dft_ttyname && dft_ttytype)
195     {
196       if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
197         return rc;
198     }
199
200 #if defined(HAVE_SETLOCALE) 
201   {
202     char *old_lc = NULL;
203     char *dft_lc = NULL;
204
205 #if defined(LC_CTYPE)
206     old_lc = setlocale (LC_CTYPE, NULL);
207     if (old_lc)
208       {
209         char *p = spwq_malloc (strlen (old_lc)+1);
210         if (!p)
211           return SPWQ_OUT_OF_CORE;
212         strcpy (p, old_lc);
213         old_lc = p;
214       }
215     dft_lc = setlocale (LC_CTYPE, "");
216     if (dft_ttyname && dft_lc)
217       rc = agent_send_option (fd, "lc-ctype", dft_lc);
218     if (old_lc)
219       {
220         setlocale (LC_CTYPE, old_lc);
221         spwq_free (old_lc);
222       }
223     if (rc)
224       return rc;
225 #endif
226
227 #if defined(LC_MESSAGES)
228     old_lc = setlocale (LC_MESSAGES, NULL);
229     if (old_lc)
230       {
231         char *p = spwq_malloc (strlen (old_lc)+1);
232         if (!p)
233           return SPWQ_OUT_OF_CORE;
234         strcpy (p, old_lc);
235         old_lc = p;
236       }
237     dft_lc = setlocale (LC_MESSAGES, "");
238     if (dft_ttyname && dft_lc)
239       rc = agent_send_option (fd, "lc-messages", dft_lc);
240     if (old_lc)
241       {
242         setlocale (LC_MESSAGES, old_lc);
243         spwq_free (old_lc);
244       }
245     if (rc)
246       return rc;
247 #endif
248   }
249 #endif /*HAVE_SETLOCALE*/
250
251   return 0;
252 }
253
254
255
256 /* Try to open a connection to the agent, send all options and return
257    the file descriptor for the connection.  Return -1 in case of
258    error. */
259 static int
260 agent_open (int *rfd)
261 {
262 #ifdef HAVE_W32_SYSTEM
263   return SPWQ_NO_AGENT;  /* FIXME */
264 #else
265   int rc;
266   int fd;
267   char *infostr, *p;
268   struct sockaddr_un client_addr;
269   size_t len;
270   int prot;
271   char line[200];
272   int nread;
273
274   *rfd = -1;
275   infostr = getenv ( "GPG_AGENT_INFO" );
276   if ( !infostr || !*infostr ) 
277     {
278 #ifdef SPWQ_USE_LOGGING
279       log_error (_("gpg-agent is not available in this session\n"));
280 #endif
281       return SPWQ_NO_AGENT;
282     }
283   p = spwq_malloc (strlen (infostr)+1);
284   if (!p)
285     return SPWQ_OUT_OF_CORE;
286   strcpy (p, infostr);
287   infostr = p;
288
289   if ( !(p = strchr ( infostr, ':')) || p == infostr
290        || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
291     {
292 #ifdef SPWQ_USE_LOGGING
293       log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
294 #endif
295       return SPWQ_NO_AGENT;
296     }
297   *p++ = 0;
298
299   while (*p && *p != ':')
300     p++;
301   prot = *p? atoi (p+1) : 0;
302   if ( prot != 1)
303     {
304 #ifdef SPWQ_USE_LOGGING
305       log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
306 #endif
307       return SPWQ_PROTOCOL_ERROR;
308     }
309        
310   if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) 
311     {
312 #ifdef SPWQ_USE_LOGGING
313       log_error ("can't create socket: %s\n", strerror(errno) );
314 #endif
315       return SPWQ_SYS_ERROR;
316     }
317     
318   memset (&client_addr, 0, sizeof client_addr);
319   client_addr.sun_family = AF_UNIX;
320   strcpy (client_addr.sun_path, infostr);
321   len = (offsetof (struct sockaddr_un, sun_path)
322          + strlen(client_addr.sun_path) + 1);
323     
324   if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1)
325     {
326 #ifdef SPWQ_USE_LOGGING
327       log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
328 #endif
329       close (fd );
330       return SPWQ_IO_ERROR;
331     }
332
333   nread = readline (fd, line, DIM(line));
334   if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
335                      && (line[2] == '\n' || line[2] == ' ')) ) 
336     {
337 #ifdef SPWQ_USE_LOGGING
338       log_error ( _("communication problem with gpg-agent\n"));
339 #endif
340       close (fd );
341       return SPWQ_PROTOCOL_ERROR;
342     }
343
344   rc = agent_send_all_options (fd);
345   if (rc)
346     {
347 #ifdef SPWQ_USE_LOGGING
348       log_error (_("problem setting the gpg-agent options\n"));
349 #endif
350       close (fd);
351       return rc;
352     }
353
354   *rfd = fd;
355   return 0;
356 #endif
357 }
358
359
360 /* Copy text to BUFFER and escape as required.  Return a pointer to
361    the end of the new buffer.  NOte that BUFFER must be large enough
362    to keep the entire text; allocataing it 3 times the size of TEXT
363    is sufficient. */
364 static char *
365 copy_and_escape (char *buffer, const char *text)
366 {
367   int i;
368   const unsigned char *s = text;
369   char *p = buffer;
370   
371
372   for (i=0; s[i]; i++)
373     {
374       if (s[i] < ' ' || s[i] == '+')
375         {
376           sprintf (p, "%%%02X", s[i]);
377           p += 3;
378         }
379       else if (s[i] == ' ')
380         *p++ = '+';
381       else
382         *p++ = s[i];
383     }
384   return p;
385 }
386
387
388 /* Ask the gpg-agent for a passphrase and present the user with a
389    DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
390    If a CACHEID is not NULL it is used to locate the passphrase in in
391    the cache and store it under this ID.  If ERRORCODE is not NULL it
392    should point a variable receiving an errorcode; thsi errocode might
393    be 0 if the user canceled the operation.  The function returns NULL
394    to indicate an error. */
395 char *
396 simple_pwquery (const char *cacheid, 
397                 const char *tryagain,
398                 const char *prompt,
399                 const char *description,
400                 int *errorcode)
401 {
402   int fd = -1;
403   int nread;
404   char *result = NULL;
405   char *pw = NULL;
406   char *p;
407   int rc, i; 
408
409   rc = agent_open (&fd);
410   if (rc)
411     goto leave;
412
413   if (!cacheid)
414     cacheid = "X";
415   if (!tryagain)
416     tryagain = "X";
417   if (!prompt)
418     prompt = "X";
419   if (!description)
420     description = "X";
421
422   {
423     char *line;
424     /* We allocate 3 times the needed space so that there is enough
425        space for escaping. */
426     line = spwq_malloc (15
427                         + 3*strlen (cacheid) + 1
428                         + 3*strlen (tryagain) + 1
429                         + 3*strlen (prompt) + 1
430                         + 3*strlen (description) + 1
431                         + 2);
432     if (!line)
433       {
434         rc = SPWQ_OUT_OF_CORE;
435         goto leave;
436       }
437     strcpy (line, "GET_PASSPHRASE ");
438     p = line+15;
439     p = copy_and_escape (p, cacheid);
440     *p++ = ' ';
441     p = copy_and_escape (p, tryagain);
442     *p++ = ' ';
443     p = copy_and_escape (p, prompt);
444     *p++ = ' ';
445     p = copy_and_escape (p, description);
446     *p++ = '\n';
447     rc = writen (fd, line, p - line);
448     spwq_free (line);
449     if (rc)
450       goto leave;
451   }
452
453   /* get response */
454   pw = spwq_secure_malloc (500);
455   nread = readline (fd, pw, 499);
456   if (nread < 0)
457     {
458       rc = -nread;
459       goto leave;
460     }
461   if (nread < 3)
462     {
463       rc = SPWQ_PROTOCOL_ERROR;
464       goto leave;
465     }
466       
467   if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
468     { /* we got a passphrase - convert it back from hex */
469       size_t pwlen = 0;
470       
471       for (i=3; i < nread && hexdigitp (pw+i); i+=2)
472         pw[pwlen++] = xtoi_2 (pw+i);
473       pw[pwlen] = 0; /* make a C String */
474       result = pw;
475       pw = NULL;
476     }
477   else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
478             && (pw[7] == ' ' || pw[7] == '\n') )
479            || ((nread > 4 && !memcmp (pw, "ERR ", 4)
480                 && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) ) 
481     {
482       /* 111 is the old Assuan code for canceled which might still
483          be in use by old installations. 99 is GPG_ERR_CANCELED as
484          used by modern gpg-agents; 0xffff is used to mask out the
485          error source.  */
486 #ifdef SPWQ_USE_LOGGING
487       log_info (_("canceled by user\n") );
488 #endif
489       *errorcode = 0; /* canceled */
490     }
491   else 
492     {
493 #ifdef SPWQ_USE_LOGGING
494       log_error (_("problem with the agent\n"));
495 #endif
496       rc = SPWQ_ERR_RESPONSE;
497     }
498         
499  leave:
500   if (errorcode)
501     *errorcode = rc;
502   if (fd != -1)
503     close (fd);
504   if (pw)
505     spwq_free (pw);
506   return result;
507 }