diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c new file mode 100644 index 0000000000..d6d13878c9 --- /dev/null +++ b/lib/dns/dst_api.c @@ -0,0 +1,1178 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2003 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: dst_api.c,v 1.1 2004/12/09 01:41:00 marka Exp $ + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "dst_internal.h" + +#define DST_AS_STR(t) ((t).value.as_textregion.base) + +static dst_func_t *dst_t_func[DST_MAX_ALGS]; +static isc_entropy_t *dst_entropy_pool = NULL; +static unsigned int dst_entropy_flags = 0; +static isc_boolean_t dst_initialized = ISC_FALSE; + +isc_mem_t *dst__memory_pool = NULL; + +/* + * Static functions. + */ +static dst_key_t * get_key_struct(dns_name_t *name, + unsigned int alg, + unsigned int flags, + unsigned int protocol, + unsigned int bits, + dns_rdataclass_t rdclass, + isc_mem_t *mctx); +static isc_result_t write_public_key(const dst_key_t *key, int type, + const char *directory); +static isc_result_t buildfilename(dns_name_t *name, + dns_keytag_t id, + unsigned int alg, + unsigned int type, + const char *directory, + isc_buffer_t *out); +static isc_result_t computeid(dst_key_t *key); +static isc_result_t frombuffer(dns_name_t *name, + unsigned int alg, + unsigned int flags, + unsigned int protocol, + dns_rdataclass_t rdclass, + isc_buffer_t *source, + isc_mem_t *mctx, + dst_key_t **keyp); + +static isc_result_t algorithm_status(unsigned int alg); + +static isc_result_t addsuffix(char *filename, unsigned int len, + const char *ofilename, const char *suffix); + +#define RETERR(x) \ + do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto out; \ + } while (0) + +#define CHECKALG(alg) \ + do { \ + isc_result_t _r; \ + _r = algorithm_status(alg); \ + if (_r != ISC_R_SUCCESS) \ + return (_r); \ + } while (0); \ + +isc_result_t +dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags) { + isc_result_t result; + + REQUIRE(mctx != NULL && ectx != NULL); + REQUIRE(dst_initialized == ISC_FALSE); + + dst__memory_pool = NULL; + +#ifdef OPENSSL + UNUSED(mctx); + /* + * When using --with-openssl, there seems to be no good way of not + * leaking memory due to the openssl error handling mechanism. + * Avoid assertions by using a local memory context and not checking + * for leaks on exit. + */ + result = isc_mem_create(0, 0, &dst__memory_pool); + if (result != ISC_R_SUCCESS) + return (result); + isc_mem_setdestroycheck(dst__memory_pool, ISC_FALSE); +#else + isc_mem_attach(mctx, &dst__memory_pool); +#endif + isc_entropy_attach(ectx, &dst_entropy_pool); + dst_entropy_flags = eflags; + + dst_result_register(); + + memset(dst_t_func, 0, sizeof(dst_t_func)); + RETERR(dst__hmacmd5_init(&dst_t_func[DST_ALG_HMACMD5])); +#ifdef OPENSSL + RETERR(dst__openssl_init()); + RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSAMD5])); + RETERR(dst__opensslrsa_init(&dst_t_func[DST_ALG_RSASHA1])); +#ifdef HAVE_OPENSSL_DSA + RETERR(dst__openssldsa_init(&dst_t_func[DST_ALG_DSA])); +#endif + RETERR(dst__openssldh_init(&dst_t_func[DST_ALG_DH])); +#endif /* OPENSSL */ +#ifdef GSSAPI + RETERR(dst__gssapi_init(&dst_t_func[DST_ALG_GSSAPI])); +#endif + dst_initialized = ISC_TRUE; + return (ISC_R_SUCCESS); + + out: + dst_lib_destroy(); + return (result); +} + +void +dst_lib_destroy(void) { + int i; + RUNTIME_CHECK(dst_initialized == ISC_TRUE); + dst_initialized = ISC_FALSE; + + for (i = 0; i < DST_MAX_ALGS; i++) + if (dst_t_func[i] != NULL && dst_t_func[i]->cleanup != NULL) + dst_t_func[i]->cleanup(); +#ifdef OPENSSL + dst__openssl_destroy(); +#endif + if (dst__memory_pool != NULL) + isc_mem_detach(&dst__memory_pool); + if (dst_entropy_pool != NULL) + isc_entropy_detach(&dst_entropy_pool); + +} + +isc_boolean_t +dst_algorithm_supported(unsigned int alg) { + REQUIRE(dst_initialized == ISC_TRUE); + + if (alg >= DST_MAX_ALGS || dst_t_func[alg] == NULL) + return (ISC_FALSE); + return (ISC_TRUE); +} + +isc_result_t +dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp) { + dst_context_t *dctx; + isc_result_t result; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(mctx != NULL); + REQUIRE(dctxp != NULL && *dctxp == NULL); + + if (key->func->createctx == NULL) + return (DST_R_UNSUPPORTEDALG); + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + dctx = isc_mem_get(mctx, sizeof(dst_context_t)); + if (dctx == NULL) + return (ISC_R_NOMEMORY); + dctx->key = key; + dctx->mctx = mctx; + result = key->func->createctx(key, dctx); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, dctx, sizeof(dst_context_t)); + return (result); + } + dctx->magic = CTX_MAGIC; + *dctxp = dctx; + return (ISC_R_SUCCESS); +} + +void +dst_context_destroy(dst_context_t **dctxp) { + dst_context_t *dctx; + + REQUIRE(dctxp != NULL && VALID_CTX(*dctxp)); + + dctx = *dctxp; + INSIST(dctx->key->func->destroyctx != NULL); + dctx->key->func->destroyctx(dctx); + dctx->magic = 0; + isc_mem_put(dctx->mctx, dctx, sizeof(dst_context_t)); + *dctxp = NULL; +} + +isc_result_t +dst_context_adddata(dst_context_t *dctx, const isc_region_t *data) { + REQUIRE(VALID_CTX(dctx)); + REQUIRE(data != NULL); + INSIST(dctx->key->func->adddata != NULL); + + return (dctx->key->func->adddata(dctx, data)); +} + +isc_result_t +dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig) { + dst_key_t *key; + + REQUIRE(VALID_CTX(dctx)); + REQUIRE(sig != NULL); + + key = dctx->key; + CHECKALG(key->key_alg); + if (key->opaque == NULL) + return (DST_R_NULLKEY); + if (key->func->sign == NULL) + return (DST_R_NOTPRIVATEKEY); + if (key->func->isprivate == NULL || + key->func->isprivate(key) == ISC_FALSE) + return (DST_R_NOTPRIVATEKEY); + + return (key->func->sign(dctx, sig)); +} + +isc_result_t +dst_context_verify(dst_context_t *dctx, isc_region_t *sig) { + REQUIRE(VALID_CTX(dctx)); + REQUIRE(sig != NULL); + + CHECKALG(dctx->key->key_alg); + if (dctx->key->opaque == NULL) + return (DST_R_NULLKEY); + if (dctx->key->func->verify == NULL) + return (DST_R_NOTPUBLICKEY); + + return (dctx->key->func->verify(dctx, sig)); +} + +isc_result_t +dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv, + isc_buffer_t *secret) +{ + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(pub) && VALID_KEY(priv)); + REQUIRE(secret != NULL); + + CHECKALG(pub->key_alg); + CHECKALG(priv->key_alg); + + if (pub->opaque == NULL || priv->opaque == NULL) + return (DST_R_NULLKEY); + + if (pub->key_alg != priv->key_alg || + pub->func->computesecret == NULL || + priv->func->computesecret == NULL) + return (DST_R_KEYCANNOTCOMPUTESECRET); + + if (dst_key_isprivate(priv) == ISC_FALSE) + return (DST_R_NOTPRIVATEKEY); + + return (pub->func->computesecret(pub, priv, secret)); +} + +isc_result_t +dst_key_tofile(const dst_key_t *key, int type, const char *directory) { + isc_result_t ret = ISC_R_SUCCESS; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); + + CHECKALG(key->key_alg); + + if (key->func->tofile == NULL) + return (DST_R_UNSUPPORTEDALG); + + if (type & DST_TYPE_PUBLIC) { + ret = write_public_key(key, type, directory); + if (ret != ISC_R_SUCCESS) + return (ret); + } + + if ((type & DST_TYPE_PRIVATE) && + (key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY) + return (key->func->tofile(key, directory)); + else + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_fromfile(dns_name_t *name, dns_keytag_t id, + unsigned int alg, int type, const char *directory, + isc_mem_t *mctx, dst_key_t **keyp) +{ + char filename[ISC_DIR_NAMEMAX]; + isc_buffer_t b; + dst_key_t *key; + isc_result_t result; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(dns_name_isabsolute(name)); + REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); + REQUIRE(mctx != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + + CHECKALG(alg); + + isc_buffer_init(&b, filename, sizeof(filename)); + result = buildfilename(name, id, alg, type, directory, &b); + if (result != ISC_R_SUCCESS) + return (result); + + key = NULL; + result = dst_key_fromnamedfile(filename, type, mctx, &key); + if (result != ISC_R_SUCCESS) + return (result); + + result = computeid(key); + if (result != ISC_R_SUCCESS) { + dst_key_free(&key); + return (result); + } + + if (!dns_name_equal(name, key->key_name) || + id != key->key_id || + alg != key->key_alg) + { + dst_key_free(&key); + return (DST_R_INVALIDPRIVATEKEY); + } + key->key_id = id; + + *keyp = key; + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx, + dst_key_t **keyp) +{ + isc_result_t result; + dst_key_t *pubkey = NULL, *key = NULL; + dns_keytag_t id; + char *newfilename = NULL; + int newfilenamelen = 0; + isc_lex_t *lex = NULL; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(filename != NULL); + REQUIRE((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) != 0); + REQUIRE(mctx != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + + newfilenamelen = strlen(filename) + 5; + newfilename = isc_mem_get(mctx, newfilenamelen); + if (newfilename == NULL) + return (ISC_R_NOMEMORY); + result = addsuffix(newfilename, newfilenamelen, filename, ".key"); + INSIST(result == ISC_R_SUCCESS); + + result = dst_key_read_public(newfilename, type, mctx, &pubkey); + isc_mem_put(mctx, newfilename, newfilenamelen); + if (result != ISC_R_SUCCESS) + return (result); + + if ((type & (DST_TYPE_PRIVATE | DST_TYPE_PUBLIC)) == DST_TYPE_PUBLIC || + (pubkey->key_flags & DNS_KEYFLAG_TYPEMASK) == DNS_KEYTYPE_NOKEY) + { + result = computeid(pubkey); + if (result != ISC_R_SUCCESS) { + dst_key_free(&pubkey); + return (result); + } + + *keyp = pubkey; + return (ISC_R_SUCCESS); + } + + result = algorithm_status(pubkey->key_alg); + if (result != ISC_R_SUCCESS) { + dst_key_free(&pubkey); + return (result); + } + + key = get_key_struct(pubkey->key_name, pubkey->key_alg, + pubkey->key_flags, pubkey->key_proto, 0, + pubkey->key_class, mctx); + id = pubkey->key_id; + dst_key_free(&pubkey); + + if (key == NULL) + return (ISC_R_NOMEMORY); + + if (key->func->parse == NULL) + RETERR(DST_R_UNSUPPORTEDALG); + + newfilenamelen = strlen(filename) + 9; + newfilename = isc_mem_get(mctx, newfilenamelen); + if (newfilename == NULL) + RETERR(ISC_R_NOMEMORY); + result = addsuffix(newfilename, newfilenamelen, filename, ".private"); + INSIST(result == ISC_R_SUCCESS); + + RETERR(isc_lex_create(mctx, 1500, &lex)); + RETERR(isc_lex_openfile(lex, newfilename)); + isc_mem_put(mctx, newfilename, newfilenamelen); + + RETERR(key->func->parse(key, lex)); + isc_lex_destroy(&lex); + + RETERR(computeid(key)); + + if (id != key->key_id) + RETERR(DST_R_INVALIDPRIVATEKEY); + + *keyp = key; + return (ISC_R_SUCCESS); + out: + if (newfilename != NULL) + isc_mem_put(mctx, newfilename, newfilenamelen); + if (lex != NULL) + isc_lex_destroy(&lex); + dst_key_free(&key); + return (result); +} + +isc_result_t +dst_key_todns(const dst_key_t *key, isc_buffer_t *target) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(target != NULL); + + CHECKALG(key->key_alg); + + if (key->func->todns == NULL) + return (DST_R_UNSUPPORTEDALG); + + if (isc_buffer_availablelength(target) < 4) + return (ISC_R_NOSPACE); + isc_buffer_putuint16(target, (isc_uint16_t)(key->key_flags & 0xffff)); + isc_buffer_putuint8(target, (isc_uint8_t)key->key_proto); + isc_buffer_putuint8(target, (isc_uint8_t)key->key_alg); + + if (key->key_flags & DNS_KEYFLAG_EXTENDED) { + if (isc_buffer_availablelength(target) < 2) + return (ISC_R_NOSPACE); + isc_buffer_putuint16(target, + (isc_uint16_t)((key->key_flags >> 16) + & 0xffff)); + } + + if (key->opaque == NULL) /* NULL KEY */ + return (ISC_R_SUCCESS); + + return (key->func->todns(key, target)); +} + +isc_result_t +dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) +{ + isc_uint8_t alg, proto; + isc_uint32_t flags, extflags; + dst_key_t *key = NULL; + dns_keytag_t id; + isc_region_t r; + isc_result_t result; + + REQUIRE(dst_initialized); + + isc_buffer_remainingregion(source, &r); + + if (isc_buffer_remaininglength(source) < 4) + return (DST_R_INVALIDPUBLICKEY); + flags = isc_buffer_getuint16(source); + proto = isc_buffer_getuint8(source); + alg = isc_buffer_getuint8(source); + + id = dst_region_computeid(&r, alg); + + if (flags & DNS_KEYFLAG_EXTENDED) { + if (isc_buffer_remaininglength(source) < 2) + return (DST_R_INVALIDPUBLICKEY); + extflags = isc_buffer_getuint16(source); + flags |= (extflags << 16); + } + + result = frombuffer(name, alg, flags, proto, rdclass, source, + mctx, &key); + if (result != ISC_R_SUCCESS) + return (result); + key->key_id = id; + + *keyp = key; + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_frombuffer(dns_name_t *name, unsigned int alg, + unsigned int flags, unsigned int protocol, + dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) +{ + dst_key_t *key = NULL; + isc_result_t result; + + REQUIRE(dst_initialized); + + result = frombuffer(name, alg, flags, protocol, rdclass, source, + mctx, &key); + if (result != ISC_R_SUCCESS) + return (result); + + result = computeid(key); + if (result != ISC_R_SUCCESS) { + dst_key_free(&key); + return (result); + } + + *keyp = key; + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(target != NULL); + + CHECKALG(key->key_alg); + + if (key->func->todns == NULL) + return (DST_R_UNSUPPORTEDALG); + + return (key->func->todns(key, target)); +} + +isc_result_t +dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer) { + isc_lex_t *lex = NULL; + isc_result_t result = ISC_R_SUCCESS; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(!dst_key_isprivate(key)); + REQUIRE(buffer != NULL); + + if (key->func->parse == NULL) + RETERR(DST_R_UNSUPPORTEDALG); + + RETERR(isc_lex_create(key->mctx, 1500, &lex)); + RETERR(isc_lex_openbuffer(lex, buffer)); + RETERR(key->func->parse(key, lex)); + out: + if (lex != NULL) + isc_lex_destroy(&lex); + return (result); +} + +isc_result_t +dst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx, + dst_key_t **keyp) +{ + dst_key_t *key; + + REQUIRE(opaque != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + + key = get_key_struct(name, DST_ALG_GSSAPI, 0, DNS_KEYPROTO_DNSSEC, + 0, dns_rdataclass_in, mctx); + if (key == NULL) + return (ISC_R_NOMEMORY); + key->opaque = opaque; + *keyp = key; + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_generate(dns_name_t *name, unsigned int alg, + unsigned int bits, unsigned int param, + unsigned int flags, unsigned int protocol, + dns_rdataclass_t rdclass, + isc_mem_t *mctx, dst_key_t **keyp) +{ + dst_key_t *key; + isc_result_t ret; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(dns_name_isabsolute(name)); + REQUIRE(mctx != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + + CHECKALG(alg); + + key = get_key_struct(name, alg, flags, protocol, bits, rdclass, mctx); + if (key == NULL) + return (ISC_R_NOMEMORY); + + if (bits == 0) { /* NULL KEY */ + key->key_flags |= DNS_KEYTYPE_NOKEY; + *keyp = key; + return (ISC_R_SUCCESS); + } + + if (key->func->generate == NULL) { + dst_key_free(&key); + return (DST_R_UNSUPPORTEDALG); + } + + ret = key->func->generate(key, param); + if (ret != ISC_R_SUCCESS) { + dst_key_free(&key); + return (ret); + } + + ret = computeid(key); + if (ret != ISC_R_SUCCESS) { + dst_key_free(&key); + return (ret); + } + + *keyp = key; + return (ISC_R_SUCCESS); +} + +isc_boolean_t +dst_key_compare(const dst_key_t *key1, const dst_key_t *key2) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key1)); + REQUIRE(VALID_KEY(key2)); + + if (key1 == key2) + return (ISC_TRUE); + if (key1 == NULL || key2 == NULL) + return (ISC_FALSE); + if (key1->key_alg == key2->key_alg && + key1->key_id == key2->key_id && + key1->func->compare != NULL && + key1->func->compare(key1, key2) == ISC_TRUE) + return (ISC_TRUE); + else + return (ISC_FALSE); +} + +isc_boolean_t +dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key1)); + REQUIRE(VALID_KEY(key2)); + + if (key1 == key2) + return (ISC_TRUE); + if (key1 == NULL || key2 == NULL) + return (ISC_FALSE); + if (key1->key_alg == key2->key_alg && + key1->func->paramcompare != NULL && + key1->func->paramcompare(key1, key2) == ISC_TRUE) + return (ISC_TRUE); + else + return (ISC_FALSE); +} + +void +dst_key_free(dst_key_t **keyp) { + isc_mem_t *mctx; + dst_key_t *key; + + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(keyp != NULL && VALID_KEY(*keyp)); + + key = *keyp; + mctx = key->mctx; + + if (key->opaque != NULL) { + INSIST(key->func->destroy != NULL); + key->func->destroy(key); + } + + dns_name_free(key->key_name, mctx); + isc_mem_put(mctx, key->key_name, sizeof(dns_name_t)); + memset(key, 0, sizeof(dst_key_t)); + isc_mem_put(mctx, key, sizeof(dst_key_t)); + *keyp = NULL; +} + +isc_boolean_t +dst_key_isprivate(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + INSIST(key->func->isprivate != NULL); + return (key->func->isprivate(key)); +} + +isc_result_t +dst_key_buildfilename(const dst_key_t *key, int type, + const char *directory, isc_buffer_t *out) { + + REQUIRE(VALID_KEY(key)); + REQUIRE(type == DST_TYPE_PRIVATE || type == DST_TYPE_PUBLIC || + type == 0); + + return (buildfilename(key->key_name, key->key_id, key->key_alg, + type, directory, out)); +} + +isc_result_t +dst_key_sigsize(const dst_key_t *key, unsigned int *n) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(n != NULL); + + /* XXXVIX this switch statement is too sparse to gen a jump table. */ + switch (key->key_alg) { + case DST_ALG_RSAMD5: + case DST_ALG_RSASHA1: + *n = (key->key_size + 7) / 8; + break; + case DST_ALG_DSA: + *n = DNS_SIG_DSASIGSIZE; + break; + case DST_ALG_HMACMD5: + *n = 16; + break; + case DST_ALG_GSSAPI: + *n = 128; /* XXX */ + break; + case DST_ALG_DH: + default: + return (DST_R_UNSUPPORTEDALG); + } + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_key_secretsize(const dst_key_t *key, unsigned int *n) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + REQUIRE(n != NULL); + + if (key->key_alg == DST_ALG_DH) + *n = (key->key_size + 7) / 8; + else + return (DST_R_UNSUPPORTEDALG); + return (ISC_R_SUCCESS); +} + +/*** + *** Static methods + ***/ + +/* + * Allocates a key structure and fills in some of the fields. + */ +static dst_key_t * +get_key_struct(dns_name_t *name, unsigned int alg, + unsigned int flags, unsigned int protocol, + unsigned int bits, dns_rdataclass_t rdclass, + isc_mem_t *mctx) +{ + dst_key_t *key; + isc_result_t result; + + key = (dst_key_t *) isc_mem_get(mctx, sizeof(dst_key_t)); + if (key == NULL) + return (NULL); + + memset(key, 0, sizeof(dst_key_t)); + key->magic = KEY_MAGIC; + + key->key_name = isc_mem_get(mctx, sizeof(dns_name_t)); + if (key->key_name == NULL) { + isc_mem_put(mctx, key, sizeof(dst_key_t)); + return (NULL); + } + dns_name_init(key->key_name, NULL); + result = dns_name_dup(name, mctx, key->key_name); + if (result != ISC_R_SUCCESS) { + isc_mem_put(mctx, key->key_name, sizeof(dns_name_t)); + isc_mem_put(mctx, key, sizeof(dst_key_t)); + return (NULL); + } + key->key_alg = alg; + key->key_flags = flags; + key->key_proto = protocol; + key->mctx = mctx; + key->opaque = NULL; + key->key_size = bits; + key->key_class = rdclass; + key->func = dst_t_func[alg]; + return (key); +} + +/* + * Reads a public key from disk + */ +isc_result_t +dst_key_read_public(const char *filename, int type, + isc_mem_t *mctx, dst_key_t **keyp) +{ + u_char rdatabuf[DST_KEY_MAXSIZE]; + isc_buffer_t b; + dns_fixedname_t name; + isc_lex_t *lex = NULL; + isc_token_t token; + isc_result_t ret; + dns_rdata_t rdata = DNS_RDATA_INIT; + unsigned int opt = ISC_LEXOPT_DNSMULTILINE; + dns_rdataclass_t rdclass = dns_rdataclass_in; + isc_lexspecials_t specials; + isc_uint32_t ttl; + isc_result_t result; + dns_rdatatype_t keytype; + + /* + * Open the file and read its formatted contents + * File format: + * domain.name [ttl] [class] [KEY|DNSKEY] + */ + + /* 1500 should be large enough for any key */ + ret = isc_lex_create(mctx, 1500, &lex); + if (ret != ISC_R_SUCCESS) + goto cleanup; + + memset(specials, 0, sizeof(specials)); + specials['('] = 1; + specials[')'] = 1; + specials['"'] = 1; + isc_lex_setspecials(lex, specials); + isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE); + + ret = isc_lex_openfile(lex, filename); + if (ret != ISC_R_SUCCESS) + goto cleanup; + +#define NEXTTOKEN(lex, opt, token) { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret != ISC_R_SUCCESS) \ + goto cleanup; \ + } + +#define BADTOKEN() { \ + ret = ISC_R_UNEXPECTEDTOKEN; \ + goto cleanup; \ + } + + /* Read the domain name */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string) + BADTOKEN(); + dns_fixedname_init(&name); + isc_buffer_init(&b, DST_AS_STR(token), strlen(DST_AS_STR(token))); + isc_buffer_add(&b, strlen(DST_AS_STR(token))); + ret = dns_name_fromtext(dns_fixedname_name(&name), &b, dns_rootname, + ISC_FALSE, NULL); + if (ret != ISC_R_SUCCESS) + goto cleanup; + + /* Read the next word: either TTL, class, or 'KEY' */ + NEXTTOKEN(lex, opt, &token); + + /* If it's a TTL, read the next one */ + result = dns_ttl_fromtext(&token.value.as_textregion, &ttl); + if (result == ISC_R_SUCCESS) + NEXTTOKEN(lex, opt, &token); + + if (token.type != isc_tokentype_string) + BADTOKEN(); + + ret = dns_rdataclass_fromtext(&rdclass, &token.value.as_textregion); + if (ret == ISC_R_SUCCESS) + NEXTTOKEN(lex, opt, &token); + + if (token.type != isc_tokentype_string) + BADTOKEN(); + + if (strcasecmp(DST_AS_STR(token), "DNSKEY") == 0) + keytype = dns_rdatatype_dnskey; + else if (strcasecmp(DST_AS_STR(token), "KEY") == 0) + keytype = dns_rdatatype_key; /* SIG(0), TKEY */ + else + BADTOKEN(); + + if (((type & DST_TYPE_KEY) != 0 && keytype != dns_rdatatype_key) || + ((type & DST_TYPE_KEY) == 0 && keytype != dns_rdatatype_dnskey)) { + ret = DST_R_BADKEYTYPE; + goto cleanup; + } + + isc_buffer_init(&b, rdatabuf, sizeof(rdatabuf)); + ret = dns_rdata_fromtext(&rdata, rdclass, keytype, lex, NULL, + ISC_FALSE, mctx, &b, NULL); + if (ret != ISC_R_SUCCESS) + goto cleanup; + + ret = dst_key_fromdns(dns_fixedname_name(&name), rdclass, &b, mctx, + keyp); + if (ret != ISC_R_SUCCESS) + goto cleanup; + + cleanup: + if (lex != NULL) + isc_lex_destroy(&lex); + return (ret); +} + +static isc_boolean_t +issymmetric(const dst_key_t *key) { + REQUIRE(dst_initialized == ISC_TRUE); + REQUIRE(VALID_KEY(key)); + + /* XXXVIX this switch statement is too sparse to gen a jump table. */ + switch (key->key_alg) { + case DST_ALG_RSAMD5: + case DST_ALG_RSASHA1: + case DST_ALG_DSA: + case DST_ALG_DH: + return (ISC_FALSE); + case DST_ALG_HMACMD5: + case DST_ALG_GSSAPI: + return (ISC_TRUE); + default: + return (ISC_FALSE); + } +} + +/* + * Writes a public key to disk in DNS format. + */ +static isc_result_t +write_public_key(const dst_key_t *key, int type, const char *directory) { + FILE *fp; + isc_buffer_t keyb, textb, fileb, classb; + isc_region_t r; + char filename[ISC_DIR_NAMEMAX]; + unsigned char key_array[DST_KEY_MAXSIZE]; + char text_array[DST_KEY_MAXTEXTSIZE]; + char class_array[10]; + isc_result_t ret; + dns_rdata_t rdata = DNS_RDATA_INIT; + isc_fsaccess_t access; + + REQUIRE(VALID_KEY(key)); + + isc_buffer_init(&keyb, key_array, sizeof(key_array)); + isc_buffer_init(&textb, text_array, sizeof(text_array)); + isc_buffer_init(&classb, class_array, sizeof(class_array)); + + ret = dst_key_todns(key, &keyb); + if (ret != ISC_R_SUCCESS) + return (ret); + + isc_buffer_usedregion(&keyb, &r); + dns_rdata_fromregion(&rdata, key->key_class, dns_rdatatype_dnskey, &r); + + ret = dns_rdata_totext(&rdata, (dns_name_t *) NULL, &textb); + if (ret != ISC_R_SUCCESS) + return (DST_R_INVALIDPUBLICKEY); + + ret = dns_rdataclass_totext(key->key_class, &classb); + if (ret != ISC_R_SUCCESS) + return (DST_R_INVALIDPUBLICKEY); + + /* + * Make the filename. + */ + isc_buffer_init(&fileb, filename, sizeof(filename)); + ret = dst_key_buildfilename(key, DST_TYPE_PUBLIC, directory, &fileb); + if (ret != ISC_R_SUCCESS) + return (ret); + + /* + * Create public key file. + */ + if ((fp = fopen(filename, "w")) == NULL) + return (DST_R_WRITEERROR); + + if (issymmetric(key)) { + access = 0; + isc_fsaccess_add(ISC_FSACCESS_OWNER, + ISC_FSACCESS_READ | ISC_FSACCESS_WRITE, + &access); + (void)isc_fsaccess_set(filename, access); + } + + ret = dns_name_print(key->key_name, fp); + if (ret != ISC_R_SUCCESS) + return (ret); + + fprintf(fp, " "); + + isc_buffer_usedregion(&classb, &r); + fwrite(r.base, 1, r.length, fp); + + if ((type & DST_TYPE_KEY) != 0) + fprintf(fp, " KEY "); + else + fprintf(fp, " DNSKEY "); + + isc_buffer_usedregion(&textb, &r); + fwrite(r.base, 1, r.length, fp); + + fputc('\n', fp); + fclose(fp); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +buildfilename(dns_name_t *name, dns_keytag_t id, + unsigned int alg, unsigned int type, + const char *directory, isc_buffer_t *out) +{ + const char *suffix = ""; + unsigned int len; + isc_result_t result; + + REQUIRE(out != NULL); + if ((type & DST_TYPE_PRIVATE) != 0) + suffix = ".private"; + else if (type == DST_TYPE_PUBLIC) + suffix = ".key"; + if (directory != NULL) { + if (isc_buffer_availablelength(out) < strlen(directory)) + return (ISC_R_NOSPACE); + isc_buffer_putstr(out, directory); + if (strlen(directory) > 0U && + directory[strlen(directory) - 1] != '/') + isc_buffer_putstr(out, "/"); + } + if (isc_buffer_availablelength(out) < 1) + return (ISC_R_NOSPACE); + isc_buffer_putstr(out, "K"); + result = dns_name_tofilenametext(name, ISC_FALSE, out); + if (result != ISC_R_SUCCESS) + return (result); + len = 1 + 3 + 1 + 5 + strlen(suffix) + 1; + if (isc_buffer_availablelength(out) < len) + return (ISC_R_NOSPACE); + sprintf((char *) isc_buffer_used(out), "+%03d+%05d%s", alg, id, suffix); + isc_buffer_add(out, len); + return (ISC_R_SUCCESS); +} + +static isc_result_t +computeid(dst_key_t *key) { + isc_buffer_t dnsbuf; + unsigned char dns_array[DST_KEY_MAXSIZE]; + isc_region_t r; + isc_result_t ret; + + isc_buffer_init(&dnsbuf, dns_array, sizeof(dns_array)); + ret = dst_key_todns(key, &dnsbuf); + if (ret != ISC_R_SUCCESS) + return (ret); + + isc_buffer_usedregion(&dnsbuf, &r); + key->key_id = dst_region_computeid(&r, key->key_alg); + return (ISC_R_SUCCESS); +} + +static isc_result_t +frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) +{ + dst_key_t *key; + isc_result_t ret; + + REQUIRE(dns_name_isabsolute(name)); + REQUIRE(source != NULL); + REQUIRE(mctx != NULL); + REQUIRE(keyp != NULL && *keyp == NULL); + + key = get_key_struct(name, alg, flags, protocol, 0, rdclass, mctx); + if (key == NULL) + return (ISC_R_NOMEMORY); + + if (isc_buffer_remaininglength(source) > 0) { + ret = algorithm_status(alg); + if (ret != ISC_R_SUCCESS) { + dst_key_free(&key); + return (ret); + } + if (key->func->fromdns == NULL) { + dst_key_free(&key); + return (DST_R_UNSUPPORTEDALG); + } + + ret = key->func->fromdns(key, source); + if (ret != ISC_R_SUCCESS) { + dst_key_free(&key); + return (ret); + } + } + + *keyp = key; + return (ISC_R_SUCCESS); +} + +static isc_result_t +algorithm_status(unsigned int alg) { + REQUIRE(dst_initialized == ISC_TRUE); + + if (dst_algorithm_supported(alg)) + return (ISC_R_SUCCESS); +#ifndef OPENSSL + if (alg == DST_ALG_RSAMD5 || alg == DST_ALG_RSASHA1 || + alg == DST_ALG_DSA || alg == DST_ALG_DH || + alg == DST_ALG_HMACMD5) + return (DST_R_NOCRYPTO); +#endif + return (DST_R_UNSUPPORTEDALG); +} + +static isc_result_t +addsuffix(char *filename, unsigned int len, const char *ofilename, + const char *suffix) +{ + int olen = strlen(ofilename); + int n; + + if (olen > 1 && ofilename[olen - 1] == '.') + olen -= 1; + else if (olen > 8 && strcmp(ofilename + olen - 8, ".private") == 0) + olen -= 8; + else if (olen > 4 && strcmp(ofilename + olen - 4, ".key") == 0) + olen -= 4; + + n = snprintf(filename, len, "%.*s%s", olen, ofilename, suffix); + if (n < 0) + return (ISC_R_NOSPACE); + return (ISC_R_SUCCESS); +} + +isc_result_t +dst__entropy_getdata(void *buf, unsigned int len, isc_boolean_t pseudo) { + unsigned int flags = dst_entropy_flags; + if (pseudo) + flags &= ~ISC_ENTROPY_GOODONLY; + return (isc_entropy_getdata(dst_entropy_pool, buf, len, NULL, flags)); +} diff --git a/lib/dns/dst_internal.h b/lib/dns/dst_internal.h new file mode 100644 index 0000000000..7b00f15d11 --- /dev/null +++ b/lib/dns/dst_internal.h @@ -0,0 +1,134 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 2000-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dst_internal.h,v 1.1 2004/12/09 01:41:00 marka Exp $ */ + +#ifndef DST_DST_INTERNAL_H +#define DST_DST_INTERNAL_H 1 + +#include +#include +#include +#include +#include +#include + +#include + +ISC_LANG_BEGINDECLS + +#define KEY_MAGIC ISC_MAGIC('D','S','T','K') +#define CTX_MAGIC ISC_MAGIC('D','S','T','C') + +#define VALID_KEY(x) ISC_MAGIC_VALID(x, KEY_MAGIC) +#define VALID_CTX(x) ISC_MAGIC_VALID(x, CTX_MAGIC) + +extern isc_mem_t *dst__memory_pool; + +/*** + *** Types + ***/ + +typedef struct dst_func dst_func_t; + +struct dst_key { + unsigned int magic; + dns_name_t * key_name; /* name of the key */ + unsigned int key_size; /* size of the key in bits */ + unsigned int key_proto; /* protocols this key is used for */ + unsigned int key_alg; /* algorithm of the key */ + isc_uint32_t key_flags; /* flags of the public key */ + isc_uint16_t key_id; /* identifier of the key */ + dns_rdataclass_t key_class; /* class of the key record */ + isc_mem_t *mctx; /* memory context */ + void * opaque; /* pointer to key in crypto pkg fmt */ + dst_func_t * func; /* crypto package specific functions */ +}; + +struct dst_context { + unsigned int magic; + dst_key_t *key; + isc_mem_t *mctx; + void *opaque; +}; + +struct dst_func { + /* + * Context functions + */ + isc_result_t (*createctx)(dst_key_t *key, dst_context_t *dctx); + void (*destroyctx)(dst_context_t *dctx); + isc_result_t (*adddata)(dst_context_t *dctx, const isc_region_t *data); + + /* + * Key operations + */ + isc_result_t (*sign)(dst_context_t *dctx, isc_buffer_t *sig); + isc_result_t (*verify)(dst_context_t *dctx, const isc_region_t *sig); + isc_result_t (*computesecret)(const dst_key_t *pub, + const dst_key_t *priv, + isc_buffer_t *secret); + isc_boolean_t (*compare)(const dst_key_t *key1, const dst_key_t *key2); + isc_boolean_t (*paramcompare)(const dst_key_t *key1, + const dst_key_t *key2); + isc_result_t (*generate)(dst_key_t *key, int parms); + isc_boolean_t (*isprivate)(const dst_key_t *key); + void (*destroy)(dst_key_t *key); + + /* conversion functions */ + isc_result_t (*todns)(const dst_key_t *key, isc_buffer_t *data); + isc_result_t (*fromdns)(dst_key_t *key, isc_buffer_t *data); + isc_result_t (*tofile)(const dst_key_t *key, const char *directory); + isc_result_t (*parse)(dst_key_t *key, isc_lex_t *lexer); + + /* cleanup */ + void (*cleanup)(void); +}; + +/* + * Initializers + */ +isc_result_t dst__openssl_init(void); + +isc_result_t dst__hmacmd5_init(struct dst_func **funcp); +isc_result_t dst__opensslrsa_init(struct dst_func **funcp); +isc_result_t dst__openssldsa_init(struct dst_func **funcp); +isc_result_t dst__openssldh_init(struct dst_func **funcp); +isc_result_t dst__gssapi_init(struct dst_func **funcp); + +/* + * Destructors + */ +void dst__openssl_destroy(void); + +/* + * Memory allocators using the DST memory pool. + */ +void * dst__mem_alloc(size_t size); +void dst__mem_free(void *ptr); +void * dst__mem_realloc(void *ptr, size_t size); + +/* + * Entropy retriever using the DST entropy pool. + */ +isc_result_t dst__entropy_getdata(void *buf, unsigned int len, + isc_boolean_t pseudo); + +ISC_LANG_ENDDECLS + +#endif /* DST_DST_INTERNAL_H */ diff --git a/lib/dns/dst_lib.c b/lib/dns/dst_lib.c new file mode 100644 index 0000000000..4935d92cb7 --- /dev/null +++ b/lib/dns/dst_lib.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: dst_lib.c,v 1.1 2004/12/09 01:41:01 marka Exp $ + */ + +#include + +#include + +#include +#include +#include + +#include + +/*** + *** Globals + ***/ + +LIBDNS_EXTERNAL_DATA isc_msgcat_t * dst_msgcat = NULL; + + +/*** + *** Private + ***/ + +static isc_once_t msgcat_once = ISC_ONCE_INIT; + + +/*** + *** Functions + ***/ + +static void +open_msgcat(void) { + isc_msgcat_open("libdst.cat", &dst_msgcat); +} + +void +dst_lib_initmsgcat(void) { + + /* + * Initialize the DST library's message catalog, dst_msgcat, if it + * has not already been initialized. + */ + + RUNTIME_CHECK(isc_once_do(&msgcat_once, open_msgcat) == ISC_R_SUCCESS); +} diff --git a/lib/dns/dst_openssl.h b/lib/dns/dst_openssl.h new file mode 100644 index 0000000000..5a29fde94e --- /dev/null +++ b/lib/dns/dst_openssl.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dst_openssl.h,v 1.1 2004/12/09 01:41:01 marka Exp $ */ + +#ifndef DST_OPENSSL_H +#define DST_OPENSSL_H 1 + +#include +#include + +ISC_LANG_BEGINDECLS + +isc_result_t +dst__openssl_toresult(isc_result_t fallback); + +ISC_LANG_ENDDECLS + +#endif /* DST_OPENSSL_H */ diff --git a/lib/dns/dst_parse.c b/lib/dns/dst_parse.c new file mode 100644 index 0000000000..9f2a93daf1 --- /dev/null +++ b/lib/dns/dst_parse.c @@ -0,0 +1,412 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: dst_parse.c,v 1.1 2004/12/09 01:41:02 marka Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dst_internal.h" +#include "dst_parse.h" +#include "dst/result.h" + +#define DST_AS_STR(t) ((t).value.as_textregion.base) + +#define PRIVATE_KEY_STR "Private-key-format:" +#define ALGORITHM_STR "Algorithm:" + +struct parse_map { + const int value; + const char *tag; +}; + +static struct parse_map map[] = { + {TAG_RSA_MODULUS, "Modulus:"}, + {TAG_RSA_PUBLICEXPONENT, "PublicExponent:"}, + {TAG_RSA_PRIVATEEXPONENT, "PrivateExponent:"}, + {TAG_RSA_PRIME1, "Prime1:"}, + {TAG_RSA_PRIME2, "Prime2:"}, + {TAG_RSA_EXPONENT1, "Exponent1:"}, + {TAG_RSA_EXPONENT2, "Exponent2:"}, + {TAG_RSA_COEFFICIENT, "Coefficient:"}, + + {TAG_DH_PRIME, "Prime(p):"}, + {TAG_DH_GENERATOR, "Generator(g):"}, + {TAG_DH_PRIVATE, "Private_value(x):"}, + {TAG_DH_PUBLIC, "Public_value(y):"}, + + {TAG_DSA_PRIME, "Prime(p):"}, + {TAG_DSA_SUBPRIME, "Subprime(q):"}, + {TAG_DSA_BASE, "Base(g):"}, + {TAG_DSA_PRIVATE, "Private_value(x):"}, + {TAG_DSA_PUBLIC, "Public_value(y):"}, + + {TAG_HMACMD5_KEY, "Key:"}, + {0, NULL} +}; + +static int +find_value(const char *s, const unsigned int alg) { + int i; + + for (i = 0; ; i++) { + if (map[i].tag == NULL) + return (-1); + else if (strcasecmp(s, map[i].tag) == 0 && + TAG_ALG(map[i].value) == alg) + return (map[i].value); + } +} + +static const char * +find_tag(const int value) { + int i; + + for (i = 0; ; i++) { + if (map[i].tag == NULL) + return (NULL); + else if (value == map[i].value) + return (map[i].tag); + } +} + +static int +check_rsa(const dst_private_t *priv) { + int i, j; + if (priv->nelements != RSA_NTAGS) + return (-1); + for (i = 0; i < RSA_NTAGS; i++) { + for (j = 0; j < priv->nelements; j++) + if (priv->elements[j].tag == TAG(DST_ALG_RSAMD5, i)) + break; + if (j == priv->nelements) + return (-1); + } + return (0); +} + +static int +check_dh(const dst_private_t *priv) { + int i, j; + if (priv->nelements != DH_NTAGS) + return (-1); + for (i = 0; i < DH_NTAGS; i++) { + for (j = 0; j < priv->nelements; j++) + if (priv->elements[j].tag == TAG(DST_ALG_DH, i)) + break; + if (j == priv->nelements) + return (-1); + } + return (0); +} + +static int +check_dsa(const dst_private_t *priv) { + int i, j; + if (priv->nelements != DSA_NTAGS) + return (-1); + for (i = 0; i < DSA_NTAGS; i++) { + for (j = 0; j < priv->nelements; j++) + if (priv->elements[j].tag == TAG(DST_ALG_DSA, i)) + break; + if (j == priv->nelements) + return (-1); + } + return (0); +} + +static int +check_hmac_md5(const dst_private_t *priv) { + if (priv->nelements != HMACMD5_NTAGS) + return (-1); + if (priv->elements[0].tag != TAG_HMACMD5_KEY) + return (-1); + return (0); +} + +static int +check_data(const dst_private_t *priv, const unsigned int alg) { + /* XXXVIX this switch statement is too sparse to gen a jump table. */ + switch (alg) { + case DST_ALG_RSAMD5: + case DST_ALG_RSASHA1: + return (check_rsa(priv)); + case DST_ALG_DH: + return (check_dh(priv)); + case DST_ALG_DSA: + return (check_dsa(priv)); + case DST_ALG_HMACMD5: + return (check_hmac_md5(priv)); + default: + return (DST_R_UNSUPPORTEDALG); + } +} + +void +dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx) { + int i; + + if (priv == NULL) + return; + for (i = 0; i < priv->nelements; i++) { + if (priv->elements[i].data == NULL) + continue; + memset(priv->elements[i].data, 0, MAXFIELDSIZE); + isc_mem_put(mctx, priv->elements[i].data, MAXFIELDSIZE); + } + priv->nelements = 0; +} + +int +dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, + isc_mem_t *mctx, dst_private_t *priv) +{ + int n = 0, major, minor; + isc_buffer_t b; + isc_token_t token; + unsigned char *data = NULL; + unsigned int opt = ISC_LEXOPT_EOL; + isc_result_t ret; + + REQUIRE(priv != NULL); + + priv->nelements = 0; + +#define NEXTTOKEN(lex, opt, token) \ + do { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret != ISC_R_SUCCESS) \ + goto fail; \ + } while (0) + +#define READLINE(lex, opt, token) \ + do { \ + ret = isc_lex_gettoken(lex, opt, token); \ + if (ret == ISC_R_EOF) \ + break; \ + else if (ret != ISC_R_SUCCESS) \ + goto fail; \ + } while ((*token).type != isc_tokentype_eol) + + /* + * Read the description line. + */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string || + strcmp(DST_AS_STR(token), PRIVATE_KEY_STR) != 0) + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string || + (DST_AS_STR(token))[0] != 'v') + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + if (sscanf(DST_AS_STR(token), "v%d.%d", &major, &minor) != 2) + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + if (major > MAJOR_VERSION || + (major == MAJOR_VERSION && minor > MINOR_VERSION)) + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + READLINE(lex, opt, &token); + + /* + * Read the algorithm line. + */ + NEXTTOKEN(lex, opt, &token); + if (token.type != isc_tokentype_string || + strcmp(DST_AS_STR(token), ALGORITHM_STR) != 0) + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + NEXTTOKEN(lex, opt | ISC_LEXOPT_NUMBER, &token); + if (token.type != isc_tokentype_number || + token.value.as_ulong != (unsigned long) dst_key_alg(key)) + { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + READLINE(lex, opt, &token); + + /* + * Read the key data. + */ + for (n = 0; n < MAXFIELDS; n++) { + int tag; + isc_region_t r; + + do { + ret = isc_lex_gettoken(lex, opt, &token); + if (ret == ISC_R_EOF) + goto done; + if (ret != ISC_R_SUCCESS) + goto fail; + } while (token.type == isc_tokentype_eol); + + if (token.type != isc_tokentype_string) { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + + memset(&priv->elements[n], 0, sizeof(dst_private_element_t)); + tag = find_value(DST_AS_STR(token), alg); + if (tag < 0 || TAG_ALG(tag) != alg) { + ret = DST_R_INVALIDPRIVATEKEY; + goto fail; + } + priv->elements[n].tag = tag; + + data = (unsigned char *) isc_mem_get(mctx, MAXFIELDSIZE); + if (data == NULL) + goto fail; + + isc_buffer_init(&b, data, MAXFIELDSIZE); + ret = isc_base64_tobuffer(lex, &b, -1); + if (ret != ISC_R_SUCCESS) + goto fail; + isc_buffer_usedregion(&b, &r); + priv->elements[n].length = r.length; + priv->elements[n].data = r.base; + + READLINE(lex, opt, &token); + data = NULL; + } + done: + priv->nelements = n; + + if (check_data(priv, alg) < 0) + goto fail; + + return (ISC_R_SUCCESS); + +fail: + priv->nelements = n; + dst__privstruct_free(priv, mctx); + if (data != NULL) + isc_mem_put(mctx, data, MAXFIELDSIZE); + + return (ret); +} + +int +dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, + const char *directory) +{ + FILE *fp; + int ret, i; + isc_result_t iret; + char filename[ISC_DIR_NAMEMAX]; + char buffer[MAXFIELDSIZE * 2]; + isc_buffer_t b; + isc_fsaccess_t access; + + REQUIRE(priv != NULL); + + if (check_data(priv, dst_key_alg(key)) < 0) + return (DST_R_INVALIDPRIVATEKEY); + + isc_buffer_init(&b, filename, sizeof(filename)); + ret = dst_key_buildfilename(key, DST_TYPE_PRIVATE, directory, &b); + if (ret != ISC_R_SUCCESS) + return (ret); + + if ((fp = fopen(filename, "w")) == NULL) + return (DST_R_WRITEERROR); + + access = 0; + isc_fsaccess_add(ISC_FSACCESS_OWNER, + ISC_FSACCESS_READ | ISC_FSACCESS_WRITE, + &access); + (void)isc_fsaccess_set(filename, access); + + /* XXXDCL return value should be checked for full filesystem */ + fprintf(fp, "%s v%d.%d\n", PRIVATE_KEY_STR, MAJOR_VERSION, + MINOR_VERSION); + + fprintf(fp, "%s %d ", ALGORITHM_STR, dst_key_alg(key)); + /* XXXVIX this switch statement is too sparse to gen a jump table. */ + switch (dst_key_alg(key)) { + case DST_ALG_RSAMD5: + fprintf(fp, "(RSA)\n"); + break; + case DST_ALG_DH: + fprintf(fp, "(DH)\n"); + break; + case DST_ALG_DSA: + fprintf(fp, "(DSA)\n"); + break; + case DST_ALG_RSASHA1: + fprintf(fp, "(RSASHA1)\n"); + break; + case DST_ALG_HMACMD5: + fprintf(fp, "(HMAC_MD5)\n"); + break; + default: + fprintf(fp, "(?)\n"); + break; + } + + for (i = 0; i < priv->nelements; i++) { + isc_buffer_t b; + isc_region_t r; + const char *s; + + s = find_tag(priv->elements[i].tag); + + r.base = priv->elements[i].data; + r.length = priv->elements[i].length; + isc_buffer_init(&b, buffer, sizeof(buffer)); + iret = isc_base64_totext(&r, sizeof(buffer), "", &b); + if (iret != ISC_R_SUCCESS) { + fclose(fp); + return (DST_R_INVALIDPRIVATEKEY); + } + isc_buffer_usedregion(&b, &r); + + fprintf(fp, "%s ", s); + fwrite(r.base, 1, r.length, fp); + fprintf(fp, "\n"); + } + + fclose(fp); + return (ISC_R_SUCCESS); +} diff --git a/lib/dns/dst_parse.h b/lib/dns/dst_parse.h new file mode 100644 index 0000000000..6a9b133a57 --- /dev/null +++ b/lib/dns/dst_parse.h @@ -0,0 +1,95 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 2000-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dst_parse.h,v 1.1 2004/12/09 01:41:02 marka Exp $ */ + +#ifndef DST_DST_PARSE_H +#define DST_DST_PARSE_H 1 + +#include + +#include + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 2 + +#define MAXFIELDSIZE 512 +#define MAXFIELDS 12 + +#define TAG_SHIFT 4 +#define TAG_ALG(tag) ((unsigned int)(tag) >> TAG_SHIFT) +#define TAG(alg, off) (((alg) << TAG_SHIFT) + (off)) + +/* These are used by both RSA-MD5 and RSA-SHA1 */ +#define RSA_NTAGS 8 +#define TAG_RSA_MODULUS ((DST_ALG_RSAMD5 << TAG_SHIFT) + 0) +#define TAG_RSA_PUBLICEXPONENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 1) +#define TAG_RSA_PRIVATEEXPONENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 2) +#define TAG_RSA_PRIME1 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 3) +#define TAG_RSA_PRIME2 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 4) +#define TAG_RSA_EXPONENT1 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 5) +#define TAG_RSA_EXPONENT2 ((DST_ALG_RSAMD5 << TAG_SHIFT) + 6) +#define TAG_RSA_COEFFICIENT ((DST_ALG_RSAMD5 << TAG_SHIFT) + 7) + +#define DH_NTAGS 4 +#define TAG_DH_PRIME ((DST_ALG_DH << TAG_SHIFT) + 0) +#define TAG_DH_GENERATOR ((DST_ALG_DH << TAG_SHIFT) + 1) +#define TAG_DH_PRIVATE ((DST_ALG_DH << TAG_SHIFT) + 2) +#define TAG_DH_PUBLIC ((DST_ALG_DH << TAG_SHIFT) + 3) + +#define DSA_NTAGS 5 +#define TAG_DSA_PRIME ((DST_ALG_DSA << TAG_SHIFT) + 0) +#define TAG_DSA_SUBPRIME ((DST_ALG_DSA << TAG_SHIFT) + 1) +#define TAG_DSA_BASE ((DST_ALG_DSA << TAG_SHIFT) + 2) +#define TAG_DSA_PRIVATE ((DST_ALG_DSA << TAG_SHIFT) + 3) +#define TAG_DSA_PUBLIC ((DST_ALG_DSA << TAG_SHIFT) + 4) + +#define HMACMD5_NTAGS 1 +#define TAG_HMACMD5_KEY ((DST_ALG_HMACMD5 << TAG_SHIFT) + 0) + +struct dst_private_element { + unsigned short tag; + unsigned short length; + unsigned char *data; +}; + +typedef struct dst_private_element dst_private_element_t; + +struct dst_private { + unsigned short nelements; + dst_private_element_t elements[MAXFIELDS]; +}; + +typedef struct dst_private dst_private_t; + +ISC_LANG_BEGINDECLS + +void +dst__privstruct_free(dst_private_t *priv, isc_mem_t *mctx); + +int +dst__privstruct_parse(dst_key_t *key, unsigned int alg, isc_lex_t *lex, + isc_mem_t *mctx, dst_private_t *priv); + +int +dst__privstruct_writefile(const dst_key_t *key, const dst_private_t *priv, + const char *directory); + +ISC_LANG_ENDDECLS + +#endif /* DST_DST_PARSE_H */ diff --git a/lib/dns/dst_result.c b/lib/dns/dst_result.c new file mode 100644 index 0000000000..d7e4929f38 --- /dev/null +++ b/lib/dns/dst_result.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: dst_result.c,v 1.1 2004/12/09 01:41:02 marka Exp $ + */ + +#include + +#include +#include + +#include +#include + +static const char *text[DST_R_NRESULTS] = { + "algorithm is unsupported", /* 0 */ + "openssl failure", /* 1 */ + "built with no crypto support", /* 2 */ + "illegal operation for a null key", /* 3 */ + "public key is invalid", /* 4 */ + "private key is invalid", /* 5 */ + "UNUSED6", /* 6 */ + "error occurred writing key to disk", /* 7 */ + "invalid algorithm specific parameter", /* 8 */ + "UNUSED9", /* 9 */ + "UNUSED10", /* 10 */ + "sign failure", /* 11 */ + "UNUSED12", /* 12 */ + "UNUSED13", /* 13 */ + "verify failure", /* 14 */ + "not a public key", /* 15 */ + "not a private key", /* 16 */ + "not a key that can compute a secret", /* 17 */ + "failure computing a shared secret", /* 18 */ + "no randomness available", /* 19 */ + "bad key type" /* 20 */ +}; + +#define DST_RESULT_RESULTSET 2 + +static isc_once_t once = ISC_ONCE_INIT; + +static void +initialize_action(void) { + isc_result_t result; + + result = isc_result_register(ISC_RESULTCLASS_DST, DST_R_NRESULTS, + text, dst_msgcat, DST_RESULT_RESULTSET); + if (result != ISC_R_SUCCESS) + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_result_register() failed: %u", result); +} + +static void +initialize(void) { + dst_lib_initmsgcat(); + RUNTIME_CHECK(isc_once_do(&once, initialize_action) == ISC_R_SUCCESS); +} + +const char * +dst_result_totext(isc_result_t result) { + initialize(); + + return (isc_result_totext(result)); +} + +void +dst_result_register(void) { + initialize(); +} diff --git a/lib/dns/gssapi_link.c b/lib/dns/gssapi_link.c new file mode 100644 index 0000000000..0ecfb046ec --- /dev/null +++ b/lib/dns/gssapi_link.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * $Id: gssapi_link.c,v 1.1 2004/12/09 01:41:02 marka Exp $ + */ + +#ifdef GSSAPI + +#include + +#include +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_parse.h" + +#include + +#define INITIAL_BUFFER_SIZE 1024 +#define BUFFER_EXTRA 1024 + +#define REGION_TO_GBUFFER(r, gb) \ + do { \ + (gb).length = (r).length; \ + (gb).value = (r).base; \ + } while (0) + +typedef struct gssapi_ctx { + isc_buffer_t *buffer; + gss_ctx_id_t *context_id; +} gssapi_ctx_t; + + +static isc_result_t +gssapi_createctx(dst_key_t *key, dst_context_t *dctx) { + gssapi_ctx_t *ctx; + isc_result_t result; + + UNUSED(key); + + ctx = isc_mem_get(dctx->mctx, sizeof(gssapi_ctx_t)); + if (ctx == NULL) + return (ISC_R_NOMEMORY); + ctx->buffer = NULL; + result = isc_buffer_allocate(dctx->mctx, &ctx->buffer, + INITIAL_BUFFER_SIZE); + if (result != ISC_R_SUCCESS) { + isc_mem_put(dctx->mctx, ctx, sizeof(gssapi_ctx_t)); + return (result); + } + ctx->context_id = key->opaque; + dctx->opaque = ctx; + return (ISC_R_SUCCESS); +} + +static void +gssapi_destroyctx(dst_context_t *dctx) { + gssapi_ctx_t *ctx = dctx->opaque; + + if (ctx != NULL) { + if (ctx->buffer != NULL) + isc_buffer_free(&ctx->buffer); + isc_mem_put(dctx->mctx, ctx, sizeof(gssapi_ctx_t)); + dctx->opaque = NULL; + } +} + +static isc_result_t +gssapi_adddata(dst_context_t *dctx, const isc_region_t *data) { + gssapi_ctx_t *ctx = dctx->opaque; + isc_buffer_t *newbuffer = NULL; + isc_region_t r; + unsigned int length; + isc_result_t result; + + result = isc_buffer_copyregion(ctx->buffer, data); + if (result == ISC_R_SUCCESS) + return (ISC_R_SUCCESS); + + length = isc_buffer_length(ctx->buffer) + data->length + BUFFER_EXTRA; + + result = isc_buffer_allocate(dctx->mctx, &newbuffer, length); + if (result != ISC_R_SUCCESS) + return (result); + + isc_buffer_usedregion(ctx->buffer, &r); + (void) isc_buffer_copyregion(newbuffer, &r); + (void) isc_buffer_copyregion(newbuffer, data); + + isc_buffer_free(&ctx->buffer); + ctx->buffer = newbuffer; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +gssapi_sign(dst_context_t *dctx, isc_buffer_t *sig) { + gssapi_ctx_t *ctx = dctx->opaque; + isc_region_t message; + gss_buffer_desc gmessage, gsig; + OM_uint32 minor, gret; + + isc_buffer_usedregion(ctx->buffer, &message); + REGION_TO_GBUFFER(message, gmessage); + + gret = gss_get_mic(&minor, ctx->context_id, + GSS_C_QOP_DEFAULT, &gmessage, &gsig); + if (gret != 0) + return (ISC_R_FAILURE); + + if (gsig.length > isc_buffer_availablelength(sig)) { + gss_release_buffer(&minor, &gsig); + return (ISC_R_NOSPACE); + } + + isc_buffer_putmem(sig, gsig.value, gsig.length); + + gss_release_buffer(&minor, &gsig); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +gssapi_verify(dst_context_t *dctx, const isc_region_t *sig) { + gssapi_ctx_t *ctx = dctx->opaque; + isc_region_t message; + gss_buffer_desc gmessage, gsig; + OM_uint32 minor, gret; + + isc_buffer_usedregion(ctx->buffer, &message); + REGION_TO_GBUFFER(message, gmessage); + + REGION_TO_GBUFFER(*sig, gsig); + + gret = gss_verify_mic(&minor, ctx->context_id, &gmessage, &gsig, NULL); + if (gret != 0) + return (ISC_R_FAILURE); + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +gssapi_compare(const dst_key_t *key1, const dst_key_t *key2) { + gss_ctx_id_t gsskey1 = key1->opaque; + gss_ctx_id_t gsskey2 = key2->opaque; + + /* No idea */ + return (ISC_TF(gsskey1 == gsskey2)); +} + +static isc_result_t +gssapi_generate(dst_key_t *key, int unused) { + UNUSED(key); + UNUSED(unused); + + /* No idea */ + return (ISC_R_FAILURE); +} + +static isc_boolean_t +gssapi_isprivate(const dst_key_t *key) { + UNUSED(key); + return (ISC_TRUE); +} + +static void +gssapi_destroy(dst_key_t *key) { + UNUSED(key); + /* No idea */ +} + +static dst_func_t gssapi_functions = { + gssapi_createctx, + gssapi_destroyctx, + gssapi_adddata, + gssapi_sign, + gssapi_verify, + NULL, /* computesecret */ + gssapi_compare, + NULL, /* paramcompare */ + gssapi_generate, + gssapi_isprivate, + gssapi_destroy, + NULL, /* todns */ + NULL, /* fromdns */ + NULL, /* tofile */ + NULL, /* parse */ + NULL, /* cleanup */ +}; + +isc_result_t +dst__gssapi_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &gssapi_functions; + return (ISC_R_SUCCESS); +} + +#else +int gssapi_link_unneeded = 1; +#endif diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c new file mode 100644 index 0000000000..ab8a311567 --- /dev/null +++ b/lib/dns/gssapictx.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: gssapictx.c,v 1.1 2004/12/09 01:41:03 marka Exp $ */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "dst_internal.h" + +#ifdef GSSAPI + +#include + +#define RETERR(x) do { \ + result = (x); \ + if (result != ISC_R_SUCCESS) \ + goto out; \ + } while (0) + +#define REGION_TO_GBUFFER(r, gb) \ + do { \ + (gb).length = (r).length; \ + (gb).value = (r).base; \ + } while (0) + +#define GBUFFER_TO_REGION(gb, r) \ + do { \ + (r).length = (gb).length; \ + (r).base = (gb).value; \ + } while (0) + +static inline void +name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer, + gss_buffer_desc *gbuffer) +{ + dns_name_t tname, *namep; + isc_region_t r; + isc_result_t result; + + if (!dns_name_isabsolute(name)) + namep = name; + else { + unsigned int labels; + dns_name_init(&tname, NULL); + labels = dns_name_countlabels(name); + dns_name_getlabelsequence(name, 0, labels - 1, &tname); + namep = &tname; + } + + result = dns_name_totext(namep, ISC_FALSE, buffer); + isc_buffer_putuint8(buffer, 0); + isc_buffer_usedregion(buffer, &r); + REGION_TO_GBUFFER(r, *gbuffer); +} + +isc_result_t +dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred) { + isc_buffer_t namebuf; + gss_name_t gname; + gss_buffer_desc gnamebuf; + unsigned char array[DNS_NAME_MAXTEXT + 1]; + OM_uint32 gret, minor; + gss_OID_set mechs; + OM_uint32 lifetime; + gss_cred_usage_t usage; + + REQUIRE(cred != NULL && *cred == NULL); + + if (name != NULL) { + isc_buffer_init(&namebuf, array, sizeof(array)); + name_to_gbuffer(name, &namebuf, &gnamebuf); + gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, + &gname); + if (gret != GSS_S_COMPLETE) + return (ISC_R_FAILURE); + } else + gname = NULL; + + if (initiate) + usage = GSS_C_INITIATE; + else + usage = GSS_C_ACCEPT; + + gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE, + GSS_C_NO_OID_SET, usage, + cred, &mechs, &lifetime); + if (gret != GSS_S_COMPLETE) + return (ISC_R_FAILURE); + return (ISC_R_SUCCESS); +} + +isc_result_t +dst_gssapi_initctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context) +{ + isc_region_t r; + isc_buffer_t namebuf; + gss_buffer_desc gnamebuf, gintoken, *gintokenp, gouttoken; + OM_uint32 gret, minor, flags, ret_flags; + gss_OID mech_type, ret_mech_type; + OM_uint32 lifetime; + gss_name_t gname; + isc_result_t result; + unsigned char array[DNS_NAME_MAXTEXT + 1]; + + isc_buffer_init(&namebuf, array, sizeof(array)); + name_to_gbuffer(name, &namebuf, &gnamebuf); + gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); + if (gret != GSS_S_COMPLETE) + return (ISC_R_FAILURE); + + if (intoken != NULL) { + REGION_TO_GBUFFER(*intoken, gintoken); + gintokenp = &gintoken; + } else + gintokenp = NULL; + + if (*context == NULL) + *context = GSS_C_NO_CONTEXT; + flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG | + GSS_C_SEQUENCE_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG; + mech_type = GSS_C_NO_OID; + + gret = gss_init_sec_context(&minor, cred, context, gname, + mech_type, flags, 0, + GSS_C_NO_CHANNEL_BINDINGS, gintokenp, + &ret_mech_type, &gouttoken, &ret_flags, + &lifetime); + if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) + return (ISC_R_FAILURE); + + GBUFFER_TO_REGION(gouttoken, r); + RETERR(isc_buffer_copyregion(outtoken, &r)); + + if (gret == GSS_S_COMPLETE) + return (ISC_R_SUCCESS); + else + return (DNS_R_CONTINUE); + + out: + return (result); +} + +isc_result_t +dst_gssapi_acceptctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context) +{ + isc_region_t r; + isc_buffer_t namebuf; + gss_buffer_desc gnamebuf, gintoken, gouttoken; + OM_uint32 gret, minor, flags; + gss_OID mech_type; + OM_uint32 lifetime; + gss_cred_id_t delegated_cred; + gss_name_t gname; + isc_result_t result; + unsigned char array[DNS_NAME_MAXTEXT + 1]; + + isc_buffer_init(&namebuf, array, sizeof(array)); + name_to_gbuffer(name, &namebuf, &gnamebuf); + gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname); + if (gret != GSS_S_COMPLETE) + return (ISC_R_FAILURE); + + REGION_TO_GBUFFER(*intoken, gintoken); + + if (*context == NULL) + *context = GSS_C_NO_CONTEXT; + + gret = gss_accept_sec_context(&minor, context, cred, &gintoken, + GSS_C_NO_CHANNEL_BINDINGS, gname, + &mech_type, &gouttoken, &flags, + &lifetime, &delegated_cred); + if (gret != GSS_S_COMPLETE) + return (ISC_R_FAILURE); + + GBUFFER_TO_REGION(gouttoken, r); + RETERR(isc_buffer_copyregion(outtoken, &r)); + + return (ISC_R_SUCCESS); + + out: + return (result); +} + +#else + +isc_result_t +dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred) { + UNUSED(name); + UNUSED(initiate); + UNUSED(cred); + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +dst_gssapi_initctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context) +{ + UNUSED(name); + UNUSED(cred); + UNUSED(intoken); + UNUSED(outtoken); + UNUSED(context); + return (ISC_R_NOTIMPLEMENTED); +} + +isc_result_t +dst_gssapi_acceptctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context) +{ + UNUSED(name); + UNUSED(cred); + UNUSED(intoken); + UNUSED(outtoken); + UNUSED(context); + return (ISC_R_NOTIMPLEMENTED); +} + +#endif diff --git a/lib/dns/hmac_link.c b/lib/dns/hmac_link.c new file mode 100644 index 0000000000..38b2e30a42 --- /dev/null +++ b/lib/dns/hmac_link.c @@ -0,0 +1,282 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: hmac_link.c,v 1.1 2004/12/09 01:41:03 marka Exp $ + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_parse.h" + +#define HMAC_LEN 64 +#define HMAC_IPAD 0x36 +#define HMAC_OPAD 0x5c + +static isc_result_t hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data); + +typedef struct hmackey { + unsigned char key[HMAC_LEN]; +} HMAC_Key; + +static isc_result_t +hmacmd5_createctx(dst_key_t *key, dst_context_t *dctx) { + isc_hmacmd5_t *hmacmd5ctx; + HMAC_Key *hkey = key->opaque; + + hmacmd5ctx = isc_mem_get(dctx->mctx, sizeof(isc_hmacmd5_t)); + if (hmacmd5ctx == NULL) + return (ISC_R_NOMEMORY); + isc_hmacmd5_init(hmacmd5ctx, hkey->key, HMAC_LEN); + dctx->opaque = hmacmd5ctx; + return (ISC_R_SUCCESS); +} + +static void +hmacmd5_destroyctx(dst_context_t *dctx) { + isc_hmacmd5_t *hmacmd5ctx = dctx->opaque; + + if (hmacmd5ctx != NULL) { + isc_hmacmd5_invalidate(hmacmd5ctx); + isc_mem_put(dctx->mctx, hmacmd5ctx, sizeof(isc_hmacmd5_t)); + dctx->opaque = NULL; + } +} + +static isc_result_t +hmacmd5_adddata(dst_context_t *dctx, const isc_region_t *data) { + isc_hmacmd5_t *hmacmd5ctx = dctx->opaque; + + isc_hmacmd5_update(hmacmd5ctx, data->base, data->length); + return (ISC_R_SUCCESS); +} + +static isc_result_t +hmacmd5_sign(dst_context_t *dctx, isc_buffer_t *sig) { + isc_hmacmd5_t *hmacmd5ctx = dctx->opaque; + unsigned char *digest; + + if (isc_buffer_availablelength(sig) < ISC_MD5_DIGESTLENGTH) + return (ISC_R_NOSPACE); + digest = isc_buffer_used(sig); + isc_hmacmd5_sign(hmacmd5ctx, digest); + isc_buffer_add(sig, ISC_MD5_DIGESTLENGTH); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +hmacmd5_verify(dst_context_t *dctx, const isc_region_t *sig) { + isc_hmacmd5_t *hmacmd5ctx = dctx->opaque; + + if (sig->length < ISC_MD5_DIGESTLENGTH) + return (DST_R_VERIFYFAILURE); + + if (isc_hmacmd5_verify(hmacmd5ctx, sig->base)) + return (ISC_R_SUCCESS); + else + return (DST_R_VERIFYFAILURE); +} + +static isc_boolean_t +hmacmd5_compare(const dst_key_t *key1, const dst_key_t *key2) { + HMAC_Key *hkey1, *hkey2; + + hkey1 = (HMAC_Key *)key1->opaque; + hkey2 = (HMAC_Key *)key2->opaque; + + if (hkey1 == NULL && hkey2 == NULL) + return (ISC_TRUE); + else if (hkey1 == NULL || hkey2 == NULL) + return (ISC_FALSE); + + if (memcmp(hkey1->key, hkey2->key, HMAC_LEN) == 0) + return (ISC_TRUE); + else + return (ISC_FALSE); +} + +static isc_result_t +hmacmd5_generate(dst_key_t *key, int pseudorandom_ok) { + isc_buffer_t b; + isc_result_t ret; + int bytes; + unsigned char data[HMAC_LEN]; + + bytes = (key->key_size + 7) / 8; + if (bytes > 64) { + bytes = 64; + key->key_size = 512; + } + + memset(data, 0, HMAC_LEN); + ret = dst__entropy_getdata(data, bytes, ISC_TF(pseudorandom_ok != 0)); + + if (ret != ISC_R_SUCCESS) + return (ret); + + isc_buffer_init(&b, data, bytes); + isc_buffer_add(&b, bytes); + ret = hmacmd5_fromdns(key, &b); + memset(data, 0, HMAC_LEN); + + return (ret); +} + +static isc_boolean_t +hmacmd5_isprivate(const dst_key_t *key) { + UNUSED(key); + return (ISC_TRUE); +} + +static void +hmacmd5_destroy(dst_key_t *key) { + HMAC_Key *hkey = key->opaque; + memset(hkey, 0, sizeof(HMAC_Key)); + isc_mem_put(key->mctx, hkey, sizeof(HMAC_Key)); + key->opaque = NULL; +} + +static isc_result_t +hmacmd5_todns(const dst_key_t *key, isc_buffer_t *data) { + HMAC_Key *hkey; + unsigned int bytes; + + REQUIRE(key->opaque != NULL); + + hkey = (HMAC_Key *) key->opaque; + + bytes = (key->key_size + 7) / 8; + if (isc_buffer_availablelength(data) < bytes) + return (ISC_R_NOSPACE); + isc_buffer_putmem(data, hkey->key, bytes); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +hmacmd5_fromdns(dst_key_t *key, isc_buffer_t *data) { + HMAC_Key *hkey; + int keylen; + isc_region_t r; + isc_md5_t md5ctx; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + hkey = (HMAC_Key *) isc_mem_get(key->mctx, sizeof(HMAC_Key)); + if (hkey == NULL) + return (ISC_R_NOMEMORY); + + memset(hkey->key, 0, sizeof(hkey->key)); + + if (r.length > HMAC_LEN) { + isc_md5_init(&md5ctx); + isc_md5_update(&md5ctx, r.base, r.length); + isc_md5_final(&md5ctx, hkey->key); + keylen = ISC_MD5_DIGESTLENGTH; + } + else { + memcpy(hkey->key, r.base, r.length); + keylen = r.length; + } + + key->key_size = keylen * 8; + key->opaque = hkey; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +hmacmd5_tofile(const dst_key_t *key, const char *directory) { + int cnt = 0; + HMAC_Key *hkey; + dst_private_t priv; + int bytes = (key->key_size + 7) / 8; + + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + hkey = (HMAC_Key *) key->opaque; + + priv.elements[cnt].tag = TAG_HMACMD5_KEY; + priv.elements[cnt].length = bytes; + priv.elements[cnt++].data = hkey->key; + + priv.nelements = cnt; + return (dst__privstruct_writefile(key, &priv, directory)); +} + +static isc_result_t +hmacmd5_parse(dst_key_t *key, isc_lex_t *lexer) { + dst_private_t priv; + isc_result_t ret; + isc_buffer_t b; + isc_mem_t *mctx = key->mctx; + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_HMACMD5, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + isc_buffer_init(&b, priv.elements[0].data, priv.elements[0].length); + isc_buffer_add(&b, priv.elements[0].length); + ret = hmacmd5_fromdns(key, &b); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static dst_func_t hmacmd5_functions = { + hmacmd5_createctx, + hmacmd5_destroyctx, + hmacmd5_adddata, + hmacmd5_sign, + hmacmd5_verify, + NULL, /* computesecret */ + hmacmd5_compare, + NULL, /* paramcompare */ + hmacmd5_generate, + hmacmd5_isprivate, + hmacmd5_destroy, + hmacmd5_todns, + hmacmd5_fromdns, + hmacmd5_tofile, + hmacmd5_parse, + NULL, /* cleanup */ +}; + +isc_result_t +dst__hmacmd5_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &hmacmd5_functions; + return (ISC_R_SUCCESS); +} diff --git a/lib/dns/include/dst/.cvsignore b/lib/dns/include/dst/.cvsignore new file mode 100644 index 0000000000..f3c7a7c5da --- /dev/null +++ b/lib/dns/include/dst/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/lib/dns/include/dst/Makefile.in b/lib/dns/include/dst/Makefile.in new file mode 100644 index 0000000000..953199e8b6 --- /dev/null +++ b/lib/dns/include/dst/Makefile.in @@ -0,0 +1,37 @@ +# Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") +# Copyright (C) 1998-2001 Internet Software Consortium. +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. + +# $Id: Makefile.in,v 1.1 2004/12/09 01:41:05 marka Exp $ + +srcdir = @srcdir@ +VPATH = @srcdir@ +top_srcdir = @top_srcdir@ + +@BIND9_VERSION@ + +HEADERS = dst.h lib.h result.h + +SUBDIRS = +TARGETS = + +@BIND9_MAKE_RULES@ + +installdirs: + $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${includedir}/dst + +install:: installdirs + for i in ${HEADERS}; do \ + ${INSTALL_DATA} ${srcdir}/$$i ${DESTDIR}${includedir}/dst ; \ + done diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h new file mode 100644 index 0000000000..7967b3114e --- /dev/null +++ b/lib/dns/include/dst/dst.h @@ -0,0 +1,593 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000-2002 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: dst.h,v 1.1 2004/12/09 01:41:05 marka Exp $ */ + +#ifndef DST_DST_H +#define DST_DST_H 1 + +#include + +#include + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +/* + * The dst_key structure is opaque. Applications should use the accessor + * functions provided to retrieve key attributes. If an application needs + * to set attributes, new accessor functions will be written. + */ + +typedef struct dst_key dst_key_t; +typedef struct dst_context dst_context_t; + +/* DST algorithm codes */ +#define DST_ALG_UNKNOWN 0 +#define DST_ALG_RSAMD5 1 +#define DST_ALG_RSA DST_ALG_RSAMD5 /* backwards compatibility */ +#define DST_ALG_DH 2 +#define DST_ALG_DSA 3 +#define DST_ALG_ECC 4 +#define DST_ALG_RSASHA1 5 +#define DST_ALG_HMACMD5 157 +#define DST_ALG_GSSAPI 160 +#define DST_ALG_PRIVATE 254 +#define DST_ALG_EXPAND 255 +#define DST_MAX_ALGS 255 + +/* A buffer of this size is large enough to hold any key */ +#define DST_KEY_MAXSIZE 1280 + +/* + * A buffer of this size is large enough to hold the textual representation + * of any key + */ +#define DST_KEY_MAXTEXTSIZE 2048 + +/* 'Type' for dst_read_key() */ +#define DST_TYPE_KEY 0x1000000 /* KEY key */ +#define DST_TYPE_PRIVATE 0x2000000 +#define DST_TYPE_PUBLIC 0x4000000 + +/*** + *** Functions + ***/ + +isc_result_t +dst_lib_init(isc_mem_t *mctx, isc_entropy_t *ectx, unsigned int eflags); +/* + * Initializes the DST subsystem. + * + * Requires: + * "mctx" is a valid memory context + * "ectx" is a valid entropy context + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOMEMORY + * + * Ensures: + * DST is properly initialized. + */ + +void +dst_lib_destroy(void); +/* + * Releases all resources allocated by DST. + */ + +isc_boolean_t +dst_algorithm_supported(unsigned int alg); +/* + * Checks that a given algorithm is supported by DST. + * + * Returns: + * ISC_TRUE + * ISC_FALSE + */ + +isc_result_t +dst_context_create(dst_key_t *key, isc_mem_t *mctx, dst_context_t **dctxp); +/* + * Creates a context to be used for a sign or verify operation. + * + * Requires: + * "key" is a valid key. + * "mctx" is a valid memory context. + * dctxp != NULL && *dctxp == NULL + * + * Returns: + * ISC_R_SUCCESS + * ISC_R_NOMEMORY + * + * Ensures: + * *dctxp will contain a usable context. + */ + +void +dst_context_destroy(dst_context_t **dctxp); +/* + * Destroys all memory associated with a context. + * + * Requires: + * *dctxp != NULL && *dctxp == NULL + * + * Ensures: + * *dctxp == NULL + */ + +isc_result_t +dst_context_adddata(dst_context_t *dctx, const isc_region_t *data); +/* + * Incrementally adds data to the context to be used in a sign or verify + * operation. + * + * Requires: + * "dctx" is a valid context + * "data" is a valid region + * + * Returns: + * ISC_R_SUCCESS + * DST_R_SIGNFAILURE + * all other errors indicate failure + */ + +isc_result_t +dst_context_sign(dst_context_t *dctx, isc_buffer_t *sig); +/* + * Computes a signature using the data and key stored in the context. + * + * Requires: + * "dctx" is a valid context. + * "sig" is a valid buffer. + * + * Returns: + * ISC_R_SUCCESS + * DST_R_VERIFYFAILURE + * all other errors indicate failure + * + * Ensures: + * "sig" will contain the signature + */ + +isc_result_t +dst_context_verify(dst_context_t *dctx, isc_region_t *sig); +/* + * Verifies the signature using the data and key stored in the context. + * + * Requires: + * "dctx" is a valid context. + * "sig" is a valid region. + * + * Returns: + * ISC_R_SUCCESS + * all other errors indicate failure + * + * Ensures: + * "sig" will contain the signature + */ + +isc_result_t +dst_key_computesecret(const dst_key_t *pub, const dst_key_t *priv, + isc_buffer_t *secret); +/* + * Computes a shared secret from two (Diffie-Hellman) keys. + * + * Requires: + * "pub" is a valid key that can be used to derive a shared secret + * "priv" is a valid private key that can be used to derive a shared secret + * "secret" is a valid buffer + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, secret will contain the derived shared secret. + */ + +isc_result_t +dst_key_fromfile(dns_name_t *name, dns_keytag_t id, unsigned int alg, int type, + const char *directory, isc_mem_t *mctx, dst_key_t **keyp); +/* + * Reads a key from permanent storage. The key can either be a public or + * private key, and is specified by name, algorithm, and id. If a private key + * is specified, the public key must also be present. If directory is NULL, + * the current directory is assumed. + * + * Requires: + * "name" is a valid absolute dns name. + * "id" is a valid key tag identifier. + * "alg" is a supported key algorithm. + * "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union. + * DST_TYPE_KEY look for a KEY record otherwise DNSKEY + * "mctx" is a valid memory context. + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key. + */ + +isc_result_t +dst_key_fromnamedfile(const char *filename, int type, isc_mem_t *mctx, + dst_key_t **keyp); +/* + * Reads a key from permanent storage. The key can either be a public or + * key, and is specified by filename. If a private key is specified, the + * public key must also be present. + * + * Requires: + * "filename" is not NULL + * "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union + * DST_TYPE_KEY look for a KEY record otherwise DNSKEY + * "mctx" is a valid memory context + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key. + */ + + +isc_result_t +dst_key_read_public(const char *filename, int type, + isc_mem_t *mctx, dst_key_t **keyp); +/* + * Reads a public key from permanent storage. The key must be a public key. + * + * Requires: + * "filename" is not NULL + * "type" is DST_TYPE_KEY look for a KEY record otherwise DNSKEY + * "mctx" is a valid memory context + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * DST_R_BADKEYTYPE if the key type is not the expected one + * ISC_R_UNEXPECTEDTOKEN if the file can not be parsed as a public key + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key. + */ + +isc_result_t +dst_key_tofile(const dst_key_t *key, int type, const char *directory); +/* + * Writes a key to permanent storage. The key can either be a public or + * private key. Public keys are written in DNS format and private keys + * are written as a set of base64 encoded values. If directory is NULL, + * the current directory is assumed. + * + * Requires: + * "key" is a valid key. + * "type" is DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or the bitwise union + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + */ + +isc_result_t +dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp); +/* + * Converts a DNS KEY record into a DST key. + * + * Requires: + * "name" is a valid absolute dns name. + * "source" is a valid buffer. There must be at least 4 bytes available. + * "mctx" is a valid memory context. + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key, and the consumed + * pointer in data will be advanced. + */ + +isc_result_t +dst_key_todns(const dst_key_t *key, isc_buffer_t *target); +/* + * Converts a DST key into a DNS KEY record. + * + * Requires: + * "key" is a valid key. + * "target" is a valid buffer. There must be at least 4 bytes unused. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, the used pointer in 'target' is advanced by at least 4. + */ + +isc_result_t +dst_key_frombuffer(dns_name_t *name, unsigned int alg, + unsigned int flags, unsigned int protocol, + dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp); +/* + * Converts a buffer containing DNS KEY RDATA into a DST key. + * + * Requires: + * "name" is a valid absolute dns name. + * "alg" is a supported key algorithm. + * "source" is a valid buffer. + * "mctx" is a valid memory context. + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key, and the consumed + * pointer in source will be advanced. + */ + +isc_result_t +dst_key_tobuffer(const dst_key_t *key, isc_buffer_t *target); +/* + * Converts a DST key into DNS KEY RDATA format. + * + * Requires: + * "key" is a valid key. + * "target" is a valid buffer. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, the used pointer in 'target' is advanced. + */ + +isc_result_t +dst_key_privatefrombuffer(dst_key_t *key, isc_buffer_t *buffer); +/* + * Converts a public key into a private key, reading the private key + * information from the buffer. The buffer should contain the same data + * as the .private key file would. + * + * Requires: + * "key" is a valid public key. + * "buffer" is not NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, key will contain a valid private key. + */ + + +isc_result_t +dst_key_fromgssapi(dns_name_t *name, void *opaque, isc_mem_t *mctx, + dst_key_t **keyp); +/* + * Converts a GSSAPI opaque context id into a DST key. + * + * Requires: + * "name" is a valid absolute dns name. + * "opaque" is a GSSAPI context id. + * "mctx" is a valid memory context. + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key and be responsible for + * the context id. + */ + +isc_result_t +dst_key_generate(dns_name_t *name, unsigned int alg, + unsigned int bits, unsigned int param, + unsigned int flags, unsigned int protocol, + dns_rdataclass_t rdclass, + isc_mem_t *mctx, dst_key_t **keyp); +/* + * Generate a DST key (or keypair) with the supplied parameters. The + * interpretation of the "param" field depends on the algorithm: + * RSA: exponent + * 0 use exponent 3 + * !0 use Fermat4 (2^16 + 1) + * DH: generator + * 0 default - use well known prime if bits == 768 or 1024, + * otherwise use 2 as the generator. + * !0 use this value as the generator. + * DSA: unused + * HMACMD5: entropy + * 0 default - require good entropy + * !0 lack of good entropy is ok + * + * Requires: + * "name" is a valid absolute dns name. + * "keyp" is not NULL and "*keyp" is NULL. + * + * Returns: + * ISC_R_SUCCESS + * any other result indicates failure + * + * Ensures: + * If successful, *keyp will contain a valid key. + */ + +isc_boolean_t +dst_key_compare(const dst_key_t *key1, const dst_key_t *key2); +/* + * Compares two DST keys. + * + * Requires: + * "key1" is a valid key. + * "key2" is a valid key. + * + * Returns: + * ISC_TRUE + * ISC_FALSE + */ + +isc_boolean_t +dst_key_paramcompare(const dst_key_t *key1, const dst_key_t *key2); +/* + * Compares the parameters of two DST keys. This is used to determine if + * two (Diffie-Hellman) keys can be used to derive a shared secret. + * + * Requires: + * "key1" is a valid key. + * "key2" is a valid key. + * + * Returns: + * ISC_TRUE + * ISC_FALSE + */ + +void +dst_key_free(dst_key_t **keyp); +/* + * Release all memory associated with the key. + * + * Requires: + * "keyp" is not NULL and "*keyp" is a valid key. + * + * Ensures: + * All memory associated with "*keyp" will be freed. + * *keyp == NULL + */ + +/* + * Accessor functions to obtain key fields. + * + * Require: + * "key" is a valid key. + */ +dns_name_t * +dst_key_name(const dst_key_t *key); + +unsigned int +dst_key_size(const dst_key_t *key); + +unsigned int +dst_key_proto(const dst_key_t *key); + +unsigned int +dst_key_alg(const dst_key_t *key); + +isc_uint32_t +dst_key_flags(const dst_key_t *key); + +dns_keytag_t +dst_key_id(const dst_key_t *key); + +dns_rdataclass_t +dst_key_class(const dst_key_t *key); + +isc_boolean_t +dst_key_isprivate(const dst_key_t *key); + +isc_boolean_t +dst_key_iszonekey(const dst_key_t *key); + +isc_boolean_t +dst_key_isnullkey(const dst_key_t *key); + +isc_result_t +dst_key_buildfilename(const dst_key_t *key, int type, + const char *directory, isc_buffer_t *out); +/* + * Generates the filename used by dst to store the specified key. + * If directory is NULL, the current directory is assumed. + * + * Requires: + * "key" is a valid key + * "type" is either DST_TYPE_PUBLIC, DST_TYPE_PRIVATE, or 0 for no suffix. + * "out" is a valid buffer + * + * Ensures: + * the file name will be written to "out", and the used pointer will + * be advanced. + */ + +isc_result_t +dst_key_sigsize(const dst_key_t *key, unsigned int *n); +/* + * Computes the size of a signature generated by the given key. + * + * Requires: + * "key" is a valid key. + * "n" is not NULL + * + * Returns: + * ISC_R_SUCCESS + * DST_R_UNSUPPORTEDALG + * + * Ensures: + * "n" stores the size of a generated signature + */ + +isc_result_t +dst_key_secretsize(const dst_key_t *key, unsigned int *n); +/* + * Computes the size of a shared secret generated by the given key. + * + * Requires: + * "key" is a valid key. + * "n" is not NULL + * + * Returns: + * ISC_R_SUCCESS + * DST_R_UNSUPPORTEDALG + * + * Ensures: + * "n" stores the size of a generated shared secret + */ + +isc_uint16_t +dst_region_computeid(const isc_region_t *source, unsigned int alg); +/* + * Computes the key id of the key stored in the provided region with the + * given algorithm. + * + * Requires: + * "source" contains a valid, non-NULL region. + * + * Returns: + * the key id + */ + +ISC_LANG_ENDDECLS + +#endif /* DST_DST_H */ diff --git a/lib/dns/include/dst/gssapi.h b/lib/dns/include/dst/gssapi.h new file mode 100644 index 0000000000..db46a2471c --- /dev/null +++ b/lib/dns/include/dst/gssapi.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000, 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: gssapi.h,v 1.1 2004/12/09 01:41:06 marka Exp $ */ + +#ifndef DST_GSSAPI_H +#define DST_GSSAPI_H 1 + +#include + +#include + +ISC_LANG_BEGINDECLS + +/*** + *** Types + ***/ + +/*** + *** Functions + ***/ + +isc_result_t +dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate, void **cred); + +isc_result_t +dst_gssapi_initctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context); + +isc_result_t +dst_gssapi_acceptctx(dns_name_t *name, void *cred, + isc_region_t *intoken, isc_buffer_t *outtoken, + void **context); + +/* + * XXX + */ + +ISC_LANG_ENDDECLS + +#endif /* DST_GSSAPI_H */ diff --git a/lib/dns/include/dst/lib.h b/lib/dns/include/dst/lib.h new file mode 100644 index 0000000000..f5addc1a7f --- /dev/null +++ b/lib/dns/include/dst/lib.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: lib.h,v 1.1 2004/12/09 01:41:06 marka Exp $ */ + +#ifndef DST_LIB_H +#define DST_LIB_H 1 + +#include +#include + +ISC_LANG_BEGINDECLS + +LIBDNS_EXTERNAL_DATA extern isc_msgcat_t *dst_msgcat; + +void +dst_lib_initmsgcat(void); +/* + * Initialize the DST library's message catalog, dst_msgcat, if it + * has not already been initialized. + */ + +ISC_LANG_ENDDECLS + +#endif /* DST_LIB_H */ diff --git a/lib/dns/include/dst/result.h b/lib/dns/include/dst/result.h new file mode 100644 index 0000000000..3ea2f84192 --- /dev/null +++ b/lib/dns/include/dst/result.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 1999-2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: result.h,v 1.1 2004/12/09 01:41:06 marka Exp $ */ + +#ifndef DST_RESULT_H +#define DST_RESULT_H 1 + +#include +#include + +/* + * Nothing in this file truly depends on , but the + * DST result codes are considered to be publicly derived from + * the ISC result codes, so including this file buys you the ISC_R_ + * namespace too. + */ +#include /* Contractual promise. */ + +#define DST_R_UNSUPPORTEDALG (ISC_RESULTCLASS_DST + 0) +#define DST_R_OPENSSLFAILURE (ISC_RESULTCLASS_DST + 1) +#define DST_R_NOCRYPTO (ISC_RESULTCLASS_DST + 2) +#define DST_R_NULLKEY (ISC_RESULTCLASS_DST + 3) +#define DST_R_INVALIDPUBLICKEY (ISC_RESULTCLASS_DST + 4) +#define DST_R_INVALIDPRIVATEKEY (ISC_RESULTCLASS_DST + 5) +/* 6 is unused */ +#define DST_R_WRITEERROR (ISC_RESULTCLASS_DST + 7) +#define DST_R_INVALIDPARAM (ISC_RESULTCLASS_DST + 8) +/* 9 is unused */ +/* 10 is unused */ +#define DST_R_SIGNFAILURE (ISC_RESULTCLASS_DST + 11) +/* 12 is unused */ +/* 13 is unused */ +#define DST_R_VERIFYFAILURE (ISC_RESULTCLASS_DST + 14) +#define DST_R_NOTPUBLICKEY (ISC_RESULTCLASS_DST + 15) +#define DST_R_NOTPRIVATEKEY (ISC_RESULTCLASS_DST + 16) +#define DST_R_KEYCANNOTCOMPUTESECRET (ISC_RESULTCLASS_DST + 17) +#define DST_R_COMPUTESECRETFAILURE (ISC_RESULTCLASS_DST + 18) +#define DST_R_NORANDOMNESS (ISC_RESULTCLASS_DST + 19) +#define DST_R_BADKEYTYPE (ISC_RESULTCLASS_DST + 20) + +#define DST_R_NRESULTS 21 /* Number of results */ + +ISC_LANG_BEGINDECLS + +const char * +dst_result_totext(isc_result_t); + +void +dst_result_register(void); + +ISC_LANG_ENDDECLS + +#endif /* DST_RESULT_H */ diff --git a/lib/dns/key.c b/lib/dns/key.c new file mode 100644 index 0000000000..3f218bba7c --- /dev/null +++ b/lib/dns/key.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2001 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: key.c,v 1.1 2004/12/09 01:41:03 marka Exp $ */ + +#include + +#include + +#include +#include + +#include + +#include + +#include "dst_internal.h" + +isc_uint16_t +dst_region_computeid(const isc_region_t *source, unsigned int alg) { + isc_uint32_t ac; + const unsigned char *p; + int size; + + REQUIRE(source != NULL); + REQUIRE(source->length >= 4); + + p = source->base; + size = source->length; + + if (alg == DST_ALG_RSAMD5) + return ((p[size - 3] << 8) + p[size - 2]); + + for (ac = 0; size > 1; size -= 2, p += 2) + ac += ((*p) << 8) + *(p + 1); + + if (size > 0) + ac += ((*p) << 8); + ac += (ac >> 16) & 0xffff; + + return ((isc_uint16_t)(ac & 0xffff)); +} + +dns_name_t * +dst_key_name(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_name); +} + +unsigned int +dst_key_size(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_size); +} + +unsigned int +dst_key_proto(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_proto); +} + +unsigned int +dst_key_alg(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_alg); +} + +isc_uint32_t +dst_key_flags(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_flags); +} + +dns_keytag_t +dst_key_id(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_id); +} + +dns_rdataclass_t +dst_key_class(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + return (key->key_class); +} + +isc_boolean_t +dst_key_iszonekey(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + + if ((key->key_flags & DNS_KEYTYPE_NOAUTH) != 0) + return (ISC_FALSE); + if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) + return (ISC_FALSE); + if (key->key_proto != DNS_KEYPROTO_DNSSEC && + key->key_proto != DNS_KEYPROTO_ANY) + return (ISC_FALSE); + return (ISC_TRUE); +} + +isc_boolean_t +dst_key_isnullkey(const dst_key_t *key) { + REQUIRE(VALID_KEY(key)); + + if ((key->key_flags & DNS_KEYFLAG_TYPEMASK) != DNS_KEYTYPE_NOKEY) + return (ISC_FALSE); + if ((key->key_flags & DNS_KEYFLAG_OWNERMASK) != DNS_KEYOWNER_ZONE) + return (ISC_FALSE); + if (key->key_proto != DNS_KEYPROTO_DNSSEC && + key->key_proto != DNS_KEYPROTO_ANY) + return (ISC_FALSE); + return (ISC_TRUE); +} diff --git a/lib/dns/openssl_link.c b/lib/dns/openssl_link.c new file mode 100644 index 0000000000..10f94ebb28 --- /dev/null +++ b/lib/dns/openssl_link.c @@ -0,0 +1,219 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2003 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: openssl_link.c,v 1.1 2004/12/09 01:41:03 marka Exp $ + */ +#ifdef OPENSSL + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "dst_internal.h" +#include "dst_openssl.h" + +#include +#include +#include + +#if defined(CRYPTO_LOCK_ENGINE) && (OPENSSL_VERSION_NUMBER < 0x00907000L) +#define USE_ENGINE 1 +#endif + +#ifdef USE_ENGINE +#include +#endif + +static RAND_METHOD *rm = NULL; +static isc_mutex_t *locks = NULL; +static int nlocks; + +#ifdef USE_ENGINE +static ENGINE *e; +#endif + + +static int +entropy_get(unsigned char *buf, int num) { + isc_result_t result; + if (num < 0) + return (-1); + result = dst__entropy_getdata(buf, (unsigned int) num, ISC_FALSE); + return (result == ISC_R_SUCCESS ? num : -1); +} + +static int +entropy_getpseudo(unsigned char *buf, int num) { + isc_result_t result; + if (num < 0) + return (-1); + result = dst__entropy_getdata(buf, (unsigned int) num, ISC_TRUE); + return (result == ISC_R_SUCCESS ? num : -1); +} + +static void +entropy_add(const void *buf, int num, double entropy) { + /* + * Do nothing. The only call to this provides no useful data anyway. + */ + UNUSED(buf); + UNUSED(num); + UNUSED(entropy); +} + +static void +lock_callback(int mode, int type, const char *file, int line) { + UNUSED(file); + UNUSED(line); + if ((mode & CRYPTO_LOCK) != 0) + LOCK(&locks[type]); + else + UNLOCK(&locks[type]); +} + +static unsigned long +id_callback(void) { + return ((unsigned long)isc_thread_self()); +} + +static void * +mem_alloc(size_t size) { + INSIST(dst__memory_pool != NULL); + return (isc_mem_allocate(dst__memory_pool, size)); +} + +static void +mem_free(void *ptr) { + INSIST(dst__memory_pool != NULL); + if (ptr != NULL) + isc_mem_free(dst__memory_pool, ptr); +} + +static void * +mem_realloc(void *ptr, size_t size) { + void *p; + + INSIST(dst__memory_pool != NULL); + p = NULL; + if (size > 0U) { + p = mem_alloc(size); + if (p != NULL && ptr != NULL) + memcpy(p, ptr, size); + } + if (ptr != NULL) + mem_free(ptr); + return (p); +} + +isc_result_t +dst__openssl_init() { + isc_result_t result; + + CRYPTO_set_mem_functions(mem_alloc, mem_realloc, mem_free); + nlocks = CRYPTO_num_locks(); + locks = mem_alloc(sizeof(isc_mutex_t) * nlocks); + if (locks == NULL) + return (ISC_R_NOMEMORY); + result = isc_mutexblock_init(locks, nlocks); + if (result != ISC_R_SUCCESS) + goto cleanup_mutexalloc; + CRYPTO_set_locking_callback(lock_callback); + CRYPTO_set_id_callback(id_callback); + rm = mem_alloc(sizeof(RAND_METHOD)); + if (rm == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_mutexinit; + } + rm->seed = NULL; + rm->bytes = entropy_get; + rm->cleanup = NULL; + rm->add = entropy_add; + rm->pseudorand = entropy_getpseudo; + rm->status = NULL; +#ifdef USE_ENGINE + e = ENGINE_new(); + if (e == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_rm; + } + ENGINE_set_RAND(e, rm); + RAND_set_rand_method(e); +#else + RAND_set_rand_method(rm); +#endif + return (ISC_R_SUCCESS); + +#ifdef USE_ENGINE + cleanup_rm: + mem_free(rm); +#endif + cleanup_mutexinit: + DESTROYMUTEXBLOCK(locks, nlocks); + cleanup_mutexalloc: + mem_free(locks); + return (result); +} + +void +dst__openssl_destroy() { + ERR_clear_error(); +#ifdef USE_ENGINE + if (e != NULL) { + ENGINE_free(e); + e = NULL; + } +#endif + if (locks != NULL) { + DESTROYMUTEXBLOCK(locks, nlocks); + mem_free(locks); + } + if (rm != NULL) + mem_free(rm); +} + +isc_result_t +dst__openssl_toresult(isc_result_t fallback) { + isc_result_t result = fallback; + int err = ERR_get_error(); + + switch (ERR_GET_REASON(err)) { + case ERR_R_MALLOC_FAILURE: + result = ISC_R_NOMEMORY; + break; + default: + break; + } + ERR_clear_error(); + return (result); +} + +#else /* OPENSSL */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* OPENSSL */ diff --git a/lib/dns/openssldh_link.c b/lib/dns/openssldh_link.c new file mode 100644 index 0000000000..6aca209c0b --- /dev/null +++ b/lib/dns/openssldh_link.c @@ -0,0 +1,608 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: openssldh_link.c,v 1.1 2004/12/09 01:41:03 marka Exp $ + */ + +#ifdef OPENSSL + +#include + +#include + +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_openssl.h" +#include "dst_parse.h" + +#include + +#define PRIME768 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088" \ + "A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25" \ + "F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A3620FFFFFFFFFFFFFFFF" + +#define PRIME1024 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08" \ + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF2" \ + "5F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406" \ + "B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF" + +#define PRIME1536 "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D" \ + "C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F" \ + "83655D23DCA3AD961C62F356208552BB9ED529077096966D" \ + "670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF" + + +static isc_result_t openssldh_todns(const dst_key_t *key, isc_buffer_t *data); + +static BIGNUM bn2, bn768, bn1024, bn1536; + +static isc_result_t +openssldh_computesecret(const dst_key_t *pub, const dst_key_t *priv, + isc_buffer_t *secret) +{ + DH *dhpub, *dhpriv; + int ret; + isc_region_t r; + unsigned int len; + + REQUIRE(pub->opaque != NULL); + REQUIRE(priv->opaque != NULL); + + dhpub = (DH *) pub->opaque; + dhpriv = (DH *) priv->opaque; + + len = DH_size(dhpriv); + isc_buffer_availableregion(secret, &r); + if (r.length < len) + return (ISC_R_NOSPACE); + ret = DH_compute_key(r.base, dhpub->pub_key, dhpriv); + if (ret == 0) + return (dst__openssl_toresult(DST_R_COMPUTESECRETFAILURE)); + isc_buffer_add(secret, len); + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldh_compare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + DH *dh1, *dh2; + + dh1 = (DH *) key1->opaque; + dh2 = (DH *) key2->opaque; + + if (dh1 == NULL && dh2 == NULL) + return (ISC_TRUE); + else if (dh1 == NULL || dh2 == NULL) + return (ISC_FALSE); + + status = BN_cmp(dh1->p, dh2->p) || + BN_cmp(dh1->g, dh2->g) || + BN_cmp(dh1->pub_key, dh2->pub_key); + + if (status != 0) + return (ISC_FALSE); + + if (dh1->priv_key != NULL || dh2->priv_key != NULL) { + if (dh1->priv_key == NULL || dh2->priv_key == NULL) + return (ISC_FALSE); + if (BN_cmp(dh1->priv_key, dh2->priv_key) != 0) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + +static isc_boolean_t +openssldh_paramcompare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + DH *dh1, *dh2; + + dh1 = (DH *) key1->opaque; + dh2 = (DH *) key2->opaque; + + if (dh1 == NULL && dh2 == NULL) + return (ISC_TRUE); + else if (dh1 == NULL || dh2 == NULL) + return (ISC_FALSE); + + status = BN_cmp(dh1->p, dh2->p) || + BN_cmp(dh1->g, dh2->g); + + if (status != 0) + return (ISC_FALSE); + return (ISC_TRUE); +} + +static isc_result_t +openssldh_generate(dst_key_t *key, int generator) { + DH *dh = NULL; + + if (generator == 0) { + if (key->key_size == 768 || + key->key_size == 1024 || + key->key_size == 1536) + { + dh = DH_new(); + if (dh == NULL) + return (ISC_R_NOMEMORY); + if (key->key_size == 768) + dh->p = &bn768; + else if (key->key_size == 1024) + dh->p = &bn1024; + else + dh->p = &bn1536; + dh->g = &bn2; + } + else + generator = 2; + } + + if (generator != 0) + dh = DH_generate_parameters(key->key_size, generator, + NULL, NULL); + + if (dh == NULL) + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + + if (DH_generate_key(dh) == 0) { + DH_free(dh); + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + dh->flags &= ~DH_FLAG_CACHE_MONT_P; + + key->opaque = dh; + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldh_isprivate(const dst_key_t *key) { + DH *dh = (DH *) key->opaque; + return (ISC_TF(dh != NULL && dh->priv_key != NULL)); +} + +static void +openssldh_destroy(dst_key_t *key) { + DH *dh = key->opaque; + + if (dh == NULL) + return; + + if (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536) + dh->p = NULL; + if (dh->g == &bn2) + dh->g = NULL; + DH_free(dh); + key->opaque = NULL; +} + +static void +uint16_toregion(isc_uint16_t val, isc_region_t *region) { + *region->base++ = (val & 0xff00) >> 8; + *region->base++ = (val & 0x00ff); +} + +static isc_uint16_t +uint16_fromregion(isc_region_t *region) { + isc_uint16_t val; + unsigned char *cp = region->base; + + val = ((unsigned int)(cp[0])) << 8; + val |= ((unsigned int)(cp[1])); + + region->base += 2; + return (val); +} + +static isc_result_t +openssldh_todns(const dst_key_t *key, isc_buffer_t *data) { + DH *dh; + isc_region_t r; + isc_uint16_t dnslen, plen, glen, publen; + + REQUIRE(key->opaque != NULL); + + dh = (DH *) key->opaque; + + isc_buffer_availableregion(data, &r); + + if (dh->g == &bn2 && + (dh->p == &bn768 || dh->p == &bn1024 || dh->p == &bn1536)) { + plen = 1; + glen = 0; + } + else { + plen = BN_num_bytes(dh->p); + glen = BN_num_bytes(dh->g); + } + publen = BN_num_bytes(dh->pub_key); + dnslen = plen + glen + publen + 6; + if (r.length < (unsigned int) dnslen) + return (ISC_R_NOSPACE); + + uint16_toregion(plen, &r); + if (plen == 1) { + if (dh->p == &bn768) + *r.base = 1; + else if (dh->p == &bn1024) + *r.base = 2; + else + *r.base = 3; + } + else + BN_bn2bin(dh->p, r.base); + r.base += plen; + + uint16_toregion(glen, &r); + if (glen > 0) + BN_bn2bin(dh->g, r.base); + r.base += glen; + + uint16_toregion(publen, &r); + BN_bn2bin(dh->pub_key, r.base); + r.base += publen; + + isc_buffer_add(data, dnslen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldh_fromdns(dst_key_t *key, isc_buffer_t *data) { + DH *dh; + isc_region_t r; + isc_uint16_t plen, glen, publen; + int special = 0; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + dh = DH_new(); + if (dh == NULL) + return (ISC_R_NOMEMORY); + dh->flags &= ~DH_FLAG_CACHE_MONT_P; + + /* + * Read the prime length. 1 & 2 are table entries, > 16 means a + * prime follows, otherwise an error. + */ + if (r.length < 2) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + plen = uint16_fromregion(&r); + if (plen < 16 && plen != 1 && plen != 2) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + if (r.length < plen) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + if (plen == 1 || plen == 2) { + if (plen == 1) + special = *r.base++; + else + special = uint16_fromregion(&r); + switch (special) { + case 1: + dh->p = &bn768; + break; + case 2: + dh->p = &bn1024; + break; + case 3: + dh->p = &bn1536; + break; + default: + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + } + else { + dh->p = BN_bin2bn(r.base, plen, NULL); + r.base += plen; + } + + /* + * Read the generator length. This should be 0 if the prime was + * special, but it might not be. If it's 0 and the prime is not + * special, we have a problem. + */ + if (r.length < 2) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + glen = uint16_fromregion(&r); + if (r.length < glen) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + if (special != 0) { + if (glen == 0) + dh->g = &bn2; + else { + dh->g = BN_bin2bn(r.base, glen, NULL); + if (BN_cmp(dh->g, &bn2) == 0) { + BN_free(dh->g); + dh->g = &bn2; + } + else { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + } + } + else { + if (glen == 0) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + dh->g = BN_bin2bn(r.base, glen, NULL); + } + r.base += glen; + + if (r.length < 2) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + publen = uint16_fromregion(&r); + if (r.length < publen) { + DH_free(dh); + return (DST_R_INVALIDPUBLICKEY); + } + dh->pub_key = BN_bin2bn(r.base, publen, NULL); + r.base += publen; + + key->key_size = BN_num_bits(dh->p); + + isc_buffer_forward(data, plen + glen + publen + 6); + + key->opaque = (void *) dh; + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldh_tofile(const dst_key_t *key, const char *directory) { + int i; + DH *dh; + dst_private_t priv; + unsigned char *bufs[4]; + isc_result_t result; + + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + dh = (DH *) key->opaque; + + for (i = 0; i < 4; i++) { + bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(dh->p)); + if (bufs[i] == NULL) { + result = ISC_R_NOMEMORY; + goto fail; + } + } + + i = 0; + + priv.elements[i].tag = TAG_DH_PRIME; + priv.elements[i].length = BN_num_bytes(dh->p); + BN_bn2bin(dh->p, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_GENERATOR; + priv.elements[i].length = BN_num_bytes(dh->g); + BN_bn2bin(dh->g, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_PRIVATE; + priv.elements[i].length = BN_num_bytes(dh->priv_key); + BN_bn2bin(dh->priv_key, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_DH_PUBLIC; + priv.elements[i].length = BN_num_bytes(dh->pub_key); + BN_bn2bin(dh->pub_key, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.nelements = i; + result = dst__privstruct_writefile(key, &priv, directory); + fail: + for (i = 0; i < 4; i++) { + if (bufs[i] == NULL) + break; + isc_mem_put(key->mctx, bufs[i], BN_num_bytes(dh->p)); + } + return (result); +} + +static isc_result_t +openssldh_parse(dst_key_t *key, isc_lex_t *lexer) { + dst_private_t priv; + isc_result_t ret; + int i; + DH *dh = NULL; + isc_mem_t *mctx; +#define DST_RET(a) {ret = a; goto err;} + + mctx = key->mctx; + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_DH, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + dh = DH_new(); + if (dh == NULL) + DST_RET(ISC_R_NOMEMORY); + dh->flags &= ~DH_FLAG_CACHE_MONT_P; + key->opaque = dh; + + for (i = 0; i < priv.nelements; i++) { + BIGNUM *bn; + bn = BN_bin2bn(priv.elements[i].data, + priv.elements[i].length, NULL); + if (bn == NULL) + DST_RET(ISC_R_NOMEMORY); + + switch (priv.elements[i].tag) { + case TAG_DH_PRIME: + dh->p = bn; + break; + case TAG_DH_GENERATOR: + dh->g = bn; + break; + case TAG_DH_PRIVATE: + dh->priv_key = bn; + break; + case TAG_DH_PUBLIC: + dh->pub_key = bn; + break; + } + } + dst__privstruct_free(&priv, mctx); + + key->key_size = BN_num_bits(dh->p); + + if ((key->key_size == 768 || + key->key_size == 1024 || + key->key_size == 1536) && + BN_cmp(dh->g, &bn2) == 0) + { + if (key->key_size == 768 && BN_cmp(dh->p, &bn768) == 0) { + BN_free(dh->p); + BN_free(dh->g); + dh->p = &bn768; + dh->g = &bn2; + } else if (key->key_size == 1024 && + BN_cmp(dh->p, &bn1024) == 0) { + BN_free(dh->p); + BN_free(dh->g); + dh->p = &bn1024; + dh->g = &bn2; + } else if (key->key_size == 1536 && + BN_cmp(dh->p, &bn1536) == 0) { + BN_free(dh->p); + BN_free(dh->g); + dh->p = &bn1536; + dh->g = &bn2; + } + } + + return (ISC_R_SUCCESS); + + err: + openssldh_destroy(key); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static void +BN_fromhex(BIGNUM *b, const char *str) { + static const char hexdigits[] = "0123456789abcdef"; + unsigned char data[512]; + unsigned int i; + BIGNUM *out; + + RUNTIME_CHECK(strlen(str) < 1024U && strlen(str) % 2 == 0U); + for (i = 0; i < strlen(str); i += 2) { + char *s; + unsigned int high, low; + + s = strchr(hexdigits, tolower((unsigned char)str[i])); + RUNTIME_CHECK(s != NULL); + high = s - hexdigits; + + s = strchr(hexdigits, tolower((unsigned char)str[i + 1])); + RUNTIME_CHECK(s != NULL); + low = s - hexdigits; + + data[i/2] = (unsigned char)((high << 4) + low); + } + out = BN_bin2bn(data, strlen(str)/2, b); + RUNTIME_CHECK(out != NULL); +} + +static void +openssldh_cleanup(void) { + BN_free(&bn2); + BN_free(&bn768); + BN_free(&bn1024); + BN_free(&bn1536); +} + +static dst_func_t openssldh_functions = { + NULL, /* createctx */ + NULL, /* destroyctx */ + NULL, /* adddata */ + NULL, /* openssldh_sign */ + NULL, /* openssldh_verify */ + openssldh_computesecret, + openssldh_compare, + openssldh_paramcompare, + openssldh_generate, + openssldh_isprivate, + openssldh_destroy, + openssldh_todns, + openssldh_fromdns, + openssldh_tofile, + openssldh_parse, + openssldh_cleanup, +}; + +isc_result_t +dst__openssldh_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) { + BN_init(&bn2); + BN_init(&bn768); + BN_init(&bn1024); + BN_init(&bn1536); + BN_set_word(&bn2, 2); + BN_fromhex(&bn768, PRIME768); + BN_fromhex(&bn1024, PRIME1024); + BN_fromhex(&bn1536, PRIME1536); + *funcp = &openssldh_functions; + } + return (ISC_R_SUCCESS); +} + +#else /* OPENSSL */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* OPENSSL */ diff --git a/lib/dns/openssldsa_link.c b/lib/dns/openssldsa_link.c new file mode 100644 index 0000000000..1079d329f2 --- /dev/null +++ b/lib/dns/openssldsa_link.c @@ -0,0 +1,443 @@ +/* + * Portions Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Portions Copyright (C) 1999-2002 Internet Software Consortium. + * Portions Copyright (C) 1995-2000 by Network Associates, Inc. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* $Id: openssldsa_link.c,v 1.1 2004/12/09 01:41:04 marka Exp $ */ + +#ifdef OPENSSL + +#include + +#include + +#include +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_openssl.h" +#include "dst_parse.h" + +#include + +static isc_result_t openssldsa_todns(const dst_key_t *key, isc_buffer_t *data); + +static isc_result_t +openssldsa_createctx(dst_key_t *key, dst_context_t *dctx) { + isc_sha1_t *sha1ctx; + + UNUSED(key); + + sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t)); + isc_sha1_init(sha1ctx); + dctx->opaque = sha1ctx; + return (ISC_R_SUCCESS); +} + +static void +openssldsa_destroyctx(dst_context_t *dctx) { + isc_sha1_t *sha1ctx = dctx->opaque; + + if (sha1ctx != NULL) { + isc_sha1_invalidate(sha1ctx); + isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t)); + dctx->opaque = NULL; + } +} + +static isc_result_t +openssldsa_adddata(dst_context_t *dctx, const isc_region_t *data) { + isc_sha1_t *sha1ctx = dctx->opaque; + + isc_sha1_update(sha1ctx, data->base, data->length); + return (ISC_R_SUCCESS); +} + +static int +BN_bn2bin_fixed(BIGNUM *bn, unsigned char *buf, int size) { + int bytes = size - BN_num_bytes(bn); + while (bytes-- > 0) + *buf++ = 0; + BN_bn2bin(bn, buf); + return (size); +} + +static isc_result_t +openssldsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + isc_sha1_t *sha1ctx = dctx->opaque; + dst_key_t *key = dctx->key; + DSA *dsa = key->opaque; + DSA_SIG *dsasig; + isc_region_t r; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + + isc_buffer_availableregion(sig, &r); + if (r.length < ISC_SHA1_DIGESTLENGTH * 2 + 1) + return (ISC_R_NOSPACE); + + isc_sha1_final(sha1ctx, digest); + + dsasig = DSA_do_sign(digest, ISC_SHA1_DIGESTLENGTH, dsa); + if (dsasig == NULL) + return (dst__openssl_toresult(DST_R_SIGNFAILURE)); + + *r.base++ = (key->key_size - 512)/64; + BN_bn2bin_fixed(dsasig->r, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + BN_bn2bin_fixed(dsasig->s, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + DSA_SIG_free(dsasig); + isc_buffer_add(sig, ISC_SHA1_DIGESTLENGTH * 2 + 1); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + isc_sha1_t *sha1ctx = dctx->opaque; + dst_key_t *key = dctx->key; + DSA *dsa = key->opaque; + DSA_SIG *dsasig; + int status = 0; + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + unsigned char *cp = sig->base; + + isc_sha1_final(sha1ctx, digest); + + if (sig->length < 2 * ISC_SHA1_DIGESTLENGTH + 1) + return (DST_R_VERIFYFAILURE); + + cp++; /* Skip T */ + dsasig = DSA_SIG_new(); + dsasig->r = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); + cp += ISC_SHA1_DIGESTLENGTH; + dsasig->s = BN_bin2bn(cp, ISC_SHA1_DIGESTLENGTH, NULL); + cp += ISC_SHA1_DIGESTLENGTH; + + status = DSA_do_verify(digest, ISC_SHA1_DIGESTLENGTH, dsasig, dsa); + DSA_SIG_free(dsasig); + if (status == 0) + return (dst__openssl_toresult(DST_R_VERIFYFAILURE)); + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + DSA *dsa1, *dsa2; + + dsa1 = (DSA *) key1->opaque; + dsa2 = (DSA *) key2->opaque; + + if (dsa1 == NULL && dsa2 == NULL) + return (ISC_TRUE); + else if (dsa1 == NULL || dsa2 == NULL) + return (ISC_FALSE); + + status = BN_cmp(dsa1->p, dsa2->p) || + BN_cmp(dsa1->q, dsa2->q) || + BN_cmp(dsa1->g, dsa2->g) || + BN_cmp(dsa1->pub_key, dsa2->pub_key); + + if (status != 0) + return (ISC_FALSE); + + if (dsa1->priv_key != NULL || dsa2->priv_key != NULL) { + if (dsa1->priv_key == NULL || dsa2->priv_key == NULL) + return (ISC_FALSE); + if (BN_cmp(dsa1->priv_key, dsa2->priv_key)) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + +static isc_result_t +openssldsa_generate(dst_key_t *key, int unused) { + DSA *dsa; + unsigned char rand_array[ISC_SHA1_DIGESTLENGTH]; + isc_result_t result; + + UNUSED(unused); + + result = dst__entropy_getdata(rand_array, sizeof(rand_array), + ISC_FALSE); + if (result != ISC_R_SUCCESS) + return (result); + + dsa = DSA_generate_parameters(key->key_size, rand_array, + ISC_SHA1_DIGESTLENGTH, NULL, NULL, + NULL, NULL); + + if (dsa == NULL) + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + + if (DSA_generate_key(dsa) == 0) { + DSA_free(dsa); + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + + key->opaque = dsa; + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +openssldsa_isprivate(const dst_key_t *key) { + DSA *dsa = (DSA *) key->opaque; + return (ISC_TF(dsa != NULL && dsa->priv_key != NULL)); +} + +static void +openssldsa_destroy(dst_key_t *key) { + DSA *dsa = key->opaque; + DSA_free(dsa); + key->opaque = NULL; +} + + +static isc_result_t +openssldsa_todns(const dst_key_t *key, isc_buffer_t *data) { + DSA *dsa; + isc_region_t r; + int dnslen; + unsigned int t, p_bytes; + + REQUIRE(key->opaque != NULL); + + dsa = (DSA *) key->opaque; + + isc_buffer_availableregion(data, &r); + + t = (BN_num_bytes(dsa->p) - 64) / 8; + if (t > 8) + return (DST_R_INVALIDPUBLICKEY); + p_bytes = 64 + 8 * t; + + dnslen = 1 + (key->key_size * 3)/8 + ISC_SHA1_DIGESTLENGTH; + if (r.length < (unsigned int) dnslen) + return (ISC_R_NOSPACE); + + *r.base++ = t; + BN_bn2bin_fixed(dsa->q, r.base, ISC_SHA1_DIGESTLENGTH); + r.base += ISC_SHA1_DIGESTLENGTH; + BN_bn2bin_fixed(dsa->p, r.base, key->key_size/8); + r.base += p_bytes; + BN_bn2bin_fixed(dsa->g, r.base, key->key_size/8); + r.base += p_bytes; + BN_bn2bin_fixed(dsa->pub_key, r.base, key->key_size/8); + r.base += p_bytes; + + isc_buffer_add(data, dnslen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +openssldsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + DSA *dsa; + isc_region_t r; + unsigned int t, p_bytes; + isc_mem_t *mctx = key->mctx; + + UNUSED(mctx); + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + dsa = DSA_new(); + if (dsa == NULL) + return (ISC_R_NOMEMORY); + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + + t = (unsigned int) *r.base++; + if (t > 8) { + DSA_free(dsa); + return (DST_R_INVALIDPUBLICKEY); + } + p_bytes = 64 + 8 * t; + + if (r.length < 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes) { + DSA_free(dsa); + return (DST_R_INVALIDPUBLICKEY); + } + + dsa->q = BN_bin2bn(r.base, ISC_SHA1_DIGESTLENGTH, NULL); + r.base += ISC_SHA1_DIGESTLENGTH; + + dsa->p = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + dsa->g = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + dsa->pub_key = BN_bin2bn(r.base, p_bytes, NULL); + r.base += p_bytes; + + key->key_size = p_bytes * 8; + + isc_buffer_forward(data, 1 + ISC_SHA1_DIGESTLENGTH + 3 * p_bytes); + + key->opaque = (void *) dsa; + + return (ISC_R_SUCCESS); +} + + +static isc_result_t +openssldsa_tofile(const dst_key_t *key, const char *directory) { + int cnt = 0; + DSA *dsa; + dst_private_t priv; + unsigned char bufs[5][128]; + + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + dsa = (DSA *) key->opaque; + + priv.elements[cnt].tag = TAG_DSA_PRIME; + priv.elements[cnt].length = BN_num_bytes(dsa->p); + BN_bn2bin(dsa->p, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_SUBPRIME; + priv.elements[cnt].length = BN_num_bytes(dsa->q); + BN_bn2bin(dsa->q, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_BASE; + priv.elements[cnt].length = BN_num_bytes(dsa->g); + BN_bn2bin(dsa->g, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PRIVATE; + priv.elements[cnt].length = BN_num_bytes(dsa->priv_key); + BN_bn2bin(dsa->priv_key, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.elements[cnt].tag = TAG_DSA_PUBLIC; + priv.elements[cnt].length = BN_num_bytes(dsa->pub_key); + BN_bn2bin(dsa->pub_key, bufs[cnt]); + priv.elements[cnt].data = bufs[cnt]; + cnt++; + + priv.nelements = cnt; + return (dst__privstruct_writefile(key, &priv, directory)); +} + +static isc_result_t +openssldsa_parse(dst_key_t *key, isc_lex_t *lexer) { + dst_private_t priv; + isc_result_t ret; + int i; + DSA *dsa = NULL; + isc_mem_t *mctx = key->mctx; +#define DST_RET(a) {ret = a; goto err;} + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_DSA, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + dsa = DSA_new(); + if (dsa == NULL) + DST_RET(ISC_R_NOMEMORY); + dsa->flags &= ~DSA_FLAG_CACHE_MONT_P; + key->opaque = dsa; + + for (i=0; i < priv.nelements; i++) { + BIGNUM *bn; + bn = BN_bin2bn(priv.elements[i].data, + priv.elements[i].length, NULL); + if (bn == NULL) + DST_RET(ISC_R_NOMEMORY); + + switch (priv.elements[i].tag) { + case TAG_DSA_PRIME: + dsa->p = bn; + break; + case TAG_DSA_SUBPRIME: + dsa->q = bn; + break; + case TAG_DSA_BASE: + dsa->g = bn; + break; + case TAG_DSA_PRIVATE: + dsa->priv_key = bn; + break; + case TAG_DSA_PUBLIC: + dsa->pub_key = bn; + break; + } + } + dst__privstruct_free(&priv, mctx); + + key->key_size = BN_num_bits(dsa->p); + + return (ISC_R_SUCCESS); + + err: + openssldsa_destroy(key); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static dst_func_t openssldsa_functions = { + openssldsa_createctx, + openssldsa_destroyctx, + openssldsa_adddata, + openssldsa_sign, + openssldsa_verify, + NULL, /* computesecret */ + openssldsa_compare, + NULL, /* paramcompare */ + openssldsa_generate, + openssldsa_isprivate, + openssldsa_destroy, + openssldsa_todns, + openssldsa_fromdns, + openssldsa_tofile, + openssldsa_parse, + NULL, /* cleanup */ +}; + +isc_result_t +dst__openssldsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &openssldsa_functions; + return (ISC_R_SUCCESS); +} + +#else /* OPENSSL */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* OPENSSL */ diff --git a/lib/dns/opensslrsa_link.c b/lib/dns/opensslrsa_link.c new file mode 100644 index 0000000000..22274819be --- /dev/null +++ b/lib/dns/opensslrsa_link.c @@ -0,0 +1,567 @@ +/* + * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") + * Copyright (C) 2000-2003 Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Principal Author: Brian Wellington + * $Id: opensslrsa_link.c,v 1.1 2004/12/09 01:41:04 marka Exp $ + */ +#ifdef OPENSSL + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "dst_internal.h" +#include "dst_openssl.h" +#include "dst_parse.h" + +#include +#include +#include + + /* + * XXXMPA Temporarially disable RSA_BLINDING as it requires + * good quality random data that cannot currently be guarenteed. + * XXXMPA Find which versions of openssl use pseudo random data + * and set RSA_FLAG_BLINDING for those. + */ + +#if 0 +#if OPENSSL_VERSION_NUMBER < 0x0090601fL +#define SET_FLAGS(rsa) \ + do { \ + (rsa)->flags &= ~(RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE); \ + (rsa)->flags |= RSA_FLAG_BLINDING; \ + } while (0) +#else +#define SET_FLAGS(rsa) \ + do { \ + (rsa)->flags |= RSA_FLAG_BLINDING; \ + } while (0) +#endif +#endif + +#if OPENSSL_VERSION_NUMBER < 0x0090601fL +#define SET_FLAGS(rsa) \ + do { \ + (rsa)->flags &= ~(RSA_FLAG_CACHE_PUBLIC | RSA_FLAG_CACHE_PRIVATE); \ + (rsa)->flags &= ~RSA_FLAG_BLINDING; \ + } while (0) +#else +#define SET_FLAGS(rsa) \ + do { \ + (rsa)->flags &= ~RSA_FLAG_BLINDING; \ + } while (0) +#endif + +static isc_result_t opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data); + +static isc_result_t +opensslrsa_createctx(dst_key_t *key, dst_context_t *dctx) { + UNUSED(key); + REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 || + dctx->key->key_alg == DST_ALG_RSASHA1); + + if (dctx->key->key_alg == DST_ALG_RSAMD5) { + isc_md5_t *md5ctx; + + md5ctx = isc_mem_get(dctx->mctx, sizeof(isc_md5_t)); + isc_md5_init(md5ctx); + dctx->opaque = md5ctx; + } else { + isc_sha1_t *sha1ctx; + + sha1ctx = isc_mem_get(dctx->mctx, sizeof(isc_sha1_t)); + isc_sha1_init(sha1ctx); + dctx->opaque = sha1ctx; + } + + return (ISC_R_SUCCESS); +} + +static void +opensslrsa_destroyctx(dst_context_t *dctx) { + REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 || + dctx->key->key_alg == DST_ALG_RSASHA1); + + if (dctx->key->key_alg == DST_ALG_RSAMD5) { + isc_md5_t *md5ctx = dctx->opaque; + + if (md5ctx != NULL) { + isc_md5_invalidate(md5ctx); + isc_mem_put(dctx->mctx, md5ctx, sizeof(isc_md5_t)); + } + } else { + isc_sha1_t *sha1ctx = dctx->opaque; + + if (sha1ctx != NULL) { + isc_sha1_invalidate(sha1ctx); + isc_mem_put(dctx->mctx, sha1ctx, sizeof(isc_sha1_t)); + } + } + dctx->opaque = NULL; +} + +static isc_result_t +opensslrsa_adddata(dst_context_t *dctx, const isc_region_t *data) { + REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 || + dctx->key->key_alg == DST_ALG_RSASHA1); + + if (dctx->key->key_alg == DST_ALG_RSAMD5) { + isc_md5_t *md5ctx = dctx->opaque; + isc_md5_update(md5ctx, data->base, data->length); + } else { + isc_sha1_t *sha1ctx = dctx->opaque; + isc_sha1_update(sha1ctx, data->base, data->length); + } + return (ISC_R_SUCCESS); +} + +static isc_result_t +opensslrsa_sign(dst_context_t *dctx, isc_buffer_t *sig) { + dst_key_t *key = dctx->key; + RSA *rsa = key->opaque; + isc_region_t r; + /* note: ISC_SHA1_DIGESTLENGTH > ISC_MD5_DIGESTLENGTH */ + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + unsigned int siglen = 0; + int status; + int type; + unsigned int digestlen; + char *message; + unsigned long err; + const char* file; + int line; + + REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 || + dctx->key->key_alg == DST_ALG_RSASHA1); + + isc_buffer_availableregion(sig, &r); + + if (r.length < (unsigned int) RSA_size(rsa)) + return (ISC_R_NOSPACE); + + if (dctx->key->key_alg == DST_ALG_RSAMD5) { + isc_md5_t *md5ctx = dctx->opaque; + isc_md5_final(md5ctx, digest); + type = NID_md5; + digestlen = ISC_MD5_DIGESTLENGTH; + } else { + isc_sha1_t *sha1ctx = dctx->opaque; + isc_sha1_final(sha1ctx, digest); + type = NID_sha1; + digestlen = ISC_SHA1_DIGESTLENGTH; + } + + status = RSA_sign(type, digest, digestlen, r.base, &siglen, rsa); + if (status == 0) { + err = ERR_peek_error_line(&file, &line); + if (err != 0U) { + message = ERR_error_string(err, NULL); + fprintf(stderr, "%s:%s:%d\n", message, + file ? file : "", line); + } + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + } + + isc_buffer_add(sig, siglen); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +opensslrsa_verify(dst_context_t *dctx, const isc_region_t *sig) { + dst_key_t *key = dctx->key; + RSA *rsa = key->opaque; + /* note: ISC_SHA1_DIGESTLENGTH > ISC_MD5_DIGESTLENGTH */ + unsigned char digest[ISC_SHA1_DIGESTLENGTH]; + int status = 0; + int type; + unsigned int digestlen; + + REQUIRE(dctx->key->key_alg == DST_ALG_RSAMD5 || + dctx->key->key_alg == DST_ALG_RSASHA1); + + if (dctx->key->key_alg == DST_ALG_RSAMD5) { + isc_md5_t *md5ctx = dctx->opaque; + isc_md5_final(md5ctx, digest); + type = NID_md5; + digestlen = ISC_MD5_DIGESTLENGTH; + } else { + isc_sha1_t *sha1ctx = dctx->opaque; + isc_sha1_final(sha1ctx, digest); + type = NID_sha1; + digestlen = ISC_SHA1_DIGESTLENGTH; + } + + if (sig->length < (unsigned int) RSA_size(rsa)) + return (DST_R_VERIFYFAILURE); + + status = RSA_verify(type, digest, digestlen, sig->base, + RSA_size(rsa), rsa); + if (status == 0) + return (dst__openssl_toresult(DST_R_VERIFYFAILURE)); + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +opensslrsa_compare(const dst_key_t *key1, const dst_key_t *key2) { + int status; + RSA *rsa1, *rsa2; + + rsa1 = (RSA *) key1->opaque; + rsa2 = (RSA *) key2->opaque; + + if (rsa1 == NULL && rsa2 == NULL) + return (ISC_TRUE); + else if (rsa1 == NULL || rsa2 == NULL) + return (ISC_FALSE); + + status = BN_cmp(rsa1->n, rsa2->n) || + BN_cmp(rsa1->e, rsa2->e); + + if (status != 0) + return (ISC_FALSE); + + if (rsa1->d != NULL || rsa2->d != NULL) { + if (rsa1->d == NULL || rsa2->d == NULL) + return (ISC_FALSE); + status = BN_cmp(rsa1->d, rsa2->d) || + BN_cmp(rsa1->p, rsa2->p) || + BN_cmp(rsa1->q, rsa2->q); + + if (status != 0) + return (ISC_FALSE); + } + return (ISC_TRUE); +} + +static isc_result_t +opensslrsa_generate(dst_key_t *key, int exp) { + RSA *rsa; + unsigned long e; + + if (exp == 0) + e = RSA_3; + else + e = RSA_F4; + rsa = RSA_generate_key(key->key_size, e, NULL, NULL); + if (rsa == NULL) + return (dst__openssl_toresult(DST_R_OPENSSLFAILURE)); + SET_FLAGS(rsa); + key->opaque = rsa; + + return (ISC_R_SUCCESS); +} + +static isc_boolean_t +opensslrsa_isprivate(const dst_key_t *key) { + RSA *rsa = (RSA *) key->opaque; + return (ISC_TF(rsa != NULL && rsa->d != NULL)); +} + +static void +opensslrsa_destroy(dst_key_t *key) { + RSA *rsa = key->opaque; + RSA_free(rsa); + key->opaque = NULL; +} + + +static isc_result_t +opensslrsa_todns(const dst_key_t *key, isc_buffer_t *data) { + RSA *rsa; + isc_region_t r; + unsigned int e_bytes; + unsigned int mod_bytes; + + REQUIRE(key->opaque != NULL); + + rsa = (RSA *) key->opaque; + + isc_buffer_availableregion(data, &r); + + e_bytes = BN_num_bytes(rsa->e); + mod_bytes = BN_num_bytes(rsa->n); + + if (e_bytes < 256) { /* key exponent is <= 2040 bits */ + if (r.length < 1) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(data, (isc_uint8_t) e_bytes); + } else { + if (r.length < 3) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(data, 0); + isc_buffer_putuint16(data, (isc_uint16_t) e_bytes); + } + + if (r.length < e_bytes + mod_bytes) + return (ISC_R_NOSPACE); + isc_buffer_availableregion(data, &r); + + BN_bn2bin(rsa->e, r.base); + r.base += e_bytes; + BN_bn2bin(rsa->n, r.base); + + isc_buffer_add(data, e_bytes + mod_bytes); + + return (ISC_R_SUCCESS); +} + +static isc_result_t +opensslrsa_fromdns(dst_key_t *key, isc_buffer_t *data) { + RSA *rsa; + isc_region_t r; + unsigned int e_bytes; + + isc_buffer_remainingregion(data, &r); + if (r.length == 0) + return (ISC_R_SUCCESS); + + rsa = RSA_new(); + if (rsa == NULL) + return (ISC_R_NOMEMORY); + SET_FLAGS(rsa); + + if (r.length < 1) { + RSA_free(rsa); + return (DST_R_INVALIDPUBLICKEY); + } + e_bytes = *r.base++; + r.length--; + + if (e_bytes == 0) { + if (r.length < 2) { + RSA_free(rsa); + return (DST_R_INVALIDPUBLICKEY); + } + e_bytes = ((*r.base++) << 8); + e_bytes += *r.base++; + r.length -= 2; + } + + if (r.length < e_bytes) { + RSA_free(rsa); + return (DST_R_INVALIDPUBLICKEY); + } + rsa->e = BN_bin2bn(r.base, e_bytes, NULL); + r.base += e_bytes; + r.length -= e_bytes; + + rsa->n = BN_bin2bn(r.base, r.length, NULL); + + key->key_size = BN_num_bits(rsa->n); + + isc_buffer_forward(data, r.length); + + key->opaque = (void *) rsa; + + return (ISC_R_SUCCESS); +} + + +static isc_result_t +opensslrsa_tofile(const dst_key_t *key, const char *directory) { + int i; + RSA *rsa; + dst_private_t priv; + unsigned char *bufs[8]; + isc_result_t result; + + if (key->opaque == NULL) + return (DST_R_NULLKEY); + + rsa = (RSA *) key->opaque; + + for (i = 0; i < 8; i++) { + bufs[i] = isc_mem_get(key->mctx, BN_num_bytes(rsa->n)); + if (bufs[i] == NULL) { + result = ISC_R_NOMEMORY; + goto fail; + } + } + + i = 0; + + priv.elements[i].tag = TAG_RSA_MODULUS; + priv.elements[i].length = BN_num_bytes(rsa->n); + BN_bn2bin(rsa->n, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_PUBLICEXPONENT; + priv.elements[i].length = BN_num_bytes(rsa->e); + BN_bn2bin(rsa->e, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_PRIVATEEXPONENT; + priv.elements[i].length = BN_num_bytes(rsa->d); + BN_bn2bin(rsa->d, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_PRIME1; + priv.elements[i].length = BN_num_bytes(rsa->p); + BN_bn2bin(rsa->p, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_PRIME2; + priv.elements[i].length = BN_num_bytes(rsa->q); + BN_bn2bin(rsa->q, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_EXPONENT1; + priv.elements[i].length = BN_num_bytes(rsa->dmp1); + BN_bn2bin(rsa->dmp1, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_EXPONENT2; + priv.elements[i].length = BN_num_bytes(rsa->dmq1); + BN_bn2bin(rsa->dmq1, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.elements[i].tag = TAG_RSA_COEFFICIENT; + priv.elements[i].length = BN_num_bytes(rsa->iqmp); + BN_bn2bin(rsa->iqmp, bufs[i]); + priv.elements[i].data = bufs[i]; + i++; + + priv.nelements = i; + result = dst__privstruct_writefile(key, &priv, directory); + fail: + for (i = 0; i < 8; i++) { + if (bufs[i] == NULL) + break; + isc_mem_put(key->mctx, bufs[i], BN_num_bytes(rsa->n)); + } + return (result); +} + +static isc_result_t +opensslrsa_parse(dst_key_t *key, isc_lex_t *lexer) { + dst_private_t priv; + isc_result_t ret; + int i; + RSA *rsa = NULL; + isc_mem_t *mctx = key->mctx; +#define DST_RET(a) {ret = a; goto err;} + + /* read private key file */ + ret = dst__privstruct_parse(key, DST_ALG_RSA, lexer, mctx, &priv); + if (ret != ISC_R_SUCCESS) + return (ret); + + rsa = RSA_new(); + if (rsa == NULL) + DST_RET(ISC_R_NOMEMORY); + SET_FLAGS(rsa); + key->opaque = rsa; + + for (i = 0; i < priv.nelements; i++) { + BIGNUM *bn; + bn = BN_bin2bn(priv.elements[i].data, + priv.elements[i].length, NULL); + if (bn == NULL) + DST_RET(ISC_R_NOMEMORY); + + switch (priv.elements[i].tag) { + case TAG_RSA_MODULUS: + rsa->n = bn; + break; + case TAG_RSA_PUBLICEXPONENT: + rsa->e = bn; + break; + case TAG_RSA_PRIVATEEXPONENT: + rsa->d = bn; + break; + case TAG_RSA_PRIME1: + rsa->p = bn; + break; + case TAG_RSA_PRIME2: + rsa->q = bn; + break; + case TAG_RSA_EXPONENT1: + rsa->dmp1 = bn; + break; + case TAG_RSA_EXPONENT2: + rsa->dmq1 = bn; + break; + case TAG_RSA_COEFFICIENT: + rsa->iqmp = bn; + break; + } + } + dst__privstruct_free(&priv, mctx); + + key->key_size = BN_num_bits(rsa->n); + + return (ISC_R_SUCCESS); + + err: + opensslrsa_destroy(key); + dst__privstruct_free(&priv, mctx); + memset(&priv, 0, sizeof(priv)); + return (ret); +} + +static dst_func_t opensslrsa_functions = { + opensslrsa_createctx, + opensslrsa_destroyctx, + opensslrsa_adddata, + opensslrsa_sign, + opensslrsa_verify, + NULL, /* computesecret */ + opensslrsa_compare, + NULL, /* paramcompare */ + opensslrsa_generate, + opensslrsa_isprivate, + opensslrsa_destroy, + opensslrsa_todns, + opensslrsa_fromdns, + opensslrsa_tofile, + opensslrsa_parse, + NULL, /* cleanup */ +}; + +isc_result_t +dst__opensslrsa_init(dst_func_t **funcp) { + REQUIRE(funcp != NULL); + if (*funcp == NULL) + *funcp = &opensslrsa_functions; + return (ISC_R_SUCCESS); +} + +#else /* OPENSSL */ + +#include + +EMPTY_TRANSLATION_UNIT + +#endif /* OPENSSL */