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