* call-dirmngr.c (gpgsm_dirmngr_isvalid): print status of dirmngr
[gnupg.git] / sm / call-dirmngr.c
1 /* call-dirmngr.c - communication with the dromngr 
2  *      Copyright (C) 2002 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
30 #include <gcrypt.h>
31
32 #include "gpgsm.h"
33 #include "../assuan/assuan.h"
34 #include "i18n.h"
35
36 struct membuf {
37   size_t len;
38   size_t size;
39   char *buf;
40   int out_of_core;
41 };
42
43
44
45 static ASSUAN_CONTEXT dirmngr_ctx = NULL;
46 static int force_pipe_server = 0;
47
48 struct inq_certificate_parm_s {
49   ASSUAN_CONTEXT ctx;
50   KsbaCert cert;
51 };
52
53 struct lookup_parm_s {
54   CTRL ctrl;
55   ASSUAN_CONTEXT ctx;
56   void (*cb)(void *, KsbaCert);
57   void *cb_value;
58   struct membuf data;
59   int error;
60 };
61
62
63
64
65 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
66    create a buffer, put_membuf to append bytes and get_membuf to
67    release and return the buffer.  Allocation errors are detected but
68    only returned at the final get_membuf(), this helps not to clutter
69    the code with out of core checks.  */
70
71 static void
72 init_membuf (struct membuf *mb, int initiallen)
73 {
74   mb->len = 0;
75   mb->size = initiallen;
76   mb->out_of_core = 0;
77   mb->buf = xtrymalloc (initiallen);
78   if (!mb->buf)
79       mb->out_of_core = 1;
80 }
81
82 static void
83 put_membuf (struct membuf *mb, const void *buf, size_t len)
84 {
85   if (mb->out_of_core)
86     return;
87
88   if (mb->len + len >= mb->size)
89     {
90       char *p;
91       
92       mb->size += len + 1024;
93       p = xtryrealloc (mb->buf, mb->size);
94       if (!p)
95         {
96           mb->out_of_core = 1;
97           return;
98         }
99       mb->buf = p;
100     }
101   memcpy (mb->buf + mb->len, buf, len);
102   mb->len += len;
103 }
104
105 static void *
106 get_membuf (struct membuf *mb, size_t *len)
107 {
108   char *p;
109
110   if (mb->out_of_core)
111     {
112       xfree (mb->buf);
113       mb->buf = NULL;
114       return NULL;
115     }
116
117   p = mb->buf;
118   *len = mb->len;
119   mb->buf = NULL;
120   mb->out_of_core = 1; /* don't allow a reuse */
121   return p;
122 }
123
124
125
126
127 \f
128 /* Try to connect to the agent via socket or fork it off and work by
129    pipes.  Handle the server's initial greeting */
130 static int
131 start_dirmngr (void)
132 {
133   int rc;
134   char *infostr, *p;
135   ASSUAN_CONTEXT ctx;
136
137   if (dirmngr_ctx)
138     return 0; /* fixme: We need a context for each thread or serialize
139                  the access to the dirmngr */
140
141   infostr = force_pipe_server? NULL : getenv ("DIRMNGR_INFO");
142   if (!infostr)
143     {
144       const char *pgmname;
145       const char *argv[3];
146       int no_close_list[3];
147       int i;
148
149       if (opt.verbose)
150         log_info (_("no running dirmngr - starting one\n"));
151       
152       if (fflush (NULL))
153         {
154           log_error ("error flushing pending output: %s\n", strerror (errno));
155           return seterr (Write_Error);
156         }
157
158       if (!opt.dirmngr_program || !*opt.dirmngr_program)
159         opt.dirmngr_program = GNUPG_DEFAULT_DIRMNGR;
160       if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
161         pgmname = opt.dirmngr_program;
162       else
163         pgmname++;
164
165       argv[0] = pgmname;
166       argv[1] = "--server";
167       argv[2] = NULL;
168
169       i=0;
170       if (log_get_fd () != -1)
171         no_close_list[i++] = log_get_fd ();
172       no_close_list[i++] = fileno (stderr);
173       no_close_list[i] = -1;
174
175       /* connect to the agent and perform initial handshaking */
176       rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv,
177                                 no_close_list);
178     }
179   else
180     {
181       int prot;
182       int pid;
183
184       infostr = xstrdup (infostr);
185       if ( !(p = strchr (infostr, ':')) || p == infostr)
186         {
187           log_error (_("malformed DIRMNGR_INFO environment variable\n"));
188           xfree (infostr);
189           force_pipe_server = 1;
190           return start_dirmngr ();
191         }
192       *p++ = 0;
193       pid = atoi (p);
194       while (*p && *p != ':')
195         p++;
196       prot = *p? atoi (p+1) : 0;
197       if (prot != 1)
198         {
199           log_error (_("dirmngr protocol version %d is not supported\n"),
200                      prot);
201           xfree (infostr);
202           force_pipe_server = 1;
203           return start_dirmngr ();
204         }
205
206       rc = assuan_socket_connect (&ctx, infostr, pid);
207       xfree (infostr);
208       if (rc == ASSUAN_Connect_Failed)
209         {
210           log_error (_("can't connect to the dirmngr - trying fall back\n"));
211           force_pipe_server = 1;
212           return start_dirmngr ();
213         }
214     }
215
216   if (rc)
217     {
218       log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc));
219       return seterr (No_Dirmngr);
220     }
221   dirmngr_ctx = ctx;
222
223   if (DBG_ASSUAN)
224     log_debug ("connection to dirmngr established\n");
225   return 0;
226 }
227
228
229 \f
230 /* Handle a SENDCERT inquiry. */
231 static AssuanError
232 inq_certificate (void *opaque, const char *line)
233 {
234   struct inq_certificate_parm_s *parm = opaque;
235   AssuanError rc;
236   const unsigned char *der;
237   size_t derlen;
238
239   if (!(!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])))
240     {
241       log_error ("unsupported inquiry `%s'\n", line);
242       return ASSUAN_Inquire_Unknown;
243     }
244   line += 8;
245
246   if (!*line)
247     { /* send the current certificate */
248       der = ksba_cert_get_image (parm->cert, &derlen);
249       if (!der)
250         rc = ASSUAN_Inquire_Error;
251       else
252         rc = assuan_send_data (parm->ctx, der, derlen);
253     }
254   else 
255     { /* send the given certificate */
256       int err;
257       KsbaCert cert;
258
259       err = gpgsm_find_cert (line, &cert);
260       if (err)
261         {
262           log_error ("certificate not found: %s\n", gnupg_strerror (err));
263           rc = ASSUAN_Inquire_Error;
264         }
265       else
266         {
267           der = ksba_cert_get_image (cert, &derlen);
268           if (!der)
269             rc = ASSUAN_Inquire_Error;
270           else
271             rc = assuan_send_data (parm->ctx, der, derlen);
272           ksba_cert_release (cert);
273         }
274     }
275
276   return rc; 
277 }
278
279
280 \f
281 /* Call the directory manager to check whether the certificate is valid
282    Returns 0 for valid or usually one of the errors:
283
284   GNUPG_Certificate_Revoked
285   GNUPG_No_CRL_Known
286   GNUPG_CRL_Too_Old
287  */
288 int
289 gpgsm_dirmngr_isvalid (KsbaCert cert)
290 {
291   int rc;
292   char *certid;
293   char line[ASSUAN_LINELENGTH];
294   struct inq_certificate_parm_s parm;
295
296   rc = start_dirmngr ();
297   if (rc)
298     return rc;
299
300   certid = gpgsm_get_certid (cert);
301   if (!certid)
302     {
303       log_error ("error getting the certificate ID\n");
304       return seterr (General_Error);
305     }
306
307   if (opt.verbose > 1)
308     {
309       char *fpr = gpgsm_get_fingerprint_string (cert, GCRY_MD_SHA1);
310       log_info ("asking dirmngr about %s\n", fpr);
311       xfree (fpr);
312     }
313
314   parm.ctx = dirmngr_ctx;
315   parm.cert = cert;
316
317   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
318   line[DIM(line)-1] = 0;
319   xfree (certid);
320
321   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
322                         inq_certificate, &parm, NULL, NULL);
323   if (opt.verbose > 1)
324     log_info ("response of dirmngr: %s\n", rc? assuan_strerror (rc): "okay");
325   return map_assuan_err (rc);
326 }
327
328
329 \f
330 /* Lookup helpers*/
331 static AssuanError
332 lookup_cb (void *opaque, const void *buffer, size_t length)
333 {
334   struct lookup_parm_s *parm = opaque;
335   size_t len;
336   char *buf;
337   KsbaCert cert;
338   int rc;
339
340   if (parm->error)
341     return 0;
342
343   if (buffer)
344     {
345       put_membuf (&parm->data, buffer, length);
346       return 0;
347     }
348   /* END encountered - process what we have */
349   buf = get_membuf (&parm->data, &len);
350   if (!buf)
351     {
352       parm->error = GNUPG_Out_Of_Core;
353       return 0;
354     }
355
356   cert = ksba_cert_new ();
357   if (!cert)
358     {
359       parm->error = GNUPG_Out_Of_Core;
360       return 0;
361     }
362   rc = ksba_cert_init_from_mem (cert, buf, len);
363   if (rc)
364     {
365       log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
366     }
367   else
368     {
369       parm->cb (parm->cb_value, cert);
370     }
371
372   ksba_cert_release (cert);
373   init_membuf (&parm->data, 4096);
374   return 0;
375 }
376
377 /* Return a properly escaped pattern from NAMES.  The only error
378    return is NULL to indicate a malloc failure. */
379 static char *
380 pattern_from_strlist (STRLIST names)
381 {
382   STRLIST sl;
383   int n;
384   const char *s;
385   char *pattern, *p;
386
387   for (n=0, sl=names; sl; sl = sl->next)
388     {
389       for (s=sl->d; *s; s++, n++)
390         {
391           if (*s == '%' || *s == ' ' || *s == '+')
392             n += 2;
393         }
394       n++;
395     }
396
397   p = pattern = xtrymalloc (n+1);
398   if (!pattern)
399     return NULL;
400
401   for (n=0, sl=names; sl; sl = sl->next)
402     {
403       for (s=sl->d; *s; s++)
404         {
405           switch (*s)
406             {
407             case '%':
408               *p++ = '%';
409               *p++ = '2';
410               *p++ = '5';
411               break;
412             case ' ':
413               *p++ = '%';
414               *p++ = '2';
415               *p++ = '0';
416               break;
417             case '+':
418               *p++ = '%';
419               *p++ = '2';
420               *p++ = 'B';
421               break;
422             default:
423               *p++ = *s;
424               break;
425             }
426         }
427       *p++ = ' ';
428     }
429   if (p == pattern)
430     *pattern = 0; /* is empty */
431   else
432     p[-1] = '\0'; /* remove trailing blank */
433   
434   return pattern;
435 }
436
437 static AssuanError
438 lookup_status_cb (void *opaque, const char *line)
439 {
440   struct lookup_parm_s *parm = opaque;
441
442   if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
443     {
444       if (parm->ctrl)
445         {
446           for (line +=9; *line == ' '; line++)
447             ;
448           gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
449         }
450     }
451   return 0;
452 }
453
454
455 /* Run the Directroy Managers lookup command using the apptern
456    compiled from the strings given in NAMES.  The caller must provide
457    the callback CB which will be passed cert by cert.  Note that CTRL
458    is optional. */
459 int 
460 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
461                       void (*cb)(void*, KsbaCert), void *cb_value)
462
463   int rc;
464   char *pattern;
465   char line[ASSUAN_LINELENGTH];
466   struct lookup_parm_s parm;
467   size_t len;
468
469   rc = start_dirmngr ();
470   if (rc)
471     return rc;
472
473   pattern = pattern_from_strlist (names);
474   if (!pattern)
475     return GNUPG_Out_Of_Core;
476   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
477   line[DIM(line)-1] = 0;
478   xfree (pattern);
479
480   parm.ctrl = ctrl;
481   parm.ctx = dirmngr_ctx;
482   parm.cb = cb;
483   parm.cb_value = cb_value;
484   parm.error = 0;
485   init_membuf (&parm.data, 4096);
486
487   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
488                         NULL, NULL, lookup_status_cb, &parm);
489   xfree (get_membuf (&parm.data, &len));
490   if (rc)
491     return map_assuan_err (rc);
492   return parm.error;
493 }
494
495