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