* configure.ac: Give warning when using capabilities. Check for
[gnupg.git] / agent / simple-pwquery.c
1 /* simple-pwquery.c - A simple password query client 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 ) 
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
277   if ( !(p = strchr ( infostr, ':')) || p == infostr
278        || (p-infostr)+1 >= sizeof client_addr.sun_path ) 
279     {
280 #ifdef SPWQ_USE_LOGGING
281       log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
282 #endif
283       return SPWQ_NO_AGENT;
284     }
285   *p++ = 0;
286
287   while (*p && *p != ':')
288     p++;
289   prot = *p? atoi (p+1) : 0;
290   if ( prot != 1)
291     {
292 #ifdef SPWQ_USE_LOGGING
293       log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
294 #endif
295       return SPWQ_PROTOCOL_ERROR;
296     }
297        
298   if( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) 
299     {
300 #ifdef SPWQ_USE_LOGGING
301       log_error ("can't create socket: %s\n", strerror(errno) );
302 #endif
303       return SPWQ_SYS_ERROR;
304     }
305     
306   memset (&client_addr, 0, sizeof client_addr);
307   client_addr.sun_family = AF_UNIX;
308   strcpy (client_addr.sun_path, infostr);
309   len = (offsetof (struct sockaddr_un, sun_path)
310          + strlen(client_addr.sun_path) + 1);
311     
312   if (connect (fd, (struct sockaddr*)&client_addr, len ) == -1)
313     {
314 #ifdef SPWQ_USE_LOGGING
315       log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
316 #endif
317       close (fd );
318       return SPWQ_IO_ERROR;
319     }
320
321   nread = readline (fd, line, DIM(line));
322   if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
323                      && (line[2] == '\n' || line[2] == ' ')) ) 
324     {
325 #ifdef SPWQ_USE_LOGGING
326       log_error ( _("communication problem with gpg-agent\n"));
327 #endif
328       close (fd );
329       return SPWQ_PROTOCOL_ERROR;
330     }
331
332   rc = agent_send_all_options (fd);
333   if (rc)
334     {
335 #ifdef SPWQ_USE_LOGGING
336       log_error (_("problem setting the gpg-agent options\n"));
337 #endif
338       close (fd);
339       return rc;
340     }
341
342   *rfd = fd;
343   return 0;
344 }
345
346
347 /* Copy text to BUFFER and escape as required.  Return a poiinter to
348    the end of the new buffer.  NOte that BUFFER must be large enough
349    to keep the entire text; allocataing it 3 times the size of TEXT
350    is sufficient. */
351 static char *
352 copy_and_escape (char *buffer, const char *text)
353 {
354   int i;
355   char *p = buffer;
356
357   for (i=0; text[i]; i++)
358     {
359       if (text[i] < ' ' || text[i] == '+')
360         {
361           sprintf (p, "%%%02X", text[i]);
362           p += 3;
363         }
364       else if (text[i] == ' ')
365         *p++ = '+';
366       else
367         *p++ = text[i];
368     }
369   return p;
370 }
371
372
373 /* Ask the gpg-agent for a passphrase and present the user with a
374    DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
375    If a CACHEID is not NULL it is used to locate the passphrase in in
376    the cache and store it under this ID.  If ERRORCODE is not NULL it
377    should point a variable receiving an errorcode; thsi errocode might
378    be 0 if the user canceled the operation.  The function returns NULL
379    to indicate an error. */
380 char *
381 simple_pwquery (const char *cacheid, 
382                 const char *tryagain,
383                 const char *prompt,
384                 const char *description,
385                 int *errorcode)
386 {
387   int fd = -1;
388   int nread;
389   char *result = NULL;
390   char *pw = NULL;
391   char *p;
392   int rc, i; 
393
394   rc = agent_open (&fd);
395   if (rc)
396     goto leave;
397
398   if (!cacheid)
399     cacheid = "X";
400   if (!tryagain)
401     tryagain = "X";
402   if (!prompt)
403     prompt = "X";
404   if (!description)
405     description = "X";
406
407   {
408     char *line;
409     /* We allocate 3 times the needed space so that there is enough
410        space for escaping. */
411     line = spwq_malloc (15
412                         + 3*strlen (cacheid) + 1
413                         + 3*strlen (tryagain) + 1
414                         + 3*strlen (prompt) + 1
415                         + 3*strlen (description) + 1
416                         + 2);
417     if (!line)
418       {
419         rc = SPWQ_OUT_OF_CORE;
420         goto leave;
421       }
422     strcpy (line, "GET_PASSPHRASE ");
423     p = line+15;
424     p = copy_and_escape (p, cacheid);
425     *p++ = ' ';
426     p = copy_and_escape (p, tryagain);
427     *p++ = ' ';
428     p = copy_and_escape (p, prompt);
429     *p++ = ' ';
430     p = copy_and_escape (p, description);
431     *p++ = '\n';
432     rc = writen (fd, line, p - line);
433     spwq_free (line);
434     if (rc)
435       goto leave;
436   }
437
438   /* get response */
439   pw = spwq_secure_malloc (500);
440   nread = readline (fd, pw, 499);
441   if (nread < 0)
442     {
443       rc = -nread;
444       goto leave;
445     }
446   if (nread < 3)
447     {
448       rc = SPWQ_PROTOCOL_ERROR;
449       goto leave;
450     }
451       
452   if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ') 
453     { /* we got a passphrase - convert it back from hex */
454       size_t pwlen = 0;
455       
456       for (i=3; i < nread && hexdigitp (pw+i); i+=2)
457         pw[pwlen++] = xtoi_2 (pw+i);
458       pw[pwlen] = 0; /* make a C String */
459       result = pw;
460       pw = NULL;
461     }
462   else if (nread > 7 && !memcmp (pw, "ERR 111", 7)
463       && (pw[7] == ' ' || pw[7] == '\n') )
464     {
465 #ifdef SPWQ_USE_LOGGING
466       log_info (_("canceled by user\n") );
467 #endif
468       *errorcode = 0; /* canceled */
469     }
470   else 
471     {
472 #ifdef SPWQ_USE_LOGGING
473       log_error (_("problem with the agent\n"));
474 #endif
475       rc = SPWQ_ERR_RESPONSE;
476     }
477         
478  leave:
479   if (errorcode)
480     *errorcode = rc;
481   if (fd != -1)
482     close (fd);
483   if (pw)
484     spwq_free (pw);
485   return result;
486 }