Back to index

opendkim  2.6.2
opendkim-testkey.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2007, 2008 Sendmail, Inc. and its suppliers.
00003 **     All rights reserved.
00004 **
00005 **  Copyright (c) 2009-2011, The OpenDKIM Project.  All rights reserved.
00006 **
00007 **  $Id: opendkim-testkey.c,v 1.10.10.1 2010/10/27 21:43:09 cm-msk Exp $
00008 */
00009 
00010 #ifndef lint
00011 static char opendkim_testkey_c_id[] = "@(#)$Id: opendkim-testkey.c,v 1.10.10.1 2010/10/27 21:43:09 cm-msk Exp $";
00012 #endif /* !lint */
00013 
00014 #include "build-config.h"
00015 
00016 /* for Solaris */
00017 #ifndef _REENTRANT
00018 # define _REENTRANT
00019 #endif /* _REENTRANT */
00020 
00021 #include "build-config.h"
00022 
00023 /* system includes */
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <stdlib.h>
00027 #include <fcntl.h>
00028 #include <errno.h>
00029 #include <stdio.h>
00030 #include <string.h>
00031 #include <sysexits.h>
00032 #include <unistd.h>
00033 #include <assert.h>
00034 
00035 #ifdef USE_GNUTLS
00036 /* gcrypt includes */
00037 # include <gnutls/gnutls.h>
00038 #else /* USE_GNUTLS */
00039 /* openssl includes */
00040 # include <openssl/err.h>
00041 #endif /* USE_GNUTLS */
00042 
00043 /* libopendkim includes */
00044 #include <dkim.h>
00045 #include <dkim-test.h>
00046 #include <dkim-strl.h>
00047 
00048 /* opendkim includes */
00049 #include "opendkim-db.h"
00050 #include "opendkim-dns.h"
00051 #include "config.h"
00052 #include "opendkim-config.h"
00053 #include "opendkim-crypto.h"
00054 
00055 /* macros */
00056 #define       CMDLINEOPTS   "d:k:s:vx:"
00057 #define       DEFCONFFILE   CONFIG_BASE "/opendkim.conf"
00058 #define       BUFRSZ        2048
00059 
00060 #ifndef MIN
00061 # define MIN(x,y)    ((x) < (y) ? (x) : (y))
00062 #endif /* !MIN */
00063 
00064 /* prototypes */
00065 void dkimf_log_ssl_errors(void);
00066 int usage(void);
00067 
00068 /* globals */
00069 char *progname;
00070 #ifdef USE_UNBOUND
00071 struct dkimf_unbound *unbound;                   /* libunbound handle */
00072 #endif /* USE_UNBOUND */
00073 
00074 /*
00075 **  DKIMF_LOG_SSL_ERRORS -- log any queued SSL library errors
00076 **
00077 **  Parameters:
00078 **     jobid -- job ID to include in log messages
00079 **
00080 **  Return value:
00081 **     None.
00082 */
00083 
00084 void
00085 dkimf_log_ssl_errors(void)
00086 {
00087 #ifdef USE_GNUTLS
00088        const char *err;
00089 
00090        err = dkimf_crypto_geterror();
00091        if (err != NULL)
00092               fprintf(stderr, "%s\n", err);
00093 #else /* USE_GNUTLS */
00094        /* log any queued SSL error messages */
00095        if (ERR_peek_error() != 0)
00096        {
00097               int n;
00098               int saveerr;
00099               u_long e;
00100               char errbuf[BUFRSZ + 1];
00101               char tmp[BUFRSZ + 1];
00102 
00103               saveerr = errno;
00104 
00105               memset(errbuf, '\0', sizeof errbuf);
00106               for (n = 0; ; n++)
00107               {
00108                      e = ERR_get_error();
00109                      if (e == 0)
00110                             break;
00111 
00112                      memset(tmp, '\0', sizeof tmp);
00113                      (void) ERR_error_string_n(e, tmp, sizeof tmp);
00114                      if (n != 0)
00115                             strlcat(errbuf, "; ", sizeof errbuf);
00116                      strlcat(errbuf, tmp, sizeof errbuf);
00117               }
00118 
00119               fprintf(stderr, "%s\n", errbuf);
00120 
00121               errno = saveerr;
00122        }
00123 #endif /* ! USE_GNUTLS */
00124 }
00125 
00126 /*
00127 **  LOADKEY -- resolve a key
00128 **
00129 **  Parameters:
00130 **     buf -- key buffer
00131 **     buflen -- pointer to key buffer's length (updated)
00132 **
00133 **  Return value:
00134 **     TRUE on successful load, false otherwise
00135 */
00136 
00137 int
00138 loadkey(char *buf, size_t *buflen)
00139 {
00140        assert(buf != NULL);
00141        assert(buflen != NULL);
00142 
00143        if (buf[0] == '/' || (buf[0] == '.' && buf[1] == '/') ||
00144            (buf[0] == '.' && buf[1] == '.' && buf[2] == '/'))
00145        {
00146               int fd;
00147               int status;
00148               ssize_t rlen;
00149               struct stat s;
00150 
00151               fd = open(buf, O_RDONLY);
00152               if (fd < 0)
00153                      return FALSE;
00154 
00155               status = fstat(fd, &s);
00156               if (status != 0)
00157               {
00158                      close(fd);
00159                      return FALSE;
00160               }
00161 
00162               *buflen = MIN(s.st_size, *buflen);
00163               rlen = read(fd, buf, *buflen);
00164               close(fd);
00165 
00166               if (rlen < *buflen)
00167                      return FALSE;
00168        }
00169        else
00170        {
00171               *buflen = strlen(buf);
00172        }
00173 
00174        return TRUE;
00175 }
00176 
00177 /*
00178 **  USAGE -- print a usage message
00179 **
00180 **  Parameters:
00181 **     None.
00182 **
00183 **  Return value:
00184 **     EX_CONFIG
00185 */
00186 
00187 int
00188 usage(void)
00189 {
00190        fprintf(stderr,
00191                "%s: usage: %s [options]\n"
00192                "\t-d domain  \tdomain name\n"
00193                "\t-k keypath \tpath to private key\n"
00194                "\t-s selector\tselector name\n"
00195                "\t-v         \tincrease verbose output\n"
00196                "\t-x conffile\tconfiguration file\n",
00197                progname, progname);
00198 
00199        return EX_CONFIG;
00200 }
00201 
00202 /*
00203 **  MAIN -- program mainline
00204 **
00205 **  Parameters:
00206 **     argc, argv -- the usual
00207 **
00208 **  Return value:
00209 **     Exit status.
00210 */
00211 
00212 int
00213 main(int argc, char **argv)
00214 {
00215        int status;
00216        int fd;
00217        int len;
00218        int c;
00219        int verbose = 0;
00220        int argv_d = 0;
00221        int argv_s = 0;
00222        int argv_k = 0;
00223        int dnssec;
00224        char *key = NULL;
00225        char *dataset = NULL;
00226        char *conffile = NULL;
00227        char *p;
00228        DKIM_LIB *lib;
00229 #ifdef USE_UNBOUND
00230        char *trustanchor = NULL;
00231        char *ubconfig = NULL;
00232 #endif /* USE_UNBOUND */
00233        struct stat s;
00234        char err[BUFRSZ];
00235        char domain[BUFRSZ];
00236        char selector[BUFRSZ];
00237        char keypath[BUFRSZ];
00238 
00239        progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
00240 
00241        memset(domain, '\0', sizeof domain);
00242        memset(selector, '\0', sizeof selector);
00243        memset(keypath, '\0', sizeof keypath);
00244 
00245        while ((c = getopt(argc, argv, CMDLINEOPTS)) != -1)
00246        {
00247               switch (c)
00248               {
00249                 case 'd':
00250                      strlcpy(domain, optarg, sizeof domain);
00251                      argv_d = 1;
00252                      break;
00253 
00254                 case 'k':
00255                      strlcpy(keypath, optarg, sizeof keypath);
00256                      argv_k = 1;
00257                      break;
00258 
00259                 case 's':
00260                      strlcpy(selector, optarg, sizeof selector);
00261                      argv_s = 1;
00262                      break;
00263 
00264                 case 'v':
00265                      verbose++;
00266                      break;
00267 
00268                 case 'x':
00269                      conffile = optarg;
00270                      break;
00271 
00272                 default:
00273                      return usage();
00274               }
00275        }
00276 
00277        /* process config file */
00278        if (conffile == NULL && access(DEFCONFFILE, R_OK) == 0)
00279               conffile = DEFCONFFILE;
00280 
00281        if (conffile != NULL)
00282        {
00283 #ifdef USE_LDAP
00284               _Bool ldap_usetls = FALSE;
00285 #endif /* USE_LDAP */
00286               u_int line = 0;
00287 #ifdef USE_LDAP
00288               char *ldap_authmech = NULL;
00289 # ifdef USE_SASL
00290               char *ldap_authname = NULL;
00291               char *ldap_authrealm = NULL;
00292               char *ldap_authuser = NULL;
00293 # endif /* USE_SASL */
00294               char *ldap_bindpw = NULL;
00295               char *ldap_binduser = NULL;
00296 #endif /* USE_LDAP */
00297               struct config *cfg;
00298               char path[MAXPATHLEN + 1];
00299 
00300               cfg = config_load(conffile, dkimf_config, &line,
00301                                 path, sizeof path);
00302 
00303               if (cfg == NULL)
00304               {
00305                      fprintf(stderr,
00306                              "%s: %s: configuration error at line %u\n",
00307                              progname, path, line);
00308                      return EX_CONFIG;
00309               }
00310 
00311               (void) config_get(cfg, "KeyTable", &dataset, sizeof dataset);
00312 
00313               if (domain[0] == '\0')
00314               {
00315                      p = NULL;
00316                      (void) config_get(cfg, "Domain", &p, sizeof p);
00317                      if (p != NULL)
00318                             strlcpy(domain, p, sizeof domain);
00319               }
00320 
00321               if (selector[0] == '\0')
00322               {
00323                      p = NULL;
00324                      (void) config_get(cfg, "Selector", &p, sizeof p);
00325                      if (p != NULL)
00326                             strlcpy(selector, p, sizeof selector);
00327               }
00328 
00329               if (keypath[0] == '\0')
00330               {
00331                      p = NULL;
00332                      (void) config_get(cfg, "KeyFile", &p, sizeof p);
00333                      if (p != NULL)
00334                             strlcpy(keypath, p, sizeof keypath);
00335               }
00336 
00337 #ifdef USE_LDAP
00338               (void) config_get(cfg, "LDAPUseTLS",
00339                                 &ldap_usetls, sizeof ldap_usetls);
00340 
00341               if (ldap_usetls)
00342                      dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_USETLS, "y");
00343               else
00344                      dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_USETLS, "n");
00345 
00346               (void) config_get(cfg, "LDAPAuthMechanism",
00347                                 &ldap_authmech, sizeof ldap_authmech);
00348 
00349               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_AUTHMECH,
00350                                       ldap_authmech);
00351 
00352 # ifdef USE_SASL
00353               (void) config_get(cfg, "LDAPAuthName",
00354                                 &ldap_authname, sizeof ldap_authname);
00355 
00356               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_AUTHNAME,
00357                                       ldap_authname);
00358 
00359               (void) config_get(cfg, "LDAPAuthRealm",
00360                                 &ldap_authrealm, sizeof ldap_authrealm);
00361 
00362               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_AUTHREALM,
00363                                       ldap_authrealm);
00364 
00365               (void) config_get(cfg, "LDAPAuthUser",
00366                                 &ldap_authuser, sizeof ldap_authuser);
00367 
00368               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_AUTHUSER,
00369                                       ldap_authuser);
00370 # endif /* USE_SASL */
00371 
00372               (void) config_get(cfg, "LDAPBindPassword",
00373                                 &ldap_bindpw, sizeof ldap_bindpw);
00374 
00375               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_BINDPW, ldap_bindpw);
00376 
00377               (void) config_get(cfg, "LDAPBindUser",
00378                                 &ldap_binduser, sizeof ldap_binduser);
00379 
00380               dkimf_db_set_ldap_param(DKIMF_LDAP_PARAM_BINDUSER,
00381                                       ldap_binduser);
00382 #endif /* USE_LDAP */
00383 
00384 #ifdef USE_UNBOUND
00385               (void) config_get(cfg, "TrustAnchorFile",
00386                                 &trustanchor, sizeof trustanchor);
00387 
00388               (void) config_get(cfg, "UnboundConfigFile",
00389                                 &ubconfig, sizeof ubconfig);
00390 #endif /* USE_UNBOUND */
00391        }
00392 
00393 #ifdef USE_UNBOUND
00394        if (dkimf_unbound_init(&unbound) != 0)
00395        {
00396               fprintf(stderr, "%s: failed to initialize libunbound\n",
00397                       progname);
00398               (void) free(key);
00399               return EX_SOFTWARE;
00400        }
00401 #endif /* USE_UNBOUND */
00402 
00403        lib = dkim_init(NULL, NULL);
00404        if (lib == NULL)
00405        {
00406               fprintf(stderr, "%s: dkim_init() failed\n", progname);
00407               (void) free(key);
00408               return EX_OSERR;
00409        }
00410 
00411 #ifdef USE_UNBOUND
00412        if (unbound != NULL)
00413        {
00414               if (trustanchor != NULL)
00415               {
00416                      status = dkimf_unbound_add_trustanchor(unbound,
00417                                                             trustanchor);
00418                      if (status != DKIM_STAT_OK)
00419                      {
00420                             fprintf(stderr,
00421                                     "%s: failed to set trust anchor\n",
00422                                     progname);
00423 
00424                             (void) free(key);
00425                             return EX_OSERR;
00426                      }
00427               }
00428 
00429               if (ubconfig != NULL)
00430               {
00431                      status = dkimf_unbound_add_conffile(unbound,
00432                                                          ubconfig);
00433                      if (status != DKIM_STAT_OK)
00434                      {
00435                             fprintf(stderr,
00436                                     "%s: failed to set unbound configuration file\n",
00437                                     progname);
00438 
00439                             (void) free(key);
00440                             return EX_OSERR;
00441                      }
00442               }
00443 
00444               (void) dkimf_unbound_setup(lib, unbound);
00445        }
00446 #endif /* USE_UNBOUND */
00447 
00448        memset(err, '\0', sizeof err);
00449 
00450 #ifndef USE_GNUTLS
00451        ERR_load_crypto_strings();
00452 #endif /* ! USE_GNUTLS */
00453 
00454        /* process a KeyTable if specified and not overridden */
00455        if (dataset != NULL && argv_d == 0 && argv_k == 0 && argv_s == 0)
00456        {
00457               int c;
00458               int pass = 0;
00459               int fail = 0;
00460               size_t keylen;
00461               DKIMF_DB db;
00462               char keyname[BUFRSZ + 1];
00463               struct dkimf_db_data dbd[3];
00464 
00465               memset(dbd, '\0', sizeof dbd);
00466 
00467               status = dkimf_db_open(&db, dataset, DKIMF_DB_FLAG_READONLY,
00468                                      NULL, NULL);
00469               if (status != 0)
00470               {
00471                      fprintf(stderr, "%s: dkimf_db_open() failed\n",
00472                              progname);
00473                      return 1;
00474               }
00475 
00476               if (dkimf_db_type(db) == DKIMF_DB_TYPE_REFILE)
00477               {
00478                      fprintf(stderr, "%s: invalid data set type\n",
00479                              progname);
00480                      (void) dkimf_db_close(db);
00481                      return 1;
00482               }
00483 
00484               for (c = 0; ; c++)
00485               {
00486                      memset(keyname, '\0', sizeof keyname);
00487                      memset(domain, '\0', sizeof domain);
00488                      memset(selector, '\0', sizeof selector);
00489                      memset(keypath, '\0', sizeof keypath);
00490 
00491                      dbd[0].dbdata_buffer = domain;
00492                      dbd[0].dbdata_buflen = sizeof domain;
00493                      dbd[1].dbdata_buffer = selector;
00494                      dbd[1].dbdata_buflen = sizeof selector;
00495                      dbd[2].dbdata_buffer = keypath;
00496                      dbd[2].dbdata_buflen = sizeof keypath;
00497 
00498                      keylen = sizeof keyname;
00499 
00500                      status = dkimf_db_walk(db, c == 0, keyname, &keylen,
00501                                             dbd, 3);
00502                      if (status == -1)
00503                      {
00504                             fprintf(stderr,
00505                                     "%s: dkimf_db_walk(%d) failed\n",
00506                                     progname, c);
00507                             (void) dkimf_db_close(db);
00508                             return 1;
00509                      }
00510                      else if (status == 1)
00511                      {
00512                             (void) dkimf_db_close(db);
00513                             break;
00514                      }
00515 
00516                      if (verbose > 1)
00517                      {
00518                             fprintf(stderr,
00519                                     "%s: record %d for '%s' retrieved\n",
00520                                     progname, c, keyname);
00521                      }
00522 
00523                      if (keypath[0] == '/' ||
00524                          strncmp(keypath, "./", 2) == 0 ||
00525                          strncmp(keypath, "../", 3) == 0)
00526                      {
00527                             status = stat(keypath, &s);
00528                             if (status != 0)
00529                             {
00530                                    fprintf(stderr,
00531                                            "%s: %s: stat(): %s\n",
00532                                            progname, keypath,
00533                                            strerror(errno));
00534                                    return EX_OSERR;
00535                             }
00536 
00537                             if (!S_ISREG(s.st_mode))
00538                             {
00539                                    fprintf(stderr,
00540                                            "%s: %s: stat(): not a regular file\n",
00541                                            progname, keypath);
00542                                    return EX_OSERR;
00543                             }
00544 
00545                             /* XXX -- should also check directories up the chain */
00546                             if ((s.st_mode &
00547                                  (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)
00548                             {
00549                                    fprintf(stderr,
00550                                            "%s: %s: WARNING: unsafe permissions\n",
00551                                            progname, keypath);
00552                             }
00553                      }
00554 
00555                      keylen = sizeof keypath;
00556                      if (!loadkey(keypath, &keylen))
00557                      {
00558                             fprintf(stderr,
00559                                     "%s: load of key '%s' failed\n",
00560                                     progname, keyname);
00561                             (void) dkimf_db_close(db);
00562                             return 1;
00563                      }
00564 
00565                      if (verbose > 1)
00566                      {
00567                             fprintf(stderr, "%s: checking key '%s'\n",
00568                                     progname, keyname);
00569                      }
00570 
00571                      dnssec = DKIM_DNSSEC_UNKNOWN;
00572 
00573                      status = dkim_test_key(lib, selector, domain,
00574                                             keypath, keylen, &dnssec,
00575                                             err, sizeof err);
00576 
00577                      switch (status)
00578                      {
00579                        case -1:
00580                        case 1:
00581                             fprintf(stderr, "%s: key %s: %s\n", progname,
00582                                     keyname, err);
00583                             fail++;
00584                             dkimf_log_ssl_errors();
00585                             break;
00586 
00587                        case 0:
00588                             if (verbose > 2)
00589                             {
00590                                    fprintf(stdout, "%s: key %s: OK\n",
00591                                            progname, keyname);
00592                             }
00593                             pass++;
00594                             break;
00595 
00596                        default:
00597                             assert(0);
00598                      }
00599 
00600                      switch (dnssec)
00601                      {
00602                        case DKIM_DNSSEC_INSECURE:
00603                             if (verbose > 0)
00604                             {
00605                                    fprintf(stderr,
00606                                            "%s: key %s not secure\n",
00607                                            progname, keyname);
00608                             }
00609                             break;
00610 
00611                        case DKIM_DNSSEC_SECURE:
00612                             if (verbose > 0)
00613                             {
00614                                    fprintf(stderr,
00615                                            "%s: key %s secure\n",
00616                                            progname, keyname);
00617                             }
00618                             break;
00619 
00620                        case DKIM_DNSSEC_BOGUS:
00621                             fprintf(stderr,
00622                                     "%s: key %s bogus (DNSSEC failed)\n",
00623                                     progname, keyname);
00624                             break;
00625 
00626                        case DKIM_DNSSEC_UNKNOWN:
00627                        default:
00628                             break;
00629                      }
00630               }
00631 
00632               if (verbose > 0)
00633               {
00634                      fprintf(stdout,
00635                              "%s: %d key%s checked; %d pass, %d fail\n",
00636                              progname, c, c == 1 ? "" : "s", pass, fail);
00637               }
00638 
00639               (void) dkim_close(lib);
00640 
00641               return 0;
00642        }
00643 
00644        if (domain[0] == '\0' || selector[0] == '\0')
00645               return usage();
00646 
00647        memset(&s, '\0', sizeof s);
00648 
00649        if (keypath[0] != '\0')
00650        {
00651               status = stat(keypath, &s);
00652               if (status != 0)
00653               {
00654                      fprintf(stderr, "%s: %s: stat(): %s\n", progname,
00655                              keypath, strerror(errno));
00656                      return EX_OSERR;
00657               }
00658 
00659               if (!S_ISREG(s.st_mode))
00660               {
00661                      fprintf(stderr, "%s: %s: stat(): not a regular file\n",
00662                              progname, keypath);
00663                      return EX_OSERR;
00664               }
00665 
00666               /* XXX -- should also check directories up the chain */
00667               if ((s.st_mode & (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) != 0)
00668               {
00669                      fprintf(stderr,
00670                              "%s: %s: WARNING: unsafe permissions\n",
00671                              progname, keypath);
00672               }
00673 
00674               key = malloc(s.st_size);
00675               if (key == NULL)
00676               {
00677                      fprintf(stderr, "%s: malloc(): %s\n", progname,
00678                              strerror(errno));
00679                      return EX_OSERR;
00680               }
00681 
00682               fd = open(keypath, O_RDONLY, 0);
00683               if (fd < 0)
00684               {
00685                      fprintf(stderr, "%s: %s: open(): %s\n", progname,
00686                              keypath, strerror(errno));
00687                      (void) free(key);
00688                      return EX_OSERR;
00689               }
00690 
00691               len = read(fd, key, s.st_size);
00692               if (len < 0)
00693               {
00694                      fprintf(stderr, "%s: %s: read(): %s\n", progname,
00695                              keypath, strerror(errno));
00696                      (void) close(fd);
00697                      (void) free(key);
00698                      return EX_OSERR;
00699               }
00700               else if (len < s.st_size)
00701               {
00702                      fprintf(stderr,
00703                              "%s: %s: read() truncated (expected %ld, got %d)\n",
00704                              progname, keypath, (long) s.st_size, len);
00705                      (void) close(fd);
00706                      (void) free(key);
00707                      return EX_OSERR;
00708               }
00709 
00710               (void) close(fd);
00711 
00712               if (verbose > 1)
00713               {
00714                      fprintf(stderr, "%s: key loaded from %s\n",
00715                              progname, keypath);
00716               }
00717        }
00718 
00719        dnssec = DKIM_DNSSEC_UNKNOWN;
00720 
00721        if (verbose > 1)
00722        {
00723               fprintf(stderr, "%s: checking key '%s._domainkey.%s'\n",
00724                       progname, selector, domain);
00725        }
00726 
00727        status = dkim_test_key(lib, selector, domain, key, (size_t) s.st_size,
00728                               &dnssec, err, sizeof err);
00729 
00730        (void) dkim_close(lib);
00731 
00732        switch (dnssec)
00733        {
00734          case DKIM_DNSSEC_INSECURE:
00735               if (verbose > 0)
00736                      fprintf(stderr, "%s: key not secure\n", progname);
00737               break;
00738 
00739          case DKIM_DNSSEC_SECURE:
00740               if (verbose > 0)
00741                      fprintf(stderr, "%s: key secure\n", progname);
00742               break;
00743 
00744          case DKIM_DNSSEC_BOGUS:
00745               fprintf(stderr, "%s: key bogus (DNSSEC failed)\n",
00746                       progname);
00747               break;
00748 
00749          case DKIM_DNSSEC_UNKNOWN:
00750          default:
00751               break;
00752        }
00753 
00754        switch (status)
00755        {
00756          case -1:
00757               fprintf(stderr, "%s: %s\n", progname, err);
00758               dkimf_log_ssl_errors();
00759               return EX_UNAVAILABLE;
00760 
00761          case 0:
00762               if (verbose > 2)
00763                      fprintf(stdout, "%s: key OK\n", progname);
00764               return EX_OK;
00765 
00766          case 1:
00767               fprintf(stdout, "%s: %s\n", progname, err);
00768               dkimf_log_ssl_errors();
00769               return EX_DATAERR;
00770        }
00771 
00772        return 0;
00773 }