Back to index

opendkim  2.6.2
dkim-atps.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2010-2012, The OpenDKIM Project.  All rights reserved.
00003 */
00004 
00005 #ifndef lint
00006 static char dkim_atps_c_id[] = "@(#)$Id$";
00007 #endif /* !lint */
00008 
00009 #include "build-config.h"
00010 
00011 /* system includes */
00012 #include <sys/types.h>
00013 #include <sys/param.h>
00014 #include <netinet/in.h>
00015 #include <arpa/nameser.h>
00016 #include <resolv.h>
00017 #include <assert.h>
00018 #include <stdio.h>
00019 #include <string.h>
00020 #include <ctype.h>
00021 #include <netdb.h>
00022 
00023 /* libopendkim includes */
00024 #include "dkim.h"
00025 #include "dkim-internal.h"
00026 #include "dkim-types.h"
00027 #include "dkim-tables.h"
00028 #include "util.h"
00029 
00030 #ifdef USE_GNUTLS
00031 /* GnuTLS includes */
00032 # include <gnutls/gnutls.h>
00033 # include <gnutls/crypto.h>
00034 # ifndef SHA_DIGEST_LENGTH
00035 #  define SHA_DIGEST_LENGTH 20
00036 # endif /* ! SHA_DIGEST_LENGTH */
00037 # ifndef SHA256_DIGEST_LENGTH
00038 #  define SHA256_DIGEST_LENGTH 32
00039 # endif /* ! SHA256_DIGEST_LENGTH */
00040 #else /* USE_GNUTLS */
00041 /* openssl includes */
00042 # include <openssl/sha.h>
00043 #endif /* USE_GNUTLS */
00044 
00045 /* prototypes */
00046 extern void dkim_error __P((DKIM *, const char *, ...));
00047 
00048 /* local definitions needed for DNS queries */
00049 #define MAXPACKET           8192
00050 #if defined(__RES) && (__RES >= 19940415)
00051 # define RES_UNC_T          char *
00052 #else /* __RES && __RES >= 19940415 */
00053 # define RES_UNC_T          unsigned char *
00054 #endif /* __RES && __RES >= 19940415 */
00055 #ifndef T_RRSIG
00056 # define T_RRSIG            46
00057 #endif /* ! T_RRSIG */
00058 #ifndef MAX
00059 # define MAX(x,y)           ((x) > (y) ? (x) : (y))
00060 #endif /* ! MAX */
00061 
00062 #define       DKIM_ATPS_QUERYLENGTH       64
00063 #define       DKIM_ATPS_VALID             "v=ATPS1"
00064 
00065 #ifdef SHA256_DIGEST_LENGTH
00066 # define MAXDIGEST          MAX(SHA_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)
00067 #else /* SHA256_DIGEST_LENGTH */
00068 # define MAXDIGEST          SHA_DIGEST_LENGTH
00069 #endif /* SHA256_DIGEST_LENGTH */
00070 
00071 /*
00072 **  DKIM_ATPS_CHECK -- check for Authorized Third Party Signing
00073 **
00074 **  Parameters:
00075 **     dkim -- DKIM message handle
00076 **     sig -- signature information handle
00077 **     timeout -- timeout (can be NULL)
00078 **     res -- ATPS result code
00079 **
00080 **  Return value:
00081 **     A DKIM_STAT_* constant.
00082 */
00083 
00084 DKIM_STAT
00085 dkim_atps_check(DKIM *dkim, DKIM_SIGINFO *sig, struct timeval *timeout,
00086                 dkim_atps_t *res)
00087 {
00088 #ifdef _FFR_ATPS
00089        int status;
00090        int qdcount;
00091        int ancount;
00092        int class;
00093        int type;
00094        int error;
00095        int n;
00096        int hash = DKIM_HASHTYPE_UNKNOWN;
00097        int diglen;
00098 #ifdef USE_GNUTLS
00099        int ghash;
00100 #endif /* USE_GNUTLS */
00101        unsigned int c;
00102 #ifdef QUERY_CACHE
00103        uint32_t ttl;
00104 #endif /* QUERY_CACHE */
00105        size_t buflen;
00106        size_t anslen;
00107        DKIM_LIB *lib;
00108        u_char *fdomain;
00109        u_char *sdomain;
00110        u_char *adomain;
00111        u_char *txtfound = NULL;
00112        u_char *ahash = NULL;
00113        void *qh;
00114        u_char *p;
00115        u_char *cp;
00116        u_char *eom;
00117 #ifdef USE_GNUTLS
00118        gnutls_hash_hd_t ctx;
00119 #else /* USE_GNUTLS */
00120         SHA_CTX ctx;
00121 # ifdef HAVE_SHA256
00122        SHA256_CTX ctx2;
00123 # endif /* HAVE_SHA256 */
00124 #endif /* USE_GNUTLS */
00125        struct timeval to;
00126        HEADER hdr;
00127        u_char ansbuf[MAXPACKET];
00128        u_char digest[MAXDIGEST];
00129        u_char b32[DKIM_ATPS_QUERYLENGTH + 1];
00130        u_char query[DKIM_MAXHOSTNAMELEN + 1];
00131        u_char buf[BUFRSZ + 1];
00132 #endif /* _FFR_ATPS */
00133 
00134        assert(dkim != NULL);
00135        assert(sig != NULL);
00136        assert(res != NULL);
00137 
00138 #ifdef _FFR_ATPS
00139        lib = dkim->dkim_libhandle;
00140        sdomain = dkim_sig_getdomain(sig);
00141        fdomain = dkim_getdomain(dkim);
00142        adomain = dkim_sig_gettagvalue(sig, FALSE, "atps");
00143        ahash = dkim_sig_gettagvalue(sig, FALSE, "atpsh");
00144 
00145        if (sdomain == NULL || fdomain == NULL || adomain == NULL ||
00146            ahash == NULL || strcasecmp(adomain, fdomain) != 0)
00147               return DKIM_STAT_INVALID;
00148 
00149        /* confirm it requested a hash we know how to do */
00150        if (strcasecmp(ahash, "none") != 0)
00151        {
00152               hash = dkim_name_to_code(hashes, ahash);
00153               if (hash == -1)
00154                      return DKIM_STAT_INVALID;
00155        }
00156 
00157        switch (hash)
00158        {
00159          case DKIM_HASHTYPE_SHA1:
00160               diglen = SHA_DIGEST_LENGTH;
00161               break;
00162 
00163 #  ifdef HAVE_SHA256
00164          case DKIM_HASHTYPE_SHA256:
00165               diglen = SHA256_DIGEST_LENGTH;
00166               break;
00167 #  endif /* HAVE_SHA256 */
00168 
00169          case DKIM_HASHTYPE_UNKNOWN:
00170               break;
00171 
00172          default:
00173               assert(0);
00174               break;
00175        }
00176 
00177        if (hash != DKIM_HASHTYPE_UNKNOWN)
00178        {
00179               /* construct a hash of the signing domain */
00180 # ifdef USE_GNUTLS
00181               switch (hash)
00182               {
00183                 case DKIM_HASHTYPE_SHA1:
00184                      ghash = GNUTLS_DIG_SHA1;
00185                      break;
00186 
00187                 case DKIM_HASHTYPE_SHA256:
00188                      ghash = GNUTLS_DIG_SHA256;
00189                      break;
00190 
00191                 default:
00192                      assert(0);
00193                      break;
00194               }
00195 
00196               if (gnutls_hash_init(&ctx, ghash) != 0 ||
00197                   gnutls_hash(ctx, sdomain, strlen(sdomain)) != 0)
00198                      return DKIM_STAT_INTERNAL;
00199               gnutls_hash_deinit(ctx, digest);
00200 # else /* USE_GNUTLS */
00201               switch (hash)
00202               {
00203                 case DKIM_HASHTYPE_SHA1:
00204                      SHA1_Init(&ctx);
00205                      SHA1_Update(&ctx, sdomain, strlen(sdomain));
00206                      SHA1_Final(digest, &ctx);
00207                      break;
00208 
00209 #  ifdef HAVE_SHA256
00210                 case DKIM_HASHTYPE_SHA256:
00211                      SHA256_Init(&ctx2);
00212                      SHA256_Update(&ctx2, sdomain, strlen(sdomain));
00213                      SHA256_Final(digest, &ctx2);
00214                      break;
00215 #  endif /* HAVE_SHA256 */
00216 
00217                 default:
00218                      assert(0);
00219                      break;
00220               }
00221 # endif /* USE_GNUTLS */
00222 
00223               /* base32-encode the hash */
00224               memset(b32, '\0', sizeof b32);
00225               buflen = sizeof b32;
00226               if (dkim_base32_encode(b32, &buflen,
00227                                      digest,
00228                                      diglen) >= DKIM_ATPS_QUERYLENGTH)
00229                      return DKIM_STAT_INTERNAL;
00230 
00231               /* form the query */
00232               snprintf(query, sizeof query, "%s._atps.%s", b32, fdomain);
00233        }
00234        else
00235        {
00236               /* form the query */
00237               snprintf(query, sizeof query, "%s._atps.%s", sdomain, fdomain);
00238        }
00239 
00240        /* XXX -- add QUERY_CACHE support here */
00241 
00242        /* send it */
00243        anslen = sizeof ansbuf;
00244        status = lib->dkiml_dns_start(lib->dkiml_dns_service, T_TXT,
00245                                      query, ansbuf, anslen, &qh);
00246        if (status != DKIM_DNS_SUCCESS)
00247        {
00248               *res = DKIM_ATPS_UNKNOWN;
00249               return DKIM_STAT_CANTVRFY;
00250        }
00251 
00252        /* wait for the reply */
00253        to.tv_sec = dkim->dkim_timeout;
00254        to.tv_usec = 0;
00255        status = lib->dkiml_dns_waitreply(lib->dkiml_dns_service, qh,
00256                                          timeout == NULL ? &to : timeout,
00257                                          &anslen, &error, NULL);
00258        (void) lib->dkiml_dns_cancel(lib->dkiml_dns_service, qh);
00259 
00260        if (status != DKIM_DNS_SUCCESS)
00261        {
00262               *res = DKIM_ATPS_UNKNOWN;
00263               return DKIM_STAT_CANTVRFY;
00264        }
00265 
00266        /* decode the reply */
00267        memcpy(&hdr, ansbuf, sizeof hdr);
00268        cp = (u_char *) &ansbuf + HFIXEDSZ;
00269        eom = (u_char *) &ansbuf + anslen;
00270 
00271        /* skip over the name at the front of the answer */
00272        for (qdcount = ntohs((unsigned short) hdr.qdcount);
00273             qdcount > 0;
00274             qdcount--)
00275        {
00276               /* copy it first */
00277               (void) dn_expand((unsigned char *) &ansbuf, eom, cp,
00278                                (char *) query, sizeof query);
00279  
00280               if ((n = dn_skipname(cp, eom)) < 0)
00281               {
00282                      dkim_error(dkim, "'%s' reply corrupt", query);
00283                      *res = DKIM_ATPS_UNKNOWN;
00284                      return DKIM_STAT_CANTVRFY;
00285               }
00286               cp += n;
00287 
00288               /* extract the type and class */
00289               if (cp + INT16SZ + INT16SZ > eom)
00290               {
00291                      dkim_error(dkim, "'%s' reply corrupt", query);
00292                      *res = DKIM_ATPS_UNKNOWN;
00293                      return DKIM_STAT_CANTVRFY;
00294               }
00295               GETSHORT(type, cp);
00296               GETSHORT(class, cp);
00297        }
00298 
00299        if (type != T_TXT || class != C_IN)
00300        {
00301               dkim_error(dkim, "'%s' unexpected reply type/class", query);
00302               *res = DKIM_ATPS_UNKNOWN;
00303               return DKIM_STAT_CANTVRFY;
00304        }
00305 
00306        if (hdr.rcode == NXDOMAIN)
00307        {
00308               *res = DKIM_ATPS_NOTFOUND;
00309               return DKIM_STAT_OK;
00310        }
00311 
00312        /* if truncated, we can't do it */
00313        if (dkim_check_dns_reply(ansbuf, anslen, C_IN, T_TXT) == 1)
00314        {
00315               dkim_error(dkim, "'%s' reply truncated", query);
00316               *res = DKIM_ATPS_UNKNOWN;
00317               return DKIM_STAT_CANTVRFY;
00318        }
00319 
00320        /* get the answer count */
00321        ancount = ntohs((unsigned short) hdr.ancount);
00322        if (ancount == 0)
00323        {
00324               *res = DKIM_ATPS_NOTFOUND;
00325               return DKIM_STAT_OK;
00326        }
00327 
00328        /*
00329        **  Extract the data from the first TXT answer.
00330        */
00331 
00332        while (--ancount >= 0 && cp < eom)
00333        {
00334               /* grab the label, even though we know what we asked... */
00335               if ((n = dn_expand((unsigned char *) &ansbuf, eom, cp,
00336                                  (RES_UNC_T) query, sizeof query)) < 0)
00337               {
00338                      dkim_error(dkim, "'%s' reply corrupt", query);
00339                      *res = DKIM_ATPS_UNKNOWN;
00340                      return DKIM_STAT_CANTVRFY;
00341               }
00342               /* ...and move past it */
00343               cp += n;
00344 
00345               /* extract the type and class */
00346               if (cp + INT16SZ + INT16SZ > eom)
00347               {
00348                      dkim_error(dkim, "'%s' reply corrupt", query);
00349                      *res = DKIM_ATPS_UNKNOWN;
00350                      return DKIM_STAT_CANTVRFY;
00351               }
00352 
00353               GETSHORT(type, cp);
00354               GETSHORT(class, cp);
00355 
00356 #ifdef QUERY_CACHE
00357               /* get the TTL */
00358               GETLONG(ttl, cp);
00359 #else /* QUERY_CACHE */
00360               /* skip the TTL */
00361               cp += INT32SZ;
00362 #endif /* QUERY_CACHE */
00363 
00364               /* skip CNAME if found; assume it was resolved */
00365               if (type == T_CNAME)
00366               {
00367                      char chost[DKIM_MAXHOSTNAMELEN + 1];
00368 
00369                      n = dn_expand((u_char *) &ansbuf, eom, cp,
00370                                    chost, DKIM_MAXHOSTNAMELEN);
00371                      cp += n;
00372                      continue;
00373               }
00374               else if (type == T_RRSIG)
00375               {
00376                      /* get payload length */
00377                      if (cp + INT16SZ > eom)
00378                      {
00379                             dkim_error(dkim, "'%s' reply corrupt", query);
00380                             *res = DKIM_ATPS_UNKNOWN;
00381                             return DKIM_STAT_CANTVRFY;
00382                      }
00383                      GETSHORT(n, cp);
00384 
00385                      cp += n;
00386 
00387                      continue;
00388               }
00389               else if (type != T_TXT)
00390               {
00391                      dkim_error(dkim, "'%s' reply was unexpected type %d",
00392                                 query, type);
00393                      *res = DKIM_ATPS_UNKNOWN;
00394                      return DKIM_STAT_CANTVRFY;
00395               }
00396 
00397               if (txtfound != NULL)
00398               {
00399                      dkim_error(dkim, "multiple DNS replies for '%s'",
00400                                 query);
00401                      *res = DKIM_ATPS_UNKNOWN;
00402                      return DKIM_STAT_MULTIDNSREPLY;
00403               }
00404 
00405               /* remember where this one started */
00406               txtfound = cp;
00407 
00408               /* get payload length */
00409               if (cp + INT16SZ > eom)
00410               {
00411                      dkim_error(dkim, "'%s' reply corrupt", query);
00412                      *res = DKIM_ATPS_UNKNOWN;
00413                      return DKIM_STAT_CANTVRFY;
00414               }
00415               GETSHORT(n, cp);
00416 
00417               /* move forward for now */
00418               cp += n;
00419        }
00420 
00421        /* if ancount went below 0, there were no good records */
00422        if (txtfound == NULL)
00423        {
00424               dkim_error(dkim, "'%s' reply was unresolved CNAME", query);
00425               *res = DKIM_ATPS_UNKNOWN;
00426               return DKIM_STAT_CANTVRFY;
00427        }
00428 
00429        /* come back to the one we found */
00430        cp = txtfound;
00431 
00432        /* get payload length */
00433        if (cp + INT16SZ > eom)
00434        {
00435               dkim_error(dkim, "'%s' reply corrupt", query);
00436               *res = DKIM_ATPS_UNKNOWN;
00437               return DKIM_STAT_CANTVRFY;
00438        }
00439        GETSHORT(n, cp);
00440 
00441        if (cp + n > eom)
00442        {
00443               dkim_error(dkim, "'%s' reply corrupt", query);
00444               *res = DKIM_ATPS_UNKNOWN;
00445               return DKIM_STAT_CANTVRFY;
00446        }
00447 
00448        /* extract the payload */
00449        memset(buf, '\0', buflen);
00450        p = buf;
00451        eom = buf + buflen - 1;
00452        while (n > 0 && p < eom)
00453        {
00454               c = *cp++;
00455               n--;
00456               while (c > 0 && p < eom)
00457               {
00458                      *p++ = *cp++;
00459                      c--;
00460                      n--;
00461               }
00462        }
00463 
00464        if (strcmp(buf, DKIM_ATPS_VALID) == 0)
00465               *res = DKIM_ATPS_FOUND;
00466        else
00467               *res = DKIM_ATPS_NOTFOUND;
00468 
00469        return DKIM_STAT_OK;
00470 
00471 #else /* ! _FFR_ATPS */
00472 
00473        return DKIM_STAT_NOTIMPLEMENT;
00474 
00475 #endif /* ! _FFR_ATPS */
00476 }