/*++
/* NAME
/*	tls_dane 3
/* SUMMARY
/*	Support for RFC 6698, 7671, 7672 (DANE) certificate matching
/* SYNOPSIS
/*	#include <tls.h>
/*
/*	void tls_dane_loglevel(log_param, log_level);
/*	const char *log_param;
/*	const char *log_level;
/*
/*	int	tls_dane_avail()
/*
/*	void	tls_dane_flush()
/*
/*	TLS_DANE *tls_dane_alloc()
/*
/*      void    tls_tlsa_free(tlsa)
/*      TLS_TLSA *tlsa;
/*
/*	void	tls_dane_free(dane)
/*	TLS_DANE *dane;
/*
/*	void	tls_dane_add_fpt_digests(dane, pkey_only, digest, delim, smtp_mode)
/*	TLS_DANE *dane;
/*	int     pkey_only;
/*	const char *digest;
/*	const char *delim;
/*	int     smtp_mode;
/*
/*	TLS_TLSA *tlsa_prepend(tlsa, usage, selector, mtype, data, len)
/*	TLS_TLSA *tlsa;
/*	uint8_t usage;
/*	uint8_t selector;
/*	uint8_t mtype;
/*	const unsigned char *data;
/*	uint16_t length;
/*
/*	int	tls_dane_load_trustfile(dane, tafile)
/*	TLS_DANE *dane;
/*	const char *tafile;
/*
/*	TLS_DANE *tls_dane_resolve(port, proto, hostrr, forcetlsa)
/*	unsigned port;
/*	const char *proto;
/*	DNS_RR *hostrr;
/*	int	forcetlsa;
/*
/*	void	tls_dane_digest_init(ctx, fpt_alg)
/*	SSL_CTX *ctx;
/*	const EVP_MD *fpt_alg;
/*
/*	void	tls_dane_enable(TLScontext)
/*	TLS_SESS_STATE *TLScontext;
/*
/*	void    tls_dane_log(TLScontext)
/*	TLS_SESS_STATE *TLScontext;
/*
/*	int	tls_dane_notfound(dane)
/*	const TLS_DANE *dane;
/* DESCRIPTION
/*	tls_dane_loglevel() allows the policy lookup functions in the DANE
/*	library to examine the application's TLS loglevel in and possibly
/*	produce a more detailed activity log.
/*
/*	tls_dane_avail() returns true if the features required to support DANE
/*	are present in libresolv.
/*
/*	tls_dane_flush() flushes all entries from the cache, and deletes
/*	the cache.
/*
/*	tls_dane_alloc() returns a pointer to a newly allocated TLS_DANE
/*	structure with null ta and ee digest sublists.
/*
/*	tls_tlsa_free() frees a TLSA record linked list.
/*
/*	tls_dane_free() frees the structure allocated by tls_dane_alloc().
/*
/*	tls_dane_digest_init() configures OpenSSL to support the configured
/*	DANE TLSA digests and private-use fingerprint digest.
/*
/*	tlsa_prepend() prepends a TLSA record to the head of a linked list
/*	which may be null when the list is empty. The result value is the
/*	new list head.
/*
/*	tls_dane_add_fpt_digests() splits "digest" using the characters in
/*	"delim" as delimiters and generates corresponding synthetic DANE TLSA
/*	records with matching type 255 (private-use), which we associated with
/*	the configured fingerprint digest algorithm.  This is an incremental
/*	interface, that builds a TLS_DANE structure outside the cache by
/*	manually adding entries.
/*
/*	tls_dane_load_trustfile() imports trust-anchor certificates and
/*	public keys from a file (rather than DNS TLSA records).
/*
/*	tls_dane_resolve() maps a (port, protocol, hostrr) tuple to a
/*	corresponding TLS_DANE policy structure found in the DNS.  The port
/*	argument is in network byte order.  A null pointer is returned when
/*	the DNS query for the TLSA record tempfailed.  In all other cases the
/*	return value is a pointer to the corresponding TLS_DANE structure.
/*	The caller must free the structure via tls_dane_free().
/*
/*	tls_dane_enable() enables DANE-style certificate checks for connections
/*	that are configured with TLSA records.  The TLSA records may be from
/*	DNS (at the "dane", "dane-only" and "half-dane" security levels), or be
/*	synthetic in support of either the "fingerprint" level or local trust
/*	anchor based validation with the "secure" and "verify" levels.  The
/*	return value is the number of "usable" TLSA records loaded, or negative
/*	if a record failed to load due to an internal OpenSSL problems, rather
/*	than an issue with the record making that record "unusable".
/*
/*	tls_dane_log() logs successful verification via DNS-based or
/*	synthetic DANE TLSA RRs (fingerprint or "tafile").
/*
/*	tls_dane_notfound() checks whether a cached TLS_DANE record is
/*	the result of a validated DNS lookup returning NODATA. In
/*	this case, TLS is not required by RFC, though users may elect
/*	a mandatory TLS fallback policy.
/*
/*	Arguments:
/* .IP  ctx
/*	SSL context to be configured with the chosen digest algorithms.
/* .IP  fpt_alg
/*	The OpenSSL EVP digest algorithm handle for the fingerprint digest.
/* .IP  pkey_only
/*	When true, generate "fingerprint" TLSA records for just the public
/*	keys.  Otherwise, for both certificates and public keys.
/* .IP  tlsa
/*	TLSA record linked list head, initially NULL.
/* .IP  usage
/*	DANE TLSA certificate usage field.
/* .IP  selector
/*	DANE TLSA selector field.
/* .IP  mtype
/*	DANE TLSA matching type field
/* .IP  data
/*	DANE TLSA associated data field (raw binary form), copied for internal
/*	use.  The caller is responsible for freeing his own copy.
/* .IP  length
/*	Length of DANE TLSA associated DATA field.
/* .IP dane
/*	Pointer to a TLS_DANE structure that lists the valid trust-anchor
/*	and end-entity full-certificate and/or public-key digests.
/* .IP port
/*	The TCP port in network byte order.
/* .IP proto
/*	Almost certainly "tcp".
/* .IP hostrr
/*	DNS_RR pointer to TLSA base domain data.
/* .IP forcetlsa
/*	When true, TLSA lookups are performed even when the qname and rname
/*	are insecure.  This is only useful in the unlikely case that DLV is
/*	used to secure the TLSA RRset in an otherwise insecure zone.
/* .IP log_param
/*	The TLS log level parameter name whose value is the log_level argument.
/* .IP log_level
/*	The application TLS log level, which may affect dane lookup verbosity.
/* .IP digest
/*	The digest (or list of digests concatenated with characters from
/*	"delim") to be added to the TLS_DANE record.
/* .IP delim
/*	The set of delimiter characters used above.
/* .IP smtp_mode
/*	Is the caller an SMTP client or an LMTP client?
/* .IP tafile;
/*	A file with trust anchor certificates or public keys in PEM format.
/* LICENSE
/* .ad
/* .fi
/*	This software is free. You can do with it whatever you want.
/*	The original author kindly requests that you acknowledge
/*	the use of his software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*
/*	Viktor Dukhovni
/*--*/

