dst_api.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Portions Copyright (C) 1999-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Portions Copyright (C) 1995-2000 by Network Associates, Inc.
*
* 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.65 2011/10/20 21:20:02 marka Exp $
*/
/*! \file */
#include <config.h>
#include <stdlib.h>
#include <time.h>
#include <isc/fsaccess.h>
#include <isc/platform.h>
#include <isc/refcount.h>
#define DST_KEY_INTERNAL
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/rdataclass.h>
#include "dst_internal.h"
static unsigned int dst_entropy_flags = 0;
/*
* Static functions.
*/
unsigned int alg,
unsigned int flags,
unsigned int protocol,
unsigned int bits,
const char *directory);
unsigned int alg,
unsigned int type,
const char *directory,
isc_buffer_t *out);
unsigned int alg,
unsigned int flags,
unsigned int protocol,
const char *suffix);
#define RETERR(x) \
do { \
result = (x); \
if (result != ISC_R_SUCCESS) \
goto out; \
} while (0)
do { \
isc_result_t _r; \
if (_r != ISC_R_SUCCESS) \
return (_r); \
} while (0); \
#if defined(OPENSSL)
static void *
if (size == 0U)
size = 1;
}
static void
}
#endif
}
#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
#endif
#if defined(OPENSSL)
/*
* 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. Note: as there are leaks we cannot use
* ISC_MEMFLAG_INTERNAL as it will free up memory still being used
* by libcrypto.
*/
NULL, &dst__memory_pool, 0);
if (result != ISC_R_SUCCESS)
return (result);
#ifndef OPENSSL_LEAKS
#endif
#else /* OPENSSL */
#endif /* OPENSSL */
}
#ifdef OPENSSL
#ifdef HAVE_OPENSSL_DSA
#endif
#ifdef HAVE_OPENSSL_GOST
#endif
#ifdef HAVE_OPENSSL_ECDSA
#endif
#ifdef HAVE_PKCS11_ECDSA
#endif
#ifdef HAVE_PKCS11_GOST
#endif
#endif /* if OPENSSL, elif PKCS11CRYPTO */
#ifdef GSSAPI
#endif
return (ISC_R_SUCCESS);
out:
/* avoid immediate crash! */
return (result);
}
void
dst_lib_destroy(void) {
int i;
for (i = 0; i < DST_MAX_ALGS; i++)
dst_t_func[i]->cleanup();
#ifdef OPENSSL
(void) dst__pkcs11_destroy();
#endif /* if OPENSSL, elif PKCS11CRYPTO */
if (dst__memory_pool != NULL)
if (dst_entropy_pool != NULL)
}
dst_algorithm_supported(unsigned int alg) {
return (ISC_FALSE);
return (ISC_TRUE);
}
dst_ds_digest_supported(unsigned int digest_type) {
#if defined(HAVE_OPENSSL_GOST) || defined(HAVE_PKCS11_GOST)
digest_type == DNS_DSDIGEST_GOST ||
#else
#endif
}
}
{
}
{
useforsigning, 0, dctxp));
}
{
return (DST_R_UNSUPPORTEDALG);
return (DST_R_NULLKEY);
return (ISC_R_NOMEMORY);
if (useforsigning)
else
else
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
void
}
}
return (DST_R_NULLKEY);
return (DST_R_NOTPRIVATEKEY);
return (DST_R_NOTPRIVATEKEY);
}
return (DST_R_NULLKEY);
return (DST_R_NOTPUBLICKEY);
}
{
return (DST_R_NULLKEY);
return (DST_R_NOTPUBLICKEY);
}
{
return (DST_R_NULLKEY);
return (DST_R_KEYCANNOTCOMPUTESECRET);
return (DST_R_NOTPRIVATEKEY);
}
return (DST_R_UNSUPPORTEDALG);
if (type & DST_TYPE_PUBLIC) {
if (ret != ISC_R_SUCCESS)
return (ret);
}
if ((type & DST_TYPE_PRIVATE) &&
else
return (ISC_R_SUCCESS);
}
void
}
}
{
if (result == ISC_R_SUCCESS) {
if (isc_buffer_availablelength(buf) > 0)
isc_buffer_putuint8(buf, 0);
else
}
return (result);
}
{
char filename[ISC_DIR_NAMEMAX];
if (result != ISC_R_SUCCESS)
goto out;
if (result != ISC_R_SUCCESS)
goto out;
if (result != ISC_R_SUCCESS)
goto out;
goto out;
}
out:
dst_key_free(&key);
return (result);
}
{
char *newfilename = NULL;
int newfilenamelen = 0;
/* If an absolute path is specified, don't use the key directory */
#ifndef WIN32
if (filename[0] == '/')
#else /* WIN32 */
#endif
if (newfilename == NULL)
return (ISC_R_NOMEMORY);
newfilename = NULL;
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_NOMEMORY);
}
if (newfilename == NULL)
return (ISC_R_SUCCESS);
out:
if (newfilename != NULL)
dst_key_free(&key);
return (result);
}
return (DST_R_UNSUPPORTEDALG);
return (ISC_R_NOSPACE);
return (ISC_R_NOSPACE);
& 0xffff));
}
return (ISC_R_SUCCESS);
}
{
isc_region_t r;
return (DST_R_INVALIDPUBLICKEY);
if (flags & DNS_KEYFLAG_EXTENDED) {
return (DST_R_INVALIDPUBLICKEY);
}
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_SUCCESS);
}
{
if (result != ISC_R_SUCCESS)
return (result);
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return (result);
}
return (ISC_R_SUCCESS);
}
return (DST_R_UNSUPPORTEDALG);
}
out:
return (result);
}
{
}
{
0, dns_rdataclass_in, 0, mctx);
return (ISC_R_NOMEMORY);
/*
* Keep the token for use by external ssu rules. They may need
* to examine the PAC in the kerberos ticket.
*/
}
out:
return result;
}
{
0, mctx);
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return (result);
}
return (ISC_R_SUCCESS);
}
{
return (ISC_R_NOMEMORY);
dst_key_free(&key);
return (DST_R_UNSUPPORTEDALG);
}
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return (result);
}
if (result != ISC_R_SUCCESS) {
dst_key_free(&key);
return (result);
}
return (ISC_R_SUCCESS);
}
{
}
void (*callback)(int))
{
return (ISC_R_NOMEMORY);
if (bits == 0) { /*%< NULL KEY */
return (ISC_R_SUCCESS);
}
dst_key_free(&key);
return (DST_R_UNSUPPORTEDALG);
}
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
return (ISC_R_SUCCESS);
}
{
return (ISC_R_NOTFOUND);
return (ISC_R_SUCCESS);
}
void
{
}
void
{
}
return (ISC_R_NOTFOUND);
return (ISC_R_SUCCESS);
}
void
}
void
}
return (ISC_R_SUCCESS);
}
void
}
static isc_boolean_t
{
return (ISC_TRUE);
return (ISC_FALSE);
if (!match_revoked_key)
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
return (ISC_FALSE);
}
else
return (ISC_FALSE);
}
/*
* Compares only the public portion of two keys, by converting them
* both to wire format and comparing the results.
*/
static isc_boolean_t
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
/* Zero out flags. */
if (result != ISC_R_SUCCESS)
return (ISC_FALSE);
/* Zero out flags. */
/* Remove extended flags. */
}
/* Remove extended flags. */
}
}
}
{
}
return (ISC_TRUE);
return (ISC_TRUE);
else
return (ISC_FALSE);
}
void
}
void
unsigned int refs;
if (refs != 0)
return;
}
if (key->key_tkeytoken) {
}
}
}
type == 0);
}
/* XXXVIX this switch statement is too sparse to gen a jump table. */
case DST_ALG_RSAMD5:
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
case DST_ALG_RSASHA256:
case DST_ALG_RSASHA512:
break;
case DST_ALG_DSA:
case DST_ALG_NSEC3DSA:
*n = DNS_SIG_DSASIGSIZE;
break;
case DST_ALG_ECCGOST:
*n = DNS_SIG_GOSTSIGSIZE;
break;
case DST_ALG_ECDSA256:
*n = DNS_SIG_ECDSA256SIZE;
break;
case DST_ALG_ECDSA384:
*n = DNS_SIG_ECDSA384SIZE;
break;
case DST_ALG_HMACMD5:
*n = 16;
break;
case DST_ALG_HMACSHA1:
*n = ISC_SHA1_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA224:
*n = ISC_SHA224_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA256:
*n = ISC_SHA256_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA384:
*n = ISC_SHA384_DIGESTLENGTH;
break;
case DST_ALG_HMACSHA512:
*n = ISC_SHA512_DIGESTLENGTH;
break;
case DST_ALG_GSSAPI:
*n = 128; /*%< XXX */
break;
case DST_ALG_DH:
default:
return (DST_R_UNSUPPORTEDALG);
}
return (ISC_R_SUCCESS);
}
else
return (DST_R_UNSUPPORTEDALG);
return (ISC_R_SUCCESS);
}
/*%
* Set the flags on a key, then recompute the key ID
*/
}
void
char namestr[DNS_NAME_FORMATSIZE];
char algstr[DNS_NAME_FORMATSIZE];
sizeof(algstr));
}
return (ISC_R_NOTIMPLEMENTED);
}
{
return (DST_R_UNSUPPORTEDALG);
return (ISC_R_NOTIMPLEMENTED);
return (ISC_R_NOMEMORY);
if (result == ISC_R_SUCCESS)
else
dst_key_free(&key);
return (result);
}
/***
*** Static methods
***/
/*%
* Allocates a key structure and fills in some of the fields.
*/
static dst_key_t *
{
int i;
return (NULL);
return (NULL);
}
if (result != ISC_R_SUCCESS) {
return (NULL);
}
if (result != ISC_R_SUCCESS) {
return (NULL);
}
for (i = 0; i < (DST_MAX_TIMES + 1); i++) {
}
return (key);
}
}
void
}
/*%
* Reads a public key from disk
*/
{
isc_buffer_t b;
unsigned int opt = ISC_LEXOPT_DNSMULTILINE;
isc_uint32_t ttl = 0;
/*
* Open the file and read its formatted contents
* File format:
* domain.name [ttl] [class] [KEY|DNSKEY] <flags> <protocol> <algorithm> <key>
*/
/* 1500 should be large enough for any key */
if (ret != ISC_R_SUCCESS)
goto cleanup;
if (ret != ISC_R_SUCCESS)
goto cleanup;
if (ret != ISC_R_SUCCESS) \
goto cleanup; \
}
#define BADTOKEN() { \
ret = ISC_R_UNEXPECTEDTOKEN; \
goto cleanup; \
}
/* Read the domain name */
BADTOKEN();
/*
* We don't support "@" in .key files.
*/
BADTOKEN();
0, NULL);
if (ret != ISC_R_SUCCESS)
goto cleanup;
/* Read the next word: either TTL, class, or 'KEY' */
BADTOKEN();
/* If it's a TTL, read the next one */
if (result == ISC_R_SUCCESS)
BADTOKEN();
if (ret == ISC_R_SUCCESS)
BADTOKEN();
else
BADTOKEN();
goto cleanup;
}
if (ret != ISC_R_SUCCESS)
goto cleanup;
keyp);
if (ret != ISC_R_SUCCESS)
goto cleanup;
return (ret);
}
static isc_boolean_t
/* XXXVIX this switch statement is too sparse to gen a jump table. */
case DST_ALG_RSAMD5:
case DST_ALG_RSASHA1:
case DST_ALG_NSEC3RSASHA1:
case DST_ALG_RSASHA256:
case DST_ALG_RSASHA512:
case DST_ALG_DSA:
case DST_ALG_NSEC3DSA:
case DST_ALG_DH:
case DST_ALG_ECCGOST:
case DST_ALG_ECDSA256:
case DST_ALG_ECDSA384:
return (ISC_FALSE);
case DST_ALG_HMACMD5:
case DST_ALG_HMACSHA1:
case DST_ALG_HMACSHA224:
case DST_ALG_HMACSHA256:
case DST_ALG_HMACSHA384:
case DST_ALG_HMACSHA512:
case DST_ALG_GSSAPI:
return (ISC_TRUE);
default:
return (ISC_FALSE);
}
}
/*%
* Write key timing metadata to a file pointer, preceded by 'tag'
*/
static void
#ifdef ISC_PLATFORM_USETHREADS
#else
const char *output;
#endif
time_t t;
char utc[sizeof("YYYYMMDDHHSSMM")];
isc_buffer_t b;
isc_region_t r;
if (result == ISC_R_NOTFOUND)
return;
/* time_t and isc_stdtime_t might be different sizes */
t = when;
#ifdef ISC_PLATFORM_USETHREADS
#ifdef WIN32
goto error;
#else
goto error;
#endif
#else
#endif
if (result != ISC_R_SUCCESS)
goto error;
isc_buffer_usedregion(&b, &r);
return;
}
/*%
* Writes a public key to disk in DNS format.
*/
static isc_result_t
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];
if (ret != ISC_R_SUCCESS)
return (ret);
isc_buffer_usedregion(&keyb, &r);
if (ret != ISC_R_SUCCESS)
return (DST_R_INVALIDPUBLICKEY);
if (ret != ISC_R_SUCCESS)
return (DST_R_INVALIDPUBLICKEY);
/*
* Make the filename.
*/
if (ret != ISC_R_SUCCESS)
return (ret);
/*
* Create public key file.
*/
return (DST_R_WRITEERROR);
if (issymmetric(key)) {
access = 0;
&access);
}
/* Write key information in comments */
if ((type & DST_TYPE_KEY) == 0) {
"revoked " :
"",
"key" :
"zone",
if (ret != ISC_R_SUCCESS) {
return (ret);
}
}
/* Now print the actual key */
isc_buffer_usedregion(&classb, &r);
if ((type & DST_TYPE_KEY) != 0)
else
isc_buffer_usedregion(&textb, &r);
return (ret);
}
static isc_result_t
{
const char *suffix = "";
unsigned int len;
if ((type & DST_TYPE_PRIVATE) != 0)
suffix = ".private";
else if (type == DST_TYPE_PUBLIC)
suffix = ".key";
return (ISC_R_NOSPACE);
}
return (ISC_R_NOSPACE);
if (result != ISC_R_SUCCESS)
return (result);
return (ISC_R_NOSPACE);
suffix);
return (ISC_R_SUCCESS);
}
static isc_result_t
unsigned char dns_array[DST_KEY_MAXSIZE];
isc_region_t r;
if (ret != ISC_R_SUCCESS)
return (ret);
isc_buffer_usedregion(&dnsbuf, &r);
return (ISC_R_SUCCESS);
}
static isc_result_t
{
return (ISC_R_NOMEMORY);
if (isc_buffer_remaininglength(source) > 0) {
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
dst_key_free(&key);
return (DST_R_UNSUPPORTEDALG);
}
if (ret != ISC_R_SUCCESS) {
dst_key_free(&key);
return (ret);
}
}
return (ISC_R_SUCCESS);
}
static isc_result_t
algorithm_status(unsigned int alg) {
if (dst_algorithm_supported(alg))
return (ISC_R_SUCCESS);
#if !defined(OPENSSL) && !defined(PKCS11CRYPTO)
alg == DST_ALG_NSEC3RSASHA1 ||
alg == DST_ALG_ECCGOST ||
return (DST_R_NOCRYPTO);
#endif
return (DST_R_UNSUPPORTEDALG);
}
static isc_result_t
{
int n;
olen -= 1;
olen -= 8;
olen -= 4;
else
if (n < 0)
return (ISC_R_FAILURE);
if (n >= len)
return (ISC_R_NOSPACE);
return (ISC_R_SUCCESS);
}
unsigned int flags = dst_entropy_flags;
if (dst_entropy_pool == NULL)
return (ISC_R_FAILURE);
if (len == 0)
return (ISC_R_SUCCESS);
#ifdef PKCS11CRYPTO
#else /* PKCS11CRYPTO */
if (pseudo)
else
#endif /* PKCS11CRYPTO */
}
unsigned int
dst__entropy_status(void) {
#ifndef PKCS11CRYPTO
#ifdef GSSAPI
unsigned int flags = dst_entropy_flags;
unsigned char buf[32];
if (dst_entropy_pool == NULL)
return (0);
if (first) {
/* Someone believes RAND_status() initializes the PRNG */
}
#endif
return (isc_entropy_status(dst_entropy_pool));
#else
return (0);
#endif
}
return (key->key_tkeytoken);
}