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