* gpgsm.c: New option --auto-issuer-key-retrieve.
[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   parm.ctx = dirmngr_ctx;
308   parm.cert = cert;
309
310   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
311   line[DIM(line)-1] = 0;
312   xfree (certid);
313
314   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
315                         inq_certificate, &parm, NULL, NULL);
316   return map_assuan_err (rc);
317 }
318
319
320 \f
321 /* Lookup helpers*/
322 static AssuanError
323 lookup_cb (void *opaque, const void *buffer, size_t length)
324 {
325   struct lookup_parm_s *parm = opaque;
326   size_t len;
327   char *buf;
328   KsbaCert cert;
329   int rc;
330
331   if (parm->error)
332     return 0;
333
334   if (buffer)
335     {
336       put_membuf (&parm->data, buffer, length);
337       return 0;
338     }
339   /* END encountered - process what we have */
340   buf = get_membuf (&parm->data, &len);
341   if (!buf)
342     {
343       parm->error = GNUPG_Out_Of_Core;
344       return 0;
345     }
346
347   cert = ksba_cert_new ();
348   if (!cert)
349     {
350       parm->error = GNUPG_Out_Of_Core;
351       return 0;
352     }
353   rc = ksba_cert_init_from_mem (cert, buf, len);
354   if (rc)
355     {
356       log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
357     }
358   else
359     {
360       parm->cb (parm->cb_value, cert);
361     }
362
363   ksba_cert_release (cert);
364   init_membuf (&parm->data, 4096);
365   return 0;
366 }
367
368 /* Return a properly escaped pattern from NAMES.  The only error
369    return is NULL to indicate a malloc failure. */
370 static char *
371 pattern_from_strlist (STRLIST names)
372 {
373   STRLIST sl;
374   int n;
375   const char *s;
376   char *pattern, *p;
377
378   for (n=0, sl=names; sl; sl = sl->next)
379     {
380       for (s=sl->d; *s; s++, n++)
381         {
382           if (*s == '%' || *s == ' ' || *s == '+')
383             n += 2;
384         }
385       n++;
386     }
387
388   p = pattern = xtrymalloc (n+1);
389   if (!pattern)
390     return NULL;
391
392   for (n=0, sl=names; sl; sl = sl->next)
393     {
394       for (s=sl->d; *s; s++)
395         {
396           switch (*s)
397             {
398             case '%':
399               *p++ = '%';
400               *p++ = '2';
401               *p++ = '5';
402               break;
403             case ' ':
404               *p++ = '%';
405               *p++ = '2';
406               *p++ = '0';
407               break;
408             case '+':
409               *p++ = '%';
410               *p++ = '2';
411               *p++ = 'B';
412               break;
413             default:
414               *p++ = *s;
415               break;
416             }
417         }
418       *p++ = ' ';
419     }
420   if (p == pattern)
421     *pattern = 0; /* is empty */
422   else
423     p[-1] = '\0'; /* remove trailing blank */
424   
425   return pattern;
426 }
427
428 static AssuanError
429 lookup_status_cb (void *opaque, const char *line)
430 {
431   struct lookup_parm_s *parm = opaque;
432
433   if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
434     {
435       if (parm->ctrl)
436         {
437           for (line +=9; *line == ' '; line++)
438             ;
439           gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
440         }
441     }
442   return 0;
443 }
444
445
446 /* Run the Directroy Managers lookup command using the apptern
447    compiled from the strings given in NAMES.  The caller must provide
448    the callback CB which will be passed cert by cert.  Note that CTRL
449    is optional. */
450 int 
451 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
452                       void (*cb)(void*, KsbaCert), void *cb_value)
453
454   int rc;
455   char *pattern;
456   char line[ASSUAN_LINELENGTH];
457   struct lookup_parm_s parm;
458   size_t len;
459
460   rc = start_dirmngr ();
461   if (rc)
462     return rc;
463
464   pattern = pattern_from_strlist (names);
465   if (!pattern)
466     return GNUPG_Out_Of_Core;
467   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
468   line[DIM(line)-1] = 0;
469   xfree (pattern);
470
471   parm.ctrl = ctrl;
472   parm.ctx = dirmngr_ctx;
473   parm.cb = cb;
474   parm.cb_value = cb_value;
475   parm.error = 0;
476   init_membuf (&parm.data, 4096);
477
478   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
479                         NULL, NULL, lookup_status_cb, &parm);
480   xfree (get_membuf (&parm.data, &len));
481   if (rc)
482     return map_assuan_err (rc);
483   return parm.error;
484 }
485
486