/*
** Copyright (c) 2005-2007 Sendmail, Inc. and its suppliers.
** All rights reserved.
*/
#ifndef lint
static char dkim_test_c_id[] = "@(#)$Id: dkim-test.c,v 1.8 2007/12/03 07:43:25 msk Exp $";
#endif /* !lint */
/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
/* libsm includes */
#include <sm/string.h>
/* openssl includes */
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
/* libdkim includes */
#include "dkim-types.h"
#include "dkim-keys.h"
#include "dkim-util.h"
#include "dkim-test.h"
#include "dkim.h"
/* prototypes from elsewhere */
extern DKIM_STAT dkim_get_key __P((DKIM *, DKIM_SIGINFO *));
/*
** DKIM_TEST_KEY -- retrieve a public key and verify it against a provided
** private key
**
** Parameters:
** lib -- DKIM library handle
** selector -- selector
** domain -- domain name
** key -- private key to verify (PEM format)
** keylen -- size of private key
** err -- error buffer (may be NULL)
** errlen -- size of error buffer
**
** Return value:
** 1 -- keys don't match
** 0 -- keys match (or no key provided)
** -1 -- error
*/
int
dkim_test_key(DKIM_LIB *lib, char *selector, char *domain,
char *key, size_t keylen, char *err, size_t errlen)
{
int status = 0;
DKIM_STAT stat;
DKIM *dkim;
DKIM_SIGINFO *sig;
BIO *keybuf;
BIO *outkey;
void *ptr;
struct dkim_rsa *rsa;
assert(lib != NULL);
assert(selector != NULL);
assert(domain != NULL);
dkim = dkim_verify(lib, "test", NULL, &stat);
if (dkim == NULL)
{
if (err != NULL)
sm_strlcpy(err, dkim_getresultstr(stat), errlen);
return -1;
}
dkim->dkim_siglist = DKIM_MALLOC(dkim, sizeof(sig) * 2);
if (dkim->dkim_siglist == NULL)
{
(void) dkim_free(dkim);
if (err != NULL)
{
snprintf(err, errlen, "unable to allocate %d byte(s)",
sizeof(sig) * 2);
}
return -1;
}
dkim->dkim_siglist[1] = NULL;
dkim->dkim_siglist[0] = DKIM_MALLOC(dkim, sizeof *sig);
if (dkim->dkim_siglist[0] == NULL)
{
(void) dkim_free(dkim);
if (err != NULL)
{
snprintf(err, errlen, "unable to allocate %d byte(s)",
sizeof *sig);
}
return -1;
}
sig = dkim->dkim_siglist[0];
memset(sig, '\0', sizeof *sig);
sig->sig_domain = domain;
sig->sig_selector = selector;
dkim->dkim_user = dkim_strdup(dkim, "nobody", 0);
if (dkim->dkim_user == NULL)
{
(void) dkim_free(dkim);
return -1;
}
stat = dkim_get_key(dkim, sig);
if (stat != DKIM_STAT_OK)
{
if (err != NULL)
{
const char *errstr;
errstr = dkim_geterror(dkim);
if (errstr != NULL)
{
sm_strlcpy(err, errstr, errlen);
}
else
{
sm_strlcpy(err, dkim_getresultstr(stat),
errlen);
}
}
(void) dkim_free(dkim);
return -1;
}
if (key != NULL)
{
keybuf = BIO_new_mem_buf(key, keylen);
if (keybuf == NULL)
{
if (err != NULL)
{
sm_strlcpy(err, "BIO_new_mem_buf() failed",
errlen);
}
(void) dkim_free(dkim);
return -1;
}
rsa = DKIM_MALLOC(dkim, sizeof(struct dkim_rsa));
if (rsa == NULL)
{
BIO_free(keybuf);
(void) dkim_free(dkim);
if (err != NULL)
{
snprintf(err, errlen,
"unable to allocate %d byte(s)",
sizeof(struct dkim_rsa));
}
return -1;
}
memset(rsa, '\0', sizeof(struct dkim_rsa));
sig->sig_signature = (void *) rsa;
sig->sig_keytype = DKIM_KEYTYPE_RSA;
rsa->rsa_pkey = PEM_read_bio_PrivateKey(keybuf, NULL,
NULL, NULL);
if (rsa->rsa_pkey == NULL)
{
BIO_free(keybuf);
(void) dkim_free(dkim);
if (err != NULL)
{
sm_strlcpy(err,
"PEM_read_bio_PrivateKey() failed",
errlen);
}
return -1;
}
rsa->rsa_rsa = EVP_PKEY_get1_RSA(rsa->rsa_pkey);
if (rsa->rsa_rsa == NULL)
{
BIO_free(keybuf);
(void) dkim_free(dkim);
if (err != NULL)
{
sm_strlcpy(err, "EVP_PKEY_get1_RSA() failed",
errlen);
}
return -1;
}
rsa->rsa_keysize = RSA_size(rsa->rsa_rsa);
rsa->rsa_pad = RSA_PKCS1_PADDING;
outkey = BIO_new(BIO_s_mem());
if (outkey == NULL)
{
BIO_free(keybuf);
(void) dkim_free(dkim);
if (err != NULL)
sm_strlcpy(err, "BIO_new() failed", errlen);
return -1;
}
status = i2d_RSA_PUBKEY_bio(outkey, rsa->rsa_rsa);
if (status == 0)
{
BIO_free(keybuf);
BIO_free(outkey);
(void) dkim_free(dkim);
if (err != NULL)
{
sm_strlcpy(err, "i2d_RSA_PUBKEY_bio() failed",
errlen);
}
return -1;
}
(void) BIO_get_mem_data(outkey, &ptr);
if (BIO_number_written(outkey) == sig->sig_keylen)
status = memcmp(ptr, sig->sig_key, sig->sig_keylen);
else
status = 1;
if (status != 0)
sm_strlcpy(err, "keys do not match", errlen);
BIO_free(keybuf);
BIO_free(outkey);
}
(void) dkim_free(dkim);
return (status == 0 ? 0 : 1);
}
/*
** DKIM_TEST_SSP -- verify that a valid sender signing policy exists in dns
**
** Parameters:
** lib -- DKIM library handle
** domain -- domain name
**
** Return value:
** 0 -- some kind of result was made available
** -1 -- error
*/
int
dkim_test_ssp(DKIM_LIB *lib, char *domain, dkim_policy_t *presult,
dkim_handling_t *hresult, int *presult2,
char *err, size_t errlen)
{
DKIM_STAT stat;
bool test = FALSE;
bool susp = FALSE;
dkim_policy_t pcode = DKIM_POLICY_DEFAULT;
dkim_handling_t hcode = DKIM_HANDLING_DEFAULT;
DKIM *dkim;
assert(lib != NULL);
assert(presult != NULL);
assert(hresult != NULL);
assert(presult2 != NULL);
dkim = dkim_verify(lib, "test", NULL, &stat);
if (dkim == NULL)
{
if (err != NULL)
sm_strlcpy(err, dkim_getresultstr(stat), errlen);
return -1;
}
dkim->dkim_mode = DKIM_MODE_VERIFY;
dkim->dkim_domain = domain;
dkim->dkim_sigcount = 0;
stat = dkim_policy(dkim, &test, &susp, &pcode, &hcode, NULL);
if (stat != DKIM_STAT_OK)
{
if (err != NULL)
{
const char *errstr;
errstr = dkim_geterror(dkim);
if (errstr != NULL)
{
sm_strlcpy(err, errstr, errlen);
}
else
{
sm_strlcpy(err, dkim_getresultstr(stat),
errlen);
}
}
dkim->dkim_domain = NULL;
(void) dkim_free(dkim);
return -1;
}
*presult = pcode;
*hresult = hcode;
*presult2 = dkim_getpresult(dkim);
dkim->dkim_domain = NULL;
(void) dkim_free(dkim);
return (stat == DKIM_STAT_OK ? 0 : -1);
}
syntax highlighted by Code2HTML, v. 0.9.1