1cdf3e243c1f60d9e1d8ff72e6d6f8a2cd895fd0
[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
147       if (opt.verbose)
148         log_info (_("no running dirmngr - starting one\n"));
149       
150       if (fflush (NULL))
151         {
152           log_error ("error flushing pending output: %s\n", strerror (errno));
153           return seterr (Write_Error);
154         }
155
156       if (!opt.dirmngr_program || !*opt.dirmngr_program)
157         opt.dirmngr_program = "/usr/sbin/dirmngr";
158       if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
159         pgmname = opt.dirmngr_program;
160       else
161         pgmname++;
162
163       argv[0] = pgmname;
164       argv[1] = "--server";
165       argv[2] = NULL;
166
167       /* connect to the agent and perform initial handshaking */
168       rc = assuan_pipe_connect (&ctx, opt.dirmngr_program, (char**)argv, 0);
169     }
170   else
171     {
172       int prot;
173       int pid;
174
175       infostr = xstrdup (infostr);
176       if ( !(p = strchr (infostr, ':')) || p == infostr)
177         {
178           log_error (_("malformed DIRMNGR_INFO environment variable\n"));
179           xfree (infostr);
180           force_pipe_server = 1;
181           return start_dirmngr ();
182         }
183       *p++ = 0;
184       pid = atoi (p);
185       while (*p && *p != ':')
186         p++;
187       prot = *p? atoi (p+1) : 0;
188       if (prot != 1)
189         {
190           log_error (_("dirmngr protocol version %d is not supported\n"),
191                      prot);
192           xfree (infostr);
193           force_pipe_server = 1;
194           return start_dirmngr ();
195         }
196
197       rc = assuan_socket_connect (&ctx, infostr, pid);
198       xfree (infostr);
199       if (rc == ASSUAN_Connect_Failed)
200         {
201           log_error (_("can't connect to the dirmngr - trying fall back\n"));
202           force_pipe_server = 1;
203           return start_dirmngr ();
204         }
205     }
206
207   if (rc)
208     {
209       log_error ("can't connect to the dirmngr: %s\n", assuan_strerror (rc));
210       return seterr (No_Dirmngr);
211     }
212   dirmngr_ctx = ctx;
213
214   if (DBG_ASSUAN)
215     log_debug ("connection to dirmngr established\n");
216   return 0;
217 }
218
219
220 \f
221 /* Handle a SENDCERT inquiry. */
222 static AssuanError
223 inq_certificate (void *opaque, const char *line)
224 {
225   struct inq_certificate_parm_s *parm = opaque;
226   AssuanError rc;
227   const unsigned char *der;
228   size_t derlen;
229
230   if (!(!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8])))
231     {
232       log_error ("unsupported inquiry `%s'\n", line);
233       return ASSUAN_Inquire_Unknown;
234     }
235   line += 8;
236
237   if (!*line)
238     { /* send the current certificate */
239       der = ksba_cert_get_image (parm->cert, &derlen);
240       if (!der)
241         rc = ASSUAN_Inquire_Error;
242       else
243         rc = assuan_send_data (parm->ctx, der, derlen);
244     }
245   else 
246     { /* send the given certificate */
247       int err;
248       KsbaCert cert;
249
250       err = gpgsm_find_cert (line, &cert);
251       if (err)
252         {
253           log_error ("certificate not found: %s\n", gnupg_strerror (err));
254           rc = ASSUAN_Inquire_Error;
255         }
256       else
257         {
258           der = ksba_cert_get_image (cert, &derlen);
259           if (!der)
260             rc = ASSUAN_Inquire_Error;
261           else
262             rc = assuan_send_data (parm->ctx, der, derlen);
263           ksba_cert_release (cert);
264         }
265     }
266
267   return rc; 
268 }
269
270
271 \f
272 /* Call the directory manager to check whether the certificate is valid
273    Returns 0 for valid or usually one of the errors:
274
275   GNUPG_Certificate_Revoked
276   GNUPG_No_CRL_Known
277   GNUPG_CRL_Too_Old
278  */
279 int
280 gpgsm_dirmngr_isvalid (KsbaCert cert)
281 {
282   int rc;
283   char *certid;
284   char line[ASSUAN_LINELENGTH];
285   struct inq_certificate_parm_s parm;
286
287   rc = start_dirmngr ();
288   if (rc)
289     return rc;
290
291   certid = gpgsm_get_certid (cert);
292   if (!certid)
293     {
294       log_error ("error getting the certificate ID\n");
295       return seterr (General_Error);
296     }
297
298   parm.ctx = dirmngr_ctx;
299   parm.cert = cert;
300
301   snprintf (line, DIM(line)-1, "ISVALID %s", certid);
302   line[DIM(line)-1] = 0;
303   xfree (certid);
304
305   rc = assuan_transact (dirmngr_ctx, line, NULL, NULL,
306                         inq_certificate, &parm, NULL, NULL);
307   return map_assuan_err (rc);
308 }
309
310
311 \f
312 /* Lookup helpers*/
313 static AssuanError
314 lookup_cb (void *opaque, const void *buffer, size_t length)
315 {
316   struct lookup_parm_s *parm = opaque;
317   size_t len;
318   char *buf;
319   KsbaCert cert;
320   int rc;
321
322   if (parm->error)
323     return 0;
324
325   if (buffer)
326     {
327       put_membuf (&parm->data, buffer, length);
328       return 0;
329     }
330   /* END encountered - process what we have */
331   buf = get_membuf (&parm->data, &len);
332   if (!buf)
333     {
334       parm->error = GNUPG_Out_Of_Core;
335       return 0;
336     }
337
338   cert = ksba_cert_new ();
339   if (!cert)
340     {
341       parm->error = GNUPG_Out_Of_Core;
342       return 0;
343     }
344   rc = ksba_cert_init_from_mem (cert, buf, len);
345   if (rc)
346     {
347       log_error ("failed to parse a certificate: %s\n", ksba_strerror (rc));
348     }
349   else
350     {
351       parm->cb (parm->cb_value, cert);
352     }
353
354   ksba_cert_release (cert);
355   init_membuf (&parm->data, 4096);
356   return 0;
357 }
358
359 /* Return a properly escaped pattern from NAMES.  The only error
360    return is NULL to indicate a malloc failure. */
361 static char *
362 pattern_from_strlist (STRLIST names)
363 {
364   STRLIST sl;
365   int n;
366   const char *s;
367   char *pattern, *p;
368
369   for (n=0, sl=names; sl; sl = sl->next)
370     {
371       for (s=sl->d; *s; s++, n++)
372         {
373           if (*s == '%' || *s == ' ' || *s == '+')
374             n += 2;
375         }
376       n++;
377     }
378
379   p = pattern = xtrymalloc (n+1);
380   if (!pattern)
381     return NULL;
382
383   for (n=0, sl=names; sl; sl = sl->next)
384     {
385       for (s=sl->d; *s; s++)
386         {
387           switch (*s)
388             {
389             case '%':
390               *p++ = '%';
391               *p++ = '2';
392               *p++ = '5';
393               break;
394             case ' ':
395               *p++ = '%';
396               *p++ = '2';
397               *p++ = '0';
398               break;
399             case '+':
400               *p++ = '%';
401               *p++ = '2';
402               *p++ = 'B';
403               break;
404             default:
405               *p++ = *s;
406               break;
407             }
408         }
409       *p++ = ' ';
410     }
411   if (p == pattern)
412     *pattern = 0; /* is empty */
413   else
414     p[-1] = '\0'; /* remove trailing blank */
415   
416   return pattern;
417 }
418
419 static AssuanError
420 lookup_status_cb (void *opaque, const char *line)
421 {
422   struct lookup_parm_s *parm = opaque;
423
424   if (!strncmp (line, "TRUNCATED", 9) && (line[9]==' ' || !line[9]))
425     {
426       for (line +=9; *line == ' '; line++)
427         ;
428       gpgsm_status (parm->ctrl, STATUS_TRUNCATED, line);
429     }
430   return 0;
431 }
432
433
434 /* Run the Directroy Managers lookup command using the apptern
435    compiled from the strings given in NAMES.  The caller must provide
436    the callback CB which will be passed cert by cert. */
437 int 
438 gpgsm_dirmngr_lookup (CTRL ctrl, STRLIST names,
439                       void (*cb)(void*, KsbaCert), void *cb_value)
440
441   int rc;
442   char *pattern;
443   char line[ASSUAN_LINELENGTH];
444   struct lookup_parm_s parm;
445   size_t len;
446
447   rc = start_dirmngr ();
448   if (rc)
449     return rc;
450
451   pattern = pattern_from_strlist (names);
452   if (!pattern)
453     return GNUPG_Out_Of_Core;
454   snprintf (line, DIM(line)-1, "LOOKUP %s", pattern);
455   line[DIM(line)-1] = 0;
456   xfree (pattern);
457
458   parm.ctrl = ctrl;
459   parm.ctx = dirmngr_ctx;
460   parm.cb = cb;
461   parm.cb_value = cb_value;
462   parm.error = 0;
463   init_membuf (&parm.data, 4096);
464
465   rc = assuan_transact (dirmngr_ctx, line, lookup_cb, &parm,
466                         NULL, NULL, lookup_status_cb, &parm);
467   xfree (get_membuf (&parm.data, &len));
468   if (rc)
469     return map_assuan_err (rc);
470   return parm.error;
471 }
472
473