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 <https://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 : #include "../common/asshelp.h"
40 :
41 : #include "i18n.h"
42 : #include "util.h"
43 : #include "init.h"
44 :
45 :
46 : /* Constants for the options. */
47 : enum
48 : {
49 : oQuiet = 'q',
50 : oVerbose = 'v',
51 : oLocal = 'l',
52 : oUrl = 'u',
53 :
54 : oOCSP = 500,
55 : oPing,
56 : oCacheCert,
57 : oValidate,
58 : oLookup,
59 : oLoadCRL,
60 : oSquidMode,
61 : oPEM,
62 : oEscapedPEM,
63 : oForceDefaultResponder
64 : };
65 :
66 :
67 : /* The list of options as used by the argparse.c code. */
68 : static ARGPARSE_OPTS opts[] = {
69 : { oVerbose, "verbose", 0, N_("verbose") },
70 : { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
71 : { oOCSP, "ocsp", 0, N_("use OCSP instead of CRLs") },
72 : { oPing, "ping", 0, N_("check whether a dirmngr is running")},
73 : { oCacheCert,"cache-cert",0, N_("add a certificate to the cache")},
74 : { oValidate, "validate", 0, N_("validate a certificate")},
75 : { oLookup, "lookup", 0, N_("lookup a certificate")},
76 : { oLocal, "local", 0, N_("lookup only locally stored certificates")},
77 : { oUrl, "url", 0, N_("expect an URL for --lookup")},
78 : { oLoadCRL, "load-crl", 0, N_("load a CRL into the dirmngr")},
79 : { oSquidMode,"squid-mode",0, N_("special mode for use by Squid")},
80 : { oPEM, "pem", 0, N_("expect certificates in PEM format")},
81 : { oForceDefaultResponder, "force-default-responder", 0,
82 : N_("force the use of the default OCSP responder")},
83 : { 0, NULL, 0, NULL }
84 : };
85 :
86 :
87 : /* The usual structure for the program flags. */
88 : static struct
89 : {
90 : int quiet;
91 : int verbose;
92 : const char *dirmngr_program;
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 : /* Build the helptable for radix64 to bin conversion. */
120 : static void
121 0 : init_asctobin (void)
122 : {
123 : static int initialized;
124 : int i;
125 : unsigned char *s;
126 :
127 0 : if (initialized)
128 0 : return;
129 0 : initialized = 1;
130 :
131 0 : for (i=0; i < 256; i++ )
132 0 : asctobin[i] = 255; /* Used to detect invalid characters. */
133 0 : for (s=bintoasc, i=0; *s; s++, i++)
134 0 : asctobin[*s] = i;
135 : }
136 :
137 :
138 : /* Prototypes. */
139 : static gpg_error_t read_certificate (const char *fname,
140 : unsigned char **rbuf, size_t *rbuflen);
141 : static gpg_error_t do_check (assuan_context_t ctx,
142 : const unsigned char *cert, size_t certlen);
143 : static gpg_error_t do_cache (assuan_context_t ctx,
144 : const unsigned char *cert, size_t certlen);
145 : static gpg_error_t do_validate (assuan_context_t ctx,
146 : const unsigned char *cert, size_t certlen);
147 : static gpg_error_t do_loadcrl (assuan_context_t ctx, const char *filename);
148 : static gpg_error_t do_lookup (assuan_context_t ctx, const char *pattern);
149 : static gpg_error_t squid_loop_body (assuan_context_t ctx);
150 :
151 :
152 :
153 : /* Function called by argparse.c to display information. */
154 : static const char *
155 0 : my_strusage (int level)
156 : {
157 : const char *p;
158 :
159 0 : switch(level)
160 : {
161 0 : case 11: p = "dirmngr-client (@GNUPG@)";
162 0 : break;
163 0 : case 13: p = VERSION; break;
164 0 : case 17: p = PRINTABLE_OS_NAME; break;
165 0 : case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
166 0 : case 49: p = PACKAGE_BUGREPORT; break;
167 : case 1:
168 0 : case 40: p =
169 : _("Usage: dirmngr-client [options] "
170 : "[certfile|pattern] (-h for help)\n");
171 0 : break;
172 0 : case 41: p =
173 : _("Syntax: dirmngr-client [options] [certfile|pattern]\n"
174 : "Test an X.509 certificate against a CRL or do an OCSP check\n"
175 : "The process returns 0 if the certificate is valid, 1 if it is\n"
176 : "not valid and other error codes for general failures\n");
177 0 : break;
178 :
179 0 : default: p = NULL;
180 : }
181 0 : return p;
182 : }
183 :
184 :
185 :
186 : int
187 0 : main (int argc, char **argv )
188 : {
189 : ARGPARSE_ARGS pargs;
190 : assuan_context_t ctx;
191 : gpg_error_t err;
192 : unsigned char *certbuf;
193 0 : size_t certbuflen = 0;
194 0 : int cmd_ping = 0;
195 0 : int cmd_cache_cert = 0;
196 0 : int cmd_validate = 0;
197 0 : int cmd_lookup = 0;
198 0 : int cmd_loadcrl = 0;
199 0 : int cmd_squid_mode = 0;
200 :
201 0 : early_system_init ();
202 0 : set_strusage (my_strusage);
203 0 : log_set_prefix ("dirmngr-client",
204 : GPGRT_LOG_WITH_PREFIX);
205 :
206 : /* For W32 we need to initialize the socket subsystem. Because we
207 : don't use Pth we need to do this explicit. */
208 : #ifdef HAVE_W32_SYSTEM
209 : {
210 : WSADATA wsadat;
211 :
212 : WSAStartup (0x202, &wsadat);
213 : }
214 : #endif /*HAVE_W32_SYSTEM*/
215 :
216 : /* Init Assuan. */
217 0 : assuan_set_assuan_log_prefix (log_get_prefix (NULL));
218 0 : assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
219 :
220 : /* Setup I18N. */
221 0 : i18n_init();
222 :
223 : /* Parse the command line. */
224 0 : pargs.argc = &argc;
225 0 : pargs.argv = &argv;
226 0 : pargs.flags= 1; /* Do not remove the args. */
227 0 : while (arg_parse (&pargs, opts) )
228 : {
229 0 : switch (pargs.r_opt)
230 : {
231 0 : case oVerbose: opt.verbose++; break;
232 0 : case oQuiet: opt.quiet++; break;
233 :
234 0 : case oOCSP: opt.use_ocsp++; break;
235 0 : case oPing: cmd_ping = 1; break;
236 0 : case oCacheCert: cmd_cache_cert = 1; break;
237 0 : case oValidate: cmd_validate = 1; break;
238 0 : case oLookup: cmd_lookup = 1; break;
239 0 : case oUrl: opt.url = 1; break;
240 0 : case oLocal: opt.local = 1; break;
241 0 : case oLoadCRL: cmd_loadcrl = 1; break;
242 0 : case oPEM: opt.pem = 1; break;
243 : case oSquidMode:
244 0 : opt.pem = 1;
245 0 : opt.escaped_pem = 1;
246 0 : cmd_squid_mode = 1;
247 0 : break;
248 0 : case oForceDefaultResponder: opt.force_default_responder = 1; break;
249 :
250 0 : default : pargs.err = 2; break;
251 : }
252 : }
253 0 : if (log_get_errorcount (0))
254 0 : exit (2);
255 :
256 0 : if (cmd_ping)
257 0 : err = 0;
258 0 : else if (cmd_lookup || cmd_loadcrl)
259 : {
260 0 : if (!argc)
261 0 : usage (1);
262 0 : err = 0;
263 : }
264 0 : else if (cmd_squid_mode)
265 : {
266 0 : err = 0;
267 0 : if (argc)
268 0 : usage (1);
269 : }
270 0 : else if (!argc)
271 : {
272 0 : err = read_certificate (NULL, &certbuf, &certbuflen);
273 0 : if (err)
274 0 : log_error (_("error reading certificate from stdin: %s\n"),
275 : gpg_strerror (err));
276 : }
277 0 : else if (argc == 1)
278 : {
279 0 : err = read_certificate (*argv, &certbuf, &certbuflen);
280 0 : if (err)
281 0 : log_error (_("error reading certificate from '%s': %s\n"),
282 : *argv, gpg_strerror (err));
283 : }
284 : else
285 : {
286 0 : err = 0;
287 0 : usage (1);
288 : }
289 :
290 0 : if (log_get_errorcount (0))
291 0 : exit (2);
292 :
293 0 : if (certbuflen > 20000)
294 : {
295 0 : log_error (_("certificate too large to make any sense\n"));
296 0 : exit (2);
297 : }
298 :
299 0 : err = start_new_dirmngr (&ctx,
300 : GPG_ERR_SOURCE_DEFAULT,
301 0 : opt.dirmngr_program
302 : ? opt.dirmngr_program
303 : : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR),
304 : ! cmd_ping,
305 : opt.verbose,
306 : 0,
307 : NULL, NULL);
308 0 : if (err)
309 : {
310 0 : log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (err));
311 0 : exit (2);
312 : }
313 :
314 0 : if (cmd_ping)
315 : ;
316 0 : else if (cmd_squid_mode)
317 : {
318 0 : while (!(err = squid_loop_body (ctx)))
319 : ;
320 0 : if (gpg_err_code (err) == GPG_ERR_EOF)
321 0 : err = 0;
322 : }
323 0 : else if (cmd_lookup)
324 : {
325 0 : int last_err = 0;
326 :
327 0 : for (; argc; argc--, argv++)
328 : {
329 0 : err = do_lookup (ctx, *argv);
330 0 : if (err)
331 : {
332 0 : log_error (_("lookup failed: %s\n"), gpg_strerror (err));
333 0 : last_err = err;
334 : }
335 : }
336 0 : err = last_err;
337 : }
338 0 : else if (cmd_loadcrl)
339 : {
340 0 : int last_err = 0;
341 :
342 0 : for (; argc; argc--, argv++)
343 : {
344 0 : err = do_loadcrl (ctx, *argv);
345 0 : if (err)
346 : {
347 0 : log_error (_("loading CRL '%s' failed: %s\n"),
348 : *argv, gpg_strerror (err));
349 0 : last_err = err;
350 : }
351 : }
352 0 : err = last_err;
353 : }
354 0 : else if (cmd_cache_cert)
355 : {
356 0 : err = do_cache (ctx, certbuf, certbuflen);
357 0 : xfree (certbuf);
358 : }
359 0 : else if (cmd_validate)
360 : {
361 0 : err = do_validate (ctx, certbuf, certbuflen);
362 0 : xfree (certbuf);
363 : }
364 : else
365 : {
366 0 : err = do_check (ctx, certbuf, certbuflen);
367 0 : xfree (certbuf);
368 : }
369 :
370 0 : assuan_release (ctx);
371 :
372 0 : if (cmd_ping)
373 : {
374 0 : if (!opt.quiet)
375 0 : log_info (_("a dirmngr daemon is up and running\n"));
376 0 : return 0;
377 : }
378 0 : else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
379 0 : return err? 1:0;
380 0 : else if (cmd_cache_cert)
381 : {
382 0 : if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
383 : {
384 0 : if (!opt.quiet)
385 0 : log_info (_("certificate already cached\n"));
386 : }
387 0 : else if (err)
388 : {
389 0 : log_error (_("error caching certificate: %s\n"),
390 : gpg_strerror (err));
391 0 : return 1;
392 : }
393 0 : return 0;
394 : }
395 0 : else if (cmd_validate && err)
396 : {
397 0 : log_error (_("validation of certificate failed: %s\n"),
398 : gpg_strerror (err));
399 0 : return 1;
400 : }
401 0 : else if (!err)
402 : {
403 0 : if (!opt.quiet)
404 0 : log_info (_("certificate is valid\n"));
405 0 : return 0;
406 : }
407 0 : else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
408 : {
409 0 : if (!opt.quiet)
410 0 : log_info (_("certificate has been revoked\n"));
411 0 : return 1;
412 : }
413 : else
414 : {
415 0 : log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
416 0 : return 2;
417 : }
418 : }
419 :
420 :
421 : /* Print status line from the assuan protocol. */
422 : static gpg_error_t
423 0 : status_cb (void *opaque, const char *line)
424 : {
425 : (void)opaque;
426 :
427 0 : if (opt.verbose > 2)
428 0 : log_info (_("got status: '%s'\n"), line);
429 0 : return 0;
430 : }
431 :
432 : /* Print data as retrieved by the lookup function. */
433 : static gpg_error_t
434 0 : data_cb (void *opaque, const void *buffer, size_t length)
435 : {
436 : gpg_error_t err;
437 0 : struct b64state *state = opaque;
438 :
439 0 : if (buffer)
440 : {
441 0 : err = b64enc_write (state, buffer, length);
442 0 : if (err)
443 0 : log_error (_("error writing base64 encoding: %s\n"),
444 : gpg_strerror (err));
445 : }
446 0 : return 0;
447 : }
448 :
449 :
450 : /* Read the first PEM certificate from the file FNAME. If fname is
451 : NULL the next certificate is read from stdin. The certificate is
452 : returned in an alloced buffer whose address will be returned in
453 : RBUF and its length in RBUFLEN. */
454 : static gpg_error_t
455 0 : read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
456 : {
457 : FILE *fp;
458 : int c;
459 : int pos;
460 : int value;
461 : unsigned char *buf;
462 : size_t bufsize, buflen;
463 : enum {
464 : s_init, s_idle, s_lfseen, s_begin,
465 : s_b64_0, s_b64_1, s_b64_2, s_b64_3,
466 : s_waitend
467 0 : } state = s_init;
468 :
469 0 : init_asctobin ();
470 :
471 0 : fp = fname? fopen (fname, "r") : stdin;
472 0 : if (!fp)
473 0 : return gpg_error_from_errno (errno);
474 :
475 0 : pos = 0;
476 0 : value = 0;
477 0 : bufsize = 8192;
478 0 : buf = xmalloc (bufsize);
479 0 : buflen = 0;
480 0 : while ((c=getc (fp)) != EOF)
481 : {
482 0 : int escaped_c = 0;
483 :
484 0 : if (opt.escaped_pem)
485 : {
486 0 : if (c == '%')
487 : {
488 : char tmp[2];
489 0 : if ((c = getc(fp)) == EOF)
490 0 : break;
491 0 : tmp[0] = c;
492 0 : if ((c = getc(fp)) == EOF)
493 0 : break;
494 0 : tmp[1] = c;
495 0 : if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
496 : {
497 0 : log_error ("invalid percent escape sequence\n");
498 0 : state = s_idle; /* Force an error. */
499 : /* Skip to end of line. */
500 0 : while ( (c=getc (fp)) != EOF && c != '\n')
501 : ;
502 0 : goto ready;
503 : }
504 0 : c = xtoi_2 (tmp);
505 0 : escaped_c = 1;
506 : }
507 0 : else if (c == '\n')
508 0 : goto ready; /* Ready. */
509 : }
510 0 : switch (state)
511 : {
512 : case s_idle:
513 0 : if (c == '\n')
514 : {
515 0 : state = s_lfseen;
516 0 : pos = 0;
517 : }
518 0 : break;
519 : case s_init:
520 0 : state = s_lfseen;
521 : case s_lfseen:
522 0 : if (c != "-----BEGIN "[pos])
523 0 : state = s_idle;
524 0 : else if (pos == 10)
525 0 : state = s_begin;
526 : else
527 0 : pos++;
528 0 : break;
529 : case s_begin:
530 0 : if (c == '\n')
531 0 : state = s_b64_0;
532 0 : break;
533 : case s_b64_0:
534 : case s_b64_1:
535 : case s_b64_2:
536 : case s_b64_3:
537 : {
538 0 : if (buflen >= bufsize)
539 : {
540 0 : bufsize += 8192;
541 0 : buf = xrealloc (buf, bufsize);
542 : }
543 :
544 0 : if (c == '-')
545 0 : state = s_waitend;
546 0 : else if ((c = asctobin[c & 0xff]) == 255 )
547 : ; /* Just skip invalid base64 characters. */
548 0 : else if (state == s_b64_0)
549 : {
550 0 : value = c << 2;
551 0 : state = s_b64_1;
552 : }
553 0 : else if (state == s_b64_1)
554 : {
555 0 : value |= (c>>4)&3;
556 0 : buf[buflen++] = value;
557 0 : value = (c<<4)&0xf0;
558 0 : state = s_b64_2;
559 : }
560 0 : else if (state == s_b64_2)
561 : {
562 0 : value |= (c>>2)&15;
563 0 : buf[buflen++] = value;
564 0 : value = (c<<6)&0xc0;
565 0 : state = s_b64_3;
566 : }
567 : else
568 : {
569 0 : value |= c&0x3f;
570 0 : buf[buflen++] = value;
571 0 : state = s_b64_0;
572 : }
573 : }
574 0 : break;
575 : case s_waitend:
576 : /* Note that we do not check that the base64 decoder has
577 : been left in the expected state. We assume that the PEM
578 : header is just fine. However we need to wait for the
579 : real LF and not a trailing percent escaped one. */
580 0 : if (c== '\n' && !escaped_c)
581 0 : goto ready;
582 0 : break;
583 : default:
584 0 : BUG();
585 : }
586 : }
587 : ready:
588 0 : if (fname)
589 0 : fclose (fp);
590 :
591 0 : if (state == s_init && c == EOF)
592 : {
593 0 : xfree (buf);
594 0 : return gpg_error (GPG_ERR_EOF);
595 : }
596 0 : else if (state != s_waitend)
597 : {
598 0 : log_error ("no certificate or invalid encoded\n");
599 0 : xfree (buf);
600 0 : return gpg_error (GPG_ERR_INV_ARMOR);
601 : }
602 :
603 0 : *rbuf = buf;
604 0 : *rbuflen = buflen;
605 0 : return 0;
606 : }
607 :
608 : /* Read a binary certificate from the file FNAME. If fname is NULL the
609 : file is read from stdin. The certificate is returned in an alloced
610 : buffer whose address will be returned in RBUF and its length in
611 : RBUFLEN. */
612 : static gpg_error_t
613 0 : read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
614 : {
615 : gpg_error_t err;
616 : FILE *fp;
617 : unsigned char *buf;
618 : size_t nread, bufsize, buflen;
619 :
620 0 : if (opt.pem)
621 0 : return read_pem_certificate (fname, rbuf, rbuflen);
622 0 : else if (fname)
623 : {
624 : /* A filename has been given. Let's just assume it is in PEM
625 : format and decode it, and fall back to interpreting it as
626 : binary certificate if that fails. */
627 0 : err = read_pem_certificate (fname, rbuf, rbuflen);
628 0 : if (! err)
629 0 : return 0;
630 : }
631 :
632 0 : fp = fname? fopen (fname, "rb") : stdin;
633 0 : if (!fp)
634 0 : return gpg_error_from_errno (errno);
635 :
636 0 : buf = NULL;
637 0 : bufsize = buflen = 0;
638 : #define NCHUNK 8192
639 : do
640 : {
641 0 : bufsize += NCHUNK;
642 0 : if (!buf)
643 0 : buf = xmalloc (bufsize);
644 : else
645 0 : buf = xrealloc (buf, bufsize);
646 :
647 0 : nread = fread (buf+buflen, 1, NCHUNK, fp);
648 0 : if (nread < NCHUNK && ferror (fp))
649 : {
650 0 : err = gpg_error_from_errno (errno);
651 0 : xfree (buf);
652 0 : if (fname)
653 0 : fclose (fp);
654 0 : return err;
655 : }
656 0 : buflen += nread;
657 : }
658 0 : while (nread == NCHUNK);
659 : #undef NCHUNK
660 0 : if (fname)
661 0 : fclose (fp);
662 0 : *rbuf = buf;
663 0 : *rbuflen = buflen;
664 0 : return 0;
665 : }
666 :
667 :
668 : /* Callback for the inquire fiunction to send back the certificate. */
669 : static gpg_error_t
670 0 : inq_cert (void *opaque, const char *line)
671 : {
672 0 : struct inq_cert_parm_s *parm = opaque;
673 : gpg_error_t err;
674 :
675 0 : if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
676 : {
677 0 : err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
678 : }
679 0 : else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
680 : {
681 : /* We don't support this but dirmngr might ask for it. So
682 : simply ignore it by sending back and empty value. */
683 0 : err = assuan_send_data (parm->ctx, NULL, 0);
684 : }
685 0 : else if (!strncmp (line, "SENDCERT_SKI", 12)
686 0 : && (line[12]==' ' || !line[12]))
687 : {
688 : /* We don't support this but dirmngr might ask for it. So
689 : simply ignore it by sending back an empty value. */
690 0 : err = assuan_send_data (parm->ctx, NULL, 0);
691 : }
692 0 : else if (!strncmp (line, "SENDISSUERCERT", 14)
693 0 : && (line[14] == ' ' || !line[14]))
694 : {
695 : /* We don't support this but dirmngr might ask for it. So
696 : simply ignore it by sending back an empty value. */
697 0 : err = assuan_send_data (parm->ctx, NULL, 0);
698 : }
699 : else
700 : {
701 0 : log_info (_("unsupported inquiry '%s'\n"), line);
702 0 : err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
703 : /* Note that this error will let assuan_transact terminate
704 : immediately instead of return the error to the caller. It is
705 : not clear whether this is the desired behaviour - it may
706 : change in future. */
707 : }
708 :
709 0 : return err;
710 : }
711 :
712 :
713 : /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
714 : Return a proper error code. */
715 : static gpg_error_t
716 0 : do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
717 : {
718 : gpg_error_t err;
719 : struct inq_cert_parm_s parm;
720 :
721 0 : memset (&parm, 0, sizeof parm);
722 0 : parm.ctx = ctx;
723 0 : parm.cert = cert;
724 0 : parm.certlen = certlen;
725 :
726 0 : err = assuan_transact (ctx,
727 0 : (opt.use_ocsp && opt.force_default_responder
728 : ? "CHECKOCSP --force-default-responder"
729 0 : : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
730 : NULL, NULL, inq_cert, &parm, status_cb, NULL);
731 0 : if (opt.verbose > 1)
732 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
733 0 : return err;
734 : }
735 :
736 : /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
737 : Return a proper error code. */
738 : static gpg_error_t
739 0 : do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
740 : {
741 : gpg_error_t err;
742 : struct inq_cert_parm_s parm;
743 :
744 0 : memset (&parm, 0, sizeof parm);
745 0 : parm.ctx = ctx;
746 0 : parm.cert = cert;
747 0 : parm.certlen = certlen;
748 :
749 0 : err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
750 : inq_cert, &parm,
751 : status_cb, NULL);
752 0 : if (opt.verbose > 1)
753 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
754 0 : return err;
755 : }
756 :
757 : /* Check the certificate CERT,CERTLEN for validity using dirmngrs
758 : internal validate feature. Return a proper error code. */
759 : static gpg_error_t
760 0 : do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
761 : {
762 : gpg_error_t err;
763 : struct inq_cert_parm_s parm;
764 :
765 0 : memset (&parm, 0, sizeof parm);
766 0 : parm.ctx = ctx;
767 0 : parm.cert = cert;
768 0 : parm.certlen = certlen;
769 :
770 0 : err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
771 : inq_cert, &parm,
772 : status_cb, NULL);
773 0 : if (opt.verbose > 1)
774 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
775 0 : return err;
776 : }
777 :
778 : /* Load a CRL into the dirmngr. */
779 : static gpg_error_t
780 0 : do_loadcrl (assuan_context_t ctx, const char *filename)
781 : {
782 : gpg_error_t err;
783 : const char *s;
784 : char *fname, *line, *p;
785 :
786 0 : if (opt.url)
787 0 : fname = xstrdup (filename);
788 : else
789 : {
790 : #ifdef HAVE_CANONICALIZE_FILE_NAME
791 0 : fname = canonicalize_file_name (filename);
792 0 : if (!fname)
793 : {
794 0 : log_error ("error canonicalizing '%s': %s\n",
795 0 : filename, strerror (errno));
796 0 : return gpg_error (GPG_ERR_GENERAL);
797 : }
798 : #else
799 : fname = xstrdup (filename);
800 : #endif
801 0 : if (*fname != '/')
802 : {
803 0 : log_error (_("absolute file name expected\n"));
804 0 : return gpg_error (GPG_ERR_GENERAL);
805 : }
806 : }
807 :
808 0 : line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
809 0 : p = stpcpy (line, "LOADCRL ");
810 0 : if (opt.url)
811 0 : p = stpcpy (p, "--url ");
812 0 : for (s = fname; *s; s++)
813 : {
814 0 : if (*s < ' ' || *s == '+')
815 : {
816 0 : sprintf (p, "%%%02X", *s);
817 0 : p += 3;
818 : }
819 0 : else if (*s == ' ')
820 0 : *p++ = '+';
821 : else
822 0 : *p++ = *s;
823 : }
824 0 : *p = 0;
825 :
826 0 : err = assuan_transact (ctx, line, NULL, NULL,
827 : NULL, NULL,
828 : 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 : xfree (line);
832 0 : xfree (fname);
833 0 : return err;
834 : }
835 :
836 :
837 : /* Do a LDAP lookup using PATTERN and print the result in a base-64
838 : encoded format. */
839 : static gpg_error_t
840 0 : do_lookup (assuan_context_t ctx, const char *pattern)
841 : {
842 : gpg_error_t err;
843 : const unsigned char *s;
844 : char *line, *p;
845 : struct b64state state;
846 :
847 0 : if (opt.verbose)
848 0 : log_info (_("looking up '%s'\n"), pattern);
849 :
850 0 : err = b64enc_start (&state, stdout, NULL);
851 0 : if (err)
852 0 : return err;
853 :
854 0 : line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
855 :
856 0 : p = stpcpy (line, "LOOKUP ");
857 0 : if (opt.url)
858 0 : p = stpcpy (p, "--url ");
859 0 : if (opt.local)
860 0 : p = stpcpy (p, "--cache-only ");
861 0 : for (s=pattern; *s; s++)
862 : {
863 0 : if (*s < ' ' || *s == '+')
864 : {
865 0 : sprintf (p, "%%%02X", *s);
866 0 : p += 3;
867 : }
868 0 : else if (*s == ' ')
869 0 : *p++ = '+';
870 : else
871 0 : *p++ = *s;
872 : }
873 0 : *p = 0;
874 :
875 :
876 0 : err = assuan_transact (ctx, line,
877 : data_cb, &state,
878 : NULL, NULL,
879 : status_cb, NULL);
880 0 : if (opt.verbose > 1)
881 0 : log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
882 :
883 0 : err = b64enc_finish (&state);
884 :
885 0 : xfree (line);
886 0 : return err;
887 : }
888 :
889 : /* The body of an endless loop: Read a line from stdin, retrieve the
890 : certificate from it, validate it and print "ERR" or "OK" to stdout.
891 : Continue. */
892 : static gpg_error_t
893 0 : squid_loop_body (assuan_context_t ctx)
894 : {
895 : gpg_error_t err;
896 : unsigned char *certbuf;
897 0 : size_t certbuflen = 0;
898 :
899 0 : err = read_pem_certificate (NULL, &certbuf, &certbuflen);
900 0 : if (gpg_err_code (err) == GPG_ERR_EOF)
901 0 : return err;
902 0 : if (err)
903 : {
904 0 : log_error (_("error reading certificate from stdin: %s\n"),
905 : gpg_strerror (err));
906 0 : puts ("ERROR");
907 0 : return 0;
908 : }
909 :
910 0 : err = do_check (ctx, certbuf, certbuflen);
911 0 : xfree (certbuf);
912 0 : if (!err)
913 : {
914 0 : if (opt.verbose)
915 0 : log_info (_("certificate is valid\n"));
916 0 : puts ("OK");
917 : }
918 : else
919 : {
920 0 : if (!opt.quiet)
921 : {
922 0 : if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
923 0 : log_info (_("certificate has been revoked\n"));
924 : else
925 0 : log_error (_("certificate check failed: %s\n"),
926 : gpg_strerror (err));
927 : }
928 0 : puts ("ERROR");
929 : }
930 :
931 0 : fflush (stdout);
932 :
933 0 : return 0;
934 : }
|