LCOV - code coverage report
Current view: top level - dirmngr - dirmngr-client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 449 0.0 %
Date: 2015-11-05 17:10:59 Functions: 0 14 0.0 %

          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             : }

Generated by: LCOV version 1.11