* configure.ac: Require libgcrypt 1.1.94.
[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   char *p = buffer;
361
362   for (i=0; text[i]; i++)
363     {
364       if (text[i] < ' ' || text[i] == '+')
365         {
366           sprintf (p, "%%%02X", text[i]);
367           p += 3;
368         }
369       else if (text[i] == ' ')
370         *p++ = '+';
371       else
372         *p++ = text[i];
373     }
374   return p;
375 }
376
377
378 /* Ask the gpg-agent for a passphrase and present the user with a
379    DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
380    If a CACHEID is not NULL it is used to locate the passphrase in in
381    the cache and store it under this ID.  If ERRORCODE is not NULL it
382    should point a variable receiving an errorcode; thsi errocode might
383    be 0 if the user canceled the operation.  The function returns NULL
384    to indicate an error. */
385 char *
386 simple_pwquery (const char *cacheid, 
387                 const char *tryagain,
388                 const char *prompt,
389                 const char *description,
390                 int *errorcode)
391 {
392   int fd = -1;
393   int nread;
394   char *result = NULL;
395   char *pw = NULL;
396   char *p;
397   int rc, i; 
398
399   rc = agent_open (&fd);
400   if (rc)
401     goto leave;
402
403   if (!cacheid)
404     cacheid = "X";
405   if (!tryagain)
406     tryagain = "X";
407   if (!prompt)
408     prompt = "X";
409   if (!description)
410     description = "X";
411
412   {
413     char *line;
414     /* We allocate 3 times the needed space so that there is enough
415        space for escaping. */
416     line = spwq_malloc (15
417                         + 3*strlen (cacheid) + 1
418                         + 3*strlen (tryagain) + 1
419                         + 3*strlen (prompt) + 1
420                         + 3*strlen (description) + 1
421                         + 2);
422     if (!line)
423       {
424         rc = SPWQ_OUT_OF_CORE;
425         goto leave;
426       }
427     strcpy (line, "GET_PASSPHRASE ");
428     p = line+15;
429     p = copy_and_escape (p, cacheid);
430     *p++ = ' ';
431     p = copy_and_escape (p, tryagain);
432     *p++ = ' ';
433     p = copy_and_escape (p, prompt);
434     *p++ = ' ';
435     p = copy_and_escape (p, description);
436     *p++ = '\n';
437     rc = writen (fd, line, p - line);
438     spwq_free (line);
439     if (rc)
440       goto leave;
441   }
442
443   /* get response */
444   pw = spwq_secure_malloc (500);
445   nread = readline (fd, pw, 499);
446   if (nread < 0)
447     {
448       rc = -nread;
449       goto leave;
450     }
451   if (nread < 3)
452     {
453       rc = SPWQ_PROTOCOL_ERROR;
454       goto leave;
455     }
456       
457   if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
458     { /* we got a passphrase - convert it back from hex */
459       size_t pwlen = 0;
460       
461       for (i=3; i < nread && hexdigitp (pw+i); i+=2)
462         pw[pwlen++] = xtoi_2 (pw+i);
463       pw[pwlen] = 0; /* make a C String */
464       result = pw;
465       pw = NULL;
466     }
467   else if (nread > 7 && !memcmp (pw, "ERR 111", 7)
468       && (pw[7] == ' ' || pw[7] == '\n') )
469     {
470 #ifdef SPWQ_USE_LOGGING
471       log_info (_("canceled by user\n") );
472 #endif
473       *errorcode = 0; /* canceled */
474     }
475   else 
476     {
477 #ifdef SPWQ_USE_LOGGING
478       log_error (_("problem with the agent\n"));
479 #endif
480       rc = SPWQ_ERR_RESPONSE;
481     }
482         
483  leave:
484   if (errorcode)
485     *errorcode = rc;
486   if (fd != -1)
487     close (fd);
488   if (pw)
489     spwq_free (pw);
490   return result;
491 }