/* System library. */

#include <sys_defs.h>
#include <ctype.h>

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

#ifdef USE_TLS
#include <string.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <midna_domain.h>
#include <vstring.h>
#include <events.h>			/* event_time() */
#include <timecmp.h>
#include <ctable.h>
#include <hex_code.h>
#include <safe_ultostr.h>
#include <split_at.h>
#include <name_code.h>

#define STR(x)	vstring_str(x)

/* Global library */

#include <mail_params.h>

/* DNS library. */

#include <dns.h>

/* TLS library. */

#define TLS_INTERNAL
#include <tls.h>

/* Application-specific. */

#undef DANE_TLSA_SUPPORT

#if RES_USE_DNSSEC && RES_USE_EDNS0
#define DANE_TLSA_SUPPORT
static int dane_tlsa_support = 1;

#else
static int dane_tlsa_support = 0;

#endif

/*
 * A NULL alg field disables the algorithm at the codepoint passed to the
 * SSL_CTX_dane_mtype_set(3) function.  The ordinals are used for digest
 * agility, higher is "better" (presumed stronger).
 */
typedef struct dane_mtype {
    const EVP_MD *alg;
    uint8_t ord;
} dane_mtype;

/*
 * This is not intended to be a long-term cache of pre-parsed TLSA data,
 * rather we primarily want to avoid fetching and parsing the TLSA records
 * for a single multi-homed MX host more than once per delivery. Therefore,
 * we keep the table reasonably small.
 */
#define CACHE_SIZE 20
static CTABLE *dane_cache;

static int log_mask;

/* tls_dane_logmask - configure policy lookup logging */

void    tls_dane_loglevel(const char *log_param, const char *log_level)
{
    log_mask = tls_log_mask(log_param, log_level);
}

/* tls_dane_avail - check for availability of dane required digests */

int     tls_dane_avail(void)
{
    return (dane_tlsa_support);
}

/* tls_dane_alloc - allocate a TLS_DANE structure */

TLS_DANE *tls_dane_alloc(void)
{
    TLS_DANE *dane = (TLS_DANE *) mymalloc(sizeof(*dane));

    dane->tlsa = 0;
    dane->base_domain = 0;
    dane->flags = 0;
    dane->expires = 0;
    dane->refs = 1;
    return (dane);
}

/* tls_tlsa_free - free a TLSA RR linked list */

void    tls_tlsa_free(TLS_TLSA *tlsa)
{
    TLS_TLSA *next;

    for (; tlsa; tlsa = next) {
	next = tlsa->next;
	myfree(tlsa->data);
	myfree(tlsa);
    }
}

/* tls_dane_free - free a TLS_DANE structure */

void    tls_dane_free(TLS_DANE *dane)
{
    if (--dane->refs > 0)
	return;
    if (dane->base_domain)
	myfree(dane->base_domain);
    if (dane->tlsa)
	tls_tlsa_free(dane->tlsa);
    myfree((void *) dane);
}

/* tlsa_prepend - Prepend internal-form TLSA record to the RRset linked list */

TLS_TLSA *tlsa_prepend(TLS_TLSA *tlsa, uint8_t usage, uint8_t selector,
		               uint8_t mtype, const unsigned char *data,
		               uint16_t data_len)
{
    TLS_TLSA *head;

    head = (TLS_TLSA *) mymalloc(sizeof(*head));
    head->usage = usage;
    head->selector = selector;
    head->mtype = mtype;
    head->length = data_len;
    head->data = (unsigned char *) mymemdup(data, data_len);
    head->next = tlsa;
    return (head);
}

#define MAX_HEAD_BYTES 32
#define MAX_TAIL_BYTES 32
#define MAX_DUMP_BYTES (MAX_HEAD_BYTES + MAX_TAIL_BYTES)

/* tlsa_info - log import of a particular TLSA record */

