Silence more warnings about unused vars and args.
[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_NAME);
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       assuan_fd_t 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] = ASSUAN_INVALID_FD;
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 %s environment variable\n"),
506                          DIRMNGR_INFO_NAME);
507               xfree (infostr);
508               if (only_daemon)
509                 return NULL;
510               /* Try again by starting a new instance.  */
511               opt.force_pipe_server = 1;
512               return start_dirmngr (0);
513             }
514           *p++ = 0;
515           pid = atoi (p);
516           while (*p && *p != ':')
517             p++;
518           prot = *p? atoi (p+1) : 0;
519           if (prot != 1)
520             {
521               log_error (_("dirmngr protocol version %d is not supported\n"),
522                          prot);
523               xfree (infostr);
524               if (only_daemon)
525                 return NULL;
526               opt.force_pipe_server = 1;
527               return start_dirmngr (0);
528             }
529         }
530       else
531         pid = -1;
532
533       rc = assuan_socket_connect (ctx, infostr, pid, 0);
534       xfree (infostr);
535       if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon)
536         {
537           log_error (_("can't connect to the dirmngr - trying fall back\n"));
538           opt.force_pipe_server = 1;
539           return start_dirmngr (0);
540         }
541     }
542
543   if (rc)
544     {
545       assuan_release (ctx);
546       log_error (_("can't connect to the dirmngr: %s\n"),
547                  gpg_strerror (rc));
548       return NULL;
549     }
550
551   return ctx;
552 }
553
554
555 /* Read the first PEM certificate from the file FNAME.  If fname is
556    NULL the next certificate is read from stdin.  The certificate is
557    returned in an alloced buffer whose address will be returned in
558    RBUF and its length in RBUFLEN.  */
559 static gpg_error_t
560 read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
561 {
562   FILE *fp;
563   int c;
564   int pos;
565   int value;
566   unsigned char *buf;
567   size_t bufsize, buflen;
568   enum {
569     s_init, s_idle, s_lfseen, s_begin,
570     s_b64_0, s_b64_1, s_b64_2, s_b64_3,
571     s_waitend
572   } state = s_init;
573
574   fp = fname? fopen (fname, "r") : stdin;
575   if (!fp)
576     return gpg_error_from_errno (errno);
577
578   pos = 0;
579   value = 0;
580   bufsize = 8192;
581   buf = xmalloc (bufsize);
582   buflen = 0;
583   while ((c=getc (fp)) != EOF)
584     {
585       int escaped_c = 0;
586
587       if (opt.escaped_pem)
588         {
589           if (c == '%')
590             {
591               char tmp[2];
592               if ((c = getc(fp)) == EOF)
593                 break;
594               tmp[0] = c;
595               if ((c = getc(fp)) == EOF)
596                 break;
597               tmp[1] = c;
598               if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
599                 {
600                   log_error ("invalid percent escape sequence\n");
601                   state = s_idle; /* Force an error. */
602                   /* Skip to end of line.  */
603                   while ( (c=getc (fp)) != EOF && c != '\n')
604                     ;
605                   goto ready;
606                 }
607               c = xtoi_2 (tmp);
608               escaped_c = 1;
609             }
610           else if (c == '\n')
611             goto ready; /* Ready.  */
612         }
613       switch (state)
614         {
615         case s_idle:
616           if (c == '\n')
617             {
618               state = s_lfseen;
619               pos = 0;
620             }
621           break;
622         case s_init:
623           state = s_lfseen;
624         case s_lfseen:
625           if (c != "-----BEGIN "[pos])
626             state = s_idle;
627           else if (pos == 10)
628             state = s_begin;
629           else
630             pos++;
631           break;
632         case s_begin:
633           if (c == '\n')
634             state = s_b64_0;
635           break;
636         case s_b64_0:
637         case s_b64_1:
638         case s_b64_2:
639         case s_b64_3:
640           {
641             if (buflen >= bufsize)
642               {
643                 bufsize += 8192;
644                 buf = xrealloc (buf, bufsize);
645               }
646
647             if (c == '-')
648               state = s_waitend;
649             else if ((c = asctobin[c & 0xff]) == 255 )
650               ; /* Just skip invalid base64 characters. */
651             else if (state == s_b64_0)
652               {
653                 value = c << 2;
654                 state = s_b64_1;
655               }
656             else if (state == s_b64_1)
657               {
658                 value |= (c>>4)&3;
659                 buf[buflen++] = value;
660                 value = (c<<4)&0xf0;
661                 state = s_b64_2;
662               }
663             else if (state == s_b64_2)
664               {
665                 value |= (c>>2)&15;
666                 buf[buflen++] = value;
667                 value = (c<<6)&0xc0;
668                 state = s_b64_3;
669               }
670             else
671               {
672                 value |= c&0x3f;
673                 buf[buflen++] = value;
674                 state = s_b64_0;
675               }
676           }
677           break;
678         case s_waitend:
679           /* Note that we do not check that the base64 decoder has
680              been left in the expected state.  We assume that the PEM
681              header is just fine.  However we need to wait for the
682              real LF and not a trailing percent escaped one. */
683           if (c== '\n' && !escaped_c)
684             goto ready;
685           break;
686         default:
687           BUG();
688         }
689     }
690  ready:
691   if (fname)
692     fclose (fp);
693
694   if (state == s_init && c == EOF)
695     {
696       xfree (buf);
697       return gpg_error (GPG_ERR_EOF);
698     }
699   else if (state != s_waitend)
700     {
701       log_error ("no certificate or invalid encoded\n");
702       xfree (buf);
703       return gpg_error (GPG_ERR_INV_ARMOR);
704     }
705
706   *rbuf = buf;
707   *rbuflen = buflen;
708   return 0;
709 }
710
711 /* Read a binary certificate from the file FNAME.  If fname is NULL the
712    file is read from stdin.  The certificate is returned in an alloced
713    buffer whose address will be returned in RBUF and its length in
714    RBUFLEN.  */
715 static gpg_error_t
716 read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
717 {
718   gpg_error_t err;
719   FILE *fp;
720   unsigned char *buf;
721   size_t nread, bufsize, buflen;
722
723   if (opt.pem)
724     return read_pem_certificate (fname, rbuf, rbuflen);
725
726   fp = fname? fopen (fname, "rb") : stdin;
727   if (!fp)
728     return gpg_error_from_errno (errno);
729
730   buf = NULL;
731   bufsize = buflen = 0;
732 #define NCHUNK 8192
733   do
734     {
735       bufsize += NCHUNK;
736       if (!buf)
737         buf = xmalloc (bufsize);
738       else
739         buf = xrealloc (buf, bufsize);
740
741       nread = fread (buf+buflen, 1, NCHUNK, fp);
742       if (nread < NCHUNK && ferror (fp))
743         {
744           err = gpg_error_from_errno (errno);
745           xfree (buf);
746           if (fname)
747             fclose (fp);
748           return err;
749         }
750       buflen += nread;
751     }
752   while (nread == NCHUNK);
753 #undef NCHUNK
754   if (fname)
755     fclose (fp);
756   *rbuf = buf;
757   *rbuflen = buflen;
758   return 0;
759 }
760
761
762 /* Callback for the inquire fiunction to send back the certificate.  */
763 static gpg_error_t
764 inq_cert (void *opaque, const char *line)
765 {
766   struct inq_cert_parm_s *parm = opaque;
767   gpg_error_t err;
768
769   if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
770     {
771       err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
772     }
773   else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
774     {
775       /* We don't support this but dirmngr might ask for it.  So
776          simply ignore it by sending back and empty value. */
777       err = assuan_send_data (parm->ctx, NULL, 0);
778     }
779   else if (!strncmp (line, "SENDCERT_SKI", 12)
780            && (line[12]==' ' || !line[12]))
781     {
782       /* We don't support this but dirmngr might ask for it.  So
783          simply ignore it by sending back an empty value. */
784       err = assuan_send_data (parm->ctx, NULL, 0);
785     }
786   else if (!strncmp (line, "SENDISSUERCERT", 14)
787            && (line[14] == ' ' || !line[14]))
788     {
789       /* We don't support this but dirmngr might ask for it.  So
790          simply ignore it by sending back an empty value. */
791       err = assuan_send_data (parm->ctx, NULL, 0);
792     }
793   else
794     {
795       log_info (_("unsupported inquiry '%s'\n"), line);
796       err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
797       /* Note that this error will let assuan_transact terminate
798          immediately instead of return the error to the caller.  It is
799          not clear whether this is the desired behaviour - it may
800          change in future. */
801     }
802
803   return err;
804 }
805
806
807 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
808    Return a proper error code. */
809 static gpg_error_t
810 do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
811 {
812   gpg_error_t err;
813   struct inq_cert_parm_s parm;
814
815   memset (&parm, 0, sizeof parm);
816   parm.ctx = ctx;
817   parm.cert = cert;
818   parm.certlen = certlen;
819
820   err = assuan_transact (ctx,
821                          (opt.use_ocsp && opt.force_default_responder
822                           ? "CHECKOCSP --force-default-responder"
823                           : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
824                          NULL, NULL, inq_cert, &parm, status_cb, NULL);
825   if (opt.verbose > 1)
826     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
827   return err;
828 }
829
830 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
831    Return a proper error code. */
832 static gpg_error_t
833 do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
834 {
835   gpg_error_t err;
836   struct inq_cert_parm_s parm;
837
838   memset (&parm, 0, sizeof parm);
839   parm.ctx = ctx;
840   parm.cert = cert;
841   parm.certlen = certlen;
842
843   err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
844                         inq_cert, &parm,
845                         status_cb, NULL);
846   if (opt.verbose > 1)
847     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
848   return err;
849 }
850
851 /* Check the certificate CERT,CERTLEN for validity using dirmngrs
852    internal validate feature.  Return a proper error code. */
853 static gpg_error_t
854 do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
855 {
856   gpg_error_t err;
857   struct inq_cert_parm_s parm;
858
859   memset (&parm, 0, sizeof parm);
860   parm.ctx = ctx;
861   parm.cert = cert;
862   parm.certlen = certlen;
863
864   err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
865                         inq_cert, &parm,
866                         status_cb, NULL);
867   if (opt.verbose > 1)
868     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
869   return err;
870 }
871
872 /* Load a CRL into the dirmngr.  */
873 static gpg_error_t
874 do_loadcrl (assuan_context_t ctx, const char *filename)
875 {
876   gpg_error_t err;
877   const char *s;
878   char *fname, *line, *p;
879
880   if (opt.url)
881     fname = xstrdup (filename);
882   else
883     {
884 #ifdef HAVE_CANONICALIZE_FILE_NAME
885       fname = canonicalize_file_name (filename);
886       if (!fname)
887         {
888           log_error ("error canonicalizing '%s': %s\n",
889                      filename, strerror (errno));
890           return gpg_error (GPG_ERR_GENERAL);
891         }
892 #else
893       fname = xstrdup (filename);
894 #endif
895       if (*fname != '/')
896         {
897           log_error (_("absolute file name expected\n"));
898           return gpg_error (GPG_ERR_GENERAL);
899         }
900     }
901
902   line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
903   p = stpcpy (line, "LOADCRL ");
904   if (opt.url)
905     p = stpcpy (p, "--url ");
906   for (s = fname; *s; s++)
907     {
908       if (*s < ' ' || *s == '+')
909         {
910           sprintf (p, "%%%02X", *s);
911           p += 3;
912         }
913       else if (*s == ' ')
914         *p++ = '+';
915       else
916         *p++ = *s;
917         }
918   *p = 0;
919
920   err = assuan_transact (ctx, line, NULL, NULL,
921                         NULL, NULL,
922                         status_cb, NULL);
923   if (opt.verbose > 1)
924     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
925   xfree (line);
926   xfree (fname);
927   return err;
928 }
929
930
931 /* Do a LDAP lookup using PATTERN and print the result in a base-64
932    encoded format.  */
933 static gpg_error_t
934 do_lookup (assuan_context_t ctx, const char *pattern)
935 {
936   gpg_error_t err;
937   const unsigned char *s;
938   char *line, *p;
939   struct b64state state;
940
941   if (opt.verbose)
942     log_info (_("looking up '%s'\n"), pattern);
943
944   err = b64enc_start (&state, stdout, NULL);
945   if (err)
946     return err;
947
948   line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
949
950   p = stpcpy (line, "LOOKUP ");
951   if (opt.url)
952     p = stpcpy (p, "--url ");
953   if (opt.local)
954     p = stpcpy (p, "--cache-only ");
955   for (s=pattern; *s; s++)
956     {
957       if (*s < ' ' || *s == '+')
958         {
959           sprintf (p, "%%%02X", *s);
960           p += 3;
961         }
962       else if (*s == ' ')
963         *p++ = '+';
964       else
965         *p++ = *s;
966     }
967   *p = 0;
968
969
970   err = assuan_transact (ctx, line,
971                          data_cb, &state,
972                          NULL, NULL,
973                          status_cb, NULL);
974   if (opt.verbose > 1)
975     log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
976
977   err = b64enc_finish (&state);
978
979   xfree (line);
980   return err;
981 }
982
983 /* The body of an endless loop: Read a line from stdin, retrieve the
984    certificate from it, validate it and print "ERR" or "OK" to stdout.
985    Continue.  */
986 static gpg_error_t
987 squid_loop_body (assuan_context_t ctx)
988 {
989   gpg_error_t err;
990   unsigned char *certbuf;
991   size_t certbuflen = 0;
992
993   err = read_pem_certificate (NULL, &certbuf, &certbuflen);
994   if (gpg_err_code (err) == GPG_ERR_EOF)
995     return err;
996   if (err)
997     {
998       log_error (_("error reading certificate from stdin: %s\n"),
999                  gpg_strerror (err));
1000       puts ("ERROR");
1001       return 0;
1002     }
1003
1004   err = do_check (ctx, certbuf, certbuflen);
1005   xfree (certbuf);
1006   if (!err)
1007     {
1008       if (opt.verbose)
1009         log_info (_("certificate is valid\n"));
1010       puts ("OK");
1011     }
1012   else
1013     {
1014       if (!opt.quiet)
1015         {
1016           if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
1017             log_info (_("certificate has been revoked\n"));
1018           else
1019             log_error (_("certificate check failed: %s\n"),
1020                        gpg_strerror (err));
1021         }
1022       puts ("ERROR");
1023     }
1024
1025   fflush (stdout);
1026
1027   return 0;
1028 }