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