* gpgsm.c: Fixed value parsing for --with-validation.
[gnupg.git] / sm / call-dirmngr.c
1 /* call-dirmngr.c - communication with the dromngr 
2  *      Copyright (C) 2002, 2003 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 <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <unistd.h> 
27 #include <time.h>
28 #include <assert.h>
29 #include <ctype.h>
30
31 #include "gpgsm.h"
32 #include <gcrypt.h>
33 #include <assuan.h>
34
35 #include "i18n.h"
36
37 struct membuf {
38   size_t len;
39   size_t size;
40   char *buf;
41   int out_of_core;
42 };
43
44
45
46 static ASSUAN_CONTEXT dirmngr_ctx = NULL;
47 static int force_pipe_server = 0;
48
49 struct inq_certificate_parm_s {
50   ASSUAN_CONTEXT ctx;
51   ksba_cert_t cert;
52 };
53
54 struct lookup_parm_s {
55   CTRL ctrl;
56   ASSUAN_CONTEXT ctx;
57   void (*cb)(void *, ksba_cert_t);
58   void *cb_value;
59   struct membuf data;
60   int error;
61 };
62
63 struct run_command_parm_s {
64   ASSUAN_CONTEXT ctx;
65 };
66
67
68 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
69    create a buffer, put_membuf to append bytes and get_membuf to
70    release and return the buffer.  Allocation errors are detected but
71    only returned at the final get_membuf(), this helps not to clutter
72    the code with out of core checks.  */
73
74 static void
75 init_membuf (struct membuf *mb, int initiallen)
76 {
77   mb->len = 0;
78   mb->size = initiallen;
79   mb->out_of_core = 0;
80   mb->buf = xtrymalloc (initiallen);
81   if (!mb->buf)
82       mb->out_of_core = 1;
83 }
84
85 static void
86 put_membuf (struct membuf *mb, const void *buf, size_t len)
87 {
88   if (mb->out_of_core)
89     return;
90
91   if (mb->len + len >= mb->size)
92     {
93       char *p;
94       
95       mb->size += len + 1024;
96       p = xtryrealloc (mb->buf, mb->size);
97       if (!p)
98         {
99           mb->out_of_core = 1;
100           return;
101         }
102       mb->buf = p;
103     }
104   memcpy (mb->buf + mb->len, buf, len);
105   mb->len += len;
106 }
107
108 static void *
109 get_membuf (struct membuf *mb, size_t *len)
110 {
111   char *p;
112
113   if (mb->out_of_core)
114     {
115       xfree (mb->buf);
116       mb->buf = NULL;
117       return NULL;
118     }
119
120   p = mb->buf;
121   *len = mb->len;
122   mb->buf = NULL;
123   mb->out_of_core = 1; /* don't allow a reuse */
124   return p;
125 }
126
127
128
129
130 \f
131 /* Try to connect to the agent via socket or fork it off and work by
132    pipes.  Handle the server's initial greeting */
133 static int
134 start_dirmngr (void)
135 {
136   int rc;
137   char *infostr, *p;
138   ASSUAN_CONTEXT ctx;
139
140   if (dirmngr_ctx)
141     return 0; /* fixme: We need a context for each thread or serialize
142                  the access to the dirmngr */
143
144   infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
145   if (!infostr || !*infostr)
146     {
147       const char *pgmname;
148       const char *argv[3];
149       int no_close_list[3];
150       int i;
151
152       if (opt.verbose)
153         log_info (_("no running dirmngr - starting one\n"));
154       
155       if (fflush (NULL))
156         {
157           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
158           log_error ("error flushing pending output: %s\n", strerror (errno));
159           return tmperr;
160         }
161
162       if (!opt.dirmngr_program || !*opt.dirmngr_program)
163         opt.dirmngr_program = GNUPG_DEFAULT_DIRMNGR;
164       if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
165         pgmname = opt.dirmngr_program;
166       else
167         pgmname++;
168
169       argv[0] = pgmname;
170       argv[1] = "--server";
171       argv[2] = NULL;
172
173       i=0;
174       if (log_get_fd () != -1)
175         no_close_list[i++] = log_get_fd ();
176       no_close_list[i++] = fileno (stderr);
177       no_close_list[i] = -1;
178
179       /* connect to the agent and perform initial handshaking */
180       rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv,
181                                 no_close_list);
182     }
183   else
184     {
185       int prot;
186       int pid;
187
188       infostr = xstrdup (infostr);
189       if ( !(p = strchr (infostr, ':')) || p == infostr)
190         {
191           log_error (_("malformed DIRMNGR_INFO environment variable\n"));
192           xfree (infostr);
193           force_pipe_server = 1;
194           return start_dirmngr ();
195         }
196       *p++ = 0;
197       pid = atoi (p);
198       while (*p && *p != ':')
199         p++;
200       prot = *p? atoi (p+1) : 0;
201       if (prot != 1)
202         {
203           log_error (_("dirmngr protocol version %d is not supported\n"),
204                      prot);
205           xfree (infostr);
206           force_pipe_server = 1;
207           return start_dirmngr ();
208         }
209
210       rc = assuan_socket_connect (&ctx, infostr, pid);
211       xfree (infostr);
212       if (rc == ASSUAN_Connect_Failed)
213         {
214           log_error (_("can't connect to the dirmngr - trying fall back\n"));
215           force_pipe_server = 1;
216           return start_dirmngr ();
217         }
218     }
219
220   if (rc)
221     {
222       log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc));
223       return gpg_error (GPG_ERR_NO_DIRMNGR);
224     }
225   dirmngr_ctx = ctx;
226
227   if (DBG_ASSUAN)
228     log_debug ("connection to dirmngr established\n");
229   return 0;
230 }
231
232
233 \f
234 /* Handle a SENDCERT inquiry. */
235 static AssuanError
236 inq_certificate (void *opaque, const char *line)
237 {
238   struct inq_certificate_parm_s *parm = opaque;
239   AssuanError rc;
240   const unsigned char *der;
241   size_t derlen;
242
243   if (!(!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])))
244     {
245       log_error ("unsupported inquiry `%s'\n", line);
246       return ASSUAN_Inquire_Unknown;
247     }
248   line += 8;
249
250   if (!*line)
251     { /* send the current certificate */
252       der = ksba_cert_get_image (parm->cert, &derlen);
253       if (!der)
254         rc = ASSUAN_Inquire_Error;
255       else
256         rc = assuan_send_data (parm->ctx, der, derlen);
257     }
258   else 
259     { /* send the given certificate */
260       int err;
261       ksba_cert_t cert;
262
263       err = gpgsm_find_cert (line, &cert);
264       if (err)
265         {
266           log_error ("certificate not found: %s\n", gpg_strerror (err));
267           rc = ASSUAN_Inquire_Error;
268         }
269       else
270         {
271           der = ksba_cert_get_image (cert, &derlen);
272           if (!der)
273             rc = ASSUAN_Inquire_Error;
274           else
275             rc = assuan_send_data (parm->ctx, der, derlen);
276           ksba_cert_release (cert);
277         }
278     }
279
280   return rc; 
281 }
282
283
284 \f
285 /* Call the directory manager to check whether the certificate is valid
286    Returns 0 for valid or usually one of the errors:
287
288   GPG_ERR_CERTIFICATE_REVOKED
289   GPG_ERR_NO_CRL_KNOWN
290   GPG_ERR_CRL_TOO_OLD
291
292   With USE_OCSP set to true, the dirmngr is asked to do an OCSP
293   request first.
294  */
295 int
296 gpgsm_dirmngr_isvalid (ksba_cert_t cert, int use_ocsp)
297 {
298   int rc;
299   char *certid;
300   char line[ASSUAN_LINELENGTH];
301   struct inq_certificate_parm_s parm;
302
303   rc = start_dirmngr ();
304   if (rc)
305     return rc;
306
307   if (use_ocsp)
308     {
309       certid = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1);
310     }
311   else
312     {
313       certid = gpgsm_get_certid (cert);
314       if (!certid)
315         {
316           log_error ("error getting the certificate ID\n");
317           return gpg_error (GPG_ERR_GENERAL);
318         }
319     }
320
321   if (opt.verbose > 1)
322     {
323       char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
324       log_info ("asking dirmngr about %s%s\n", fpr,
325                 use_ocsp? " (using OCSP)":"");
326       xfree (fpr);
327     }
328
329   parm.ctx = dirmngr_ctx;
330   parm.cert = cert;
331
332   /* FIXME: If --disable-crl-checks has been set, we should pass an
333      option to dirmngr, so that no fallback CRL check is done after an
334      ocsp check. */
335
336   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
337   line[DIM(line)-1] = 0;
338   xfree (certid);
339
340   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
341                         inq_certificate, &parm, NULL, NULL);
342   if (opt.verbose > 1)
343     log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
344   return map_assuan_err (rc);
345 }
346
347
348 \f
349 /* Lookup helpers*/
350 static AssuanError
351 lookup_cb (void *opaque, const void *buffer, size_t length)
352 {
353   struct lookup_parm_s *parm = opaque;
354   size_t len;
355   char *buf;
356   ksba_cert_t cert;
357   int rc;
358
359   if (parm->error)
360     return 0;
361
362   if (buffer)
363     {
364       put_membuf (&parm->data, buffer, length);
365       return 0;
366     }
367   /* END encountered - process what we have */
368   buf = get_membuf (&parm->data, &len);
369   if (!buf)
370     {
371       parm->error = gpg_error (GPG_ERR_ENOMEM);
372       return 0;
373     }
374
375   rc = ksba_cert_new (&cert);
376   if (rc)
377     {
378       parm->error = rc;
379       return 0;
380     }
381   rc = ksba_cert_init_from_mem (cert, buf, len);
382   if (rc)
383     {
384       log_error ("failed to parse a certificate: %s\n", gpg_strerror (rc));
385     }
386   else
387     {
388       parm->cb (parm->cb_value, cert);
389     }
390
391   ksba_cert_release (cert);
392   init_membuf (&parm->data, 4096);
393   return 0;
394 }
395
396 /* Return a properly escaped pattern from NAMES.  The only error
397    return is NULL to indicate a malloc failure. */
398 static char *
399 pattern_from_strlist (STRLIST names)
400 {
401   STRLIST sl;
402   int n;
403   const char *s;
404   char *pattern, *p;
405
406   for (n=0, sl=names; sl; sl = sl->next)
407     {
408       for (s=sl->d; *s; s++, n++)
409         {
410           if (*s == '%' || *s == ' ' || *s == '+')
411             n += 2;
412         }
413       n++;
414     }
415
416   p = pattern = xtrymalloc (n+1);
417   if (!pattern)
418     return NULL;
419
420   for (n=0, sl=names; sl; sl = sl->next)
421     {
422       for (s=sl->d; *s; s++)
423         {
424           switch (*s)
425             {
426             case '%':
427               *p++ = '%';
428               *p++ = '2';
429               *p++ = '5';
430               break;
431             case ' ':
432               *p++ = '%';
433               *p++ = '2';
434               *p++ = '0';
435               break;
436             case '+':
437               *p++ = '%';
438               *p++ = '2';
439               *p++ = 'B';
440               break;
441             default:
442               *p++ = *s;
443               break;
444             }
445         }
446       *p++ = ' ';
447     }
448   if (p == pattern)
449     *pattern = 0; /* is empty */
450   else
451     p[-1] = '\0'; /* remove trailing blank */
452   
453   return pattern;
454 }
455
456 static AssuanError
457 lookup_status_cb (void *opaque, const char *line)
458 {
459   struct lookup_parm_s *parm = opaque;
460
461   if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
462     {
463       if (parm->ctrl)
464         {
465           for (line +=9; *line == ' '; line++)
466             ;
467           gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
468         }
469     }
470   return 0;
471 }
472
473
474 /* Run the Directroy Managers lookup command using the pattern
475    compiled from the strings given in NAMES.  The caller must provide
476    the callback CB which will be passed cert by cert.  Note that CTRL
477    is optional. */
478 int 
479 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
480                       void (*cb)(void*, ksba_cert_t), void *cb_value)
481
482   int rc;
483   char *pattern;
484   char line[ASSUAN_LINELENGTH];
485   struct lookup_parm_s parm;
486   size_t len;
487
488   rc = start_dirmngr ();
489   if (rc)
490     return rc;
491
492   pattern = pattern_from_strlist (names);
493   if (!pattern)
494     return OUT_OF_CORE (errno);
495   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
496   line[DIM(line)-1] = 0;
497   xfree (pattern);
498
499   parm.ctrl = ctrl;
500   parm.ctx = dirmngr_ctx;
501   parm.cb = cb;
502   parm.cb_value = cb_value;
503   parm.error = 0;
504   init_membuf (&parm.data, 4096);
505
506   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
507                         NULL, NULL, lookup_status_cb, &parm);
508   xfree (get_membuf (&parm.data, &len));
509   if (rc)
510     return map_assuan_err (rc);
511   return parm.error;
512 }
513
514
515 \f
516 /* Run Command helpers*/
517
518 /* Fairly simple callback to write all output of dirmngr to stdout. */
519 static AssuanError
520 run_command_cb (void *opaque, const void *buffer, size_t length)
521 {
522   if (buffer)
523     {
524       if ( fwrite (buffer, length, 1, stdout) != 1 )
525         log_error ("error writing to stdout: %s\n", strerror (errno));
526     }
527   return 0;
528 }
529
530 /* Handle inquiries from the dirmngr COMMAND. */
531 static AssuanError
532 run_command_inq_cb (void *opaque, const char *line)
533 {
534   struct run_command_parm_s *parm = opaque;
535   AssuanError rc = 0;
536
537   if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) )
538     { /* send the given certificate */
539       int err;
540       ksba_cert_t cert;
541       const unsigned char *der;
542       size_t derlen;
543
544       line += 8;
545       if (!*line)
546         return ASSUAN_Inquire_Error;
547
548       err = gpgsm_find_cert (line, &cert);
549       if (err)
550         {
551           log_error ("certificate not found: %s\n", gpg_strerror (err));
552           rc = ASSUAN_Inquire_Error;
553         }
554       else
555         {
556           der = ksba_cert_get_image (cert, &derlen);
557           if (!der)
558             rc = ASSUAN_Inquire_Error;
559           else
560             rc = assuan_send_data (parm->ctx, der, derlen);
561           ksba_cert_release (cert);
562         }
563     }
564   else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) )
565     { /* Simply show the message given in the argument. */
566       line += 9;
567       log_info ("dirmngr: %s\n", line);
568     }
569   else
570     {
571       log_error ("unsupported inquiry `%s'\n", line);
572       rc = ASSUAN_Inquire_Unknown;
573     }
574
575   return rc; 
576 }
577
578 static AssuanError
579 run_command_status_cb (void *opaque, const char *line)
580 {
581   if (opt.verbose)
582     {
583       log_info ("dirmngr status: %s\n", line);
584     }
585   return 0;
586 }
587
588
589
590 /* Pass COMMAND to dirmngr and print all output generated by Dirmngr
591    to stdout.  A couple of inquiries are defined (see above).  ARGC
592    arguments in ARGV are given to the Dirmngr.  Spaces, plus and
593    percent characters within the argument strings are percent escaped
594    so that blanks can act as delimiters. */
595 int
596 gpgsm_dirmngr_run_command (CTRL ctrl, const char *command,
597                            int argc, char **argv)
598
599   int rc;
600   int i;
601   const char *s;
602   char *line, *p;
603   size_t len;
604   struct run_command_parm_s parm;
605
606   rc = start_dirmngr ();
607   if (rc)
608     return rc;
609
610   parm.ctx = dirmngr_ctx;
611
612   len = strlen (command) + 1;
613   for (i=0; i < argc; i++)
614     len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
615   line = xtrymalloc (len);
616   if (!line)
617     return OUT_OF_CORE (errno);
618
619   p = stpcpy (line, command);
620   for (i=0; i < argc; i++)
621     {
622       *p++ = ' ';
623       for (s=argv[i]; *s; s++)
624         {
625           if (!isascii (*s))
626             *p++ = *s;
627           else if (*s == ' ')
628             *p++ = '+';
629           else if (!isprint (*s) || *s == '+')
630             {
631               sprintf (p, "%%%02X", *s);
632               p += 3;
633             }
634           else
635             *p++ = *s;
636         }
637     }
638   *p = 0;
639
640   rc = assuan_transact (dirmngr_ctx, line,
641                         run_command_cb, NULL,
642                         run_command_inq_cb, &parm,
643                         run_command_status_cb, NULL);
644   xfree (line);
645   log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
646   return map_assuan_err (rc);
647 }