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