2002-04-25 Marcus Brinkmann <marcus@g10code.de>
[gnupg.git] / agent / query.c
1 /* query.c - fork of the pinentry to query stuff from the user
2  *      Copyright (C) 2001 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 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30
31 #include "agent.h"
32 #include "../assuan/assuan.h"
33
34 #ifdef _POSIX_OPEN_MAX
35 #define MAX_OPEN_FDS _POSIX_OPEN_MAX
36 #else
37 #define MAX_OPEN_FDS 20
38 #endif
39
40 static ASSUAN_CONTEXT entry_ctx = NULL;
41
42 /* data to be passed to our callbacks */
43 struct entry_parm_s {
44   int lines;
45   size_t size;
46   char *buffer;
47 };
48
49
50
51 \f
52 /* Fork off the pin entry if this has not already been done */
53 static int
54 start_pinentry (void)
55 {
56   int rc;
57   const char *pgmname;
58   ASSUAN_CONTEXT ctx;
59   const char *argv[5];
60
61   if (entry_ctx)
62     return 0; /* No need to serialize things becuase the agent is
63                  expected to tun as a single-thread (or may be in
64                  future using libpth) */
65
66
67   log_debug ("no running PIN Entry - starting it\n");
68       
69   if (fflush (NULL))
70     {
71       log_error ("error flushing pending output: %s\n", strerror (errno));
72       return seterr (Write_Error);
73     }
74
75   /* FIXME: change the default location of the program */
76   if (!opt.pinentry_program || !*opt.pinentry_program)
77     opt.pinentry_program = "../../pinentry/kpinentry/kpinentry";
78   if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
79     pgmname = opt.pinentry_program;
80   else
81     pgmname++;
82
83   argv[0] = pgmname;
84   if (opt.display)
85     {
86       argv[1] = "--display";
87       argv[2] = opt.display;
88       argv[3] = NULL;
89     }
90   else
91     argv[1] = NULL;
92
93   /* connect to the pinentry and perform initial handshaking */
94   rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv, 0);
95   if (rc)
96     {
97       log_error ("can't connect to the PIN entry module: %s\n",
98                  assuan_strerror (rc));
99       return seterr (No_PIN_Entry);
100     }
101   entry_ctx = ctx;
102   
103   log_debug ("connection to PIN entry established\n");
104
105   rc = assuan_transact (entry_ctx, 
106                         opt.no_grab? "OPTION no-grab":"OPTION grab",
107                         NULL, NULL, NULL, NULL, NULL, NULL);
108   if (rc)
109     return map_assuan_err (rc);
110   if (opt.ttyname)
111     {
112       char *optstr;
113       if (asprintf (&optstr, "OPTION ttyname=%s", opt.ttyname) < 0 )
114         return GNUPG_Out_Of_Core;
115       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
116                             NULL);
117       free (optstr);
118       if (rc)
119         return map_assuan_err (rc);
120     }
121   if (opt.ttytype)
122     {
123       char *optstr;
124       if (asprintf (&optstr, "OPTION ttytype=%s", opt.ttytype) < 0 )
125         return GNUPG_Out_Of_Core;
126       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
127                             NULL);
128       if (rc)
129         return map_assuan_err (rc);
130     }
131   if (opt.lc_ctype)
132     {
133       char *optstr;
134       if (asprintf (&optstr, "OPTION lc-ctype=%s", opt.lc_ctype) < 0 )
135         return GNUPG_Out_Of_Core;
136       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
137                             NULL);
138       if (rc)
139         return map_assuan_err (rc);
140     }
141   if (opt.lc_messages)
142     {
143       char *optstr;
144       if (asprintf (&optstr, "OPTION lc-messages=%s", opt.lc_messages) < 0 )
145         return GNUPG_Out_Of_Core;
146       rc = assuan_transact (entry_ctx, optstr, NULL, NULL, NULL, NULL, NULL,
147                             NULL);
148       if (rc)
149         return map_assuan_err (rc);
150     }
151   return 0;
152 }
153
154
155 static AssuanError
156 getpin_cb (void *opaque, const void *buffer, size_t length)
157 {
158   struct entry_parm_s *parm = opaque;
159
160   if (!buffer)
161     return 0;
162
163   /* we expect the pin to fit on one line */
164   if (parm->lines || length >= parm->size)
165     return ASSUAN_Too_Much_Data;
166
167   /* fixme: we should make sure that the assuan buffer is allocated in
168      secure memory or read the response byte by byte */
169   memcpy (parm->buffer, buffer, length);
170   parm->buffer[length] = 0;
171   parm->lines++;
172   return 0;
173 }
174
175
176 static int
177 all_digitsp( const char *s)
178 {
179   for (; *s && *s >= '0' && *s <= '9'; s++)
180     ;
181   return !*s;
182 }  
183
184
185 \f
186 /* Call the Entry and ask for the PIN.  We do check for a valid PIN
187    number here and repeat it as long as we have invalid formed
188    numbers. */
189 int
190 agent_askpin (const char *desc_text, const char *start_err_text,
191               struct pin_entry_info_s *pininfo)
192 {
193   int rc;
194   char line[ASSUAN_LINELENGTH];
195   struct entry_parm_s parm;
196   const char *errtext = start_err_text;
197
198   if (opt.batch)
199     return 0; /* fixme: we should return BAD PIN */
200
201   if (!pininfo || pininfo->max_length < 1)
202     return seterr (Invalid_Value);
203   if (!desc_text)
204     desc_text = trans ("Please enter you PIN, so that the secret key "
205                        "can be unlocked for this session");
206   
207   rc = start_pinentry ();
208   if (rc)
209     return rc;
210
211   snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
212   line[DIM(line)-1] = 0;
213   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
214   if (rc)
215     return map_assuan_err (rc);
216
217   rc = assuan_transact (entry_ctx,
218                         pininfo->min_digits? "SETPROMPT PIN:"
219                                            : "SETPROMPT Passphrase:",
220                         NULL, NULL, NULL, NULL, NULL, NULL);
221   if (rc)
222     return map_assuan_err (rc);
223
224   for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
225     {
226       memset (&parm, 0, sizeof parm);
227       parm.size = pininfo->max_length;
228       parm.buffer = pininfo->pin;
229
230       if (errtext)
231         { 
232           /* fixme: should we show the try count? It must be translated */
233           if (start_err_text)
234             {
235               snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
236               start_err_text = NULL;
237             }
238           else
239             snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)",
240                       errtext, pininfo->failed_tries+1, pininfo->max_tries);
241           line[DIM(line)-1] = 0;
242           rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
243           if (rc)
244             return map_assuan_err (rc);
245           errtext = NULL;
246         }
247       
248       rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
249       if (rc == ASSUAN_Too_Much_Data)
250         errtext = pininfo->min_digits? trans ("PIN too long")
251                                      : trans ("Passphrase too long");
252       else if (rc)
253         return map_assuan_err (rc);
254       if (!errtext && !pininfo->min_digits)
255         return 0; /* okay, got a passphrase */
256       if (!errtext && !all_digitsp (pininfo->pin))
257         errtext = trans ("Invalid characters in PIN");
258       if (!errtext && pininfo->max_digits
259           && strlen (pininfo->pin) > pininfo->max_digits)
260         errtext = trans ("PIN too long");
261       if (!errtext
262           && strlen (pininfo->pin) < pininfo->min_digits)
263         errtext = trans ("PIN too short");
264
265       if (!errtext)
266         return 0; /* okay, got a PIN */
267     }
268
269   return pininfo->min_digits? GNUPG_Bad_PIN : GNUPG_Bad_Passphrase;
270 }
271
272
273 \f
274 /* Ask for the passphrase using the supplied arguments.  The
275    passphrase is returned in RETPASS as an hex encoded string to be
276    freed by the caller */
277 int 
278 agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
279                       const char *errtext)
280 {
281
282   int rc;
283   char line[ASSUAN_LINELENGTH];
284   struct entry_parm_s parm;
285   unsigned char *p, *hexstring;
286   int i;
287
288   *retpass = NULL;
289   if (opt.batch)
290     return GNUPG_Bad_Passphrase; 
291
292   rc = start_pinentry ();
293   if (rc)
294     return rc;
295
296   if (desc)
297     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
298   else
299     snprintf (line, DIM(line)-1, "RESET");
300   line[DIM(line)-1] = 0;
301   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
302   if (rc)
303     return map_assuan_err (rc);
304
305   snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase");
306   line[DIM(line)-1] = 0;
307   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
308   if (rc)
309     return map_assuan_err (rc);
310
311   if (errtext)
312     {
313       snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
314       line[DIM(line)-1] = 0;
315       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
316       if (rc)
317         return map_assuan_err (rc);
318     }
319
320   memset (&parm, 0, sizeof parm);
321   parm.size = ASSUAN_LINELENGTH/2 - 5;
322   parm.buffer = gcry_malloc_secure (parm.size+10);
323   if (!parm.buffer)
324     return seterr (Out_Of_Core);
325
326   assuan_begin_confidential (entry_ctx);
327   rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL, NULL, NULL);
328   if (rc)
329     {
330       xfree (parm.buffer);
331       return map_assuan_err (rc);
332     }
333   
334   hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1);
335   if (!hexstring)
336     {
337       xfree (parm.buffer);
338       return seterr (Out_Of_Core);
339     }
340
341   for (i=0, p=parm.buffer; *p; p++, i += 2)
342     sprintf (hexstring+i, "%02X", *p);
343   
344   xfree (parm.buffer);
345   *retpass = hexstring;
346   return 0;
347 }
348
349
350 \f
351 /* Pop up the PIN-entry, display the text and the prompt and ask the
352    user to confirm this.  We return 0 for success, ie. the used
353    confirmed it, GNUPG_Not_Confirmed for what the text says or an
354    other error. */
355 int 
356 agent_get_confirmation (const char *desc, const char *ok, const char *cancel)
357 {
358   int rc;
359   char line[ASSUAN_LINELENGTH];
360
361   rc = start_pinentry ();
362   if (rc)
363     return rc;
364
365   if (desc)
366     snprintf (line, DIM(line)-1, "SETDESC %s", desc);
367   else
368     snprintf (line, DIM(line)-1, "RESET");
369   line[DIM(line)-1] = 0;
370   rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
371   if (rc)
372     return map_assuan_err (rc);
373
374   if (ok)
375     {
376       snprintf (line, DIM(line)-1, "SETOK %s", ok);
377       line[DIM(line)-1] = 0;
378       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
379       if (rc)
380         return map_assuan_err (rc);
381     }
382   if (cancel)
383     {
384       snprintf (line, DIM(line)-1, "SETCANCEL %s", cancel);
385       line[DIM(line)-1] = 0;
386       rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
387       if (rc)
388         return map_assuan_err (rc);
389     }
390
391   rc = assuan_transact (entry_ctx, "CONFIRM", NULL, NULL, NULL, NULL, NULL, NULL);
392   return map_assuan_err (rc);
393 }
394
395
396