* Makefile.am (DISTCHECK_CONFIGURE_FLAGS): New.
[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   KsbaCert cert;
52 };
53
54 struct lookup_parm_s {
55   CTRL ctrl;
56   ASSUAN_CONTEXT ctx;
57   void (*cb)(void *, KsbaCert);
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)
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       KsbaCert 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 int
293 gpgsm_dirmngr_isvalid (KsbaCert cert)
294 {
295   int rc;
296   char *certid;
297   char line[ASSUAN_LINELENGTH];
298   struct inq_certificate_parm_s parm;
299
300   rc = start_dirmngr ();
301   if (rc)
302     return rc;
303
304   certid = gpgsm_get_certid (cert);
305   if (!certid)
306     {
307       log_error ("error getting the certificate ID\n");
308       return gpg_error (GPG_ERR_GENERAL);
309     }
310
311   if (opt.verbose > 1)
312     {
313       char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
314       log_info ("asking dirmngr about %s\n", fpr);
315       xfree (fpr);
316     }
317
318   parm.ctx = dirmngr_ctx;
319   parm.cert = cert;
320
321   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
322   line[DIM(line)-1] = 0;
323   xfree (certid);
324
325   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
326                         inq_certificate, &parm, NULL, NULL);
327   if (opt.verbose > 1)
328     log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
329   return map_assuan_err (rc);
330 }
331
332
333 \f
334 /* Lookup helpers*/
335 static AssuanError
336 lookup_cb (void *opaque, const void *buffer, size_t length)
337 {
338   struct lookup_parm_s *parm = opaque;
339   size_t len;
340   char *buf;
341   KsbaCert cert;
342   int rc;
343
344   if (parm->error)
345     return 0;
346
347   if (buffer)
348     {
349       put_membuf (&parm->data, buffer, length);
350       return 0;
351     }
352   /* END encountered - process what we have */
353   buf = get_membuf (&parm->data, &len);
354   if (!buf)
355     {
356       parm->error = gpg_error (GPG_ERR_ENOMEM);
357       return 0;
358     }
359
360   cert = ksba_cert_new ();
361   if (!cert)
362     {
363       parm->error = gpg_error (GPG_ERR_ENOMEM);
364       return 0;
365     }
366   rc = ksba_cert_init_from_mem (cert, buf, len);
367   if (rc)
368     {
369       log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
370     }
371   else
372     {
373       parm->cb (parm->cb_value, cert);
374     }
375
376   ksba_cert_release (cert);
377   init_membuf (&parm->data, 4096);
378   return 0;
379 }
380
381 /* Return a properly escaped pattern from NAMES.  The only error
382    return is NULL to indicate a malloc failure. */
383 static char *
384 pattern_from_strlist (STRLIST names)
385 {
386   STRLIST sl;
387   int n;
388   const char *s;
389   char *pattern, *p;
390
391   for (n=0, sl=names; sl; sl = sl->next)
392     {
393       for (s=sl->d; *s; s++, n++)
394         {
395           if (*s == '%' || *s == ' ' || *s == '+')
396             n += 2;
397         }
398       n++;
399     }
400
401   p = pattern = xtrymalloc (n+1);
402   if (!pattern)
403     return NULL;
404
405   for (n=0, sl=names; sl; sl = sl->next)
406     {
407       for (s=sl->d; *s; s++)
408         {
409           switch (*s)
410             {
411             case '%':
412               *p++ = '%';
413               *p++ = '2';
414               *p++ = '5';
415               break;
416             case ' ':
417               *p++ = '%';
418               *p++ = '2';
419               *p++ = '0';
420               break;
421             case '+':
422               *p++ = '%';
423               *p++ = '2';
424               *p++ = 'B';
425               break;
426             default:
427               *p++ = *s;
428               break;
429             }
430         }
431       *p++ = ' ';
432     }
433   if (p == pattern)
434     *pattern = 0; /* is empty */
435   else
436     p[-1] = '\0'; /* remove trailing blank */
437   
438   return pattern;
439 }
440
441 static AssuanError
442 lookup_status_cb (void *opaque, const char *line)
443 {
444   struct lookup_parm_s *parm = opaque;
445
446   if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
447     {
448       if (parm->ctrl)
449         {
450           for (line +=9; *line == ' '; line++)
451             ;
452           gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
453         }
454     }
455   return 0;
456 }
457
458
459 /* Run the Directroy Managers lookup command using the pattern
460    compiled from the strings given in NAMES.  The caller must provide
461    the callback CB which will be passed cert by cert.  Note that CTRL
462    is optional. */
463 int 
464 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
465                       void (*cb)(void*, KsbaCert), void *cb_value)
466
467   int rc;
468   char *pattern;
469   char line[ASSUAN_LINELENGTH];
470   struct lookup_parm_s parm;
471   size_t len;
472
473   rc = start_dirmngr ();
474   if (rc)
475     return rc;
476
477   pattern = pattern_from_strlist (names);
478   if (!pattern)
479     return OUT_OF_CORE (errno);
480   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
481   line[DIM(line)-1] = 0;
482   xfree (pattern);
483
484   parm.ctrl = ctrl;
485   parm.ctx = dirmngr_ctx;
486   parm.cb = cb;
487   parm.cb_value = cb_value;
488   parm.error = 0;
489   init_membuf (&parm.data, 4096);
490
491   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
492                         NULL, NULL, lookup_status_cb, &parm);
493   xfree (get_membuf (&parm.data, &len));
494   if (rc)
495     return map_assuan_err (rc);
496   return parm.error;
497 }
498
499
500 \f
501 /* Run Command helpers*/
502
503 /* Fairly simple callback to write all output of dirmngr to stdout. */
504 static AssuanError
505 run_command_cb (void *opaque, const void *buffer, size_t length)
506 {
507   if (buffer)
508     {
509       if ( fwrite (buffer, length, 1, stdout) != 1 )
510         log_error ("error writing to stdout: %s\n", strerror (errno));
511     }
512   return 0;
513 }
514
515 /* Handle inquiries from the dirmngr COMMAND. */
516 static AssuanError
517 run_command_inq_cb (void *opaque, const char *line)
518 {
519   struct run_command_parm_s *parm = opaque;
520   AssuanError rc = 0;
521
522   if ( !strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]) )
523     { /* send the given certificate */
524       int err;
525       KsbaCert cert;
526       const unsigned char *der;
527       size_t derlen;
528
529       line += 8;
530       if (!*line)
531         return ASSUAN_Inquire_Error;
532
533       err = gpgsm_find_cert (line, &cert);
534       if (err)
535         {
536           log_error ("certificate not found: %s\n", gpg_strerror (err));
537           rc = ASSUAN_Inquire_Error;
538         }
539       else
540         {
541           der = ksba_cert_get_image (cert, &derlen);
542           if (!der)
543             rc = ASSUAN_Inquire_Error;
544           else
545             rc = assuan_send_data (parm->ctx, der, derlen);
546           ksba_cert_release (cert);
547         }
548     }
549   else if ( !strncmp (line, "PRINTINFO", 9) && (line[9] == ' ' || !line[9]) )
550     { /* Simply show the message given in the argument. */
551       line += 9;
552       log_info ("dirmngr: %s\n", line);
553     }
554   else
555     {
556       log_error ("unsupported inquiry `%s'\n", line);
557       rc = ASSUAN_Inquire_Unknown;
558     }
559
560   return rc; 
561 }
562
563 static AssuanError
564 run_command_status_cb (void *opaque, const char *line)
565 {
566   if (opt.verbose)
567     {
568       log_info ("dirmngr status: %s\n", line);
569     }
570   return 0;
571 }
572
573
574
575 /* Pass COMMAND to dirmngr and print all output generated by Dirmngr
576    to stdout.  A couple of inquiries are defined (see above).  ARGC
577    arguments in ARGV are given to the Dirmngr.  Spaces, plus and
578    percent characters within the argument strings are percent escaped
579    so that blanks can act as delimiters. */
580 int
581 gpgsm_dirmngr_run_command (CTRL ctrl, const char *command,
582                            int argc, char **argv)
583
584   int rc;
585   int i;
586   const char *s;
587   char *line, *p;
588   size_t len;
589   struct run_command_parm_s parm;
590
591   rc = start_dirmngr ();
592   if (rc)
593     return rc;
594
595   parm.ctx = dirmngr_ctx;
596
597   len = strlen (command) + 1;
598   for (i=0; i < argc; i++)
599     len += 1 + 3*strlen (argv[i]); /* enough space for percent escaping */
600   line = xtrymalloc (len);
601   if (!line)
602     return OUT_OF_CORE (errno);
603
604   p = stpcpy (line, command);
605   for (i=0; i < argc; i++)
606     {
607       *p++ = ' ';
608       for (s=argv[i]; *s; s++)
609         {
610           if (!isascii (*s))
611             *p++ = *s;
612           else if (*s == ' ')
613             *p++ = '+';
614           else if (!isprint (*s) || *s == '+')
615             {
616               sprintf (p, "%%%02X", *s);
617               p += 3;
618             }
619           else
620             *p++ = *s;
621         }
622     }
623   *p = 0;
624
625   rc = assuan_transact (dirmngr_ctx, line,
626                         run_command_cb, NULL,
627                         run_command_inq_cb, &parm,
628                         run_command_status_cb, NULL);
629   xfree (line);
630   log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
631   return map_assuan_err (rc);
632 }