Back to index

opendkim  2.6.4
opendkim-atpszone.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2010-2012, The OpenDKIM Project.  All rights reserved.
00003 **
00004 **  $Id: opendkim-genzone.c,v 1.12.10.1 2010/10/27 21:43:09 cm-msk Exp $
00005 */
00006 
00007 #ifndef lint
00008 static char opendkim_atpszone_c_id[] = "$Id: opendkim-genzone.c,v 1.12.10.1 2010/10/27 21:43:09 cm-msk Exp $";
00009 #endif /* !lint */
00010 
00011 #include "build-config.h"
00012 
00013 /* system includes */
00014 #include <sys/param.h>
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <sysexits.h>
00018 #include <string.h>
00019 #include <assert.h>
00020 #include <fcntl.h>
00021 #include <ctype.h>
00022 #include <stdlib.h>
00023 #include <stdio.h>
00024 #include <errno.h>
00025 #include <unistd.h>
00026 #include <pwd.h>
00027 
00028 /* openssl includes */
00029 #ifdef USE_GNUTLS
00030 # include <gnutls/gnutls.h>
00031 # include <gnutls/crypto.h>
00032 # include <gnutls/abstract.h>
00033 # include <gnutls/x509.h>
00034 #else /* USE_GNUTLS */
00035 # include <openssl/rsa.h>
00036 # include <openssl/pem.h>
00037 # include <openssl/evp.h>
00038 # include <openssl/bio.h>
00039 #endif /* USE_GNUTLS */
00040 
00041 #ifndef FALSE
00042 # define FALSE              0
00043 #endif /* ! FALSE */
00044 #ifndef TRUE
00045 # define TRUE        1
00046 #endif /* ! TRUE */
00047 #ifndef SHA_DIGEST_LENGTH
00048 # define SHA_DIGEST_LENGTH 20
00049 #endif /* ! SHA_DIGEST_LENGTH */
00050 #ifndef SHA256_DIGEST_LENGTH
00051 # define SHA256_DIGEST_LENGTH 32
00052 #endif /* ! SHA256_DIGEST_LENGTH */
00053 
00054 /* libopendkim includes */
00055 #include <dkim.h>
00056 
00057 /* opendkim includes */
00058 #include "opendkim-db.h"
00059 #include "util.h"
00060 #include "config.h"
00061 #include "opendkim-config.h"
00062 
00063 /* definitions */
00064 #define       ATPSZONE      "._atps"
00065 #define       BASE32_LENGTH 32
00066 #define       BUFRSZ        256
00067 #define       CMDLINEOPTS   "AC:E:h:o:N:r:R:St:T:v"
00068 #define       DEFEXPIRE     604800
00069 #define       DEFREFRESH    10800
00070 #define       DEFRETRY      1800
00071 #define       DEFTTL        86400
00072 #define       HOSTMASTER    "hostmaster"
00073 #define       MAXNS         16
00074 #define       VALIDATPS     "v=ATPS1"
00075 
00076 /* globals */
00077 char *progname;
00078 
00079 /*
00080 **  USAGE -- print usage message and exit
00081 **
00082 **  Parameters:
00083 **     None.
00084 **
00085 **  Return value:
00086 **     EX_USAGE
00087 */
00088 
00089 int
00090 usage(void)
00091 {
00092        fprintf(stderr, "%s: usage: %s [opts] [dataset]\n"
00093                        "\t-A          \tinclude '._atps' suffix\n"
00094                        "\t-C user@host\tcontact address to include in SOA\n"
00095                        "\t-E secs     \tuse specified expiration time in SOA\n"
00096                        "\t-h hash     \thash algorithm\n"
00097                        "\t-o file     \toutput file\n"
00098                        "\t-N ns[,...] \tlist NS records\n"
00099                        "\t-r secs     \tuse specified refresh time in SOA\n"
00100                        "\t-R secs     \tuse specified retry time in SOA\n"
00101                        "\t-S          \twrite an SOA record\n"
00102                        "\t-t secs     \tuse specified per-record TTL\n"
00103                        "\t-T secs     \tuse specified default TTL in SOA\n"
00104                        "\t-v          \tverbose output\n",
00105               progname, progname);
00106 
00107        return EX_USAGE;
00108 }
00109 
00110 /*
00111 **  MAIN -- program mainline
00112 **
00113 **  Parameters:
00114 **     argc, argv -- the usual
00115 **
00116 **  Return value:
00117 **     Exit status.
00118 */
00119 
00120 int
00121 main(int argc, char **argv)
00122 {
00123        _Bool suffix = FALSE;
00124        _Bool writesoa = FALSE;
00125        int c;
00126        int status;
00127        int verbose = 0;
00128        int ttl = -1;
00129        int defttl = DEFTTL;
00130        int expire = DEFEXPIRE;
00131        int refresh = DEFREFRESH;
00132        int retry = DEFRETRY;
00133        int nscount = 0;
00134        time_t now;
00135        size_t dlen;
00136        size_t b32len;
00137        size_t shalen;
00138        char *p;
00139        char *dataset = NULL;
00140        char *outfile = NULL;
00141        char *hash = NULL;
00142        char *contact = NULL;
00143        char *nameservers = NULL;
00144        char *nslist[MAXNS];
00145        FILE *out;
00146        DKIMF_DB db;
00147 #ifdef USE_GNUTLS
00148        gnutls_hash_hd_t sha;
00149 #else /* USE_GNUTLS */
00150        SHA_CTX sha;
00151 # ifdef HAVE_SHA256
00152        SHA256_CTX sha256;
00153 # endif /* HAVE_SHA256 */
00154 #endif /* USE_GNUTLS */
00155        char domain[DKIM_MAXHOSTNAMELEN + 1];
00156        char hostname[DKIM_MAXHOSTNAMELEN + 1];
00157 #ifdef HAVE_SHA256
00158        char shaout[SHA256_DIGEST_LENGTH];
00159 #else /* HAVE_SHA256 */
00160        char shaout[SHA_DIGEST_LENGTH];
00161 #endif /* HAVE_SHA256 */
00162        char base32[BASE32_LENGTH + 1];
00163 
00164        progname = (p = strrchr(argv[0], '/')) == NULL ? argv[0] : p + 1;
00165 
00166        while ((c = getopt(argc, argv, CMDLINEOPTS)) != -1)
00167        {
00168               switch (c)
00169               {
00170                 case 'A':
00171                      suffix = TRUE;
00172                      break;
00173 
00174                 case 'C':
00175                      contact = strdup(optarg);
00176                      break;
00177 
00178                 case 'E':
00179                      expire = strtol(optarg, &p, 10);
00180                      if (*p != '\0' || expire < 0)
00181                      {
00182                             fprintf(stderr, "%s: invalid expire value\n",
00183                                     progname);
00184                             return EX_USAGE;
00185                      }
00186                      break;
00187 
00188                 case 'h':
00189                      if (strcasecmp(optarg, "sha1") != 0 &&
00190                          strcasecmp(optarg, "sha256") != 0 &&
00191                          strcasecmp(optarg, "none") != 0)
00192                      {
00193                             fprintf(stderr, "%s: invalid hash algorithm\n",
00194                                     progname);
00195                             return EX_USAGE;
00196                      }
00197 #ifndef HAVE_SHA256
00198                      else if (strcasecmp(optarg, "sha256") == 0)
00199                      {
00200                             fprintf(stderr,
00201                                     "%s: hash algorithm \"%s\" not supported\n",
00202                                     progname, optarg);
00203                             return EX_SOFTWARE;
00204                      }
00205 #endif /* ! HAVE_SHA256 */
00206                      hash = optarg;
00207                      if (strcasecmp(hash, "sha1") == 0)
00208                             shalen = SHA_DIGEST_LENGTH;
00209                      else
00210                             shalen = SHA256_DIGEST_LENGTH;
00211                      break;
00212 
00213                 case 'N':
00214                      nameservers = strdup(optarg);
00215                      break;
00216 
00217                 case 'o':
00218                      outfile = optarg;
00219                      break;
00220 
00221                 case 'r':
00222                      refresh = strtol(optarg, &p, 10);
00223                      if (*p != '\0' || refresh < 0)
00224                      {
00225                             fprintf(stderr, "%s: invalid refresh value\n",
00226                                     progname);
00227                             return EX_USAGE;
00228                      }
00229                      break;
00230 
00231                 case 'R':
00232                      retry = strtol(optarg, &p, 10);
00233                      if (*p != '\0' || retry < 0)
00234                      {
00235                             fprintf(stderr, "%s: invalid retry value\n",
00236                                     progname);
00237                             return EX_USAGE;
00238                      }
00239                      break;
00240 
00241                 case 't':
00242                      ttl = strtol(optarg, &p, 10);
00243                      if (*p != '\0' || ttl < 0)
00244                      {
00245                             fprintf(stderr, "%s: invalid TTL value\n",
00246                                     progname);
00247                             return EX_USAGE;
00248                      }
00249                      break;
00250 
00251                 case 'T':
00252                      defttl = strtol(optarg, &p, 10);
00253                      if (*p != '\0' || defttl < 0)
00254                      {
00255                             fprintf(stderr,
00256                                     "%s: invalid default TTL value\n",
00257                                     progname);
00258                             return EX_USAGE;
00259                      }
00260                      break;
00261 
00262                 case 'S':
00263                      writesoa = TRUE;
00264                      break;
00265 
00266                 case 'v':
00267                      verbose++;
00268                      break;
00269 
00270                 default:
00271                      return usage();
00272               }
00273        }
00274 
00275        if (optind != argc)
00276               dataset = argv[optind];
00277 
00278        status = dkimf_db_open(&db, dataset, DKIMF_DB_FLAG_READONLY,
00279                               NULL, NULL);
00280        if (status != 0)
00281        {
00282               fprintf(stderr, "%s: dkimf_db_open() failed\n", progname);
00283               return 1;
00284        }
00285 
00286        if (dkimf_db_type(db) == DKIMF_DB_TYPE_REFILE)
00287        {
00288               fprintf(stderr, "%s: invalid data set type\n", progname);
00289               (void) dkimf_db_close(db);
00290               return 1;
00291        }
00292 
00293        if (verbose > 0)
00294               fprintf(stderr, "%s: database opened\n", progname);
00295 
00296        if (outfile != NULL)
00297        {
00298               out = fopen(outfile, "w");
00299               if (out == NULL)
00300               {
00301                      fprintf(stderr, "%s: %s: fopen(): %s\n",
00302                              progname, outfile, strerror(errno));
00303                      (void) dkimf_db_close(db);
00304                      return 1;
00305               }
00306        }
00307        else
00308        {
00309               out = stdout;
00310        }
00311 
00312        if (nameservers != NULL)
00313        {
00314               for (p = strtok(nameservers, ",");
00315                    p != NULL && nscount < MAXNS;
00316                    p = strtok(NULL, ","))
00317                      nslist[nscount++] = p;
00318        }
00319 
00320        memset(hostname, '\0', sizeof hostname);
00321        gethostname(hostname, sizeof hostname);
00322 
00323        if (nscount == 0)
00324               nslist[nscount++] = hostname;
00325 
00326        (void) time(&now);
00327 
00328        fprintf(out, "; DKIM ATPS zone data\n");
00329        fprintf(out, "; auto-generated by %s at %s\n", progname, ctime(&now));
00330 
00331        if (writesoa)
00332        {
00333               struct tm *tm;
00334 
00335               fprintf(out, "@\tIN\tSOA\t%s\t", nslist[0]);
00336 
00337               if (contact != NULL)
00338               {
00339                      for (p = contact; *p != '\0'; p++)
00340                      {
00341                             if (*p == '@')
00342                                    *p = '.';
00343                      }
00344 
00345                      fprintf(out, "%s", contact);
00346               }
00347               else
00348               {
00349                      struct passwd *pwd;
00350 
00351                      pwd = getpwuid(getuid());
00352 
00353                      fprintf(out, "%s.%s",
00354                              pwd == NULL ? HOSTMASTER : pwd->pw_name,
00355                              hostname);
00356               }
00357 
00358               tm = localtime(&now);
00359 
00360               fprintf(out,
00361                       "\t (\n"
00362                       "\t%04d%02d%02d%02d   ; Serial (yyyymmddhh)\n"
00363                       "\t%-10d   ; Refresh\n"
00364                       "\t%-10d   ; Retry\n"
00365                       "\t%-10d   ; Expire\n"
00366                       "\t%-10d ) ; Default\n\n",
00367                       tm->tm_year + 1900,
00368                       tm->tm_mon + 1,
00369                       tm->tm_mday,
00370                       tm->tm_hour,
00371                       refresh, retry, expire, defttl);
00372        }
00373 
00374        if (nameservers != NULL)
00375        {
00376               for (c = 0; c < nscount; c++)
00377                      fprintf(out, "\tIN\tNS\t%s\n", nslist[c]);
00378 
00379               fprintf(out, "\n");
00380        }
00381 
00382 #ifdef USE_GNUTLS
00383        (void) gnutls_global_init();
00384 #endif /* USE_GNUTLS */
00385 
00386        for (c = 0; ; c++)
00387        {
00388               memset(domain, '\0', sizeof domain);
00389               dlen = sizeof domain;
00390 
00391               status = dkimf_db_walk(db, c == 0, domain, &dlen, NULL, 0);
00392               if (status == -1)
00393               {
00394                      fprintf(stderr, "%s: dkimf_db_walk(%d) failed\n",
00395                              progname, c);
00396                      (void) dkimf_db_close(db);
00397                      return 1;
00398               }
00399               else if (status == 1)
00400               {
00401                      break;
00402               }
00403 
00404               /* convert to lowercase */
00405               dkimf_lowercase(domain);
00406 
00407               memset(base32, '\0', sizeof base32);
00408 
00409               if (hash == NULL || strcasecmp(hash, "none") != 0)
00410               {
00411                      /* compute SHA1 hash */
00412 #ifdef USE_GNUTLS
00413 # ifdef HAVE_SHA256
00414                      if (hash == NULL || strcasecmp(hash, "sha256") == 0)
00415                      {
00416                             (void) gnutls_hash_init(&sha,
00417                                                     GNUTLS_DIG_SHA256);
00418                      }
00419                      else
00420                      {
00421                             (void) gnutls_hash_init(&sha, GNUTLS_DIG_SHA1);
00422                      }
00423 # else /* HAVE_SHA256 */
00424                      (void) gnutls_hash_init(&sha, GNUTLS_DIG_SHA1);
00425 # endif /* HAVE_SHA256 */
00426                      (void) gnutls_hash(sha, domain, strlen(domain));
00427                      (void) gnutls_hash_deinit(sha, shaout);
00428 #else /* USE_GNUTLS */
00429 # ifdef HAVE_SHA256
00430                      if (hash == NULL || strcasecmp(hash, "sha256") == 0)
00431                      {
00432                             SHA256_Init(&sha256);
00433                             SHA256_Update(&sha256, domain, strlen(domain));
00434                             SHA256_Final(shaout, &sha256);
00435                      }
00436                      else
00437                      {
00438                             SHA1_Init(&sha);
00439                             SHA1_Update(&sha, domain, strlen(domain));
00440                             SHA1_Final(shaout, &sha);
00441                      }
00442 # else /* HAVE_SHA256 */
00443                      SHA1_Init(&sha);
00444                      SHA1_Update(&sha, domain, strlen(domain));
00445                      SHA1_Final(shaout, &sha);
00446 # endif /* HAVE_SHA256 */
00447 #endif /* USE_GNUTLS */
00448 
00449                      /* encode with base32 */
00450                      memset(base32, '\0', sizeof base32);
00451                      b32len = sizeof base32 - 1;
00452                      (void) dkim_base32_encode(base32, &b32len, shaout,
00453                                                shalen);
00454               }
00455 
00456               /* generate output */
00457               if (ttl == -1)
00458               {
00459                      fprintf(out, "%s%s\tIN\tTXT\t\"%s\"\n",
00460                              base32[0] == '\0' ? domain : base32,
00461                              suffix ? ATPSZONE : "",
00462                              VALIDATPS);
00463               }
00464               else
00465               {
00466                      fprintf(out, "%s%s\t%d\tIN\tTXT\t\"%s\"\n",
00467                              base32[0] == '\0' ? domain : base32,
00468                              suffix ? ATPSZONE : "", ttl,
00469                              VALIDATPS);
00470               }
00471        }
00472 
00473        (void) dkimf_db_close(db);
00474 
00475        if (out != stdout)
00476               fclose(out);
00477 
00478        if (verbose > 0)
00479        {
00480               fprintf(stdout, "%s: %d record%s written\n",
00481                       progname, c, c == 1 ? "" : "s");
00482        }
00483 
00484        return 0;
00485 }