Line data Source code
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 0 : my_strusage (int level)
138 : {
139 : const char *p;
140 :
141 0 : switch(level)
142 : {
143 0 : case 11: p = "dirmngr-client (@GNUPG@)";
144 0 : break;
145 0 : case 13: p = VERSION; break;
146 0 : case 17: p = PRINTABLE_OS_NAME; break;
147 0 : case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
148 0 : case 49: p = PACKAGE_BUGREPORT; break;
149 : case 1:
150 0 : case 40: p =
151 : _("Usage: dirmngr-client [options] "
152 : "[certfile|pattern] (-h for help)\n");
153 0 : break;
154 0 : 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 0 : break;
160 :
161 0 : default: p = NULL;
162 : }
163 0 : return p;
164 : }
165 :
166 :
167 :
168 : int
169 0 : 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 0 : size_t certbuflen = 0;
176 0 : int cmd_ping = 0;
177 0 : int cmd_cache_cert = 0;
178 0 : int cmd_validate = 0;
179 0 : int cmd_lookup = 0;
180 0 : int cmd_loadcrl = 0;
181 0 : int cmd_squid_mode = 0;
182 :
183 0 : early_system_init ();
184 0 : set_strusage (my_strusage);
185 0 : log_set_prefix ("dirmngr-client",
186 : GPGRT_LOG_WITH_PREFIX);
187 :
188 : /* For W32 we need to initialize the socket subsystem. Becuase 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 0 : assuan_set_assuan_log_prefix (log_get_prefix (NULL));
200 0 : assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
201 :
202 : /* Setup I18N. */
203 0 : i18n_init();
204 :
205 : /* Parse the command line. */
206 0 : pargs.argc = &argc;
207 0 : pargs.argv = &argv;
208 0 : pargs.flags= 1; /* Do not remove the args. */
209 0 : while (arg_parse (&pargs, opts) )
210 : {
211 0 : switch (pargs.r_opt)
212 : {
213 0 : case oVerbose: opt.verbose++; break;
214 0 : case oQuiet: opt.quiet++; break;
215 :
216 0 : case oOCSP: opt.use_ocsp++; break;
217 0 : case oPing: cmd_ping = 1; break;
218 0 : case oCacheCert: cmd_cache_cert = 1; break;
219 0 : case oValidate: cmd_validate = 1; break;
220 0 : case oLookup: cmd_lookup = 1; break;
221 0 : case oUrl: opt.url = 1; break;
222 0 : case oLocal: opt.local = 1; break;
223 0 : case oLoadCRL: cmd_loadcrl = 1; break;
224 0 : case oPEM: opt.pem = 1; break;
225 : case oSquidMode:
226 0 : opt.pem = 1;
227 0 : opt.escaped_pem = 1;
228 0 : cmd_squid_mode = 1;
229 0 : break;
230 0 : case oForceDefaultResponder: opt.force_default_responder = 1; break;
231 :
232 0 : default : pargs.err = 2; break;
233 : }
234 : }
235 0 : if (log_get_errorcount (0))
236 0 : exit (2);
237 :
238 : /* Build the helptable for radix64 to bin conversion. */
239 0 : if (opt.pem)
240 : {
241 : int i;
242 : unsigned char *s;
243 :
244 0 : for (i=0; i < 256; i++ )
245 0 : asctobin[i] = 255; /* Used to detect invalid characters. */
246 0 : for (s=bintoasc, i=0; *s; s++, i++)
247 0 : asctobin[*s] = i;
248 : }
249 :
250 :
251 0 : if (cmd_ping)
252 0 : err = 0;
253 0 : else if (cmd_lookup || cmd_loadcrl)
254 : {
255 0 : if (!argc)
256 0 : usage (1);
257 0 : err = 0;
258 : }
259 0 : else if (cmd_squid_mode)
260 : {
261 0 : err = 0;
262 0 : if (argc)
263 0 : usage (1);
264 : }
265 0 : else if (!argc)
266 : {
267 0 : err = read_certificate (NULL, &certbuf, &certbuflen);
268 0 : if (err)
269 0 : log_error (_("error reading certificate from stdin: %s\n"),
270 : gpg_strerror (err));
271 : }
272 0 : else if (argc == 1)
273 : {
274 0 : err = read_certificate (*argv, &certbuf, &certbuflen);
275 0 : if (err)
276 0 : log_error (_("error reading certificate from '%s': %s\n"),
277 : *argv, gpg_strerror (err));
278 : }
279 : else
280 : {
281 0 : err = 0;
282 0 : usage (1);
283 : }
284 :
285 0 : if (log_get_errorcount (0))
286 0 : exit (2);
287 :
288 0 : if (certbuflen > 20000)
289 : {
290 0 : log_error (_("certificate too large to make any sense\n"));
291 0 : exit (2);
292 : }
293 :
294 0 : ctx = start_dirmngr (1);
295 0 : if (!ctx)
296 0 : exit (2);
297 :
298 0 : if (cmd_ping)
299 : ;
300 0 : else if (cmd_squid_mode)
301 : {
302 0 : while (!(err = squid_loop_body (ctx)))
303 : ;
304 0 : if (gpg_err_code (err) == GPG_ERR_EOF)
305 0 : err = 0;
306 : }
307 0 : else if (cmd_lookup)
308 : {
309 0 : int last_err = 0;
310 :
311 0 : for (; argc; argc--, argv++)
312 : {
313 0 : err = do_lookup (ctx, *argv);
314 0 : if (err)
315 : {
316 0 : log_error (_("lookup failed: %s\n"), gpg_strerror (err));
317 0 : last_err = err;
318 : }
319 : }
320 0 : err = last_err;
321 : }
322 0 : else if (cmd_loadcrl)
323 : {
324 0 : int last_err = 0;
325 :
326 0 : for (; argc; argc--, argv++)
327 : {
328 0 : err = do_loadcrl (ctx, *argv);
329 0 : if (err)
330 : {
331 0 : log_error (_("loading CRL '%s' failed: %s\n"),
332 : *argv, gpg_strerror (err));
333 0 : last_err = err;
334 : }
335 : }
336 0 : err = last_err;
337 : }
338 0 : else if (cmd_cache_cert)
339 : {
340 0 : err = do_cache (ctx, certbuf, certbuflen);
341 0 : xfree (certbuf);
342 : }
343 0 : else if (cmd_validate)
344 : {
345 0 : err = do_validate (ctx, certbuf, certbuflen);
346 0 : xfree (certbuf);
347 : }
348 : else
349 : {
350 0 : err = do_check (ctx, certbuf, certbuflen);
351 0 : xfree (certbuf);
352 : }
353 :
354 0 : assuan_release (ctx);
355 :
356 0 : if (cmd_ping)
357 : {
358 0 : if (!opt.quiet)
359 0 : log_info (_("a dirmngr daemon is up and running\n"));
360 0 : return 0;
361 : }
362 0 : else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
363 0 : return err? 1:0;
364 0 : else if (cmd_cache_cert)
365 : {
366 0 : if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
367 : {
368 0 : if (!opt.quiet)
369 0 : log_info (_("certificate already cached\n"));
370 : }
371 0 : else if (err)
372 : {
373 0 : log_error (_("error caching certificate: %s\n"),
374 : gpg_strerror (err));
375 0 : return 1;
376 : }
377 0 : return 0;
378 : }
379 0 : else if (cmd_validate && err)
380 : {
381 0 : log_error (_("validation of certificate failed: %s\n"),
382 : gpg_strerror (err));
383 0 : return 1;
384 : }
385 0 : else if (!err)
386 : {
387 0 : if (!opt.quiet)
388 0 : log_info (_("certificate is valid\n"));
389 0 : return 0;
390 : }
391 0 : else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
392 : {
393 0 : if (!opt.quiet)
394 0 : log_info (_("certificate has been revoked\n"));
395 0 : return 1;
396 : }
397 : else
398 : {
399 0 : log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
400 0 : return 2;
401 : }
402 : }
403 :
404 :
405 : /* Print status line from the assuan protocol. */
406 : static gpg_error_t
407 0 : status_cb (void *opaque, const char *line)
408 : {
409 : (void)opaque;
410 :
411 0 : if (opt.verbose > 2)
412 0 : log_info (_("got status: '%s'\n"), line);
413 0 : return 0;
414 : }
415 :
416 : /* Print data as retrieved by the lookup function. */
417 : static gpg_error_t
418 0 : data_cb (void *opaque, const void *buffer, size_t length)
419 : {
420 : gpg_error_t err;
421 0 : struct b64state *state = opaque;
422 :
423 0 : if (buffer)
424 : {
425 0 : err = b64enc_write (state, buffer, length);
426 0 : if (err)
427 0 : log_error (_("error writing base64 encoding: %s\n"),
428 : gpg_strerror (err));
429 : }
430 0 : 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 0 : start_dirmngr (int only_daemon)
438 : {
439 : int rc;
440 : char *infostr, *p;
441 : assuan_context_t ctx;
442 0 : int try_default = 0;
443 :
444 0 : infostr = opt.force_pipe_server? NULL : getenv (DIRMNGR_INFO_NAME);
445 0 : if (only_daemon && (!infostr || !*infostr))
446 : {
447 0 : if (dirmngr_user_socket_name ())
448 0 : infostr = xstrdup (dirmngr_user_socket_name ());
449 : else
450 0 : infostr = xstrdup (dirmngr_sys_socket_name ());
451 0 : try_default = 1;
452 : }
453 :
454 0 : rc = assuan_new (&ctx);
455 0 : if (rc)
456 : {
457 0 : log_error (_("failed to allocate assuan context: %s\n"),
458 : gpg_strerror (rc));
459 0 : return NULL;
460 : }
461 :
462 0 : if (!infostr || !*infostr)
463 0 : {
464 : const char *pgmname;
465 : const char *argv[3];
466 : assuan_fd_t no_close_list[3];
467 : int i;
468 :
469 0 : if (only_daemon)
470 : {
471 0 : log_error (_("apparently no running dirmngr\n"));
472 0 : return NULL;
473 : }
474 :
475 0 : if (opt.verbose)
476 0 : log_info (_("no running dirmngr - starting one\n"));
477 :
478 0 : if (!opt.dirmngr_program || !*opt.dirmngr_program)
479 0 : opt.dirmngr_program = "./dirmngr";
480 0 : if ( !(pgmname = strrchr (opt.dirmngr_program, '/')))
481 0 : pgmname = opt.dirmngr_program;
482 : else
483 0 : pgmname++;
484 :
485 0 : argv[0] = pgmname;
486 0 : argv[1] = "--server";
487 0 : argv[2] = NULL;
488 :
489 0 : i=0;
490 0 : if (log_get_fd () != -1)
491 0 : no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
492 0 : no_close_list[i++] = assuan_fd_from_posix_fd (es_fileno (es_stderr));
493 0 : no_close_list[i] = ASSUAN_INVALID_FD;
494 :
495 : /* Connect to the agent and perform initial handshaking. */
496 0 : 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 0 : infostr = xstrdup (infostr);
505 0 : if (!try_default && *infostr)
506 : {
507 0 : if ( !(p = strchr (infostr, ':')) || p == infostr)
508 : {
509 0 : log_error (_("malformed %s environment variable\n"),
510 : DIRMNGR_INFO_NAME);
511 0 : xfree (infostr);
512 0 : if (only_daemon)
513 0 : return NULL;
514 : /* Try again by starting a new instance. */
515 0 : opt.force_pipe_server = 1;
516 0 : return start_dirmngr (0);
517 : }
518 0 : *p++ = 0;
519 0 : pid = atoi (p);
520 0 : while (*p && *p != ':')
521 0 : p++;
522 0 : prot = *p? atoi (p+1) : 0;
523 0 : if (prot != 1)
524 : {
525 0 : log_error (_("dirmngr protocol version %d is not supported\n"),
526 : prot);
527 0 : xfree (infostr);
528 0 : if (only_daemon)
529 0 : return NULL;
530 0 : opt.force_pipe_server = 1;
531 0 : return start_dirmngr (0);
532 : }
533 : }
534 : else
535 0 : pid = -1;
536 :
537 0 : rc = assuan_socket_connect (ctx, infostr, pid, 0);
538 0 : xfree (infostr);
539 0 : if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED && !only_daemon)
540 : {
541 0 : log_error (_("can't connect to the dirmngr - trying fall back\n"));
542 0 : opt.force_pipe_server = 1;
543 0 : return start_dirmngr (0);
544 : }
545 : }
546 :
547 0 : if (rc)
548 : {
549 0 : assuan_release (ctx);
550 0 : log_error (_("can't connect to the dirmngr: %s\n"),
551 : gpg_strerror (rc));
552 0 : return NULL;
553 : }
554 :
555 0 : 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 0 : 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 0 : } state = s_init;
577 :
578 0 : fp = fname? fopen (fname, "r") : stdin;
579 0 : if (!fp)
580 0 : return gpg_error_from_errno (errno);
581 :
582 0 : pos = 0;
583 0 : value = 0;
584 0 : bufsize = 8192;
585 0 : buf = xmalloc (bufsize);
586 0 : buflen = 0;
587 0 : while ((c=getc (fp)) != EOF)
588 : {
589 0 : int escaped_c = 0;
590 :
591 0 : if (opt.escaped_pem)
592 : {
593 0 : if (c == '%')
594 : {
595 : char tmp[2];
596 0 : if ((c = getc(fp)) == EOF)
597 0 : break;
598 0 : tmp[0] = c;
599 0 : if ((c = getc(fp)) == EOF)
600 0 : break;
601 0 : tmp[1] = c;
602 0 : if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
603 : {
604 0 : log_error ("invalid percent escape sequence\n");
605 0 : state = s_idle; /* Force an error. */
606 : /* Skip to end of line. */
607 0 : while ( (c=getc (fp)) != EOF && c != '\n')
608 : ;
609 0 : goto ready;
610 : }
611 0 : c = xtoi_2 (tmp);
612 0 : escaped_c = 1;
613 : }
614 0 : else if (c == '\n')
615 0 : goto ready; /* Ready. */
616 : }
617 0 : switch (state)
618 : {
619 : case s_idle:
620 0 : if (c == '\n')
621 : {
622 0 : state = s_lfseen;
623 0 : pos = 0;
624 : }
625 0 : break;
626 : case s_init:
627 0 : state = s_lfseen;
628 : case s_lfseen:
629 0 : if (c != "-----BEGIN "[pos])
630 0 : state = s_idle;
631 0 : else if (pos == 10)
632 0 : state = s_begin;
633 : else
634 0 : pos++;
635 0 : break;
636 : case s_begin:
637 0 : if (c == '\n')
638 0 : state = s_b64_0;
639 0 : break;
640 : case s_b64_0:
641 : case s_b64_1:
642 : case s_b64_2:
643 : case s_b64_3:
644 : {
645 0 : if (buflen >= bufsize)
646 : {
647 0 : bufsize += 8192;
648 0 : buf = xrealloc (buf, bufsize);
649 : }
650 :
651 0 : if (c == '-')
652 0 : state = s_waitend;
653 0 : else if ((c = asctobin[c & 0xff]) == 255 )
654 : ; /* Just skip invalid base64 characters. */
655 0 : else if (state == s_b64_0)
656 : {
657 0 : value = c << 2;
658 0 : state = s_b64_1;
659 : }
660 0 : else if (state == s_b64_1)
661 : {
662 0 : value |= (c>>4)&3;
663 0 : buf[buflen++] = value;
664 0 : value = (c<<4)&0xf0;
665 0 : state = s_b64_2;
666 : }
667 0 : else if (state == s_b64_2)
668 : {
669 0 : value |= (c>>2)&15;
670 0 : buf[buflen++] = value;
671 0 : value = (c<<6)&0xc0;
672 0 : state = s_b64_3;
673 : }
674 : else
675 : {
676 0 : value |= c&0x3f;
677 0 : buf[buflen++] = value;
678 0 : state = s_b64_0;
679 : }
680 : }
681 0 : 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 0 : if (c== '\n' && !escaped_c)
688 0 : goto ready;
689 0 : break;
690 : default:
691 0 : BUG();
692 : }
693 : }
694 : ready:
695 0 : if (fname)
696 0 : fclose (fp);
697 :
698 0 : if (state == s_init && c == EOF)
699 : {
700 0 : xfree (buf);
701 0 : return gpg_error (GPG_ERR_EOF);
702 : }
703 0 : else if (state != s_waitend)
704 : {
705 0 : log_error ("no certificate or invalid encoded\n");
706 0 : xfree (buf);
707 0 : return gpg_error (GPG_ERR_INV_ARMOR);
708 : }
709 :
710 0 : *rbuf = buf;
711 0 : *rbuflen = buflen;
712 0 : 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 0 : 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 0 : if (opt.pem)
728 0 : return read_pem_certificate (fname, rbuf, rbuflen);
729 :
730 0 : fp = fname? fopen (fname, "rb") : stdin;
731 0 : if (!fp)
732 0 : return gpg_error_from_errno (errno);
733 :
734 0 : buf = NULL;
735 0 : bufsize = buflen = 0;
736 : #define NCHUNK 8192
737 : do
738 : {
739 0 : bufsize += NCHUNK;
740 0 : if (!buf)
741 0 : buf = xmalloc (bufsize);
742 : else
743 0 : buf = xrealloc (buf, bufsize);
744 :
745 0 : nread = fread (buf+buflen, 1, NCHUNK, fp);
746 0 : if (nread < NCHUNK && ferror (fp))
747 : {
748 0 : err = gpg_error_from_errno (errno);
749 0 : xfree (buf);
750 0 : if (fname)
751 0 : fclose (fp);
752 0 : return err;
753 : }
754 0 : buflen += nread;
755 : }
756 0 : while (nread == NCHUNK);
757 : #undef NCHUNK
758 0 : if (fname)
759 0 : fclose (fp);
760 0 : *rbuf = buf;
761 0 : *rbuflen = buflen;
762 0 : return 0;
763 : }
764 :
765 :
766 : /* Callback for the inquire fiunction to send back the certificate. */
767 : static gpg_error_t
768 0 : inq_cert (void *opaque, const char *line)
769 : {
770 0 : struct inq_cert_parm_s *parm = opaque;
771 : gpg_error_t err;
772 :
773 0 : if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
774 : {
775 0 : err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
776 : }
777 0 : 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 0 : err = assuan_send_data (parm->ctx, NULL, 0);
782 : }
783 0 : else if (!strncmp (line, "SENDCERT_SKI", 12)
784 0 : && (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 0 : err = assuan_send_data (parm->ctx, NULL, 0);
789 : }
790 0 : else if (!strncmp (line, "SENDISSUERCERT", 14)
791 0 : && (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 0 : err = assuan_send_data (parm->ctx, NULL, 0);
796 : }
797 : else
798 : {
799 0 : log_info (_("unsupported inquiry '%s'\n"), line);
800 0 : 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 0 : 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 0 : 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 0 : memset (&parm, 0, sizeof parm);
820 0 : parm.ctx = ctx;
821 0 : parm.cert = cert;
822 0 : parm.certlen = certlen;
823 :
824 0 : err = assuan_transact (ctx,
825 0 : (opt.use_ocsp && opt.force_default_responder
826 : ? "CHECKOCSP --force-default-responder"
827 0 : : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
828 : NULL, NULL, inq_cert, &parm, status_cb, NULL);
829 0 : if (opt.verbose > 1)
830 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
831 0 : 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 0 : 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 0 : memset (&parm, 0, sizeof parm);
843 0 : parm.ctx = ctx;
844 0 : parm.cert = cert;
845 0 : parm.certlen = certlen;
846 :
847 0 : err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
848 : inq_cert, &parm,
849 : status_cb, NULL);
850 0 : if (opt.verbose > 1)
851 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
852 0 : 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 0 : 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 0 : memset (&parm, 0, sizeof parm);
864 0 : parm.ctx = ctx;
865 0 : parm.cert = cert;
866 0 : parm.certlen = certlen;
867 :
868 0 : err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
869 : inq_cert, &parm,
870 : status_cb, NULL);
871 0 : if (opt.verbose > 1)
872 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
873 0 : return err;
874 : }
875 :
876 : /* Load a CRL into the dirmngr. */
877 : static gpg_error_t
878 0 : 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 0 : if (opt.url)
885 0 : fname = xstrdup (filename);
886 : else
887 : {
888 : #ifdef HAVE_CANONICALIZE_FILE_NAME
889 0 : fname = canonicalize_file_name (filename);
890 0 : if (!fname)
891 : {
892 0 : log_error ("error canonicalizing '%s': %s\n",
893 0 : filename, strerror (errno));
894 0 : return gpg_error (GPG_ERR_GENERAL);
895 : }
896 : #else
897 : fname = xstrdup (filename);
898 : #endif
899 0 : if (*fname != '/')
900 : {
901 0 : log_error (_("absolute file name expected\n"));
902 0 : return gpg_error (GPG_ERR_GENERAL);
903 : }
904 : }
905 :
906 0 : line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
907 0 : p = stpcpy (line, "LOADCRL ");
908 0 : if (opt.url)
909 0 : p = stpcpy (p, "--url ");
910 0 : for (s = fname; *s; s++)
911 : {
912 0 : if (*s < ' ' || *s == '+')
913 : {
914 0 : sprintf (p, "%%%02X", *s);
915 0 : p += 3;
916 : }
917 0 : else if (*s == ' ')
918 0 : *p++ = '+';
919 : else
920 0 : *p++ = *s;
921 : }
922 0 : *p = 0;
923 :
924 0 : err = assuan_transact (ctx, line, NULL, NULL,
925 : NULL, NULL,
926 : status_cb, NULL);
927 0 : if (opt.verbose > 1)
928 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
929 0 : xfree (line);
930 0 : xfree (fname);
931 0 : 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 0 : 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 0 : if (opt.verbose)
946 0 : log_info (_("looking up '%s'\n"), pattern);
947 :
948 0 : err = b64enc_start (&state, stdout, NULL);
949 0 : if (err)
950 0 : return err;
951 :
952 0 : line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
953 :
954 0 : p = stpcpy (line, "LOOKUP ");
955 0 : if (opt.url)
956 0 : p = stpcpy (p, "--url ");
957 0 : if (opt.local)
958 0 : p = stpcpy (p, "--cache-only ");
959 0 : for (s=pattern; *s; s++)
960 : {
961 0 : if (*s < ' ' || *s == '+')
962 : {
963 0 : sprintf (p, "%%%02X", *s);
964 0 : p += 3;
965 : }
966 0 : else if (*s == ' ')
967 0 : *p++ = '+';
968 : else
969 0 : *p++ = *s;
970 : }
971 0 : *p = 0;
972 :
973 :
974 0 : err = assuan_transact (ctx, line,
975 : data_cb, &state,
976 : NULL, NULL,
977 : status_cb, NULL);
978 0 : if (opt.verbose > 1)
979 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
980 :
981 0 : err = b64enc_finish (&state);
982 :
983 0 : xfree (line);
984 0 : 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 0 : squid_loop_body (assuan_context_t ctx)
992 : {
993 : gpg_error_t err;
994 : unsigned char *certbuf;
995 0 : size_t certbuflen = 0;
996 :
997 0 : err = read_pem_certificate (NULL, &certbuf, &certbuflen);
998 0 : if (gpg_err_code (err) == GPG_ERR_EOF)
999 0 : return err;
1000 0 : if (err)
1001 : {
1002 0 : log_error (_("error reading certificate from stdin: %s\n"),
1003 : gpg_strerror (err));
1004 0 : puts ("ERROR");
1005 0 : return 0;
1006 : }
1007 :
1008 0 : err = do_check (ctx, certbuf, certbuflen);
1009 0 : xfree (certbuf);
1010 0 : if (!err)
1011 : {
1012 0 : if (opt.verbose)
1013 0 : log_info (_("certificate is valid\n"));
1014 0 : puts ("OK");
1015 : }
1016 : else
1017 : {
1018 0 : if (!opt.quiet)
1019 : {
1020 0 : if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
1021 0 : log_info (_("certificate has been revoked\n"));
1022 : else
1023 0 : log_error (_("certificate check failed: %s\n"),
1024 : gpg_strerror (err));
1025 : }
1026 0 : puts ("ERROR");
1027 : }
1028 :
1029 0 : fflush (stdout);
1030 :
1031 0 : return 0;
1032 : }
|