Back to index

opendkim  2.6.4
dkim-test.c
Go to the documentation of this file.
00001 /*
00002 **  Copyright (c) 2005-2009 Sendmail, Inc. and its suppliers.
00003 **    All rights reserved.
00004 **
00005 **  Copyright (c) 2009, 2010, 2012, The OpenDKIM Project.  All rights reserved.
00006 */
00007 
00008 #ifndef lint
00009 static char dkim_test_c_id[] = "@(#)$Id: dkim-test.c,v 1.16.10.1 2010/10/27 21:43:08 cm-msk Exp $";
00010 #endif /* !lint */
00011 
00012 #include "build-config.h"
00013 
00014 /* system includes */
00015 #include <sys/param.h>
00016 #include <sys/types.h>
00017 #include <arpa/nameser.h>
00018 #include <netinet/in.h>
00019 #include <assert.h>
00020 #include <stdio.h>
00021 #include <ctype.h>
00022 #include <string.h>
00023 #include <resolv.h>
00024 #include <errno.h>
00025 
00026 #ifdef USE_GNUTLS
00027 # include <gnutls/gnutls.h>
00028 #else /* USE_GNUTLS */
00029 /* openssl includes */
00030 # include <openssl/bio.h>
00031 # include <openssl/rsa.h>
00032 # include <openssl/evp.h>
00033 #endif /* USE_GNUTLS */
00034 
00035 /* libopendkim includes */
00036 #include "dkim-internal.h"
00037 #include "dkim-types.h"
00038 #include "dkim-keys.h"
00039 #include "dkim-util.h"
00040 #include "dkim-test.h"
00041 #include "dkim-strl.h"
00042 
00043 /* definitions */
00044 #define       TESTTTL                     300
00045 #define MAXPACKET           8192
00046 
00047 /* prototypes from elsewhere */
00048 extern DKIM_STAT dkim_get_key __P((DKIM *, DKIM_SIGINFO *, _Bool));
00049 
00050 /*
00051 **  DKIM_TEST_DNS_PUT -- enqueue a DNS reply for automated testing
00052 **
00053 **  Parameters:
00054 **     dkim -- DKIM handle
00055 **     class -- class of the reply
00056 **     type -- type of the reply
00057 **     prec -- precedence (for MX replies)
00058 **     name -- name of the reply (i.e. the "question")
00059 **     data -- content of the reply
00060 **
00061 **  Return value:
00062 **     0 -- success
00063 **     -1 -- failure (e.g. malloc() failed)
00064 */
00065 
00066 int
00067 dkim_test_dns_put(DKIM *dkim, int class, int type, int prec, u_char *name,
00068                   u_char *data)
00069 {
00070        struct dkim_test_dns_data *td;
00071 
00072        assert(dkim != NULL);
00073        assert(name != NULL);
00074 
00075        td = (struct dkim_test_dns_data *) DKIM_MALLOC(dkim, sizeof *td);
00076        if (td == NULL)
00077               return -1;
00078 
00079        td->dns_class = class;
00080        td->dns_type = type;
00081        td->dns_prec = prec;
00082 
00083        td->dns_query = dkim_strdup(dkim, name, 0);
00084        if (td->dns_query == NULL)
00085        {
00086               DKIM_FREE(dkim, td);
00087               return -1;
00088        }
00089 
00090        if (data != NULL)
00091        {
00092               td->dns_reply = dkim_strdup(dkim, data, 0);
00093               if (td->dns_reply == NULL)
00094               {
00095                      DKIM_FREE(dkim, td->dns_query);
00096                      DKIM_FREE(dkim, td);
00097                      return -1;
00098               }
00099        }
00100 
00101        td->dns_next = NULL;
00102 
00103        if (dkim->dkim_dnstesth == NULL)
00104               dkim->dkim_dnstesth = td;
00105        else
00106               dkim->dkim_dnstestt->dns_next = td;
00107 
00108        dkim->dkim_dnstestt = td;
00109 
00110        return 0;
00111 }
00112 
00113 /*
00114 **  DKIM_TEST_DNS_GET -- dequeue a DNS reply for automated testing
00115 **
00116 **  Parameters:
00117 **     dkim -- DKIM handle
00118 **     buf -- target buffer
00119 **     buflen -- bytes available at "buf"
00120 **
00121 **  Return value:
00122 **     -1 -- nothing queued
00123 **     other -- size of the reply
00124 */
00125 
00126 size_t
00127 dkim_test_dns_get(DKIM *dkim, u_char *buf, size_t buflen)
00128 {
00129        int n;
00130        int len;
00131        uint32_t testttl;
00132        struct dkim_test_dns_data *td;
00133        u_char *cp;
00134        u_char *p;
00135        u_char *end;
00136        u_char answer[MAXPACKET];
00137        HEADER hdr;
00138 
00139        td = dkim->dkim_dnstesth;
00140        if (td == NULL)
00141               return -1;
00142 
00143        dkim->dkim_dnstesth = td->dns_next;
00144 
00145        /* encode the reply */
00146        memset(&hdr, '\0', sizeof hdr);
00147        hdr.qdcount = htons(1);
00148        hdr.rcode = (td->dns_reply == NULL ? NXDOMAIN : NOERROR);
00149        hdr.ancount = (td->dns_reply == NULL ? htons(0) : htons(1));
00150 
00151        memcpy(answer, &hdr, sizeof hdr);
00152 
00153        cp = answer + HFIXEDSZ;
00154        end = answer + sizeof answer;
00155 
00156        /* repeat the question */
00157        n = dn_comp((char *) td->dns_query, cp, end - cp, NULL, NULL);
00158        if (n < 0)
00159        {
00160               DKIM_FREE(dkim, td);
00161               return -1;
00162        }
00163        cp += n;
00164        if (end - cp < 2 * sizeof(uint16_t))
00165        {
00166               DKIM_FREE(dkim, td);
00167               return -1;
00168        }
00169 
00170        PUTSHORT(td->dns_type, cp);
00171        PUTSHORT(td->dns_class, cp);
00172 
00173        /* short-circuit? */
00174        if (hdr.rcode == NXDOMAIN)
00175        {
00176               DKIM_FREE(dkim, td);
00177 
00178               memcpy(buf, answer, buflen);
00179 
00180               return cp - answer;
00181        }
00182 
00183        /* the answer starts out the same way */
00184        n = dn_comp((char *) td->dns_query, cp, end - cp, NULL, NULL);
00185        if (n < 0)
00186        {
00187               DKIM_FREE(dkim, td);
00188               return -1;
00189        }
00190        cp += n;
00191 
00192        if (end - cp < 2 * sizeof(uint16_t) + sizeof(uint32_t))
00193        {
00194               DKIM_FREE(dkim, td);
00195               return -1;
00196        }
00197 
00198        PUTSHORT(td->dns_type, cp);
00199        PUTSHORT(td->dns_class, cp);
00200 
00201        testttl = htonl(TESTTTL);
00202        PUTLONG(testttl, cp);
00203 
00204        switch (td->dns_type)
00205        {
00206          case T_TXT:
00207               /* figure out how many bytes we need total */
00208               n = strlen((char *) td->dns_reply);
00209               len = n + n / 255 + 1;
00210               if (end - cp < len + sizeof(uint16_t))
00211               {
00212                      DKIM_FREE(dkim, td);
00213                      return -1;
00214               }
00215               PUTSHORT(len, cp);
00216 
00217               /* write the buffer, inserting length bytes as needed */
00218               len = 0;
00219               p = td->dns_reply;
00220               while (n > 0)
00221               {
00222                      if (len == 0)
00223                      {
00224                             len = MIN(255, n);
00225                             *cp++ = len;
00226                      }
00227                      *cp++ = *p++;
00228                      n--;
00229                      len--;
00230               }
00231               break;
00232 
00233          case T_MX:
00234               if (end - cp < sizeof(uint16_t))
00235               {
00236                      DKIM_FREE(dkim, td);
00237                      return -1;
00238               }
00239               PUTSHORT(td->dns_prec, cp);
00240               n = dn_comp((char *) td->dns_reply, cp, end - cp, NULL, NULL);
00241               if (n < 0)
00242               {
00243                      DKIM_FREE(dkim, td);
00244                      return -1;
00245               }
00246 
00247               cp += n;
00248               break;
00249 
00250          default:
00251               DKIM_FREE(dkim, td);
00252               return -1;
00253        }
00254 
00255        DKIM_FREE(dkim, td);
00256 
00257        memcpy(buf, answer, buflen);
00258 
00259        return cp - answer;
00260 }
00261 
00262 /*
00263 **  DKIM_TEST_KEY -- retrieve a public key and verify it against a provided
00264 **                   private key
00265 **
00266 **  Parameters:
00267 **     lib -- DKIM library handle
00268 **     selector -- selector
00269 **     domain -- domain name
00270 **     key -- private key to verify (PEM format)
00271 **     keylen -- size of private key
00272 **     dnssec -- DNSSEC result (may be NULL)
00273 **     err -- error buffer (may be NULL)
00274 **     errlen -- size of error buffer
00275 **
00276 **  Return value:
00277 **     1 -- keys don't match
00278 **     0 -- keys match (or no key provided)
00279 **     -1 -- error
00280 */
00281 
00282 int
00283 dkim_test_key(DKIM_LIB *lib, char *selector, char *domain,
00284               char *key, size_t keylen, int *dnssec, char *err, size_t errlen)
00285 {
00286        int status = 0;
00287        DKIM_STAT stat;
00288        DKIM *dkim;
00289        DKIM_SIGINFO *sig;
00290 #ifdef USE_GNUTLS
00291        gnutls_datum_t keybuf;
00292        gnutls_datum_t outkey;
00293 #else /* USE_GNUTLS */
00294        BIO *keybuf;
00295        BIO *outkey;
00296 #endif /* USE_GNUTLS */
00297        void *ptr;
00298        struct dkim_rsa *rsa;
00299        char buf[BUFRSZ];
00300 
00301        assert(lib != NULL);
00302        assert(selector != NULL);
00303        assert(domain != NULL);
00304 
00305        dkim = dkim_verify(lib, (u_char *) "test", NULL, &stat);
00306        if (dkim == NULL)
00307        {
00308               if (err != NULL)
00309                      strlcpy(err, dkim_getresultstr(stat), errlen);
00310               return -1;
00311        }
00312 
00313        snprintf(buf, sizeof buf, "v=1; d=%s; s=%s; h=x; b=x; a=x",
00314                 domain, selector);
00315 
00316        stat = dkim_process_set(dkim, DKIM_SETTYPE_SIGNATURE, (u_char *) buf,
00317                                strlen(buf), NULL, FALSE, NULL);
00318        if (stat != DKIM_STAT_OK)
00319        {
00320               strlcpy(err, "syntax error on input", errlen);
00321               (void) dkim_free(dkim);
00322               return -1;
00323        }
00324 
00325        dkim->dkim_sigcount = 1;
00326 
00327        stat = dkim_siglist_setup(dkim);
00328        if (stat != DKIM_STAT_OK)
00329        {
00330               (void) dkim_free(dkim);
00331               return -1;
00332        }
00333 
00334        sig = dkim->dkim_siglist[0];
00335 
00336        dkim->dkim_user = dkim_strdup(dkim, (u_char *) "nobody", 0);
00337        if (dkim->dkim_user == NULL)
00338        {
00339               (void) dkim_free(dkim);
00340               return -1;
00341        }
00342 
00343        stat = dkim_get_key(dkim, sig, TRUE);
00344        if (stat != DKIM_STAT_OK)
00345        {
00346               if (err != NULL)
00347               {
00348                      const char *errstr;
00349 
00350                      errstr = dkim_geterror(dkim);
00351                      if (errstr != NULL)
00352                      {
00353                             strlcpy(err, errstr, errlen);
00354                      }
00355                      else
00356                      {
00357                             strlcpy(err, dkim_getresultstr(stat),
00358                                     errlen);
00359                      }
00360               }
00361 
00362               (void) dkim_free(dkim);
00363               return -1;
00364        }
00365 
00366        if (dnssec != NULL)
00367               *dnssec = dkim_sig_getdnssec(sig);
00368 
00369        if (key != NULL)
00370        {
00371               rsa = DKIM_MALLOC(dkim, sizeof(struct dkim_rsa));
00372               if (rsa == NULL)
00373               {
00374 #ifndef USE_GNUTLS
00375                      BIO_free(keybuf);
00376 #endif /* ! USE_GNUTLS */
00377                      (void) dkim_free(dkim);
00378                      if (err != NULL)
00379                      {
00380                             snprintf(err, errlen,
00381                                      "unable to allocate %zu byte(s)",
00382                                      sizeof(struct dkim_rsa));
00383                      }
00384                      return -1;
00385               }
00386               memset(rsa, '\0', sizeof(struct dkim_rsa));
00387 
00388 #ifdef USE_GNUTLS
00389               keybuf.data = key;
00390               keybuf.size = keylen;
00391 #else /* USE_GNUTLS */
00392               keybuf = BIO_new_mem_buf(key, keylen);
00393               if (keybuf == NULL)
00394               {
00395                      if (err != NULL)
00396                      {
00397                             strlcpy(err, "BIO_new_mem_buf() failed",
00398                                     errlen);
00399                      }
00400 
00401                      (void) dkim_free(dkim);
00402                      return -1;
00403               }
00404 #endif /* USE_GNUTLS */
00405 
00406               sig->sig_signature = (void *) rsa;
00407               sig->sig_keytype = DKIM_KEYTYPE_RSA;
00408 
00409 #ifdef USE_GNUTLS
00410               if (err != NULL)
00411                      strlcpy(err, "function not implemented", errlen);
00412 
00413               (void) dkim_free(dkim);
00414               return -1;
00415 #else /* USE_GNUTLS */
00416               rsa->rsa_pkey = PEM_read_bio_PrivateKey(keybuf, NULL,
00417                                                       NULL, NULL);
00418               if (rsa->rsa_pkey == NULL)
00419               {
00420                      BIO_free(keybuf);
00421                      (void) dkim_free(dkim);
00422                      if (err != NULL)
00423                      {
00424                             strlcpy(err,
00425                                     "PEM_read_bio_PrivateKey() failed",
00426                                     errlen);
00427                      }
00428                      return -1;
00429               }
00430 
00431               rsa->rsa_rsa = EVP_PKEY_get1_RSA(rsa->rsa_pkey);
00432               if (rsa->rsa_rsa == NULL)
00433               {
00434                      BIO_free(keybuf);
00435                      (void) dkim_free(dkim);
00436                      if (err != NULL)
00437                      {
00438                             strlcpy(err, "EVP_PKEY_get1_RSA() failed",
00439                                     errlen);
00440                      }
00441                      return -1;
00442               }
00443        
00444               rsa->rsa_keysize = RSA_size(rsa->rsa_rsa);
00445               rsa->rsa_pad = RSA_PKCS1_PADDING;
00446 
00447               outkey = BIO_new(BIO_s_mem());
00448               if (outkey == NULL)
00449               {
00450                      BIO_free(keybuf);
00451                      (void) dkim_free(dkim);
00452                      if (err != NULL)
00453                             strlcpy(err, "BIO_new() failed", errlen);
00454                      return -1;
00455               }
00456 
00457               status = i2d_RSA_PUBKEY_bio(outkey, rsa->rsa_rsa);
00458               if (status == 0)
00459               {
00460                      BIO_free(keybuf);
00461                      BIO_free(outkey);
00462                      (void) dkim_free(dkim);
00463                      if (err != NULL)
00464                      {
00465                             strlcpy(err, "i2d_RSA_PUBKEY_bio() failed",
00466                                        errlen);
00467                      }
00468                      return -1;
00469               }
00470 
00471               (void) BIO_get_mem_data(outkey, &ptr);
00472 
00473               if (BIO_number_written(outkey) == sig->sig_keylen)
00474                      status = memcmp(ptr, sig->sig_key, sig->sig_keylen);
00475               else
00476                      status = 1;
00477 
00478               if (status != 0)
00479                      strlcpy(err, "keys do not match", errlen);
00480 
00481               BIO_free(keybuf);
00482               BIO_free(outkey);
00483 #endif /* USE_GNUTLS */
00484        }
00485 
00486        (void) dkim_free(dkim);
00487 
00488        return (status == 0 ? 0 : 1);
00489 }
00490 
00491 /*
00492 **  DKIM_TEST_ADSP -- verify that a valid author domain signing policy exists
00493 **                    in DNS
00494 **
00495 **  Parameters:
00496 **     lib -- DKIM library handle
00497 **     domain -- domain name
00498 **     presult -- policy discovered (returned)
00499 **     presult2 -- policy stage at which result was decided (returned)
00500 **     err -- error buffer
00501 **     errlen -- bytes available at "err"
00502 **
00503 **  Return value:
00504 **     0 -- some kind of result was made available
00505 **     -1 -- error
00506 */
00507 
00508 int
00509 dkim_test_adsp(DKIM_LIB *lib, const char *domain, dkim_policy_t *presult,
00510                int *presult2, char *err, size_t errlen)
00511 {
00512        DKIM_STAT stat;
00513        dkim_policy_t pcode = DKIM_POLICY_DEFAULT;
00514        DKIM *dkim;
00515 
00516        assert(lib != NULL);
00517        assert(presult != NULL);
00518        assert(presult2 != NULL);
00519 
00520        dkim = dkim_verify(lib, (u_char *) "test", NULL, &stat);
00521        if (dkim == NULL)
00522        {
00523               if (err != NULL)
00524                      strlcpy(err, dkim_getresultstr(stat), errlen);
00525               return -1;
00526        }
00527 
00528        dkim->dkim_mode = DKIM_MODE_VERIFY;
00529        dkim->dkim_domain = (u_char *) domain;
00530        dkim->dkim_sigcount = 0;
00531 
00532        stat = dkim_policy(dkim, &pcode, NULL, NULL);
00533        if (stat != DKIM_STAT_OK)
00534        {
00535               if (err != NULL)
00536               {
00537                      const char *errstr;
00538 
00539                      errstr = dkim_geterror(dkim);
00540                      if (errstr != NULL)
00541                      {
00542                             strlcpy(err, errstr, errlen);
00543                      }
00544                      else
00545                      {
00546                             strlcpy(err, dkim_getresultstr(stat),
00547                                     errlen);
00548                      }
00549               }
00550 
00551               dkim->dkim_domain = NULL;
00552               (void) dkim_free(dkim);
00553 
00554               return -1;
00555        }
00556 
00557        *presult = pcode;
00558        *presult2 = dkim_getpresult(dkim);
00559 
00560        dkim->dkim_domain = NULL;
00561        (void) dkim_free(dkim);
00562 
00563        return 0;
00564 }