common: Rework the simple password query module.
[gnupg.git] / common / simple-pwquery.c
1 /* simple-pwquery.c - A simple password query client for gpg-agent
2  *      Copyright (C) 2002, 2004, 2007 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* This module is intended as a simple client implementation to
21    gpg-agent's GET_PASSPHRASE command.  It can only cope with an
22    already running gpg-agent.  Some stuff is configurable in the
23    header file. */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 #include <stdlib.h>
29 #include <stddef.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <assuan.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 GNUPG_COMMON_NEED_AFLOCAL
45 #include "../common/mischelp.h"
46 #include "sysutils.h"
47 #include "membuf.h"
48
49
50 #define SIMPLE_PWQUERY_IMPLEMENTATION 1
51 #include "simple-pwquery.h"
52
53 #define SPWQ_OUT_OF_CORE        gpg_error_from_errno (ENOMEM)
54 #define SPWQ_IO_ERROR           gpg_error_from_errno (EIO)
55 #define SPWQ_PROTOCOL_ERROR     gpg_error (GPG_ERR_PROTOCOL_VIOLATION)
56 #define SPWQ_ERR_RESPONSE       gpg_error (GPG_ERR_INV_RESPONSE)
57 #define SPWQ_NO_AGENT           gpg_error (GPG_ERR_NO_AGENT)
58 #define SPWQ_SYS_ERROR          gpg_error_from_syserror ()
59 #define SPWQ_GENERAL_ERROR      gpg_error (GPG_ERR_GENERAL)
60 #define SPWQ_NO_PIN_ENTRY       gpg_error (GPG_ERR_NO_PIN_ENTRY)
61
62 #ifndef _
63 #define _(a) (a)
64 #endif
65
66 #if !defined (hexdigitp) && !defined (xtoi_2)
67 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
68 #define hexdigitp(a) (digitp (a)                     \
69                       || (*(a) >= 'A' && *(a) <= 'F')  \
70                       || (*(a) >= 'a' && *(a) <= 'f'))
71 #define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
72                      *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
73 #define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
74 #endif
75
76
77 /* Name of the socket to be used.  This is a kludge to keep on using
78    the existsing code despite that we only support a standard socket.  */
79 static char *default_gpg_agent_info;
80
81
82
83 \f
84
85 #ifndef HAVE_STPCPY
86 static char *
87 my_stpcpy(char *a,const char *b)
88 {
89     while( *b )
90         *a++ = *b++;
91     *a = 0;
92
93     return (char*)a;
94 }
95 #define stpcpy(a,b)  my_stpcpy((a), (b))
96 #endif
97
98
99 /* Send an option to the agent */
100 static int
101 agent_send_option (assuan_context_t ctx, const char *name, const char *value)
102 {
103   int err;
104   char buf[200];
105   int nread;
106   char *line;
107   int i;
108
109   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
110   if (!line)
111     return SPWQ_OUT_OF_CORE;
112   strcpy (stpcpy (stpcpy (stpcpy (
113                      stpcpy (line, "OPTION "), name), "="), value), "\n");
114
115   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
116
117   spwq_free (line);
118   return err;
119 }
120
121
122 /* Send all available options to the agent. */
123 static int
124 agent_send_all_options (assuan_context_t ctx)
125 {
126   char *dft_display = NULL;
127   char *dft_ttyname = NULL;
128   char *dft_ttytype = NULL;
129   char *dft_xauthority = NULL;
130   char *dft_pinentry_user_data = NULL;
131   int rc = 0;
132
133   dft_display = getenv ("DISPLAY");
134   if (dft_display)
135     {
136       if ((rc = agent_send_option (ctx, "display", dft_display)))
137         return rc;
138     }
139
140   dft_ttyname = getenv ("GPG_TTY");
141 #if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME)
142   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
143     dft_ttyname = ttyname (0);
144 #endif
145   if (dft_ttyname && *dft_ttyname)
146     {
147       if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname)))
148         return rc;
149     }
150
151   dft_ttytype = getenv ("TERM");
152   if (dft_ttyname && dft_ttytype)
153     {
154       if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype)))
155         return rc;
156     }
157
158 #if defined(HAVE_SETLOCALE)
159   {
160     char *old_lc = NULL;
161     char *dft_lc = NULL;
162
163 #if defined(LC_CTYPE)
164     old_lc = setlocale (LC_CTYPE, NULL);
165     if (old_lc)
166       {
167         char *p = spwq_malloc (strlen (old_lc)+1);
168         if (!p)
169           return SPWQ_OUT_OF_CORE;
170         strcpy (p, old_lc);
171         old_lc = p;
172       }
173     dft_lc = setlocale (LC_CTYPE, "");
174     if (dft_ttyname && dft_lc)
175       rc = agent_send_option (ctx, "lc-ctype", dft_lc);
176     if (old_lc)
177       {
178         setlocale (LC_CTYPE, old_lc);
179         spwq_free (old_lc);
180       }
181     if (rc)
182       return rc;
183 #endif
184
185 #if defined(LC_MESSAGES)
186     old_lc = setlocale (LC_MESSAGES, NULL);
187     if (old_lc)
188       {
189         char *p = spwq_malloc (strlen (old_lc)+1);
190         if (!p)
191           return SPWQ_OUT_OF_CORE;
192         strcpy (p, old_lc);
193         old_lc = p;
194       }
195     dft_lc = setlocale (LC_MESSAGES, "");
196     if (dft_ttyname && dft_lc)
197       rc = agent_send_option (ctx, "lc-messages", dft_lc);
198     if (old_lc)
199       {
200         setlocale (LC_MESSAGES, old_lc);
201         spwq_free (old_lc);
202       }
203     if (rc)
204       return rc;
205 #endif
206   }
207 #endif /*HAVE_SETLOCALE*/
208
209   /* Send the XAUTHORITY variable.  */
210   dft_xauthority = getenv ("XAUTHORITY");
211   if (dft_xauthority)
212     {
213       /* We ignore errors here because older gpg-agents don't support
214          this option.  */
215       agent_send_option (ctx, "xauthority", dft_xauthority);
216     }
217
218   /* Send the PINENTRY_USER_DATA variable.  */
219   dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
220   if (dft_pinentry_user_data)
221     {
222       /* We ignore errors here because older gpg-agents don't support
223          this option.  */
224       agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data);
225     }
226
227   /* Tell the agent that we support Pinentry notifications.  No
228      error checking so that it will work with older agents.  */
229   assuan_transact (ctx, "OPTION allow-pinentry-notify",
230                    NULL, NULL, NULL, NULL, NULL, NULL);
231
232   return 0;
233 }
234
235
236
237 /* Try to open a connection to the agent, send all options and return
238    the file descriptor for the connection.  Return -1 in case of
239    error. */
240 static int
241 agent_open (assuan_context_t *ctx)
242 {
243   int rc;
244   int fd;
245   char *infostr, *p;
246   struct sockaddr_un client_addr;
247   size_t len;
248   char line[200];
249   int nread;
250
251   infostr = default_gpg_agent_info;
252   if ( !infostr || !*infostr )
253     {
254 #ifdef SPWQ_USE_LOGGING
255       log_error (_("no gpg-agent running in this session\n"));
256 #endif
257       return SPWQ_NO_AGENT;
258     }
259
260   rc = assuan_new (ctx);
261   if (rc)
262     return rc;
263
264   rc = assuan_socket_connect (*ctx, infostr, 0, 0);
265   if (rc)
266     {
267 #ifdef SPWQ_USE_LOGGING
268       log_error (_("can't connect to '%s': %s\n"),
269                  infostr, gpg_strerror (rc));
270 #endif
271       goto errout;
272     }
273
274   rc = agent_send_all_options (*ctx);
275   if (rc)
276     {
277 #ifdef SPWQ_USE_LOGGING
278       log_error (_("problem setting the gpg-agent options\n"));
279 #endif
280       goto errout;
281     }
282
283   return 0;
284
285  errout:
286   assuan_release (*ctx);
287   *ctx = NULL;
288 }
289
290
291 /* Copy text to BUFFER and escape as required.  Return a pointer to
292    the end of the new buffer.  Note that BUFFER must be large enough
293    to keep the entire text; allocataing it 3 times the size of TEXT
294    is sufficient. */
295 static char *
296 copy_and_escape (char *buffer, const char *text)
297 {
298   int i;
299   const unsigned char *s = (unsigned char *)text;
300   char *p = buffer;
301
302
303   for (i=0; s[i]; i++)
304     {
305       if (s[i] < ' ' || s[i] == '+')
306         {
307           sprintf (p, "%%%02X", s[i]);
308           p += 3;
309         }
310       else if (s[i] == ' ')
311         *p++ = '+';
312       else
313         *p++ = s[i];
314     }
315   return p;
316 }
317
318
319 /* Set the name of the default socket to NAME.  */
320 int
321 simple_pw_set_socket (const char *name)
322 {
323   spwq_free (default_gpg_agent_info);
324   default_gpg_agent_info = NULL;
325   if (name)
326     {
327       default_gpg_agent_info = spwq_malloc (strlen (name) + 1);
328       if (!default_gpg_agent_info)
329         return SPWQ_OUT_OF_CORE;
330       strcpy (default_gpg_agent_info, name);
331     }
332
333   return 0;
334 }
335
336
337 /* This is the default inquiry callback.  It merely handles the
338    Pinentry notification.  */
339 static gpg_error_t
340 default_inq_cb (void *opaque, const char *line)
341 {
342   (void)opaque;
343
344   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
345     {
346       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
347       /* We do not return errors to avoid breaking other code.  */
348     }
349   else
350     {
351 #ifdef SPWQ_USE_LOGGING
352       log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
353 #endif
354     }
355
356   return 0;
357 }
358
359
360 /* Ask the gpg-agent for a passphrase and present the user with a
361    DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
362    If a CACHEID is not NULL it is used to locate the passphrase in in
363    the cache and store it under this ID.  If OPT_CHECK is true
364    gpg-agent is asked to apply some checks on the passphrase security.
365    If ERRORCODE is not NULL it should point a variable receiving an
366    errorcode; this error code might be 0 if the user canceled the
367    operation.  The function returns NULL to indicate an error.  */
368 char *
369 simple_pwquery (const char *cacheid,
370                 const char *tryagain,
371                 const char *prompt,
372                 const char *description,
373                 int opt_check,
374                 int *errorcode)
375 {
376   assuan_context_t ctx;
377   membuf_t data;
378   int nread;
379   char *result = NULL;
380   char *pw = NULL;
381   char *p;
382   int rc, i;
383
384   rc = agent_open (&ctx);
385   if (rc)
386     goto leave;
387
388   if (!cacheid)
389     cacheid = "X";
390   if (!tryagain)
391     tryagain = "X";
392   if (!prompt)
393     prompt = "X";
394   if (!description)
395     description = "X";
396
397   {
398     char *line;
399     /* We allocate 3 times the needed space so that there is enough
400        space for escaping. */
401     line = spwq_malloc (15 + 10
402                         + 3*strlen (cacheid) + 1
403                         + 3*strlen (tryagain) + 1
404                         + 3*strlen (prompt) + 1
405                         + 3*strlen (description) + 1
406                         + 2);
407     if (!line)
408       {
409         rc = SPWQ_OUT_OF_CORE;
410         goto leave;
411       }
412     strcpy (line, "GET_PASSPHRASE ");
413     p = line+15;
414     if (opt_check)
415       p = stpcpy (p, "--check ");
416     p = copy_and_escape (p, cacheid);
417     *p++ = ' ';
418     p = copy_and_escape (p, tryagain);
419     *p++ = ' ';
420     p = copy_and_escape (p, prompt);
421     *p++ = ' ';
422     p = copy_and_escape (p, description);
423     *p++ = '\n';
424
425     init_membuf_secure (&data, 64);
426
427     rc = assuan_transact (ctx, line, put_membuf_cb, &data,
428                           default_inq_cb, NULL, NULL, NULL);
429     spwq_free (line);
430
431     /* Older Pinentries return the old assuan error code for canceled
432        which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
433        not to the code for a user cancel.  Fix this here. */
434     if (rc && gpg_err_source (rc)
435         && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
436       rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
437
438     if (rc)
439       {
440         void *p;
441         size_t n;
442
443         p = get_membuf (&data, &n);
444         if (p)
445           wipememory (p, n);
446         spwq_free (p);
447       }
448     else
449       {
450         put_membuf (&data, "", 1);
451         result = get_membuf (&data, NULL);
452         if (pw == NULL)
453           rc = gpg_error_from_syserror ();
454       }
455   }
456
457  leave:
458   if (errorcode)
459     *errorcode = rc;
460   assuan_release (ctx);
461   return result;
462 }
463
464
465 /* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID.  */
466 int
467 simple_pwclear (const char *cacheid)
468 {
469   char line[500];
470   char *p;
471
472   /* We need not more than 50 characters for the command and the
473      terminating nul.  */
474   if (strlen (cacheid) * 3 > sizeof (line) - 50)
475     return SPWQ_PROTOCOL_ERROR;
476
477   strcpy (line, "CLEAR_PASSPHRASE ");
478   p = line + 17;
479   p = copy_and_escape (p, cacheid);
480   *p++ = '\n';
481   *p++ = '\0';
482
483   return simple_query (line);
484 }
485
486
487 /* Perform the simple query QUERY (which must be new-line and 0
488    terminated) and return the error code.  */
489 int
490 simple_query (const char *query)
491 {
492   assuan_context_t ctx;
493   char response[500];
494   int have = 0;
495   int rc;
496
497   rc = agent_open (&ctx);
498   if (rc)
499     return rc;
500
501   rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL);
502
503   assuan_release (ctx);
504   return rc;
505 }