static void tlsa_info(const char *tag, const char *msg,
		              uint8_t u, uint8_t s, uint8_t m,
		              const unsigned char *data, ssize_t dlen)
{
    static VSTRING *top;
    static VSTRING *bot;

    if (top == 0)
	top = vstring_alloc(2 * MAX_HEAD_BYTES);
    if (bot == 0)
	bot = vstring_alloc(2 * MAX_TAIL_BYTES);

    if (dlen > MAX_DUMP_BYTES) {
	hex_encode(top, (char *) data, MAX_HEAD_BYTES);
	hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
    } else if (dlen > 0) {
	hex_encode(top, (char *) data, dlen);
    } else {
	vstring_sprintf(top, "...");
    }

    msg_info("%s: %s: %u %u %u %s%s%s", tag, msg, u, s, m, STR(top),
	     dlen > MAX_DUMP_BYTES ? "..." : "",
	     dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}

/* tlsa_carp - carp about a particular TLSA record */

static void tlsa_carp(const char *s1, const char *s2, const char *s3,
		            const char *s4, uint8_t u, uint8_t s, uint8_t m,
		              const unsigned char *data, ssize_t dlen)
{
    static VSTRING *top;
    static VSTRING *bot;

    if (top == 0)
	top = vstring_alloc(2 * MAX_HEAD_BYTES);
    if (bot == 0)
	bot = vstring_alloc(2 * MAX_TAIL_BYTES);

    if (dlen > MAX_DUMP_BYTES) {
	hex_encode(top, (char *) data, MAX_HEAD_BYTES);
	hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
    } else if (dlen > 0) {
	hex_encode(top, (char *) data, dlen);
    } else {
	vstring_sprintf(top, "...");
    }

    msg_warn("%s%s%s %s: %u %u %u %s%s%s", s1, s2, s3, s4, u, s, m, STR(top),
	     dlen > MAX_DUMP_BYTES ? "..." : "",
	     dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}

/* tls_dane_flush - flush the cache */

void    tls_dane_flush(void)
{
    if (dane_cache)
	ctable_free(dane_cache);
    dane_cache = 0;
}

/* dane_free - ctable style */

static void dane_free(void *dane, void *unused_context)
{
    tls_dane_free((TLS_DANE *) dane);
}

/* tls_dane_add_fpt_digests - map fingerprint list to DANE TLSA RRset */

void    tls_dane_add_fpt_digests(TLS_DANE *dane, int pkey_only,
			              const char *digest, const char *delim,
				         int smtp_mode)
{
    ARGV   *values = argv_split(digest, delim);
    ssize_t i;

    if (smtp_mode) {
	if (warn_compat_break_smtp_tls_fpt_dgst)
	    msg_info("using backwards-compatible default setting "
		     VAR_SMTP_TLS_FPT_DGST "=md5 to compute certificate "
		     "fingerprints");
    } else {
	if (warn_compat_break_lmtp_tls_fpt_dgst)
	    msg_info("using backwards-compatible default setting "
		     VAR_LMTP_TLS_FPT_DGST "=md5 to compute certificate "
		     "fingerprints");
    }

    for (i = 0; i < values->argc; ++i) {
	const char *cp = values->argv[i];
	size_t  ilen = strlen(cp);
	VSTRING *raw;

	/*
	 * Decode optionally colon-separated hex-encoded string, the input
	 * value requires at most 3 bytes per byte of payload, which must not
	 * exceed the size of the widest supported hash function.
	 */
	if (ilen > 3 * EVP_MAX_MD_SIZE) {
	    msg_warn("malformed fingerprint value: %.100s...",
		     values->argv[i]);
	    continue;
	}
	raw = vstring_alloc(ilen / 2);
	if (hex_decode_opt(raw, cp, ilen, HEX_DECODE_FLAG_ALLOW_COLON) == 0) {
	    myfree(raw);
	    msg_warn("malformed fingerprint value: %.384s", values->argv[i]);
	    continue;
	}
#define USTR_LEN(raw) (unsigned char *) STR(raw), VSTRING_LEN(raw)

	/*
	 * At the "fingerprint" security level certificate digests and public
	 * key digests are by default interchangeable.  Each leaf certificate
	 * is matched via either the public key digest or full certificate
	 * digest.  The DER encoding of a certificate is not a valid public
	 * key, and conversely, the DER encoding of a public key is not a
	 * valid certificate.  An attacker would need a 2nd-preimage that is
	 * feasible across types (given cert digest == some pkey digest) and
	 * yet presumably difficult within a type (e.g. given cert digest ==
	 * some other cert digest).  No such attacks are known at this time,
	 * and it is expected that if any are found they would work within as
	 * well as across the cert/pkey data types.
	 * 
	 * That said, when `pkey_only` is true, we match only public keys.
	 * 
	 * The private-use matching type "255" is mapped to the configured
	 * fingerprint digest, which may (harmlessly) coincide with one of
	 * the standard DANE digest algorithms.  The private code point is
	 * however unconditionally enabled.
	 */
	if (!pkey_only) {
	    dane->tlsa = tlsa_prepend(dane->tlsa, 3, 0, 255, USTR_LEN(raw));
	    if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
		tlsa_info("fingerprint", "digest as private-use TLSA record",
			  3, 0, 255, USTR_LEN(raw));
	}
	/* The public key match is unconditional */
	dane->tlsa = tlsa_prepend(dane->tlsa, 3, 1, 255, USTR_LEN(raw));
	if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
	    tlsa_info("fingerprint", "digest as private-use TLSA record",
		      3, 1, 255, USTR_LEN(raw));

	vstring_free(raw);
    }
    argv_free(values);
}

/* parse_tlsa_rr - parse a validated TLSA RRset */

static int parse_tlsa_rr(TLS_DANE *dane, DNS_RR *rr)
{
    const uint8_t *ip;
    uint8_t usage;
    uint8_t selector;
    uint8_t mtype;
    ssize_t dlen;
    unsigned const char *data;
    int     iscname = strcasecmp(rr->rname, rr->qname);
    const char *q = iscname ? rr->qname : "";
    const char *a = iscname ? " -> " : "";
    const char *r = rr->rname;

    if (rr->type != T_TLSA)
	msg_panic("%s%s%s: unexpected non-TLSA RR type: %u",
		  q, a, r, rr->type);

    /* Drop truncated records */
    if ((dlen = rr->data_len - 3) <= 0) {
	msg_warn("%s%s%s: truncated TLSA RR length == %u",
		 q, a, r, (unsigned) rr->data_len);
	return (0);
    }
    ip = (const uint8_t *) rr->data;
    usage = *ip++;
    selector = *ip++;
    mtype = *ip++;
    data = (const unsigned char *) ip;

    /*-
     * Drop unsupported usages.
     * Note: NO SUPPORT for usages 0/1 which do not apply to SMTP.
     */
    switch (usage) {
    case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
    case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
	break;
    default:
	tlsa_carp(q, a, r, "unsupported TLSA certificate usage",
		  usage, selector, mtype, data, dlen);
	return (0);
    }

    /*
     * Drop private-use matching type, reserved for fingerprint matching.
     */
    if (mtype == 255) {
	tlsa_carp(q, a, r, "reserved private-use matching type",
		  usage, selector, mtype, data, dlen);
	return (0);
    }
    if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
	tlsa_info("DNSSEC-signed TLSA record", r,
		  usage, selector, mtype, data, dlen);
    dane->tlsa = tlsa_prepend(dane->tlsa, usage, selector, mtype, data, dlen);
    return (1);
}

/* dane_lookup - TLSA record lookup, ctable style */

static void *dane_lookup(const char *tlsa_fqdn, void *unused_ctx)
{
    static VSTRING *why = 0;
    DNS_RR *rrs = 0;
    DNS_RR *rr;
    TLS_DANE *dane = tls_dane_alloc();
    int     ret;

    if (why == 0)
	why = vstring_alloc(10);

    ret = dns_lookup(tlsa_fqdn, T_TLSA, RES_USE_DNSSEC, &rrs, 0, why);

    switch (ret) {
    case DNS_OK:
	if (TLS_DANE_CACHE_TTL_MIN && rrs->ttl < TLS_DANE_CACHE_TTL_MIN)
	    rrs->ttl = TLS_DANE_CACHE_TTL_MIN;
	if (TLS_DANE_CACHE_TTL_MAX && rrs->ttl > TLS_DANE_CACHE_TTL_MAX)
	    rrs->ttl = TLS_DANE_CACHE_TTL_MAX;

	/* One more second to account for discrete time */
	dane->expires = 1 + event_time() + rrs->ttl;

	if (rrs->dnssec_valid) {
	    int     n = 0;

	    for (rr = rrs; rr != 0; rr = rr->next)
		n += parse_tlsa_rr(dane, rr);
	    if (n == 0)
		dane->flags |= TLS_DANE_FLAG_EMPTY;
	} else
	    dane->flags |= TLS_DANE_FLAG_NORRS;

	if (rrs)
	    dns_rr_free(rrs);
	break;

    case DNS_NOTFOUND:
	dane->flags |= TLS_DANE_FLAG_NORRS;
	dane->expires = 1 + event_time() + TLS_DANE_CACHE_TTL_MIN;
	break;

    default:
	msg_warn("DANE TLSA lookup problem: %s", STR(why));
	dane->flags |= TLS_DANE_FLAG_ERROR;
	break;
    }

    return (void *) dane;
}

/* resolve_host - resolve TLSA RRs for hostname (rname or qname) */

static TLS_DANE *resolve_host(const char *host, const char *proto,
			              unsigned port)
{
    static VSTRING *query_domain;
    TLS_DANE *dane;

    if (query_domain == 0)
	query_domain = vstring_alloc(64);

    vstring_sprintf(query_domain, "_%u._%s.%s", ntohs(port), proto, host);
    dane = (TLS_DANE *) ctable_locate(dane_cache, STR(query_domain));
    if (timecmp(event_time(), dane->expires) > 0)
	dane = (TLS_DANE *) ctable_refresh(dane_cache, STR(query_domain));
    if (dane->base_domain == 0)
	dane->base_domain = mystrdup(host);
    /* Increment ref-count of cached entry */
    ++dane->refs;
    return (dane);
}

/* qname_secure - Lookup qname DNSSEC status */

static int qname_secure(const char *qname)
{
    static VSTRING *why;
    int     ret = 0;
    DNS_RR *rrs;

    if (!why)
	why = vstring_alloc(10);

    /*
     * We assume that qname is already an fqdn, and does not need any
     * suffixes from RES_DEFNAME or RES_DNSRCH.  This is typically the name
     * of an MX host, and must be a complete DNS name.  DANE initialization
     * code in the SMTP client is responsible for checking that the default
     * resolver flags do not include RES_DEFNAME and RES_DNSRCH.
     */
    ret = dns_lookup(qname, T_CNAME, RES_USE_DNSSEC, &rrs, 0, why);
    if (ret == DNS_OK) {
	ret = rrs->dnssec_valid;
	dns_rr_free(rrs);
	return (ret);
    }
    if (ret == DNS_NOTFOUND)
	vstring_sprintf(why, "no longer a CNAME");
    msg_warn("DNSSEC status lookup error for %s: %s", qname, STR(why));
    return (-1);
}

/* tls_dane_resolve - cached map: (name, proto, port) -> TLS_DANE */

TLS_DANE *tls_dane_resolve(unsigned port, const char *proto, DNS_RR *hostrr,
			           int forcetlsa)
{
    TLS_DANE *dane = 0;
    int     iscname = strcasecmp(hostrr->rname, hostrr->qname);
    int     isvalid = 1;

    if (!tls_dane_avail())
	return (0);				/* Error */

    /*
     * By default suppress TLSA lookups for hosts in non-DNSSEC zones.  If
     * the host zone is not DNSSEC validated, the TLSA qname sub-domain is
     * safely assumed to not be in a DNSSEC Look-aside Validation child zone.
     */
    if (!forcetlsa && !hostrr->dnssec_valid) {
	isvalid = iscname ? qname_secure(hostrr->qname) : 0;
	if (isvalid < 0)
	    return (0);				/* Error */
    }
    if (!isvalid) {
	dane = tls_dane_alloc();
	dane->flags = TLS_DANE_FLAG_NORRS;
    } else {
	if (!dane_cache)
	    dane_cache = ctable_create(CACHE_SIZE, dane_lookup, dane_free, 0);

	/*
	 * Try the rname first if secure, if nothing there, try the qname if
	 * different.  Note, lookup errors are distinct from success with
	 * nothing found.  If the rname lookup fails we don't try the qname.
	 */
	if (hostrr->dnssec_valid) {
	    dane = resolve_host(hostrr->rname, proto, port);
	    if (tls_dane_notfound(dane) && iscname) {
		tls_dane_free(dane);
		dane = 0;
	    }
	}
	if (!dane)
	    dane = resolve_host(hostrr->qname, proto, port);
	if (dane->flags & TLS_DANE_FLAG_ERROR) {
	    /* We don't return this object. */
	    tls_dane_free(dane);
	    dane = 0;
	}
    }

    return (dane);
}

/* tls_dane_load_trustfile - load trust anchor certs or keys from file */

int     tls_dane_load_trustfile(TLS_DANE *dane, const char *tafile)
{
    BIO    *bp;
    char   *name = 0;
    char   *header = 0;
    unsigned char *data = 0;
    long    len;
    int     tacount;
    char   *errtype = 0;		/* if error: cert or pkey? */

    /* nop */
    if (tafile == 0 || *tafile == 0)
	return (1);

    /*
     * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio,
     * calls PEM_read_bio() and then frees the bio.  It is just as easy to
     * open a BIO as a stdio file, so we use BIOs and call PEM_read_bio()
     * directly.
     */
    if ((bp = BIO_new_file(tafile, "r")) == NULL) {
	msg_warn("error opening trust anchor file: %s: %m", tafile);
	return (0);
    }
    /* Don't report old news */
    ERR_clear_error();

    /*
     * OpenSSL implements DANE strictly, with DANE-TA(2) only matching issuer
     * certificates, and never the leaf cert.  We also allow the
     * trust-anchors to directly match the leaf certificate or public key.
     */
    for (tacount = 0;
	 errtype == 0 && PEM_read_bio(bp, &name, &header, &data, &len);
	 ++tacount) {
	uint8_t daneta = DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION;
	uint8_t daneee = DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE;
	uint8_t mtype = DNS_TLSA_MATCHING_TYPE_NO_HASH_USED;

	if (strcmp(name, PEM_STRING_X509) == 0
	    || strcmp(name, PEM_STRING_X509_OLD) == 0) {
	    uint8_t selector = DNS_TLSA_SELECTOR_FULL_CERTIFICATE;

	    if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
		tlsa_info("TA cert as TLSA record", tafile,
			  daneta, selector, mtype, data, len);
	    dane->tlsa =
		tlsa_prepend(dane->tlsa, daneta, selector, mtype, data, len);
	    dane->tlsa =
		tlsa_prepend(dane->tlsa, daneee, selector, mtype, data, len);
	} else if (strcmp(name, PEM_STRING_PUBLIC) == 0) {
	    uint8_t selector = DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO;

	    if (log_mask & (TLS_LOG_VERBOSE | TLS_LOG_DANE))
		tlsa_info("TA pkey as TLSA record", tafile,
			  daneta, selector, mtype, data, len);
	    dane->tlsa =
		tlsa_prepend(dane->tlsa, daneta, selector, mtype, data, len);
	    dane->tlsa = tlsa_prepend(dane->tlsa, daneee, selector, mtype, data, len);
	}

	/*
	 * If any of these were null, PEM_read() would have failed.
	 */
	OPENSSL_free(name);
	OPENSSL_free(header);
	OPENSSL_free(data);
    }
    BIO_free(bp);

    if (errtype) {
	tls_print_errors();
	msg_warn("error reading: %s: malformed trust-anchor %s",
		 tafile, errtype);
	return (0);
    }
    if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
	/* Reached end of PEM file */
	ERR_clear_error();
	return (tacount > 0);
    }
    /* Some other PEM read error */
    tls_print_errors();
    return (0);
}

int     tls_dane_enable(TLS_SESS_STATE *TLScontext)
{
    const TLS_DANE *dane = TLScontext->dane;
    TLS_TLSA *tp;
    SSL    *ssl = TLScontext->con;
    int     usable = 0;
    int     ret;
    int     rpk_compat = 1;

    for (tp = dane->tlsa; tp != 0; tp = tp->next) {
	ret = SSL_dane_tlsa_add(ssl, tp->usage, tp->selector,
				tp->mtype, tp->data, tp->length);
	if (ret > 0) {
	    ++usable;

	    /*
	     * Disable use of RFC7250 raw public keys if any TLSA record
	     * depends on X.509 certificates.  Only DANE-EE(3) SPKI(1)
	     * records can get by with just a public key.
	     */
	    if (tp->usage != DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE
		|| tp->selector != DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO)
		rpk_compat = 0;
	    continue;
	}
	if (ret == 0) {
	    tlsa_carp(TLScontext->namaddr, ":", "", "unusable TLSA RR",
		      tp->usage, tp->selector, tp->mtype, tp->data,
		      tp->length);
	    continue;
	}
	/* Internal problem in OpenSSL */
	tlsa_carp(TLScontext->namaddr, ":", "", "error loading trust settings",
		  tp->usage, tp->selector, tp->mtype, tp->data, tp->length);
	tls_print_errors();
	return (-1);
    }
    if (rpk_compat)
	tls_enable_server_rpk(NULL, ssl);

    return (usable);
}

/* tls_dane_digest_init - configure supported DANE digests */

void    tls_dane_digest_init(SSL_CTX *ctx, const EVP_MD *fpt_alg)
{
    dane_mtype mtypes[256];
    char   *cp;
    char   *save;
    char   *algname;
    uint8_t m;
    uint8_t ord = 0;
    uint8_t maxtype;

    memset((char *) mtypes, 0, sizeof(mtypes));

    /*
     * The DANE SHA2-256(1) and SHA2-512(2) algorithms are disabled, unless
     * explicitly enabled.  Other codepoints can be disabled explicitly by
     * giving them an empty digest name, which also implicitly disables all
     * smaller codepoints that are not explicitly assigned.
     * 
     * We reserve the private-use code point (255) for use with fingerprint
     * matching.  It MUST NOT be accepted in DNS replies.
     */
    mtypes[1].alg = NULL;
    mtypes[2].alg = NULL;
    mtypes[255].alg = fpt_alg;
    maxtype = 2;

    save = cp = mystrdup(var_tls_dane_digests);
    while ((algname = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
	char   *algcode = split_at(algname, '=');
	int     codepoint = -1;

	if (algcode && *algcode) {
	    unsigned long l;
	    char   *endcp;

	    /*
	     * XXX: safe_strtoul() does not flag empty or white-space only
	     * input.  Since we get algcode by splitting white-space/comma
	     * delimited tokens, this is not a problem here.
	     */
	    l = safe_strtoul(algcode, &endcp, 10);
	    if ((l == 0 && (errno == EINVAL || endcp == algcode))
		|| l >= 255 || *endcp) {
		msg_warn("Invalid matching type number in %s: %s=%s",
			 VAR_TLS_DANE_DIGESTS, algname, algcode);
		continue;
	    }
	    if (l == 0 || l == 255) {
		msg_warn("Reserved matching type number in %s: %s=%s",
			 VAR_TLS_DANE_DIGESTS, algname, algcode);
		continue;
	    }
	    codepoint = l;
	}
	/* Disable any codepoint gaps */
	if (codepoint > maxtype) {
	    while (++maxtype < codepoint)
		mtypes[codepoint].alg = NULL;
	    maxtype = codepoint;
	}
	/* Handle explicitly disabled codepoints */
	if (*algname == 0) {
	    /* Skip empty specifiers */
	    if (codepoint < 0)
		continue;
	    mtypes[codepoint].alg = NULL;
	    continue;
	}
	switch (codepoint) {
	case -1:
	    if (strcasecmp(algname, LN_sha256) == 0)
		codepoint = 1;			/* SHA2-256(1) */
	    else if (strcasecmp(algname, LN_sha512) == 0)
		codepoint = 2;			/* SHA2-512(2) */
	    else {
		msg_warn("%s: digest algorithm %s needs an explicit number",
			 VAR_TLS_DANE_DIGESTS, algname);
		continue;
	    }
	    break;
	case 1:
	    if (strcasecmp(algname, LN_sha256) != 0) {
		msg_warn("%s: matching type 1 can only be %s",
			 VAR_TLS_DANE_DIGESTS, LN_sha256);
		continue;
	    }
	    algname = LN_sha256;
	    break;
	case 2:
	    if (strcasecmp(algname, LN_sha512) != 0) {
		msg_warn("%s: matching type 2 can only be %s",
			 VAR_TLS_DANE_DIGESTS, LN_sha512);
		continue;
	    }
	    algname = LN_sha512;
	    break;
	default:
	    break;
	}

	if (mtypes[codepoint].ord != 0) {
	    msg_warn("%s: matching type %d specified more than once",
		     VAR_TLS_DANE_DIGESTS, codepoint);
	    continue;
	}
	mtypes[codepoint].ord = ++ord;

	if ((mtypes[codepoint].alg = tls_digest_byname(algname, NULL)) == 0) {
	    msg_warn("%s: digest algorithm \"%s\"(%d) unknown",
		     VAR_TLS_DANE_DIGESTS, algname, codepoint);
	    continue;
	}
    }
    myfree(save);

    for (m = 1; m != 0; m = m != maxtype ? m + 1 : 255) {

	/*
	 * In OpenSSL higher order ordinals are preferred, but we list the
	 * most preferred algorithms first, so the last ordinal becomes 1,
	 * next-to-last, 2, ...
	 * 
	 * The ordinals of non-disabled algorithms are always positive, and the
	 * computed value cannot overflow 254 (the largest possible value of
	 * 'ord' after loading each valid codepoint at most once).
	 */
	if (SSL_CTX_dane_mtype_set(ctx, mtypes[m].alg, m,
				   ord - mtypes[m].ord + 1) <= 0) {
	    msg_warn("%s: error configuring matching type %d",
		     VAR_TLS_DANE_DIGESTS, m);
	    tls_print_errors();
	}
    }
}

/* tls_dane_log - log DANE-based verification success */

void    tls_dane_log(TLS_SESS_STATE *TLScontext)
{
    static VSTRING *top;
    static VSTRING *bot;
    X509   *mcert = 0;
    EVP_PKEY *mspki = 0;
    int     depth = SSL_get0_dane_authority(TLScontext->con, &mcert, &mspki);
    uint8_t u, s, m;
    unsigned const char *data;
    size_t  dlen;

    if (depth < 0)
	return;					/* No DANE auth */

    switch (TLScontext->level) {
    case TLS_LEV_SECURE:
    case TLS_LEV_VERIFY:
	msg_info("%s: Matched trust anchor at depth %d",
		 TLScontext->namaddr, depth);
	return;
    }

    if (top == 0)
	top = vstring_alloc(2 * MAX_HEAD_BYTES);
    if (bot == 0)
	bot = vstring_alloc(2 * MAX_TAIL_BYTES);

    (void) SSL_get0_dane_tlsa(TLScontext->con, &u, &s, &m, &data, &dlen);
    if (dlen > MAX_DUMP_BYTES) {
	hex_encode(top, (char *) data, MAX_HEAD_BYTES);
	hex_encode(bot, (char *) data + dlen - MAX_TAIL_BYTES, MAX_TAIL_BYTES);
    } else {
	hex_encode(top, (char *) data, dlen);
    }

    if (TLScontext->level == TLS_LEV_FPRINT) {
	msg_info("%s: Matched fingerprint: %s%s%s", TLScontext->namaddr,
		 STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
		 dlen > MAX_DUMP_BYTES ? STR(bot) : "");
	return;
    }
#if OPENSSL_VERSION_PREREQ(3,2)
    if (SSL_get0_peer_rpk(TLScontext->con) != NULL) {
	msg_info("%s: Matched DANE raw public key: %u %u %u %s%s%s",
		 TLScontext->namaddr, u, s, m,
		 STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
		 dlen > MAX_DUMP_BYTES ? STR(bot) : "");
	return;
    }
#endif
    msg_info("%s: Matched DANE %s at depth %d: %u %u %u %s%s%s",
	     TLScontext->namaddr, mspki ?
	     "TA public key verified certificate" : depth ?
	     "TA certificate" : "EE certificate", depth, u, s, m,
	     STR(top), dlen > MAX_DUMP_BYTES ? "..." : "",
	     dlen > MAX_DUMP_BYTES ? STR(bot) : "");
}

#ifdef TEST

#include <unistd.h>
#include <stdarg.h>

#include <mail_params.h>
#include <mail_conf.h>
#include <msg_vstream.h>

static int verify_chain(SSL *ssl, x509_stack_t *chain, TLS_SESS_STATE *tctx)
{
    int     ret;
    X509   *cert;
    X509_STORE_CTX *store_ctx;
    SSL_CTX *ssl_ctx = SSL_get_SSL_CTX(ssl);
    X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
    int     store_ctx_idx = SSL_get_ex_data_X509_STORE_CTX_idx();

    cert = sk_X509_value(chain, 0);
    if ((store_ctx = X509_STORE_CTX_new()) == NULL) {
	SSLerr(SSL_F_SSL_VERIFY_CERT_CHAIN, ERR_R_MALLOC_FAILURE);
	return 0;
    }
    if (!X509_STORE_CTX_init(store_ctx, store, cert, chain)) {
	X509_STORE_CTX_free(store_ctx);
	return 0;
    }
    X509_STORE_CTX_set_ex_data(store_ctx, store_ctx_idx, ssl);

    /* We're *verifying* a server chain */
    X509_STORE_CTX_set_default(store_ctx, "ssl_server");
    X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(store_ctx),
			   SSL_get0_param(ssl));
    X509_STORE_CTX_set0_dane(store_ctx, SSL_get0_dane(ssl));

    ret = X509_verify_cert(store_ctx);

    SSL_set_verify_result(ssl, X509_STORE_CTX_get_error(store_ctx));
    X509_STORE_CTX_free(store_ctx);

    return (ret);
}

static void load_tlsa_args(SSL *ssl, char *argv[])
{
    const EVP_MD *md = 0;
    X509   *cert = 0;
    BIO    *bp;
    unsigned char *buf;
    unsigned char *buf2;
    int     len;
    uint8_t u = atoi(argv[1]);
    uint8_t s = atoi(argv[2]);
    uint8_t m = atoi(argv[3]);
    EVP_PKEY *pkey;

    /* Unsupported usages are fatal */
    switch (u) {
    case DNS_TLSA_USAGE_TRUST_ANCHOR_ASSERTION:
    case DNS_TLSA_USAGE_DOMAIN_ISSUED_CERTIFICATE:
	break;
    default:
	msg_fatal("unsupported certificate usage %u", u);
    }

    /* Unsupported selectors are fatal */
    switch (s) {
    case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
    case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
	break;
    default:
	msg_fatal("unsupported selector %u", s);
    }

    /* Unsupported selectors are fatal */
    switch (m) {
    case DNS_TLSA_MATCHING_TYPE_NO_HASH_USED:
    case DNS_TLSA_MATCHING_TYPE_SHA256:
    case DNS_TLSA_MATCHING_TYPE_SHA512:
	break;
    default:
	msg_fatal("unsupported matching type %u", m);
    }

    if ((bp = BIO_new_file(argv[4], "r")) == NULL)
	msg_fatal("error opening %s: %m", argv[4]);
    if (!PEM_read_bio_X509(bp, &cert, 0, 0)) {
	tls_print_errors();
	msg_fatal("error loading certificate from %s: %m", argv[4]);
    }
    BIO_free(bp);

    /*
     * Extract ASN.1 DER form of certificate or public key.
     */
    switch (s) {
    case DNS_TLSA_SELECTOR_FULL_CERTIFICATE:
	len = i2d_X509(cert, NULL);
	if (len > 0xffff)
	    msg_fatal("certificate too long: %d", len);
	buf2 = buf = (unsigned char *) mymalloc(len);
	i2d_X509(cert, &buf2);
	break;
    case DNS_TLSA_SELECTOR_SUBJECTPUBLICKEYINFO:
	pkey = X509_get_pubkey(cert);
	len = i2d_PUBKEY(pkey, NULL);
	if (len > 0xffff)
	    msg_fatal("public key too long: %d", len);
	buf2 = buf = (unsigned char *) mymalloc(len);
	i2d_PUBKEY(pkey, &buf2);
	EVP_PKEY_free(pkey);
	break;
    }
    X509_free(cert);
    OPENSSL_assert(buf2 - buf == len);

    switch (m) {
    case 0:
	break;
    case 1:
	if ((md = tls_digest_byname(LN_sha256, NULL)) == 0)
	    msg_fatal("Digest %s not found", LN_sha256);
	break;
    case 2:
	if ((md = tls_digest_byname(LN_sha512, NULL)) == 0)
	    msg_fatal("Digest %s not found", LN_sha512);
	break;
    default:
	msg_fatal("Unsupported DANE mtype: %d", m);
    }

    if (md != 0) {
	unsigned char mdbuf[EVP_MAX_MD_SIZE];
	unsigned int mdlen = sizeof(mdbuf);

	if (!EVP_Digest(buf, len, mdbuf, &mdlen, md, 0))
	    msg_fatal("Digest failure for mtype: %d", m);
	myfree(buf);
	buf = (unsigned char *) mymemdup(mdbuf, len = mdlen);
    }
    SSL_dane_tlsa_add(ssl, u, s, m, buf, len);
    myfree((void *) buf);
}

static x509_stack_t *load_chain(const char *chainfile)
{
    BIO    *bp;
    char   *name = 0;
    char   *header = 0;
    unsigned char *data = 0;
    long    len;
    int     count;
    char   *errtype = 0;		/* if error: cert or pkey? */
    x509_stack_t *chain;
    typedef X509 *(*d2i_X509_t) (X509 **, const unsigned char **, long);

    if ((chain = sk_X509_new_null()) == 0) {
	perror("malloc");
	exit(1);
    }

    /*
     * On each call, PEM_read() wraps a stdio file in a BIO_NOCLOSE bio,
     * calls PEM_read_bio() and then frees the bio.  It is just as easy to
     * open a BIO as a stdio file, so we use BIOs and call PEM_read_bio()
     * directly.
     */
    if ((bp = BIO_new_file(chainfile, "r")) == NULL) {
	fprintf(stderr, "error opening chainfile: %s: %m\n", chainfile);
	exit(1);
    }
    /* Don't report old news */
    ERR_clear_error();

    for (count = 0;
	 errtype == 0 && PEM_read_bio(bp, &name, &header, &data, &len);
	 ++count) {
	const unsigned char *p = data;

	if (strcmp(name, PEM_STRING_X509) == 0
	    || strcmp(name, PEM_STRING_X509_TRUSTED) == 0
	    || strcmp(name, PEM_STRING_X509_OLD) == 0) {
	    d2i_X509_t d;
	    X509   *cert;

	    d = strcmp(name, PEM_STRING_X509_TRUSTED) ? d2i_X509_AUX : d2i_X509;
	    if ((cert = d(0, &p, len)) == 0 || (p - data) != len)
		errtype = "certificate";
	    else if (sk_X509_push(chain, cert) == 0) {
		perror("malloc");
		exit(1);
	    }
	} else {
	    fprintf(stderr, "unexpected chain file object: %s\n", name);
	    exit(1);
	}

	/*
	 * If any of these were null, PEM_read() would have failed.
	 */
	OPENSSL_free(name);
	OPENSSL_free(header);
	OPENSSL_free(data);
    }
    BIO_free(bp);

    if (errtype) {
	tls_print_errors();
	fprintf(stderr, "error reading: %s: malformed %s", chainfile, errtype);
	exit(1);
    }
    if (ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) {
	/* Reached end of PEM file */
	ERR_clear_error();
	if (count > 0)
	    return chain;
	fprintf(stderr, "no certificates found in: %s\n", chainfile);
	exit(1);
    }
    /* Some other PEM read error */
    tls_print_errors();
    fprintf(stderr, "error reading: %s\n", chainfile);
    exit(1);
}

static void usage(const char *progname)
{
    fprintf(stderr, "Usage: %s certificate-usage selector matching-type"
	    " certfile \\\n\t\tCAfile chainfile hostname [certname ...]\n",
	    progname);
    fprintf(stderr, "  where, certificate-usage = TLSA certificate usage,\n");
    fprintf(stderr, "\t selector = TLSA selector,\n");
    fprintf(stderr, "\t matching-type = empty string or OpenSSL digest algorithm name,\n");
    fprintf(stderr, "\t PEM certfile provides certificate association data,\n");
    fprintf(stderr, "\t PEM CAfile contains any usage 0/1 trusted roots,\n");
    fprintf(stderr, "\t PEM chainfile = server chain file to verify\n");
    fprintf(stderr, "\t hostname = destination hostname,\n");
    fprintf(stderr, "\t each certname augments the hostname for name checks.\n");
    exit(1);
}

static SSL_CTX *ctx_init(const char *CAfile)
{
    SSL_CTX *client_ctx;

    tls_param_init();
    tls_check_version();

    if (TLScontext_index < 0)
	if ((TLScontext_index = SSL_get_ex_new_index(0, 0, 0, 0, 0)) < 0)
	    msg_fatal("Cannot allocate SSL application data index");

    ERR_clear_error();
    if ((client_ctx = SSL_CTX_new(TLS_client_method())) == 0)
	msg_fatal("cannot allocate client SSL_CTX");
    SSL_CTX_set_verify_depth(client_ctx, 5);

    /* Enable DANE support in OpenSSL */
    if (SSL_CTX_dane_enable(client_ctx) <= 0) {
	tls_print_errors();
	msg_fatal("OpenSSL DANE initialization failed");
    }
    if (tls_set_ca_certificate_info(client_ctx, CAfile, "") < 0) {
	tls_print_errors();
	msg_fatal("cannot load CAfile: %s", CAfile);
    }
    SSL_CTX_set_verify(client_ctx, SSL_VERIFY_NONE,
		       tls_verify_certificate_callback);
    return (client_ctx);
}

int     main(int argc, char *argv[])
{
    SSL_CTX *ssl_ctx;
    const EVP_MD *fpt_alg;
    TLS_SESS_STATE *tctx;
    x509_stack_t *chain;
    int     i;

    var_procname = mystrdup(basename(argv[0]));
    set_mail_conf_str(VAR_PROCNAME, var_procname);
    msg_vstream_init(var_procname, VSTREAM_OUT);

    if (argc < 8)
	usage(argv[0]);

    ssl_ctx = ctx_init(argv[5]);
    if (!tls_dane_avail())
	msg_fatal("DANE TLSA support not available");

    tctx = tls_alloc_sess_context(TLS_LOG_NONE, argv[7]);
    tctx->namaddr = argv[7];
    tctx->mdalg = atoi(argv[3]) == 2 ? LN_sha512 : LN_sha256;
    tctx->dane = tls_dane_alloc();

    if ((fpt_alg = tls_validate_digest(tctx->mdalg)) == 0)
	msg_fatal("fingerprint digest algorithm %s not found",
		  tctx->mdalg);
    tls_dane_digest_init(ssl_ctx, fpt_alg);

    if ((tctx->con = SSL_new(ssl_ctx)) == 0
	|| !SSL_set_ex_data(tctx->con, TLScontext_index, tctx)) {
	tls_print_errors();
	msg_fatal("Error allocating SSL connection");
    }
    if (SSL_dane_enable(tctx->con, 0) <= 0) {
	tls_print_errors();
	msg_fatal("Error enabling DANE for SSL handle");
    }
    SSL_dane_set_flags(tctx->con, DANE_FLAG_NO_DANE_EE_NAMECHECKS);
    SSL_dane_set_flags(tctx->con, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
    for (i = 7; i < argc; ++i)
	if (!TLS_ADD1_HOST(tctx->con, argv[i]))
	    msg_fatal("error adding hostname: %s", argv[i]);
    load_tlsa_args(tctx->con, argv);
    SSL_set_connect_state(tctx->con);

    /* Verify saved server chain */
    chain = load_chain(argv[6]);
    i = verify_chain(tctx->con, chain, tctx);
    tls_print_errors();

    if (i > 0) {
	const char *peername = SSL_get0_peername(tctx->con);

	if (peername == 0)
	    peername = argv[7];
	msg_info("Verified %s", peername);
    } else {
	int     r = SSL_get_verify_result(tctx->con);

	msg_info("certificate verification failed for %s:%s: num=%d:%s",
		 argv[6], argv[7], r, X509_verify_cert_error_string(r));
    }

    return (i <= 0);
}

#endif					/* TEST */

#endif					/* USE_TLS */
