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.
5 * This file is part of DirMngr.
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.
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.
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/>.
31 #include <gpg-error.h>
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"
46 /* Constants for the options. */
63 oForceDefaultResponder
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")},
87 /* The usual structure for the program flags. */
92 const char *dirmngr_program;
93 int force_default_responder;
95 int escaped_pem; /* PEM is additional percent encoded. */
96 int url; /* Expect an URL. */
97 int local; /* Lookup up only local certificates. */
103 /* Communication structure for the certificate inquire callback. */
104 struct inq_cert_parm_s
106 assuan_context_t ctx;
107 const unsigned char *cert;
112 /* Base64 conversion tables. */
113 static unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
114 "abcdefghijklmnopqrstuvwxyz"
116 static unsigned char asctobin[256]; /* runtime initialized */
119 /* Build the helptable for radix64 to bin conversion. */
123 static int initialized;
131 for (i=0; i < 256; i++ )
132 asctobin[i] = 255; /* Used to detect invalid characters. */
133 for (s=bintoasc, i=0; *s; s++, i++)
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);
153 /* Function called by argparse.c to display information. */
155 my_strusage (int level)
161 case 11: p = "dirmngr-client (@GNUPG@)";
163 case 13: p = VERSION; break;
164 case 17: p = PRINTABLE_OS_NAME; break;
165 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
166 case 49: p = PACKAGE_BUGREPORT; break;
169 _("Usage: dirmngr-client [options] "
170 "[certfile|pattern] (-h for help)\n");
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");
187 main (int argc, char **argv )
190 assuan_context_t ctx;
192 unsigned char *certbuf;
193 size_t certbuflen = 0;
195 int cmd_cache_cert = 0;
196 int cmd_validate = 0;
199 int cmd_squid_mode = 0;
201 early_system_init ();
202 set_strusage (my_strusage);
203 log_set_prefix ("dirmngr-client",
204 GPGRT_LOG_WITH_PREFIX);
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
212 WSAStartup (0x202, &wsadat);
214 #endif /*HAVE_W32_SYSTEM*/
217 assuan_set_assuan_log_prefix (log_get_prefix (NULL));
218 assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
223 /* Parse the command line. */
226 pargs.flags= 1; /* Do not remove the args. */
227 while (arg_parse (&pargs, opts) )
231 case oVerbose: opt.verbose++; break;
232 case oQuiet: opt.quiet++; break;
234 case oOCSP: opt.use_ocsp++; break;
235 case oPing: cmd_ping = 1; break;
236 case oCacheCert: cmd_cache_cert = 1; break;
237 case oValidate: cmd_validate = 1; break;
238 case oLookup: cmd_lookup = 1; break;
239 case oUrl: opt.url = 1; break;
240 case oLocal: opt.local = 1; break;
241 case oLoadCRL: cmd_loadcrl = 1; break;
242 case oPEM: opt.pem = 1; break;
248 case oForceDefaultResponder: opt.force_default_responder = 1; break;
250 default : pargs.err = 2; break;
253 if (log_get_errorcount (0))
258 else if (cmd_lookup || cmd_loadcrl)
264 else if (cmd_squid_mode)
272 err = read_certificate (NULL, &certbuf, &certbuflen);
274 log_error (_("error reading certificate from stdin: %s\n"),
279 err = read_certificate (*argv, &certbuf, &certbuflen);
281 log_error (_("error reading certificate from '%s': %s\n"),
282 *argv, gpg_strerror (err));
290 if (log_get_errorcount (0))
293 if (certbuflen > 20000)
295 log_error (_("certificate too large to make any sense\n"));
299 err = start_new_dirmngr (&ctx,
300 GPG_ERR_SOURCE_DEFAULT,
302 ? opt.dirmngr_program
303 : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR),
310 log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (err));
316 else if (cmd_squid_mode)
318 while (!(err = squid_loop_body (ctx)))
320 if (gpg_err_code (err) == GPG_ERR_EOF)
327 for (; argc; argc--, argv++)
329 err = do_lookup (ctx, *argv);
332 log_error (_("lookup failed: %s\n"), gpg_strerror (err));
338 else if (cmd_loadcrl)
342 for (; argc; argc--, argv++)
344 err = do_loadcrl (ctx, *argv);
347 log_error (_("loading CRL '%s' failed: %s\n"),
348 *argv, gpg_strerror (err));
354 else if (cmd_cache_cert)
356 err = do_cache (ctx, certbuf, certbuflen);
359 else if (cmd_validate)
361 err = do_validate (ctx, certbuf, certbuflen);
366 err = do_check (ctx, certbuf, certbuflen);
370 assuan_release (ctx);
375 log_info (_("a dirmngr daemon is up and running\n"));
378 else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
380 else if (cmd_cache_cert)
382 if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
385 log_info (_("certificate already cached\n"));
389 log_error (_("error caching certificate: %s\n"),
395 else if (cmd_validate && err)
397 log_error (_("validation of certificate failed: %s\n"),
404 log_info (_("certificate is valid\n"));
407 else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
410 log_info (_("certificate has been revoked\n"));
415 log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
421 /* Print status line from the assuan protocol. */
423 status_cb (void *opaque, const char *line)
428 log_info (_("got status: '%s'\n"), line);
432 /* Print data as retrieved by the lookup function. */
434 data_cb (void *opaque, const void *buffer, size_t length)
437 struct b64state *state = opaque;
441 err = b64enc_write (state, buffer, length);
443 log_error (_("error writing base64 encoding: %s\n"),
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. */
455 read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
462 size_t bufsize, buflen;
464 s_init, s_idle, s_lfseen, s_begin,
465 s_b64_0, s_b64_1, s_b64_2, s_b64_3,
471 fp = fname? fopen (fname, "r") : stdin;
473 return gpg_error_from_errno (errno);
478 buf = xmalloc (bufsize);
480 while ((c=getc (fp)) != EOF)
489 if ((c = getc(fp)) == EOF)
492 if ((c = getc(fp)) == EOF)
495 if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
497 log_error ("invalid percent escape sequence\n");
498 state = s_idle; /* Force an error. */
499 /* Skip to end of line. */
500 while ( (c=getc (fp)) != EOF && c != '\n')
508 goto ready; /* Ready. */
522 if (c != "-----BEGIN "[pos])
538 if (buflen >= bufsize)
541 buf = xrealloc (buf, bufsize);
546 else if ((c = asctobin[c & 0xff]) == 255 )
547 ; /* Just skip invalid base64 characters. */
548 else if (state == s_b64_0)
553 else if (state == s_b64_1)
556 buf[buflen++] = value;
560 else if (state == s_b64_2)
563 buf[buflen++] = value;
570 buf[buflen++] = value;
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 if (c== '\n' && !escaped_c)
591 if (state == s_init && c == EOF)
594 return gpg_error (GPG_ERR_EOF);
596 else if (state != s_waitend)
598 log_error ("no certificate or invalid encoded\n");
600 return gpg_error (GPG_ERR_INV_ARMOR);
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
613 read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
618 size_t nread, bufsize, buflen;
621 return read_pem_certificate (fname, rbuf, rbuflen);
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 err = read_pem_certificate (fname, rbuf, rbuflen);
632 fp = fname? fopen (fname, "rb") : stdin;
634 return gpg_error_from_errno (errno);
637 bufsize = buflen = 0;
643 buf = xmalloc (bufsize);
645 buf = xrealloc (buf, bufsize);
647 nread = fread (buf+buflen, 1, NCHUNK, fp);
648 if (nread < NCHUNK && ferror (fp))
650 err = gpg_error_from_errno (errno);
658 while (nread == NCHUNK);
668 /* Callback for the inquire fiunction to send back the certificate. */
670 inq_cert (void *opaque, const char *line)
672 struct inq_cert_parm_s *parm = opaque;
675 if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
677 err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
679 else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
681 /* We don't support this but dirmngr might ask for it. So
682 simply ignore it by sending back and empty value. */
683 err = assuan_send_data (parm->ctx, NULL, 0);
685 else if (!strncmp (line, "SENDCERT_SKI", 12)
686 && (line[12]==' ' || !line[12]))
688 /* We don't support this but dirmngr might ask for it. So
689 simply ignore it by sending back an empty value. */
690 err = assuan_send_data (parm->ctx, NULL, 0);
692 else if (!strncmp (line, "SENDISSUERCERT", 14)
693 && (line[14] == ' ' || !line[14]))
695 /* We don't support this but dirmngr might ask for it. So
696 simply ignore it by sending back an empty value. */
697 err = assuan_send_data (parm->ctx, NULL, 0);
701 log_info (_("unsupported inquiry '%s'\n"), line);
702 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
713 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
714 Return a proper error code. */
716 do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
719 struct inq_cert_parm_s parm;
721 memset (&parm, 0, sizeof parm);
724 parm.certlen = certlen;
726 err = assuan_transact (ctx,
727 (opt.use_ocsp && opt.force_default_responder
728 ? "CHECKOCSP --force-default-responder"
729 : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
730 NULL, NULL, inq_cert, &parm, status_cb, NULL);
732 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
736 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
737 Return a proper error code. */
739 do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
742 struct inq_cert_parm_s parm;
744 memset (&parm, 0, sizeof parm);
747 parm.certlen = certlen;
749 err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
753 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
757 /* Check the certificate CERT,CERTLEN for validity using dirmngrs
758 internal validate feature. Return a proper error code. */
760 do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
763 struct inq_cert_parm_s parm;
765 memset (&parm, 0, sizeof parm);
768 parm.certlen = certlen;
770 err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
774 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
778 /* Load a CRL into the dirmngr. */
780 do_loadcrl (assuan_context_t ctx, const char *filename)
784 char *fname, *line, *p;
787 fname = xstrdup (filename);
790 #ifdef HAVE_CANONICALIZE_FILE_NAME
791 fname = canonicalize_file_name (filename);
794 log_error ("error canonicalizing '%s': %s\n",
795 filename, strerror (errno));
796 return gpg_error (GPG_ERR_GENERAL);
799 fname = xstrdup (filename);
803 log_error (_("absolute file name expected\n"));
804 return gpg_error (GPG_ERR_GENERAL);
808 line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
809 p = stpcpy (line, "LOADCRL ");
811 p = stpcpy (p, "--url ");
812 for (s = fname; *s; s++)
814 if (*s < ' ' || *s == '+')
816 sprintf (p, "%%%02X", *s);
826 err = assuan_transact (ctx, line, NULL, NULL,
830 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
837 /* Do a LDAP lookup using PATTERN and print the result in a base-64
840 do_lookup (assuan_context_t ctx, const char *pattern)
843 const unsigned char *s;
845 struct b64state state;
848 log_info (_("looking up '%s'\n"), pattern);
850 err = b64enc_start (&state, stdout, NULL);
854 line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
856 p = stpcpy (line, "LOOKUP ");
858 p = stpcpy (p, "--url ");
860 p = stpcpy (p, "--cache-only ");
861 for (s=pattern; *s; s++)
863 if (*s < ' ' || *s == '+')
865 sprintf (p, "%%%02X", *s);
876 err = assuan_transact (ctx, line,
881 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
883 err = b64enc_finish (&state);
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.
893 squid_loop_body (assuan_context_t ctx)
896 unsigned char *certbuf;
897 size_t certbuflen = 0;
899 err = read_pem_certificate (NULL, &certbuf, &certbuflen);
900 if (gpg_err_code (err) == GPG_ERR_EOF)
904 log_error (_("error reading certificate from stdin: %s\n"),
910 err = do_check (ctx, certbuf, certbuflen);
915 log_info (_("certificate is valid\n"));
922 if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
923 log_info (_("certificate has been revoked\n"));
925 log_error (_("certificate check failed: %s\n"),