chiark / gitweb /
Import gnupg2_2.1.17.orig.tar.bz2
[gnupg2.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 <https://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 *line;
105
106   line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
107   if (!line)
108     return SPWQ_OUT_OF_CORE;
109   strcpy (stpcpy (stpcpy (stpcpy (
110                      stpcpy (line, "OPTION "), name), "="), value), "\n");
111
112   err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
113
114   spwq_free (line);
115   return err;
116 }
117
118
119 /* Send all available options to the agent. */
120 static int
121 agent_send_all_options (assuan_context_t ctx)
122 {
123   char *dft_display = NULL;
124   char *dft_ttyname = NULL;
125   char *dft_ttytype = NULL;
126   char *dft_xauthority = NULL;
127   char *dft_pinentry_user_data = NULL;
128   int rc = 0;
129
130   dft_display = getenv ("DISPLAY");
131   if (dft_display)
132     {
133       if ((rc = agent_send_option (ctx, "display", dft_display)))
134         return rc;
135     }
136
137   dft_ttyname = getenv ("GPG_TTY");
138 #if !defined(HAVE_W32_SYSTEM) && !defined(HAVE_BROKEN_TTYNAME)
139   if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
140     dft_ttyname = ttyname (0);
141 #endif
142   if (dft_ttyname && *dft_ttyname)
143     {
144       if ((rc=agent_send_option (ctx, "ttyname", dft_ttyname)))
145         return rc;
146     }
147
148   dft_ttytype = getenv ("TERM");
149   if (dft_ttyname && dft_ttytype)
150     {
151       if ((rc = agent_send_option (ctx, "ttytype", dft_ttytype)))
152         return rc;
153     }
154
155 #if defined(HAVE_SETLOCALE)
156   {
157     char *old_lc = NULL;
158     char *dft_lc = NULL;
159
160 #if defined(LC_CTYPE)
161     old_lc = setlocale (LC_CTYPE, NULL);
162     if (old_lc)
163       {
164         char *p = spwq_malloc (strlen (old_lc)+1);
165         if (!p)
166           return SPWQ_OUT_OF_CORE;
167         strcpy (p, old_lc);
168         old_lc = p;
169       }
170     dft_lc = setlocale (LC_CTYPE, "");
171     if (dft_ttyname && dft_lc)
172       rc = agent_send_option (ctx, "lc-ctype", dft_lc);
173     if (old_lc)
174       {
175         setlocale (LC_CTYPE, old_lc);
176         spwq_free (old_lc);
177       }
178     if (rc)
179       return rc;
180 #endif
181
182 #if defined(LC_MESSAGES)
183     old_lc = setlocale (LC_MESSAGES, NULL);
184     if (old_lc)
185       {
186         char *p = spwq_malloc (strlen (old_lc)+1);
187         if (!p)
188           return SPWQ_OUT_OF_CORE;
189         strcpy (p, old_lc);
190         old_lc = p;
191       }
192     dft_lc = setlocale (LC_MESSAGES, "");
193     if (dft_ttyname && dft_lc)
194       rc = agent_send_option (ctx, "lc-messages", dft_lc);
195     if (old_lc)
196       {
197         setlocale (LC_MESSAGES, old_lc);
198         spwq_free (old_lc);
199       }
200     if (rc)
201       return rc;
202 #endif
203   }
204 #endif /*HAVE_SETLOCALE*/
205
206   /* Send the XAUTHORITY variable.  */
207   dft_xauthority = getenv ("XAUTHORITY");
208   if (dft_xauthority)
209     {
210       /* We ignore errors here because older gpg-agents don't support
211          this option.  */
212       agent_send_option (ctx, "xauthority", dft_xauthority);
213     }
214
215   /* Send the PINENTRY_USER_DATA variable.  */
216   dft_pinentry_user_data = getenv ("PINENTRY_USER_DATA");
217   if (dft_pinentry_user_data)
218     {
219       /* We ignore errors here because older gpg-agents don't support
220          this option.  */
221       agent_send_option (ctx, "pinentry-user-data", dft_pinentry_user_data);
222     }
223
224   /* Tell the agent that we support Pinentry notifications.  No
225      error checking so that it will work with older agents.  */
226   assuan_transact (ctx, "OPTION allow-pinentry-notify",
227                    NULL, NULL, NULL, NULL, NULL, NULL);
228
229   return 0;
230 }
231
232
233
234 /* Try to open a connection to the agent, send all options and return
235    the file descriptor for the connection.  Return -1 in case of
236    error. */
237 static int
238 agent_open (assuan_context_t *ctx)
239 {
240   int rc;
241   char *infostr;
242
243   infostr = default_gpg_agent_info;
244   if ( !infostr || !*infostr )
245     {
246 #ifdef SPWQ_USE_LOGGING
247       log_error (_("no gpg-agent running in this session\n"));
248 #endif
249       return SPWQ_NO_AGENT;
250     }
251
252   rc = assuan_new (ctx);
253   if (rc)
254     return rc;
255
256   rc = assuan_socket_connect (*ctx, infostr, 0, 0);
257   if (rc)
258     {
259 #ifdef SPWQ_USE_LOGGING
260       log_error (_("can't connect to '%s': %s\n"),
261                  infostr, gpg_strerror (rc));
262 #endif
263       goto errout;
264     }
265
266   rc = agent_send_all_options (*ctx);
267   if (rc)
268     {
269 #ifdef SPWQ_USE_LOGGING
270       log_error (_("problem setting the gpg-agent options\n"));
271 #endif
272       goto errout;
273     }
274
275   return 0;
276
277  errout:
278   assuan_release (*ctx);
279   *ctx = NULL;
280   return rc;
281 }
282
283
284 /* Copy text to BUFFER and escape as required.  Return a pointer to
285    the end of the new buffer.  Note that BUFFER must be large enough
286    to keep the entire text; allocataing it 3 times the size of TEXT
287    is sufficient. */
288 static char *
289 copy_and_escape (char *buffer, const char *text)
290 {
291   int i;
292   const unsigned char *s = (unsigned char *)text;
293   char *p = buffer;
294
295
296   for (i=0; s[i]; i++)
297     {
298       if (s[i] < ' ' || s[i] == '+')
299         {
300           sprintf (p, "%%%02X", s[i]);
301           p += 3;
302         }
303       else if (s[i] == ' ')
304         *p++ = '+';
305       else
306         *p++ = s[i];
307     }
308   return p;
309 }
310
311
312 /* Set the name of the default socket to NAME.  */
313 int
314 simple_pw_set_socket (const char *name)
315 {
316   spwq_free (default_gpg_agent_info);
317   default_gpg_agent_info = NULL;
318   if (name)
319     {
320       default_gpg_agent_info = spwq_malloc (strlen (name) + 1);
321       if (!default_gpg_agent_info)
322         return SPWQ_OUT_OF_CORE;
323       strcpy (default_gpg_agent_info, name);
324     }
325
326   return 0;
327 }
328
329
330 /* This is the default inquiry callback.  It merely handles the
331    Pinentry notification.  */
332 static gpg_error_t
333 default_inq_cb (void *opaque, const char *line)
334 {
335   (void)opaque;
336
337   if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17]))
338     {
339       gnupg_allow_set_foregound_window ((pid_t)strtoul (line+17, NULL, 10));
340       /* We do not return errors to avoid breaking other code.  */
341     }
342   else
343     {
344 #ifdef SPWQ_USE_LOGGING
345       log_debug ("ignoring gpg-agent inquiry '%s'\n", line);
346 #endif
347     }
348
349   return 0;
350 }
351
352
353 /* Ask the gpg-agent for a passphrase and present the user with a
354    DESCRIPTION, a PROMPT and optionally with a TRYAGAIN extra text.
355    If a CACHEID is not NULL it is used to locate the passphrase in in
356    the cache and store it under this ID.  If OPT_CHECK is true
357    gpg-agent is asked to apply some checks on the passphrase security.
358    If ERRORCODE is not NULL it should point a variable receiving an
359    errorcode; this error code might be 0 if the user canceled the
360    operation.  The function returns NULL to indicate an error.  */
361 char *
362 simple_pwquery (const char *cacheid,
363                 const char *tryagain,
364                 const char *prompt,
365                 const char *description,
366                 int opt_check,
367                 int *errorcode)
368 {
369   int rc;
370   assuan_context_t ctx;
371   membuf_t data;
372   char *result = NULL;
373   char *pw = NULL;
374   char *p;
375   size_t n;
376
377
378   rc = agent_open (&ctx);
379   if (rc)
380     goto leave;
381
382   if (!cacheid)
383     cacheid = "X";
384   if (!tryagain)
385     tryagain = "X";
386   if (!prompt)
387     prompt = "X";
388   if (!description)
389     description = "X";
390
391   {
392     char *line;
393     /* We allocate 3 times the needed space so that there is enough
394        space for escaping. */
395     line = spwq_malloc (15 + 10
396                         + 3*strlen (cacheid) + 1
397                         + 3*strlen (tryagain) + 1
398                         + 3*strlen (prompt) + 1
399                         + 3*strlen (description) + 1
400                         + 2);
401     if (!line)
402       {
403         rc = SPWQ_OUT_OF_CORE;
404         goto leave;
405       }
406     strcpy (line, "GET_PASSPHRASE ");
407     p = line+15;
408     if (opt_check)
409       p = stpcpy (p, "--check ");
410     p = copy_and_escape (p, cacheid);
411     *p++ = ' ';
412     p = copy_and_escape (p, tryagain);
413     *p++ = ' ';
414     p = copy_and_escape (p, prompt);
415     *p++ = ' ';
416     p = copy_and_escape (p, description);
417     *p++ = '\n';
418
419     init_membuf_secure (&data, 64);
420
421     rc = assuan_transact (ctx, line, put_membuf_cb, &data,
422                           default_inq_cb, NULL, NULL, NULL);
423     spwq_free (line);
424
425     /* Older Pinentries return the old assuan error code for canceled
426        which gets translated by libassuan to GPG_ERR_ASS_CANCELED and
427        not to the code for a user cancel.  Fix this here. */
428     if (rc && gpg_err_source (rc)
429         && gpg_err_code (rc) == GPG_ERR_ASS_CANCELED)
430       rc = gpg_err_make (gpg_err_source (rc), GPG_ERR_CANCELED);
431
432     if (rc)
433       {
434         p = get_membuf (&data, &n);
435         if (p)
436           wipememory (p, n);
437         spwq_free (p);
438       }
439     else
440       {
441         put_membuf (&data, "", 1);
442         result = get_membuf (&data, NULL);
443         if (pw == NULL)
444           rc = gpg_error_from_syserror ();
445       }
446   }
447
448  leave:
449   if (errorcode)
450     *errorcode = rc;
451   assuan_release (ctx);
452   return result;
453 }
454
455
456 /* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID.  */
457 int
458 simple_pwclear (const char *cacheid)
459 {
460   char line[500];
461   char *p;
462
463   /* We need not more than 50 characters for the command and the
464      terminating nul.  */
465   if (strlen (cacheid) * 3 > sizeof (line) - 50)
466     return SPWQ_PROTOCOL_ERROR;
467
468   strcpy (line, "CLEAR_PASSPHRASE ");
469   p = line + 17;
470   p = copy_and_escape (p, cacheid);
471   *p++ = '\n';
472   *p++ = '\0';
473
474   return simple_query (line);
475 }
476
477
478 /* Perform the simple query QUERY (which must be new-line and 0
479    terminated) and return the error code.  */
480 int
481 simple_query (const char *query)
482 {
483   assuan_context_t ctx;
484   int rc;
485
486   rc = agent_open (&ctx);
487   if (rc)
488     return rc;
489
490   rc = assuan_transact (ctx, query, NULL, NULL, NULL, NULL, NULL, NULL);
491
492   assuan_release (ctx);
493   return rc;
494 }