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 <http://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 */
120 static gpg_error_t read_certificate (const char *fname,
121 unsigned char **rbuf, size_t *rbuflen);
122 static gpg_error_t do_check (assuan_context_t ctx,
123 const unsigned char *cert, size_t certlen);
124 static gpg_error_t do_cache (assuan_context_t ctx,
125 const unsigned char *cert, size_t certlen);
126 static gpg_error_t do_validate (assuan_context_t ctx,
127 const unsigned char *cert, size_t certlen);
128 static gpg_error_t do_loadcrl (assuan_context_t ctx, const char *filename);
129 static gpg_error_t do_lookup (assuan_context_t ctx, const char *pattern);
130 static gpg_error_t squid_loop_body (assuan_context_t ctx);
134 /* Function called by argparse.c to display information. */
136 my_strusage (int level)
142 case 11: p = "dirmngr-client (@GNUPG@)";
144 case 13: p = VERSION; break;
145 case 17: p = PRINTABLE_OS_NAME; break;
146 case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
147 case 49: p = PACKAGE_BUGREPORT; break;
150 _("Usage: dirmngr-client [options] "
151 "[certfile|pattern] (-h for help)\n");
154 _("Syntax: dirmngr-client [options] [certfile|pattern]\n"
155 "Test an X.509 certificate against a CRL or do an OCSP check\n"
156 "The process returns 0 if the certificate is valid, 1 if it is\n"
157 "not valid and other error codes for general failures\n");
168 main (int argc, char **argv )
171 assuan_context_t ctx;
173 unsigned char *certbuf;
174 size_t certbuflen = 0;
176 int cmd_cache_cert = 0;
177 int cmd_validate = 0;
180 int cmd_squid_mode = 0;
182 early_system_init ();
183 set_strusage (my_strusage);
184 log_set_prefix ("dirmngr-client",
185 GPGRT_LOG_WITH_PREFIX);
187 /* For W32 we need to initialize the socket subsystem. Because we
188 don't use Pth we need to do this explicit. */
189 #ifdef HAVE_W32_SYSTEM
193 WSAStartup (0x202, &wsadat);
195 #endif /*HAVE_W32_SYSTEM*/
198 assuan_set_assuan_log_prefix (log_get_prefix (NULL));
199 assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
204 /* Parse the command line. */
207 pargs.flags= 1; /* Do not remove the args. */
208 while (arg_parse (&pargs, opts) )
212 case oVerbose: opt.verbose++; break;
213 case oQuiet: opt.quiet++; break;
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;
229 case oForceDefaultResponder: opt.force_default_responder = 1; break;
231 default : pargs.err = 2; break;
234 if (log_get_errorcount (0))
237 /* Build the helptable for radix64 to bin conversion. */
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++)
252 else if (cmd_lookup || cmd_loadcrl)
258 else if (cmd_squid_mode)
266 err = read_certificate (NULL, &certbuf, &certbuflen);
268 log_error (_("error reading certificate from stdin: %s\n"),
273 err = read_certificate (*argv, &certbuf, &certbuflen);
275 log_error (_("error reading certificate from '%s': %s\n"),
276 *argv, gpg_strerror (err));
284 if (log_get_errorcount (0))
287 if (certbuflen > 20000)
289 log_error (_("certificate too large to make any sense\n"));
293 err = start_new_dirmngr (&ctx,
294 GPG_ERR_SOURCE_DEFAULT,
297 ? opt.dirmngr_program
298 : gnupg_module_name (GNUPG_MODULE_NAME_DIRMNGR),
305 log_error (_("can't connect to the dirmngr: %s\n"), gpg_strerror (err));
311 else if (cmd_squid_mode)
313 while (!(err = squid_loop_body (ctx)))
315 if (gpg_err_code (err) == GPG_ERR_EOF)
322 for (; argc; argc--, argv++)
324 err = do_lookup (ctx, *argv);
327 log_error (_("lookup failed: %s\n"), gpg_strerror (err));
333 else if (cmd_loadcrl)
337 for (; argc; argc--, argv++)
339 err = do_loadcrl (ctx, *argv);
342 log_error (_("loading CRL '%s' failed: %s\n"),
343 *argv, gpg_strerror (err));
349 else if (cmd_cache_cert)
351 err = do_cache (ctx, certbuf, certbuflen);
354 else if (cmd_validate)
356 err = do_validate (ctx, certbuf, certbuflen);
361 err = do_check (ctx, certbuf, certbuflen);
365 assuan_release (ctx);
370 log_info (_("a dirmngr daemon is up and running\n"));
373 else if (cmd_lookup|| cmd_loadcrl || cmd_squid_mode)
375 else if (cmd_cache_cert)
377 if (err && gpg_err_code (err) == GPG_ERR_DUP_VALUE )
380 log_info (_("certificate already cached\n"));
384 log_error (_("error caching certificate: %s\n"),
390 else if (cmd_validate && err)
392 log_error (_("validation of certificate failed: %s\n"),
399 log_info (_("certificate is valid\n"));
402 else if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
405 log_info (_("certificate has been revoked\n"));
410 log_error (_("certificate check failed: %s\n"), gpg_strerror (err));
416 /* Print status line from the assuan protocol. */
418 status_cb (void *opaque, const char *line)
423 log_info (_("got status: '%s'\n"), line);
427 /* Print data as retrieved by the lookup function. */
429 data_cb (void *opaque, const void *buffer, size_t length)
432 struct b64state *state = opaque;
436 err = b64enc_write (state, buffer, length);
438 log_error (_("error writing base64 encoding: %s\n"),
445 /* Read the first PEM certificate from the file FNAME. If fname is
446 NULL the next certificate is read from stdin. The certificate is
447 returned in an alloced buffer whose address will be returned in
448 RBUF and its length in RBUFLEN. */
450 read_pem_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
457 size_t bufsize, buflen;
459 s_init, s_idle, s_lfseen, s_begin,
460 s_b64_0, s_b64_1, s_b64_2, s_b64_3,
464 fp = fname? fopen (fname, "r") : stdin;
466 return gpg_error_from_errno (errno);
471 buf = xmalloc (bufsize);
473 while ((c=getc (fp)) != EOF)
482 if ((c = getc(fp)) == EOF)
485 if ((c = getc(fp)) == EOF)
488 if (!hexdigitp (tmp) || !hexdigitp (tmp+1))
490 log_error ("invalid percent escape sequence\n");
491 state = s_idle; /* Force an error. */
492 /* Skip to end of line. */
493 while ( (c=getc (fp)) != EOF && c != '\n')
501 goto ready; /* Ready. */
515 if (c != "-----BEGIN "[pos])
531 if (buflen >= bufsize)
534 buf = xrealloc (buf, bufsize);
539 else if ((c = asctobin[c & 0xff]) == 255 )
540 ; /* Just skip invalid base64 characters. */
541 else if (state == s_b64_0)
546 else if (state == s_b64_1)
549 buf[buflen++] = value;
553 else if (state == s_b64_2)
556 buf[buflen++] = value;
563 buf[buflen++] = value;
569 /* Note that we do not check that the base64 decoder has
570 been left in the expected state. We assume that the PEM
571 header is just fine. However we need to wait for the
572 real LF and not a trailing percent escaped one. */
573 if (c== '\n' && !escaped_c)
584 if (state == s_init && c == EOF)
587 return gpg_error (GPG_ERR_EOF);
589 else if (state != s_waitend)
591 log_error ("no certificate or invalid encoded\n");
593 return gpg_error (GPG_ERR_INV_ARMOR);
601 /* Read a binary certificate from the file FNAME. If fname is NULL the
602 file is read from stdin. The certificate is returned in an alloced
603 buffer whose address will be returned in RBUF and its length in
606 read_certificate (const char *fname, unsigned char **rbuf, size_t *rbuflen)
611 size_t nread, bufsize, buflen;
614 return read_pem_certificate (fname, rbuf, rbuflen);
616 fp = fname? fopen (fname, "rb") : stdin;
618 return gpg_error_from_errno (errno);
621 bufsize = buflen = 0;
627 buf = xmalloc (bufsize);
629 buf = xrealloc (buf, bufsize);
631 nread = fread (buf+buflen, 1, NCHUNK, fp);
632 if (nread < NCHUNK && ferror (fp))
634 err = gpg_error_from_errno (errno);
642 while (nread == NCHUNK);
652 /* Callback for the inquire fiunction to send back the certificate. */
654 inq_cert (void *opaque, const char *line)
656 struct inq_cert_parm_s *parm = opaque;
659 if (!strncmp (line, "TARGETCERT", 10) && (line[10] == ' ' || !line[10]))
661 err = assuan_send_data (parm->ctx, parm->cert, parm->certlen);
663 else if (!strncmp (line, "SENDCERT", 8) && (line[8] == ' ' || !line[8]))
665 /* We don't support this but dirmngr might ask for it. So
666 simply ignore it by sending back and empty value. */
667 err = assuan_send_data (parm->ctx, NULL, 0);
669 else if (!strncmp (line, "SENDCERT_SKI", 12)
670 && (line[12]==' ' || !line[12]))
672 /* We don't support this but dirmngr might ask for it. So
673 simply ignore it by sending back an empty value. */
674 err = assuan_send_data (parm->ctx, NULL, 0);
676 else if (!strncmp (line, "SENDISSUERCERT", 14)
677 && (line[14] == ' ' || !line[14]))
679 /* We don't support this but dirmngr might ask for it. So
680 simply ignore it by sending back an empty value. */
681 err = assuan_send_data (parm->ctx, NULL, 0);
685 log_info (_("unsupported inquiry '%s'\n"), line);
686 err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
687 /* Note that this error will let assuan_transact terminate
688 immediately instead of return the error to the caller. It is
689 not clear whether this is the desired behaviour - it may
697 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
698 Return a proper error code. */
700 do_check (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
703 struct inq_cert_parm_s parm;
705 memset (&parm, 0, sizeof parm);
708 parm.certlen = certlen;
710 err = assuan_transact (ctx,
711 (opt.use_ocsp && opt.force_default_responder
712 ? "CHECKOCSP --force-default-responder"
713 : opt.use_ocsp? "CHECKOCSP" : "CHECKCRL"),
714 NULL, NULL, inq_cert, &parm, status_cb, NULL);
716 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
720 /* Check the certificate CERT,CERTLEN for validity using a CRL or OCSP.
721 Return a proper error code. */
723 do_cache (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
726 struct inq_cert_parm_s parm;
728 memset (&parm, 0, sizeof parm);
731 parm.certlen = certlen;
733 err = assuan_transact (ctx, "CACHECERT", NULL, NULL,
737 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
741 /* Check the certificate CERT,CERTLEN for validity using dirmngrs
742 internal validate feature. Return a proper error code. */
744 do_validate (assuan_context_t ctx, const unsigned char *cert, size_t certlen)
747 struct inq_cert_parm_s parm;
749 memset (&parm, 0, sizeof parm);
752 parm.certlen = certlen;
754 err = assuan_transact (ctx, "VALIDATE", NULL, NULL,
758 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
762 /* Load a CRL into the dirmngr. */
764 do_loadcrl (assuan_context_t ctx, const char *filename)
768 char *fname, *line, *p;
771 fname = xstrdup (filename);
774 #ifdef HAVE_CANONICALIZE_FILE_NAME
775 fname = canonicalize_file_name (filename);
778 log_error ("error canonicalizing '%s': %s\n",
779 filename, strerror (errno));
780 return gpg_error (GPG_ERR_GENERAL);
783 fname = xstrdup (filename);
787 log_error (_("absolute file name expected\n"));
788 return gpg_error (GPG_ERR_GENERAL);
792 line = xmalloc (8 + 6 + strlen (fname) * 3 + 1);
793 p = stpcpy (line, "LOADCRL ");
795 p = stpcpy (p, "--url ");
796 for (s = fname; *s; s++)
798 if (*s < ' ' || *s == '+')
800 sprintf (p, "%%%02X", *s);
810 err = assuan_transact (ctx, line, NULL, NULL,
814 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
821 /* Do a LDAP lookup using PATTERN and print the result in a base-64
824 do_lookup (assuan_context_t ctx, const char *pattern)
827 const unsigned char *s;
829 struct b64state state;
832 log_info (_("looking up '%s'\n"), pattern);
834 err = b64enc_start (&state, stdout, NULL);
838 line = xmalloc (10 + 6 + 13 + strlen (pattern)*3 + 1);
840 p = stpcpy (line, "LOOKUP ");
842 p = stpcpy (p, "--url ");
844 p = stpcpy (p, "--cache-only ");
845 for (s=pattern; *s; s++)
847 if (*s < ' ' || *s == '+')
849 sprintf (p, "%%%02X", *s);
860 err = assuan_transact (ctx, line,
865 log_info ("response of dirmngr: %s\n", err? gpg_strerror (err): "okay");
867 err = b64enc_finish (&state);
873 /* The body of an endless loop: Read a line from stdin, retrieve the
874 certificate from it, validate it and print "ERR" or "OK" to stdout.
877 squid_loop_body (assuan_context_t ctx)
880 unsigned char *certbuf;
881 size_t certbuflen = 0;
883 err = read_pem_certificate (NULL, &certbuf, &certbuflen);
884 if (gpg_err_code (err) == GPG_ERR_EOF)
888 log_error (_("error reading certificate from stdin: %s\n"),
894 err = do_check (ctx, certbuf, certbuflen);
899 log_info (_("certificate is valid\n"));
906 if (gpg_err_code (err) == GPG_ERR_CERT_REVOKED )
907 log_info (_("certificate has been revoked\n"));
909 log_error (_("certificate check failed: %s\n"),