Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
radsecproxy/tlscommon.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
685 lines (618 sloc)
17.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2007-2009, UNINETT AS | |
* Copyright (c) 2010-2011,2015-2016, NORDUnet A/S */ | |
/* See LICENSE for licensing information. */ | |
#if defined(RADPROT_TLS) || defined(RADPROT_DTLS) | |
#include <signal.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#include <netdb.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <limits.h> | |
#ifdef SYS_SOLARIS9 | |
#include <fcntl.h> | |
#endif | |
#include <sys/time.h> | |
#include <sys/types.h> | |
#include <sys/select.h> | |
#include <ctype.h> | |
#include <sys/wait.h> | |
#include <arpa/inet.h> | |
#include <regex.h> | |
#include <libgen.h> | |
#include <pthread.h> | |
#include <openssl/ssl.h> | |
#include <openssl/rand.h> | |
#include <openssl/err.h> | |
#include <openssl/md5.h> | |
#include <openssl/x509v3.h> | |
#include "debug.h" | |
#include "hash.h" | |
#include "util.h" | |
#include "hostport.h" | |
#include "radsecproxy.h" | |
static struct hash *tlsconfs = NULL; | |
static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) { | |
int pwdlen = strlen(userdata); | |
if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */ | |
return 0; | |
memcpy(buf, userdata, pwdlen); | |
return pwdlen; | |
} | |
static int verify_cb(int ok, X509_STORE_CTX *ctx) { | |
char *buf = NULL; | |
X509 *err_cert; | |
int err, depth; | |
err_cert = X509_STORE_CTX_get_current_cert(ctx); | |
err = X509_STORE_CTX_get_error(ctx); | |
depth = X509_STORE_CTX_get_error_depth(ctx); | |
if (depth > MAX_CERT_DEPTH) { | |
ok = 0; | |
err = X509_V_ERR_CERT_CHAIN_TOO_LONG; | |
X509_STORE_CTX_set_error(ctx, err); | |
} | |
if (!ok) { | |
if (err_cert) | |
buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0); | |
debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : ""); | |
free(buf); | |
buf = NULL; | |
switch (err) { | |
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: | |
if (err_cert) { | |
buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0); | |
if (buf) { | |
debug(DBG_WARN, "\tIssuer=%s", buf); | |
free(buf); | |
buf = NULL; | |
} | |
} | |
break; | |
case X509_V_ERR_CERT_NOT_YET_VALID: | |
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: | |
debug(DBG_WARN, "\tCertificate not yet valid"); | |
break; | |
case X509_V_ERR_CERT_HAS_EXPIRED: | |
debug(DBG_WARN, "Certificate has expired"); | |
break; | |
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: | |
debug(DBG_WARN, "Certificate no longer valid (after notAfter)"); | |
break; | |
case X509_V_ERR_NO_EXPLICIT_POLICY: | |
debug(DBG_WARN, "No Explicit Certificate Policy"); | |
break; | |
} | |
} | |
#ifdef DEBUG | |
printf("certificate verify returns %d\n", ok); | |
#endif | |
return ok; | |
} | |
#ifdef DEBUG | |
static void ssl_info_callback(const SSL *ssl, int where, int ret) { | |
const char *s; | |
int w; | |
w = where & ~SSL_ST_MASK; | |
if (w & SSL_ST_CONNECT) | |
s = "SSL_connect"; | |
else if (w & SSL_ST_ACCEPT) | |
s = "SSL_accept"; | |
else | |
s = "undefined"; | |
if (where & SSL_CB_LOOP) | |
debug(DBG_DBG, "%s:%s\n", s, SSL_state_string_long(ssl)); | |
else if (where & SSL_CB_ALERT) { | |
s = (where & SSL_CB_READ) ? "read" : "write"; | |
debug(DBG_DBG, "SSL3 alert %s:%s:%s\n", s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); | |
} | |
else if (where & SSL_CB_EXIT) { | |
if (ret == 0) | |
debug(DBG_DBG, "%s:failed in %s\n", s, SSL_state_string_long(ssl)); | |
else if (ret < 0) | |
debug(DBG_DBG, "%s:error in %s\n", s, SSL_state_string_long(ssl)); | |
} | |
} | |
#endif | |
static X509_VERIFY_PARAM *createverifyparams(char **poids) { | |
X509_VERIFY_PARAM *pm; | |
ASN1_OBJECT *pobject; | |
int i; | |
pm = X509_VERIFY_PARAM_new(); | |
if (!pm) | |
return NULL; | |
for (i = 0; poids[i]; i++) { | |
pobject = OBJ_txt2obj(poids[i], 0); | |
if (!pobject) { | |
X509_VERIFY_PARAM_free(pm); | |
return NULL; | |
} | |
X509_VERIFY_PARAM_add0_policy(pm, pobject); | |
} | |
X509_VERIFY_PARAM_set_flags(pm, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY); | |
return pm; | |
} | |
static int tlsaddcacrl(SSL_CTX *ctx, struct tls *conf) { | |
STACK_OF(X509_NAME) *calist; | |
X509_STORE *x509_s; | |
unsigned long error; | |
SSL_CTX_set_cert_store(ctx, X509_STORE_new()); | |
if (!SSL_CTX_load_verify_locations(ctx, conf->cacertfile, conf->cacertpath)) { | |
while ((error = ERR_get_error())) | |
debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); | |
debug(DBG_ERR, "tlsaddcacrl: Error updating TLS context %s", conf->name); | |
return 0; | |
} | |
calist = conf->cacertfile ? SSL_load_client_CA_file(conf->cacertfile) : NULL; | |
if (!conf->cacertfile || calist) { | |
if (conf->cacertpath) { | |
if (!calist) | |
calist = sk_X509_NAME_new_null(); | |
if (!SSL_add_dir_cert_subjects_to_stack(calist, conf->cacertpath)) { | |
sk_X509_NAME_free(calist); | |
calist = NULL; | |
} | |
} | |
} | |
if (!calist) { | |
while ((error = ERR_get_error())) | |
debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); | |
debug(DBG_ERR, "tlsaddcacrl: Error adding CA subjects in TLS context %s", conf->name); | |
return 0; | |
} | |
ERR_clear_error(); /* add_dir_cert_subj returns errors on success */ | |
SSL_CTX_set_client_CA_list(ctx, calist); | |
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb); | |
SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1); | |
if (conf->crlcheck || conf->vpm) { | |
x509_s = SSL_CTX_get_cert_store(ctx); | |
if (conf->crlcheck) | |
X509_STORE_set_flags(x509_s, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); | |
if (conf->vpm) | |
X509_STORE_set1_param(x509_s, conf->vpm); | |
} | |
debug(DBG_DBG, "tlsaddcacrl: updated TLS context %s", conf->name); | |
return 1; | |
} | |
static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) { | |
SSL_CTX *ctx = NULL; | |
unsigned long error; | |
long sslversion = SSLeay(); | |
switch (type) { | |
#ifdef RADPROT_TLS | |
case RAD_TLS: | |
#if OPENSSL_VERSION_NUMBER >= 0x10100000 | |
/* TLS_method() was introduced in OpenSSL 1.1.0. */ | |
ctx = SSL_CTX_new(TLS_method()); | |
#else | |
/* No TLS_method(), use SSLv23_method() and disable SSLv2 and SSLv3. */ | |
ctx = SSL_CTX_new(SSLv23_method()); | |
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | |
#endif | |
#ifdef DEBUG | |
SSL_CTX_set_info_callback(ctx, ssl_info_callback); | |
#endif | |
break; | |
#endif | |
#ifdef RADPROT_DTLS | |
case RAD_DTLS: | |
#if OPENSSL_VERSION_NUMBER >= 0x10002000 | |
/* DTLS_method() seems to have been introduced in OpenSSL 1.0.2. */ | |
ctx = SSL_CTX_new(DTLS_method()); | |
#else | |
ctx = SSL_CTX_new(DTLSv1_method()); | |
#endif | |
#ifdef DEBUG | |
SSL_CTX_set_info_callback(ctx, ssl_info_callback); | |
#endif | |
SSL_CTX_set_read_ahead(ctx, 1); | |
break; | |
#endif | |
} | |
if (!ctx) { | |
debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name); | |
return NULL; | |
} | |
if (sslversion < 0x00908100L || | |
(sslversion >= 0x10000000L && sslversion < 0x10000020L)) { | |
debug(DBG_WARN, "%s: %s seems to be of a version with a " | |
"certain security critical bug (fixed in OpenSSL 0.9.8p and " | |
"1.0.0b). Disabling OpenSSL session caching for context %p.", | |
__func__, SSLeay_version(SSLEAY_VERSION), ctx); | |
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); | |
} | |
if (conf->certkeypwd) { | |
SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->certkeypwd); | |
SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb); | |
} | |
if (!SSL_CTX_use_certificate_chain_file(ctx, conf->certfile) || | |
!SSL_CTX_use_PrivateKey_file(ctx, conf->certkeyfile, SSL_FILETYPE_PEM) || | |
!SSL_CTX_check_private_key(ctx)) { | |
while ((error = ERR_get_error())) | |
debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL)); | |
debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name); | |
SSL_CTX_free(ctx); | |
return NULL; | |
} | |
if (conf->policyoids) { | |
if (!conf->vpm) { | |
conf->vpm = createverifyparams(conf->policyoids); | |
if (!conf->vpm) { | |
debug(DBG_ERR, "tlscreatectx: Failed to add policyOIDs in TLS context %s", conf->name); | |
SSL_CTX_free(ctx); | |
return NULL; | |
} | |
} | |
} | |
if (!tlsaddcacrl(ctx, conf)) { | |
if (conf->vpm) { | |
X509_VERIFY_PARAM_free(conf->vpm); | |
conf->vpm = NULL; | |
} | |
SSL_CTX_free(ctx); | |
return NULL; | |
} | |
debug(DBG_DBG, "tlscreatectx: created TLS context %s", conf->name); | |
return ctx; | |
} | |
struct tls *tlsgettls(char *alt1, char *alt2) { | |
struct tls *t; | |
t = hash_read(tlsconfs, alt1, strlen(alt1)); | |
if (!t) | |
t = hash_read(tlsconfs, alt2, strlen(alt2)); | |
return t; | |
} | |
SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) { | |
struct timeval now; | |
if (!t) | |
return NULL; | |
gettimeofday(&now, NULL); | |
switch (type) { | |
#ifdef RADPROT_TLS | |
case RAD_TLS: | |
if (t->tlsexpiry && t->tlsctx) { | |
if (t->tlsexpiry < now.tv_sec) { | |
t->tlsexpiry = now.tv_sec + t->cacheexpiry; | |
tlsaddcacrl(t->tlsctx, t); | |
} | |
} | |
if (!t->tlsctx) { | |
t->tlsctx = tlscreatectx(RAD_TLS, t); | |
if (t->cacheexpiry) | |
t->tlsexpiry = now.tv_sec + t->cacheexpiry; | |
} | |
return t->tlsctx; | |
#endif | |
#ifdef RADPROT_DTLS | |
case RAD_DTLS: | |
if (t->dtlsexpiry && t->dtlsctx) { | |
if (t->dtlsexpiry < now.tv_sec) { | |
t->dtlsexpiry = now.tv_sec + t->cacheexpiry; | |
tlsaddcacrl(t->dtlsctx, t); | |
} | |
} | |
if (!t->dtlsctx) { | |
t->dtlsctx = tlscreatectx(RAD_DTLS, t); | |
if (t->cacheexpiry) | |
t->dtlsexpiry = now.tv_sec + t->cacheexpiry; | |
} | |
return t->dtlsctx; | |
#endif | |
} | |
return NULL; | |
} | |
void tlsreloadcrls() { | |
struct tls *conf; | |
struct hash_entry *entry; | |
struct timeval now; | |
gettimeofday(&now, NULL); | |
for (entry = hash_first(tlsconfs); entry; entry = hash_next(entry)) { | |
conf = (struct tls *)entry->data; | |
#ifdef RADPROT_TLS | |
if (conf->tlsctx) { | |
if (conf->tlsexpiry) | |
conf->tlsexpiry = now.tv_sec + conf->cacheexpiry; | |
tlsaddcacrl(conf->tlsctx, conf); | |
} | |
#endif | |
#ifdef RADPROT_DTLS | |
if (conf->dtlsctx) { | |
if (conf->dtlsexpiry) | |
conf->dtlsexpiry = now.tv_sec + conf->cacheexpiry; | |
tlsaddcacrl(conf->dtlsctx, conf); | |
} | |
#endif | |
} | |
} | |
X509 *verifytlscert(SSL *ssl) { | |
X509 *cert; | |
unsigned long error; | |
if (SSL_get_verify_result(ssl) != X509_V_OK) { | |
debug(DBG_ERR, "verifytlscert: basic validation failed"); | |
while ((error = ERR_get_error())) | |
debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL)); | |
return NULL; | |
} | |
cert = SSL_get_peer_certificate(ssl); | |
if (!cert) | |
debug(DBG_ERR, "verifytlscert: failed to obtain certificate"); | |
return cert; | |
} | |
static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) { | |
int loc, i, l, n, r = 0; | |
char *v; | |
X509_EXTENSION *ex; | |
STACK_OF(GENERAL_NAME) *alt; | |
GENERAL_NAME *gn; | |
debug(DBG_DBG, "subjectaltnameaddr"); | |
loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); | |
if (loc < 0) | |
return r; | |
ex = X509_get_ext(cert, loc); | |
alt = X509V3_EXT_d2i(ex); | |
if (!alt) | |
return r; | |
n = sk_GENERAL_NAME_num(alt); | |
for (i = 0; i < n; i++) { | |
gn = sk_GENERAL_NAME_value(alt, i); | |
if (gn->type != GEN_IPADD) | |
continue; | |
r = -1; | |
v = (char *)ASN1_STRING_get0_data(gn->d.ia5); | |
l = ASN1_STRING_length(gn->d.ia5); | |
if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr))) | |
&& !memcmp(v, &addr, l)) { | |
r = 1; | |
break; | |
} | |
} | |
GENERAL_NAMES_free(alt); | |
return r; | |
} | |
static int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *regex) { | |
int loc, i, l, n, r = 0; | |
char *s, *v; | |
X509_EXTENSION *ex; | |
STACK_OF(GENERAL_NAME) *alt; | |
GENERAL_NAME *gn; | |
debug(DBG_DBG, "subjectaltnameregexp"); | |
loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); | |
if (loc < 0) | |
return r; | |
ex = X509_get_ext(cert, loc); | |
alt = X509V3_EXT_d2i(ex); | |
if (!alt) | |
return r; | |
n = sk_GENERAL_NAME_num(alt); | |
for (i = 0; i < n; i++) { | |
gn = sk_GENERAL_NAME_value(alt, i); | |
if (gn->type != type) | |
continue; | |
r = -1; | |
v = (char *)ASN1_STRING_get0_data(gn->d.ia5); | |
l = ASN1_STRING_length(gn->d.ia5); | |
if (l <= 0) | |
continue; | |
#ifdef DEBUG | |
printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, (uint8_t *) v, l); | |
#endif | |
if (exact) { | |
if (memcmp(v, exact, l)) | |
continue; | |
} else { | |
s = stringcopy((char *)v, l); | |
if (!s) { | |
debug(DBG_ERR, "malloc failed"); | |
continue; | |
} | |
if (regexec(regex, s, 0, NULL, 0)) { | |
free(s); | |
continue; | |
} | |
free(s); | |
} | |
r = 1; | |
break; | |
} | |
GENERAL_NAMES_free(alt); | |
return r; | |
} | |
static int cnregexp(X509 *cert, char *exact, regex_t *regex) { | |
int loc, l; | |
char *v, *s; | |
X509_NAME *nm; | |
X509_NAME_ENTRY *e; | |
ASN1_STRING *t; | |
nm = X509_get_subject_name(cert); | |
loc = -1; | |
for (;;) { | |
loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc); | |
if (loc == -1) | |
break; | |
e = X509_NAME_get_entry(nm, loc); | |
t = X509_NAME_ENTRY_get_data(e); | |
v = (char *) ASN1_STRING_get0_data(t); | |
l = ASN1_STRING_length(t); | |
if (l < 0) | |
continue; | |
if (exact) { | |
if (l == strlen(exact) && !strncasecmp(exact, v, l)) | |
return 1; | |
} else { | |
s = stringcopy((char *)v, l); | |
if (!s) { | |
debug(DBG_ERR, "malloc failed"); | |
continue; | |
} | |
if (regexec(regex, s, 0, NULL, 0)) { | |
free(s); | |
continue; | |
} | |
free(s); | |
return 1; | |
} | |
} | |
return 0; | |
} | |
/* this is a bit sloppy, should not always accept match to any */ | |
int certnamecheck(X509 *cert, struct list *hostports) { | |
struct list_node *entry; | |
struct hostportres *hp; | |
int r; | |
uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */ | |
struct in6_addr addr; | |
for (entry = list_first(hostports); entry; entry = list_next(entry)) { | |
hp = (struct hostportres *)entry->data; | |
if (hp->prefixlen != 255) { | |
/* we disable the check for prefixes */ | |
return 1; | |
} | |
if (inet_pton(AF_INET, hp->host, &addr)) | |
type = AF_INET; | |
else if (inet_pton(AF_INET6, hp->host, &addr)) | |
type = AF_INET6; | |
else | |
type = 0; | |
r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, hp->host, NULL); | |
if (r) { | |
if (r > 0) { | |
debug(DBG_DBG, "certnamecheck: Found subjectaltname matching %s %s", type ? "address" : "host", hp->host); | |
return 1; | |
} | |
debug(DBG_WARN, "certnamecheck: No subjectaltname matching %s %s", type ? "address" : "host", hp->host); | |
} else { | |
if (cnregexp(cert, hp->host, NULL)) { | |
debug(DBG_DBG, "certnamecheck: Found cn matching host %s", hp->host); | |
return 1; | |
} | |
debug(DBG_WARN, "certnamecheck: cn not matching host %s", hp->host); | |
} | |
} | |
return 0; | |
} | |
int verifyconfcert(X509 *cert, struct clsrvconf *conf) { | |
if (conf->certnamecheck) { | |
if (!certnamecheck(cert, conf->hostports)) { | |
debug(DBG_WARN, "verifyconfcert: certificate name check failed"); | |
return 0; | |
} | |
debug(DBG_WARN, "verifyconfcert: certificate name check ok"); | |
} | |
if (conf->certcnregex) { | |
if (cnregexp(cert, NULL, conf->certcnregex) < 1) { | |
debug(DBG_WARN, "verifyconfcert: CN not matching regex"); | |
return 0; | |
} | |
debug(DBG_DBG, "verifyconfcert: CN matching regex"); | |
} | |
if (conf->certuriregex) { | |
if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) { | |
debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex"); | |
return 0; | |
} | |
debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex"); | |
} | |
return 1; | |
} | |
int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) { | |
struct tls *conf; | |
long int expiry = LONG_MIN; | |
debug(DBG_DBG, "conftls_cb called for %s", block); | |
conf = malloc(sizeof(struct tls)); | |
if (!conf) { | |
debug(DBG_ERR, "conftls_cb: malloc failed"); | |
return 0; | |
} | |
memset(conf, 0, sizeof(struct tls)); | |
if (!getgenericconfig(cf, block, | |
"CACertificateFile", CONF_STR, &conf->cacertfile, | |
"CACertificatePath", CONF_STR, &conf->cacertpath, | |
"CertificateFile", CONF_STR, &conf->certfile, | |
"CertificateKeyFile", CONF_STR, &conf->certkeyfile, | |
"CertificateKeyPassword", CONF_STR, &conf->certkeypwd, | |
"CacheExpiry", CONF_LINT, &expiry, | |
"CRLCheck", CONF_BLN, &conf->crlcheck, | |
"PolicyOID", CONF_MSTR, &conf->policyoids, | |
NULL | |
)) { | |
debug(DBG_ERR, "conftls_cb: configuration error in block %s", val); | |
goto errexit; | |
} | |
if (!conf->certfile || !conf->certkeyfile) { | |
debug(DBG_ERR, "conftls_cb: TLSCertificateFile and TLSCertificateKeyFile must be specified in block %s", val); | |
goto errexit; | |
} | |
if (!conf->cacertfile && !conf->cacertpath) { | |
debug(DBG_ERR, "conftls_cb: CA Certificate file or path need to be specified in block %s", val); | |
goto errexit; | |
} | |
if (expiry != LONG_MIN) { | |
if (expiry < 0) { | |
debug(DBG_ERR, "error in block %s, value of option CacheExpiry is %ld, may not be negative", val, expiry); | |
goto errexit; | |
} | |
conf->cacheexpiry = expiry; | |
} | |
conf->name = stringcopy(val, 0); | |
if (!conf->name) { | |
debug(DBG_ERR, "conftls_cb: malloc failed"); | |
goto errexit; | |
} | |
if (!tlsconfs) | |
tlsconfs = hash_create(); | |
if (!hash_insert(tlsconfs, val, strlen(val), conf)) { | |
debug(DBG_ERR, "conftls_cb: malloc failed"); | |
goto errexit; | |
} | |
if (!tlsgetctx(RAD_TLS, conf)) | |
debug(DBG_ERR, "conftls_cb: error creating ctx for TLS block %s", val); | |
debug(DBG_DBG, "conftls_cb: added TLS block %s", val); | |
return 1; | |
errexit: | |
free(conf->cacertfile); | |
free(conf->cacertpath); | |
free(conf->certfile); | |
free(conf->certkeyfile); | |
free(conf->certkeypwd); | |
freegconfmstr(conf->policyoids); | |
free(conf); | |
return 0; | |
} | |
int addmatchcertattr(struct clsrvconf *conf) { | |
char *v; | |
regex_t **r; | |
if (!strncasecmp(conf->matchcertattr, "CN:/", 4)) { | |
r = &conf->certcnregex; | |
v = conf->matchcertattr + 4; | |
} else if (!strncasecmp(conf->matchcertattr, "SubjectAltName:URI:/", 20)) { | |
r = &conf->certuriregex; | |
v = conf->matchcertattr + 20; | |
} else | |
return 0; | |
if (!*v) | |
return 0; | |
/* regexp, remove optional trailing / if present */ | |
if (v[strlen(v) - 1] == '/') | |
v[strlen(v) - 1] = '\0'; | |
if (!*v) | |
return 0; | |
*r = malloc(sizeof(regex_t)); | |
if (!*r) { | |
debug(DBG_ERR, "malloc failed"); | |
return 0; | |
} | |
if (regcomp(*r, v, REG_EXTENDED | REG_ICASE | REG_NOSUB)) { | |
free(*r); | |
*r = NULL; | |
debug(DBG_ERR, "failed to compile regular expression %s", v); | |
return 0; | |
} | |
return 1; | |
} | |
#else | |
/* Just to makes file non-empty, should rather avoid compiling this file when not needed */ | |
static void tlsdummy() { | |
} | |
#endif | |
/* Local Variables: */ | |
/* c-file-style: "stroustrup" */ | |
/* End: */ |