dirmngr: Add http proxy support for keyservers.
[gnupg.git] / dirmngr / dirmngr-client.c
1 /* dirmngr-client.c  -  A client for the dirmngr daemon
2  *      Copyright (C) 2004, 2007 g10 Code GmbH
3  *      Copyright (C) 2002, 2003 Free Software Foundation, Inc.
4  *
5  * This file is part of DirMngr.
6  *
7  * DirMngr is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * DirMngr is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stddef.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <assert.h>
30
31 #include <gpg-error.h>
32 #include <assuan.h>
33
34 #define JNLIB_NEED_LOG_LOGV
35 #include "../common/logging.h"
36 #include "../common/argparse.h"
37 #include "../common/stringhelp.h"
38 #include "../common/mischelp.h"
39 #include "../common/strlist.h"
40
41 #include "i18n.h"
42 #include "util.h"
43 #include "init.h"
44
45
46 /* Constants for the options.  */
47 enum
48   {
49     oQuiet        = 'q',
50     oVerbose      = 'v',
51     oLocal        = 'l',
52     oUrl          = 'u',
53
54     oOCSP         = 500,
55     oPing,
56     oCacheCert,
57     oValidate,
58     oLookup,
59     oLoadCRL,
60     oSquidMode,
61     oPEM,
62     oEscapedPEM,
63     oForceDefaultResponder
64   };
65
66
67 /* The list of options as used by the argparse.c code.  */
68 static ARGPARSE_OPTS opts[] = {
69   { oVerbose,  "verbose",   0, N_("verbose") },
70   { oQuiet,    "quiet",     0, N_("be somewhat more quiet") },
71   { oOCSP,     "ocsp",      0, N_("use OCSP instead of CRLs") },
72   { oPing,     "ping",      0, N_("check whether a dirmngr is running")},
73   { oCacheCert,"cache-cert",0, N_("add a certificate to the cache")},
74   { oValidate, "validate",  0, N_("validate a certificate")},
75   { oLookup,   "lookup",    0, N_("lookup a certificate")},
76   { oLocal,    "local",     0, N_("lookup only locally stored certificates")},
77   { oUrl,      "url",       0, N_("expect an URL for --lookup")},
78   { oLoadCRL,  "load-crl",  0, N_("load a CRL into the dirmngr")},
79   { oSquidMode,"squid-mode",0, N_("special mode for use by Squid")},
80   { oPEM,      "pem",       0, N_("expect certificates in PEM format")},
81   { oForceDefaultResponder, "force-default-responder", 0,
82     N_("force the use of the default OCSP responder")},
83   { 0, NULL, 0, NULL }
84 };
85
86
87 /* The usual structure for the program flags.  */
88 static struct
89 {
90   int quiet;
91   int verbose;
92   const char *dirmngr_program;
93   int force_pipe_server;
94   int force_default_responder;
95   int pem;
96   int escaped_pem; /* PEM is additional percent encoded.  */
97   int url;         /* Expect an URL.  */
98   int local;       /* Lookup up only local certificates.  */
99
100   int use_ocsp;
101 } opt;
102
103
104 /* Communication structure for the certificate inquire callback. */
105 struct inq_cert_parm_s
106 {
107   assuan_context_t ctx;
108   const unsigned char *cert;
109   size_t certlen;
110 };
111
112
113 /* Base64 conversion tables. */
114 static unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
115                                   "abcdefghijklmnopqrstuvwxyz"
116                                   "0123456789+/";
117 static unsigned char asctobin[256]; /* runtime initialized */
118
119
120 /* Prototypes.  */
121 static assuan_context_t start_dirmngr (int only_daemon);
122 static gpg_error_t read_certificate (const char *fname,
123                                      unsigned char **rbuf, size_t *rbuflen);
124 static gpg_error_t do_check (assuan_context_t ctx,
125                              const unsigned char *cert, size_t certlen);
126 static gpg_error_t do_cache (assuan_context_t ctx,
127                              const unsigned char *cert, size_t certlen);
128 static gpg_error_t do_validate (assuan_context_t ctx,
129                                 const unsigned char *cert, size_t certlen);
130 static gpg_error_t do_loadcrl (assuan_context_t ctx, const char *filename);
131 static gpg_error_t do_lookup (assuan_context_t ctx, const char *pattern);
132 static gpg_error_t squid_loop_body (assuan_context_t ctx);
133
134
135
136 /* Function called by argparse.c to display information.  */
137 static const char *
138 my_strusage (int level)
139 {
140   const char *p;
141
142   switch(level)
143     {
144     case 11: p = "dirmngr-client (@GNUPG@)";
145       break;
146     case 13: p = VERSION; break;
147     case 17: p = PRINTABLE_OS_NAME; break;
148     case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
149     case 49: p = PACKAGE_BUGREPORT; break;
150     case 1:
151     case 40: p =
152                  _("Usage: dirmngr-client [options] "
153                    "[certfile|pattern] (-h for help)\n");
154       break;
155     case 41: p =
156           _("Syntax: dirmngr-client [options] [certfile|pattern]\n"
157             "Test an X.509 certificate against a CRL or do an OCSP check\n"
158             "The process returns 0 if the certificate is valid, 1 if it is\n"
159             "not valid and other error codes for general failures\n");
160       break;
161
162     default: p = NULL;
163     }
164   return p;
165 }
166
167
168
169 int
170 main (int argc, char **argv )
171 {
172   ARGPARSE_ARGS pargs;
173   assuan_context_t ctx;
174   gpg_error_t err;
175   unsigned char *certbuf;
176   size_t certbuflen = 0;
177   int cmd_ping = 0;
178   int cmd_cache_cert = 0;
179   int cmd_validate = 0;
180   int cmd_lookup = 0;
181   int cmd_loadcrl = 0;
182   int cmd_squid_mode = 0;
183
184   early_system_init ();
185   set_strusage (my_strusage);
186   log_set_prefix ("dirmngr-client",
187                   JNLIB_LOG_WITH_PREFIX);
188
189   /* For W32 we need to initialize the socket subsystem.  Becuase we
190      don't use Pth we need to do this explicit. */
191 #ifdef HAVE_W32_SYSTEM
192  {
193    WSADATA wsadat;
194
195    WSAStartup (0x202, &wsadat);
196  }
197 #endif /*HAVE_W32_SYSTEM*/
198
199   /* Init Assuan.  */
200   assuan_set_assuan_log_prefix (log_get_prefix (NULL));
201   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
202
203   /* Setup I18N. */
204   i18n_init();
205
206   /* Parse the command line.  */
207   pargs.argc = &argc;
208   pargs.argv = &argv;
209   pargs.flags= 1;  /* Do not remove the args. */
210   while (arg_parse (&pargs, opts) )
211     {
212       switch (pargs.r_opt)
213         {
214         case oVerbose: opt.verbose++; break;
215         case oQuiet: opt.quiet++; break;
216
217         case oOCSP: opt.use_ocsp++; break;
218         case oPing: cmd_ping = 1; break;
219         case oCacheCert: cmd_cache_cert = 1; break;
220         case oValidate: cmd_validate = 1; break;
221         case oLookup: cmd_lookup = 1; break;
222         case oUrl: opt.url = 1; break;
223         case oLocal: opt.local = 1; break;
224         case oLoadCRL: cmd_loadcrl = 1; break;
225         case oPEM: opt.pem = 1; break;
226         case oSquidMode:
227           opt.pem = 1;
228           opt.escaped_pem = 1;
229           cmd_squid_mode = 1;
230           break;
231         case oForceDefaultResponder: opt.force_default_responder = 1; break;
232
233         default : pargs.err = 2; break;
234         }
235     }
236   if (log_get_errorcount (0))
237     exit (2);
238
239   /* Build the helptable for radix64 to bin conversion. */
240   if (opt.pem)
241     {
242       int i;
243       unsigned char *s;
244
245       for (i=0; i < 256; i++ )
246         asctobin[i] = 255; /* Used to detect invalid characters. */
247       for (s=bintoasc, i=0; *s; s++, i++)
248         asctobin[*s] = i;
249     }
250
251
252   if (cmd_ping)
253     err = 0;
254   else if (cmd_lookup || cmd_loadcrl)
255     {
256       if (!argc)
257         usage (1);
258       err = 0;
259     }
260   else if (cmd_squid_mode)
261     {
262       err = 0;
263       if (argc)
264         usage (1);
265     }
266   else if (!argc)
267     {
268       err = read_certificate (NULL, &certbuf, &certbuflen);
269       if (err)
270         log_error (_("error reading certificate from stdin: %s\n"),
271                    gpg_strerror (err));
272     }
273   else if (argc == 1)
274     {
275       err = read_certificate (*argv, &certbuf, &certbuflen);
276       if (err)
277         log_error (_("error reading certificate from '%s': %s\n"),
278                    *argv, gpg_strerror (err));
279     }
280   else
281     {
282       err = 0;
283       usage (1);
284     }
285
286   if (log_get_errorcount (0))
287     exit (2);
288
289   if (certbuflen > 20000)
290     {
291       log_error (_("certificate too large to make any sense\n"));
292       exit (2);
293     }
294
295   ctx = start_dirmngr (1);
296   if (!ctx)
297     exit (2);
298
299   if (cmd_ping)
300     ;
301   else if (cmd_squid_mode)
302     {
303       while (!(err = squid_loop_body (ctx)))
304         ;
305       if (gpg_err_code (err) == GPG_ERR_EOF)
306         err = 0;
307     }
308   else if (cmd_lookup)
309     {
310       int last_err = 0;
311
312       for (; argc; argc--, argv++)
313         {
314           err = do_lookup (ctx, *argv);
315           if (err)
316             {
317               log_error (_("lookup failed: %s\n"), gpg_strerror (err));
318               last_err = err;
319             }
320         }
321       err = last_err;
322     }
323   else if (cmd_loadcrl)
324     {
325       int last_err = 0;
326
327       for (; argc; argc--, argv++)
328         {
329           err = do_loadcrl (ctx, *argv);
330           if (err)
331             {
332               log_error (_("loading CRL '%s' failed: %s\n"),
333                          *argv, gpg_strerror (err));
334               last_err = err;
335             }
336         }
337       err = last_err;
338     }
339   else if (cmd_cache_cert)
340     {
341       err = do_cache (ctx, certbuf, certbuflen);
342       xfree (certbuf);
343     }
344   else if (cmd_validate)
345     {
346       err = do_validate (ctx, certbuf, certbuflen);
347       xfree (certbuf);
348     }
349   else
350     {
351       err = do_check (ctx, certbuf, certbuflen);
352       xfree (certbuf);
353     }
354
355   assuan_release (ctx);
356
357   if (cmd_ping)
358     {
359       if (!opt.quiet)
360         log_info (_("a dirmngr daemon is up and running\n"));
361       return 0;
362     }
363   else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
364     return err? 1:0;
365   else if (cmd_cache_cert)
366     {
367       if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
368         {
369           if (!opt.quiet)
370             log_info (_("certificate already cached\n"));
371         }
372       else if (err)
373         {
374           log_error (_("error caching certificate: %s\n"),
375                      gpg_strerror (err));
376           return 1;
377         }
378       return 0;
379     }
380   else if (cmd_validate && err)
381     {
382       log_error (_("validation of certificate failed: %s\n"),
383                  gpg_strerror (err));
384       return 1;
385     }
386   else if (!err)
387     {
388       if (!opt.quiet)
389         log_info (_("certificate is valid\n"));
390       return 0;
391     }
392   else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
393     {
394       if (!opt.quiet)
395         log_info (_("certificate has been revoked\n"));
396       return 1;
397     }
398   else
399     {
400       log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
401       return 2;
402     }
403 }
404
405
406 /* Print status line from the assuan protocol.  */
407 static gpg_error_t
408 status_cb (void *opaque, const char *line)
409 {
410   (void)opaque;
411
412   if (opt.verbose > 2)
413     log_info (_("got status: '%s'\n"), line);
414   return 0;
415 }
416
417 /* Print data as retrieved by the lookup function.  */
418 static gpg_error_t
419 data_cb (void *opaque, const void *buffer, size_t length)
420 {
421   gpg_error_t err;
422   struct b64state *state = opaque;
423
424   if (buffer)
425     {
426       err = b64enc_write (state, buffer, length);
427       if (err)
428         log_error (_("error writing base64 encoding: %s\n"),
429                    gpg_strerror (err));
430     }
431   return 0;
432 }
433
434
435 /* Try to connect to the dirmngr via socket or fork it off and work by
436    pipes.  Handle the server's initial greeting */
437 static assuan_context_t
438 start_dirmngr (int only_daemon)
439 {
440   int rc;
441   char *infostr, *p;
442   assuan_context_t ctx;
443   int try_default = 0;
444
445   infostr = opt.force_pipe_server? NULL : getenv (DIRMNGR_INFO_NAME);
446   if (only_daemon && (!infostr || !*infostr))
447     {
448       if (dirmngr_user_socket_name ())
449         infostr = xstrdup (dirmngr_user_socket_name ());
450       else
451         infostr = xstrdup (dirmngr_sys_socket_name ());
452       try_default = 1;
453     }
454
455   rc = assuan_new (&ctx);
456   if (rc)
457     {
458       log_error (_("failed to allocate assuan context: %s\n"),
459                  gpg_strerror (rc));
460       return NULL;
461     }
462
463   if (!infostr || !*infostr)
464     {
465       const char *pgmname;
466       const char *argv[3];
467       assuan_fd_t no_close_list[3];
468       int i;
469
470       if (only_daemon)
471         {
472           log_error (_("apparently no running dirmngr\n"));
473           return NULL;
474         }
475
476       if (opt.verbose)
477         log_info (_("no running dirmngr - starting one\n"));
478
479       if (!opt.dirmngr_program || !*opt.dirmngr_program)
480         opt.dirmngr_program = "./dirmngr";
481       if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
482         pgmname = opt.dirmngr_program;
483       else
484         pgmname++;
485
486       argv[0] = pgmname;
487       argv[1] = "--server";
488       argv[2] = NULL;
489
490       i=0;
491       if (log_get_fd () != -1)
492         no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
493       no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
494       no_close_list[i] = ASSUAN_INVALID_FD;
495
496       /* Connect to the agent and perform initial handshaking.  */
497       rc = assuan_pipe_connect (ctx, opt.dirmngr_program, argv,
498                                 no_close_list, NULL, NULL, 0);
499     }
500   else /* Connect to a daemon.  */
501     {
502       int prot;
503       int pid;
504
505       infostr = xstrdup (infostr);
506       if (!try_default && *infostr)
507         {
508           if ( !(p = strchr (infostr, ':')) || p == infostr)
509             {
510               log_error (_("malformed %s environment variable\n"),
511                          DIRMNGR_INFO_NAME);
512               xfree (infostr);
513               if (only_daemon)
514                 return NULL;
515               /* Try again by starting a new instance.  */
516               opt.force_pipe_server = 1;
517               return start_dirmngr (0);
518             }
519           *p++ = 0;
520           pid = atoi (p);
521           while (*p && *p != ':')
522             p++;
523           prot = *p? atoi (p+1) : 0;
524           if (prot != 1)
525             {
526               log_error (_("dirmngr protocol version %d is not supported\n"),
527                          prot);
528               xfree (infostr);
529               if (only_daemon)
530                 return NULL;
531               opt.force_pipe_server = 1;
532               return start_dirmngr (0);
533             }
534         }
535       else
536         pid = -1;
537
538       rc = assuan_socket_connect (ctx, infostr, pid, 0);
539       xfree (infostr);
540       if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon)
541         {
542           log_error (_("can't connect to the dirmngr - trying fall back\n"));
543           opt.force_pipe_server = 1;
544           return start_dirmngr (0);
545         }
546     }
547
548   if (rc)
549     {
550       assuan_release (ctx);
551       log_error (_("can't connect to the dirmngr: %s\n"),
552                  gpg_strerror (rc));
553       return NULL;
554     }
555
556   return ctx;
557 }
558
559
560 /* Read the first PEM certificate from the file FNAME.  If fname is
561    NULL the next certificate is read from stdin.  The certificate is
562    returned in an alloced buffer whose address will be returned in
563    RBUF and its length in RBUFLEN.  */
564 static gpg_error_t
565 read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
566 {
567   FILE *fp;
568   int c;
569   int pos;
570   int value;
571   unsigned char *buf;
572   size_t bufsize, buflen;
573   enum {
574     s_init, s_idle, s_lfseen, s_begin,
575     s_b64_0, s_b64_1, s_b64_2, s_b64_3,
576     s_waitend
577   } state = s_init;
578
579   fp = fname? fopen (fname, "r") : stdin;
580   if (!fp)
581     return gpg_error_from_errno (errno);
582
583   pos = 0;
584   value = 0;
585   bufsize = 8192;
586   buf = xmalloc (bufsize);
587   buflen = 0;
588   while ((c=getc (fp)) != EOF)
589     {
590       int escaped_c = 0;
591
592       if (opt.escaped_pem)
593         {
594           if (c == '%')
595             {
596               char tmp[2];
597               if ((c = getc(fp)) == EOF)
598                 break;
599               tmp[0] = c;
600               if ((c = getc(fp)) == EOF)
601                 break;
602               tmp[1] = c;
603               if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
604                 {
605                   log_error ("invalid percent escape sequence\n");
606                   state = s_idle; /* Force an error. */
607                   /* Skip to end of line.  */
608                   while ( (c=getc (fp)) != EOF && c != '\n')
609                     ;
610                   goto ready;
611                 }
612               c = xtoi_2 (tmp);
613               escaped_c = 1;
614             }
615           else if (c == '\n')
616             goto ready; /* Ready.  */
617         }
618       switch (state)
619         {
620         case s_idle:
621           if (c == '\n')
622             {
623               state = s_lfseen;
624               pos = 0;
625             }
626           break;
627         case s_init:
628           state = s_lfseen;
629         case s_lfseen:
630           if (c != "-----BEGIN "[pos])
631             state = s_idle;
632           else if (pos == 10)
633             state = s_begin;
634           else
635             pos++;
636           break;
637         case s_begin:
638           if (c == '\n')
639             state = s_b64_0;
640           break;
641         case s_b64_0:
642         case s_b64_1:
643         case s_b64_2:
644         case s_b64_3:
645           {
646             if (buflen >= bufsize)
647               {
648                 bufsize += 8192;
649                 buf = xrealloc (buf, bufsize);
650               }
651
652             if (c == '-')
653               state = s_waitend;
654             else if ((c = asctobin[c & 0xff]) == 255 )
655               ; /* Just skip invalid base64 characters. */
656             else if (state == s_b64_0)
657               {
658                 value = c << 2;
659                 state = s_b64_1;
660               }
661             else if (state == s_b64_1)
662               {
663                 value |= (c>>4)&3;
664                 buf[buflen++] = value;
665                 value = (c<<4)&0xf0;
666                 state = s_b64_2;
667               }
668             else if (state == s_b64_2)
669               {
670                 value |= (c>>2)&15;
671                 buf[buflen++] = value;
672                 value = (c<<6)&0xc0;
673                 state = s_b64_3;
674               }
675             else
676               {
677                 value |= c&0x3f;
678                 buf[buflen++] = value;
679                 state = s_b64_0;
680               }
681           }
682           break;
683         case s_waitend:
684           /* Note that we do not check that the base64 decoder has
685              been left in the expected state.  We assume that the PEM
686              header is just fine.  However we need to wait for the
687              real LF and not a trailing percent escaped one. */
688           if (c== '\n' && !escaped_c)
689             goto ready;
690           break;
691         default:
692           BUG();
693         }
694     }
695  ready:
696   if (fname)
697     fclose (fp);
698
699   if (state == s_init && c == EOF)
700     {
701       xfree (buf);
702       return gpg_error (GPG_ERR_EOF);
703     }
704   else if (state != s_waitend)
705     {
706       log_error ("no certificate or invalid encoded\n");
707       xfree (buf);
708       return gpg_error (GPG_ERR_INV_ARMOR);
709     }
710
711   *rbuf = buf;
712   *rbuflen = buflen;
713   return 0;
714 }
715
716 /* Read a binary certificate from the file FNAME.  If fname is NULL the
717    file is read from stdin.  The certificate is returned in an alloced
718    buffer whose address will be returned in RBUF and its length in
719    RBUFLEN.  */
720 static gpg_error_t
721 read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
722 {
723   gpg_error_t err;
724   FILE *fp;
725   unsigned char *buf;
726   size_t nread, bufsize, buflen;
727
728   if (opt.pem)
729     return read_pem_certificate (fname, rbuf, rbuflen);
730
731   fp = fname? fopen (fname, "rb") : stdin;
732   if (!fp)
733     return gpg_error_from_errno (errno);
734
735   buf = NULL;
736   bufsize = buflen = 0;
737 #define NCHUNK 8192
738   do
739     {
740       bufsize += NCHUNK;
741       if (!buf)
742         buf = xmalloc (bufsize);
743       else
744         buf = xrealloc (buf, bufsize);
745
746       nread = fread (buf+buflen, 1, NCHUNK, fp);
747       if (nread < NCHUNK && ferror (fp))
748         {
749           err = gpg_error_from_errno (errno);
750           xfree (buf);
751           if (fname)
752             fclose (fp);
753           return err;
754         }
755       buflen += nread;
756     }
757   while (nread == NCHUNK);
758 #undef NCHUNK
759   if (fname)
760     fclose (fp);
761   *rbuf = buf;
762   *rbuflen = buflen;
763   return 0;
764 }
765
766
767 /* Callback for the inquire fiunction to send back the certificate.  */
768 static gpg_error_t
769 inq_cert (void *opaque, const char *line)
770 {
771   struct inq_cert_parm_s *parm = opaque;
772   gpg_error_t err;
773
774   if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
775     {
776       err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
777     }
778   else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
779     {
780       /* We don't support this but dirmngr might ask for it.  So
781          simply ignore it by sending back and empty value. */
782       err = assuan_send_data (parm->ctx, NULL, 0);
783     }
784   else if (!strncmp (line, "SENDCERT_SKI", 12)
785            && (line[12]==' ' || !line[12]))
786     {
787       /* We don't support this but dirmngr might ask for it.  So
788          simply ignore it by sending back an empty value. */
789       err = assuan_send_data (parm->ctx, NULL, 0);
790     }
791   else if (!strncmp (line, "SENDISSUERCERT", 14)
792            && (line[14] == ' ' || !line[14]))
793     {
794       /* We don't support this but dirmngr might ask for it.  So
795          simply ignore it by sending back an empty value. */
796       err = assuan_send_data (parm->ctx, NULL, 0);
797     }
798   else
799     {
800       log_info (_("unsupported inquiry '%s'\n"), line);
801       err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
802       /* Note that this error will let assuan_transact terminate
803          immediately instead of return the error to the caller.  It is
804          not clear whether this is the desired behaviour - it may
805          change in future. */
806     }
807
808   return err;
809 }
810
811
812 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
813    Return a proper error code. */
814 static gpg_error_t
815 do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
816 {
817   gpg_error_t err;
818   struct inq_cert_parm_s parm;
819
820   memset (&parm, 0, sizeof parm);
821   parm.ctx = ctx;
822   parm.cert = cert;
823   parm.certlen = certlen;
824
825   err = assuan_transact (ctx,
826                          (opt.use_ocsp && opt.force_default_responder
827                           ? "CHECKOCSP --force-default-responder"
828                           : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
829                          NULL, NULL, inq_cert, &parm, status_cb, NULL);
830   if (opt.verbose > 1)
831     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
832   return err;
833 }
834
835 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
836    Return a proper error code. */
837 static gpg_error_t
838 do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
839 {
840   gpg_error_t err;
841   struct inq_cert_parm_s parm;
842
843   memset (&parm, 0, sizeof parm);
844   parm.ctx = ctx;
845   parm.cert = cert;
846   parm.certlen = certlen;
847
848   err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
849                         inq_cert, &parm,
850                         status_cb, NULL);
851   if (opt.verbose > 1)
852     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
853   return err;
854 }
855
856 /* Check the certificate CERT,CERTLEN for validity using dirmngrs
857    internal validate feature.  Return a proper error code. */
858 static gpg_error_t
859 do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
860 {
861   gpg_error_t err;
862   struct inq_cert_parm_s parm;
863
864   memset (&parm, 0, sizeof parm);
865   parm.ctx = ctx;
866   parm.cert = cert;
867   parm.certlen = certlen;
868
869   err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
870                         inq_cert, &parm,
871                         status_cb, NULL);
872   if (opt.verbose > 1)
873     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
874   return err;
875 }
876
877 /* Load a CRL into the dirmngr.  */
878 static gpg_error_t
879 do_loadcrl (assuan_context_t ctx, const char *filename)
880 {
881   gpg_error_t err;
882   const char *s;
883   char *fname, *line, *p;
884
885   if (opt.url)
886     fname = xstrdup (filename);
887   else
888     {
889 #ifdef HAVE_CANONICALIZE_FILE_NAME
890       fname = canonicalize_file_name (filename);
891       if (!fname)
892         {
893           log_error ("error canonicalizing '%s': %s\n",
894                      filename, strerror (errno));
895           return gpg_error (GPG_ERR_GENERAL);
896         }
897 #else
898       fname = xstrdup (filename);
899 #endif
900       if (*fname != '/')
901         {
902           log_error (_("absolute file name expected\n"));
903           return gpg_error (GPG_ERR_GENERAL);
904         }
905     }
906
907   line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
908   p = stpcpy (line, "LOADCRL ");
909   if (opt.url)
910     p = stpcpy (p, "--url ");
911   for (s = fname; *s; s++)
912     {
913       if (*s < ' ' || *s == '+')
914         {
915           sprintf (p, "%%%02X", *s);
916           p += 3;
917         }
918       else if (*s == ' ')
919         *p++ = '+';
920       else
921         *p++ = *s;
922         }
923   *p = 0;
924
925   err = assuan_transact (ctx, line, NULL, NULL,
926                         NULL, NULL,
927                         status_cb, NULL);
928   if (opt.verbose > 1)
929     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
930   xfree (line);
931   xfree (fname);
932   return err;
933 }
934
935
936 /* Do a LDAP lookup using PATTERN and print the result in a base-64
937    encoded format.  */
938 static gpg_error_t
939 do_lookup (assuan_context_t ctx, const char *pattern)
940 {
941   gpg_error_t err;
942   const unsigned char *s;
943   char *line, *p;
944   struct b64state state;
945
946   if (opt.verbose)
947     log_info (_("looking up '%s'\n"), pattern);
948
949   err = b64enc_start (&state, stdout, NULL);
950   if (err)
951     return err;
952
953   line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
954
955   p = stpcpy (line, "LOOKUP ");
956   if (opt.url)
957     p = stpcpy (p, "--url ");
958   if (opt.local)
959     p = stpcpy (p, "--cache-only ");
960   for (s=pattern; *s; s++)
961     {
962       if (*s < ' ' || *s == '+')
963         {
964           sprintf (p, "%%%02X", *s);
965           p += 3;
966         }
967       else if (*s == ' ')
968         *p++ = '+';
969       else
970         *p++ = *s;
971     }
972   *p = 0;
973
974
975   err = assuan_transact (ctx, line,
976                          data_cb, &state,
977                          NULL, NULL,
978                          status_cb, NULL);
979   if (opt.verbose > 1)
980     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
981
982   err = b64enc_finish (&state);
983
984   xfree (line);
985   return err;
986 }
987
988 /* The body of an endless loop: Read a line from stdin, retrieve the
989    certificate from it, validate it and print "ERR" or "OK" to stdout.
990    Continue.  */
991 static gpg_error_t
992 squid_loop_body (assuan_context_t ctx)
993 {
994   gpg_error_t err;
995   unsigned char *certbuf;
996   size_t certbuflen = 0;
997
998   err = read_pem_certificate (NULL, &certbuf, &certbuflen);
999   if (gpg_err_code (err) == GPG_ERR_EOF)
1000     return err;
1001   if (err)
1002     {
1003       log_error (_("error reading certificate from stdin: %s\n"),
1004                  gpg_strerror (err));
1005       puts ("ERROR");
1006       return 0;
1007     }
1008
1009   err = do_check (ctx, certbuf, certbuflen);
1010   xfree (certbuf);
1011   if (!err)
1012     {
1013       if (opt.verbose)
1014         log_info (_("certificate is valid\n"));
1015       puts ("OK");
1016     }
1017   else
1018     {
1019       if (!opt.quiet)
1020         {
1021           if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
1022             log_info (_("certificate has been revoked\n"));
1023           else
1024             log_error (_("certificate check failed: %s\n"),
1025                        gpg_strerror (err));
1026         }
1027       puts ("ERROR");
1028     }
1029
1030   fflush (stdout);
1031
1032   return 0;
1033 }