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