/* * Copyright (c) 2015 Proofpoint, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ #include SM_RCSID("@(#)$Id: tls.c,v 8.127 2013-11-27 02:51:11 gshapiro Exp $") #if STARTTLS # include /* ** DATA2HEX -- create a printable hex string from binary data ("%02X:") ** ** Parameters: ** buf -- data ** len -- length of data ** hex -- output buffer ** hlen -- length of output buffer ** ** Returns: ** <0: errno ** >0: length of data in hex */ int data2hex(buf, blen, hex, hlen) unsigned char *buf; int blen; unsigned char *hex; int hlen; { int r, h; static const char hexcodes[] = "0123456789ABCDEF"; SM_REQUIRE(buf != NULL); SM_REQUIRE(hex != NULL); if (blen * 3 + 2 > hlen) return -ERANGE; for (r = 0, h = 0; r < blen && h + 3 < hlen; r++) { hex[h++] = hexcodes[(buf[r] & 0xf0) >> 4]; hex[h++] = hexcodes[(buf[r] & 0x0f)]; if (r + 1 < blen) hex[h++] = ':'; } if (h >= hlen) return -ERANGE; hex[h] = '\0'; return h; } /* ** TLS_DATA_MD -- calculate MD for data ** ** Parameters: ** buf -- data (in and out!) ** len -- length of data ** md -- digest algorithm ** ** Returns: ** <=0: cert fp calculation failed ** >0: len of fp ** ** Side Effects: ** writes digest to buf */ static int tls_data_md(buf, len, md) unsigned char *buf; int len; const EVP_MD *md; { unsigned int md_len; EVP_MD_CTX *mdctx; unsigned char md_buf[EVP_MAX_MD_SIZE]; SM_REQUIRE(buf != NULL); SM_REQUIRE(md != NULL); SM_REQUIRE(len >= EVP_MAX_MD_SIZE); mdctx = EVP_MD_CTX_create(); if (EVP_DigestInit_ex(mdctx, md, NULL) != 1) return -EINVAL; if (EVP_DigestUpdate(mdctx, (void *)buf, len) != 1) return -EINVAL; if (EVP_DigestFinal_ex(mdctx, md_buf, &md_len) != 1) return -EINVAL; EVP_MD_CTX_destroy(mdctx); if (md_len > len) return -ERANGE; (void) memcpy(buf, md_buf, md_len); return (int)md_len; } #if DANE /* ** PUBKEY_FP -- get public key fingerprint ** ** Parameters: ** cert -- TLS cert ** mdalg -- name of digest algorithm ** fp -- (pointer to) fingerprint buffer ** ** Returns: ** <=0: cert fp calculation failed ** >0: len of fp */ int pubkey_fp(cert, mdalg, fp) X509 *cert; const char *mdalg; char **fp; { int len, r; unsigned char *buf, *end; const EVP_MD *md; SM_ASSERT(cert != NULL); SM_ASSERT(fp != NULL); SM_ASSERT(mdalg != NULL); len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL); /* what's an acceptable upper limit? */ if (len <= 0 || len >= 8192) return -EINVAL; if (len < EVP_MAX_MD_SIZE) len = EVP_MAX_MD_SIZE; end = buf = sm_malloc(len); if (NULL == buf) return -ENOMEM; if ('\0' == mdalg[0]) { r = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &end); if (r <= 0 || r != len) return -EINVAL; *fp = (char *)buf; return len; } md = EVP_get_digestbyname(mdalg); if (NULL == md) return DANE_VRFY_FAIL; len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &end); r = tls_data_md(buf, len, md); if (r < 0) sm_free(buf); else *fp = (char *)buf; return r; } /* ** DANE_TLSA_CHK -- check whether a TLSA RR is ok to use ** ** Parameters: ** rr -- RR ** len -- length of RR ** host -- name of host for RR (only for logging) ** log -- whether to log problems ** ** Returns: ** TLSA_*, see tls.h */ int dane_tlsa_chk(rr, len, host, log) const char *rr; int len; const char *host; bool log; { int alg; if (len < 4) { if (log && LogLevel > 8) sm_syslog(LOG_WARNING, NOQID, "TLSA=%s, len=%d, status=bogus", host, len); return TLSA_BOGUS; } SM_ASSERT(rr != NULL); alg = (int)rr[2]; if ((int)rr[0] == 3 && (int)rr[1] == 1 && (alg >= 0 || alg <= 2)) return alg; if (log && LogLevel > 9) sm_syslog(LOG_NOTICE, NOQID, "TLSA=%s, type=%d-%d-%d:%02x, status=unsupported", host, (int)rr[0], (int)rr[1], (int)rr[2], (int)rr[3]); return TLSA_UNSUPP; } /* ** DANE_TLSA_CLR -- clear data in a dane_tlsa structure (for use) ** ** Parameters: ** dane_tlsa -- dane_tlsa to clear ** ** Returns: ** 1 if NULL ** 0 if ok */ int dane_tlsa_clr(dane_tlsa) dane_tlsa_P dane_tlsa; { int i; if (dane_tlsa == NULL) return 1; for (i = 0; i < dane_tlsa->dane_tlsa_n; i++) { SM_FREE(dane_tlsa->dane_tlsa_rr[i]); dane_tlsa->dane_tlsa_len[i] = 0; } SM_FREE(dane_tlsa->dane_tlsa_sni); memset(dane_tlsa, '\0', sizeof(*dane_tlsa)); return 0; } /* ** DANE_TLSA_FREE -- free a dane_tlsa structure ** ** Parameters: ** dane_tlsa -- dane_tlsa to free ** ** Returns: ** 0 if ok ** 1 if NULL */ int dane_tlsa_free(dane_tlsa) dane_tlsa_P dane_tlsa; { if (dane_tlsa == NULL) return 1; dane_tlsa_clr(dane_tlsa); SM_FREE(dane_tlsa); return 0; } #endif /* DANE */ #endif /* STARTTLS */