2 * Copyright (C) 2006-2009 Stig Venaas <venaas@uninett.no>
3 * Copyright (C) 2010,2011 NORDUnet A/S
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
10 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
12 #include <sys/socket.h>
13 #include <netinet/in.h>
22 #include <sys/types.h>
23 #include <sys/select.h>
26 #include <arpa/inet.h>
30 #include <openssl/ssl.h>
31 #include <openssl/rand.h>
32 #include <openssl/err.h>
33 #include <openssl/md5.h>
34 #include <openssl/x509v3.h>
39 #include "radsecproxy.h"
41 static struct hash *tlsconfs = NULL;
43 static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) {
44 int pwdlen = strlen(userdata);
45 if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */
47 memcpy(buf, userdata, pwdlen);
51 static int verify_cb(int ok, X509_STORE_CTX *ctx) {
56 err_cert = X509_STORE_CTX_get_current_cert(ctx);
57 err = X509_STORE_CTX_get_error(ctx);
58 depth = X509_STORE_CTX_get_error_depth(ctx);
60 if (depth > MAX_CERT_DEPTH) {
62 err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
63 X509_STORE_CTX_set_error(ctx, err);
68 buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0);
69 debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : "");
74 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
76 buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0);
78 debug(DBG_WARN, "\tIssuer=%s", buf);
84 case X509_V_ERR_CERT_NOT_YET_VALID:
85 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
86 debug(DBG_WARN, "\tCertificate not yet valid");
88 case X509_V_ERR_CERT_HAS_EXPIRED:
89 debug(DBG_WARN, "Certificate has expired");
91 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
92 debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
94 case X509_V_ERR_NO_EXPLICIT_POLICY:
95 debug(DBG_WARN, "No Explicit Certificate Policy");
100 printf("certificate verify returns %d\n", ok);
106 static void ssl_info_callback(const SSL *ssl, int where, int ret) {
110 w = where & ~SSL_ST_MASK;
112 if (w & SSL_ST_CONNECT)
114 else if (w & SSL_ST_ACCEPT)
119 if (where & SSL_CB_LOOP)
120 debug(DBG_DBG, "%s:%s\n", s, SSL_state_string_long(ssl));
121 else if (where & SSL_CB_ALERT) {
122 s = (where & SSL_CB_READ) ? "read" : "write";
123 debug(DBG_DBG, "SSL3 alert %s:%s:%s\n", s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
125 else if (where & SSL_CB_EXIT) {
127 debug(DBG_DBG, "%s:failed in %s\n", s, SSL_state_string_long(ssl));
129 debug(DBG_DBG, "%s:error in %s\n", s, SSL_state_string_long(ssl));
134 static X509_VERIFY_PARAM *createverifyparams(char **poids) {
135 X509_VERIFY_PARAM *pm;
136 ASN1_OBJECT *pobject;
139 pm = X509_VERIFY_PARAM_new();
143 for (i = 0; poids[i]; i++) {
144 pobject = OBJ_txt2obj(poids[i], 0);
146 X509_VERIFY_PARAM_free(pm);
149 X509_VERIFY_PARAM_add0_policy(pm, pobject);
152 X509_VERIFY_PARAM_set_flags(pm, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY);
156 static int tlsaddcacrl(SSL_CTX *ctx, struct tls *conf) {
157 STACK_OF(X509_NAME) *calist;
161 if (!SSL_CTX_load_verify_locations(ctx, conf->cacertfile, conf->cacertpath)) {
162 while ((error = ERR_get_error()))
163 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
164 debug(DBG_ERR, "tlsaddcacrl: Error updating TLS context %s", conf->name);
168 calist = conf->cacertfile ? SSL_load_client_CA_file(conf->cacertfile) : NULL;
169 if (!conf->cacertfile || calist) {
170 if (conf->cacertpath) {
172 calist = sk_X509_NAME_new_null();
173 if (!SSL_add_dir_cert_subjects_to_stack(calist, conf->cacertpath)) {
174 sk_X509_NAME_free(calist);
180 while ((error = ERR_get_error()))
181 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
182 debug(DBG_ERR, "tlsaddcacrl: Error adding CA subjects in TLS context %s", conf->name);
185 ERR_clear_error(); /* add_dir_cert_subj returns errors on success */
186 SSL_CTX_set_client_CA_list(ctx, calist);
188 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
189 SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
191 if (conf->crlcheck || conf->vpm) {
192 x509_s = SSL_CTX_get_cert_store(ctx);
194 X509_STORE_set_flags(x509_s, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
196 X509_STORE_set1_param(x509_s, conf->vpm);
199 debug(DBG_DBG, "tlsaddcacrl: updated TLS context %s", conf->name);
203 static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
206 long sslversion = SSLeay();
211 ctx = SSL_CTX_new(TLSv1_method());
213 SSL_CTX_set_info_callback(ctx, ssl_info_callback);
219 ctx = SSL_CTX_new(DTLSv1_method());
221 SSL_CTX_set_info_callback(ctx, ssl_info_callback);
223 SSL_CTX_set_read_ahead(ctx, 1);
228 debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name);
232 if (sslversion < 0x00908100L ||
233 (sslversion >= 0x10000000L && sslversion < 0x10000020L)) {
234 debug(DBG_WARN, "%s: %s seems to be of a version with a "
235 "certain security critical bug (fixed in OpenSSL 0.9.8p and "
236 "1.0.0b). Disabling OpenSSL session caching for context %p.",
237 __func__, SSLeay_version(SSLEAY_VERSION), ctx);
238 SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
241 if (conf->certkeypwd) {
242 SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->certkeypwd);
243 SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
245 if (!SSL_CTX_use_certificate_chain_file(ctx, conf->certfile) ||
246 !SSL_CTX_use_PrivateKey_file(ctx, conf->certkeyfile, SSL_FILETYPE_PEM) ||
247 !SSL_CTX_check_private_key(ctx)) {
248 while ((error = ERR_get_error()))
249 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
250 debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name);
255 if (conf->policyoids) {
257 conf->vpm = createverifyparams(conf->policyoids);
259 debug(DBG_ERR, "tlscreatectx: Failed to add policyOIDs in TLS context %s", conf->name);
266 if (!tlsaddcacrl(ctx, conf)) {
268 X509_VERIFY_PARAM_free(conf->vpm);
275 debug(DBG_DBG, "tlscreatectx: created TLS context %s", conf->name);
279 struct tls *tlsgettls(char *alt1, char *alt2) {
282 t = hash_read(tlsconfs, alt1, strlen(alt1));
284 t = hash_read(tlsconfs, alt2, strlen(alt2));
288 SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) {
293 gettimeofday(&now, NULL);
298 if (t->tlsexpiry && t->tlsctx) {
299 if (t->tlsexpiry < now.tv_sec) {
300 t->tlsexpiry = now.tv_sec + t->cacheexpiry;
301 tlsaddcacrl(t->tlsctx, t);
305 t->tlsctx = tlscreatectx(RAD_TLS, t);
307 t->tlsexpiry = now.tv_sec + t->cacheexpiry;
313 if (t->dtlsexpiry && t->dtlsctx) {
314 if (t->dtlsexpiry < now.tv_sec) {
315 t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
316 tlsaddcacrl(t->dtlsctx, t);
320 t->dtlsctx = tlscreatectx(RAD_DTLS, t);
322 t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
330 X509 *verifytlscert(SSL *ssl) {
334 if (SSL_get_verify_result(ssl) != X509_V_OK) {
335 debug(DBG_ERR, "verifytlscert: basic validation failed");
336 while ((error = ERR_get_error()))
337 debug(DBG_ERR, "verifytlscert: TLS: %s", ERR_error_string(error, NULL));
341 cert = SSL_get_peer_certificate(ssl);
343 debug(DBG_ERR, "verifytlscert: failed to obtain certificate");
347 static int subjectaltnameaddr(X509 *cert, int family, struct in6_addr *addr) {
348 int loc, i, l, n, r = 0;
351 STACK_OF(GENERAL_NAME) *alt;
354 debug(DBG_DBG, "subjectaltnameaddr");
356 loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
360 ex = X509_get_ext(cert, loc);
361 alt = X509V3_EXT_d2i(ex);
365 n = sk_GENERAL_NAME_num(alt);
366 for (i = 0; i < n; i++) {
367 gn = sk_GENERAL_NAME_value(alt, i);
368 if (gn->type != GEN_IPADD)
371 v = (char *)ASN1_STRING_data(gn->d.ia5);
372 l = ASN1_STRING_length(gn->d.ia5);
373 if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr)))
374 && !memcmp(v, &addr, l)) {
379 GENERAL_NAMES_free(alt);
383 static int subjectaltnameregexp(X509 *cert, int type, char *exact, regex_t *regex) {
384 int loc, i, l, n, r = 0;
387 STACK_OF(GENERAL_NAME) *alt;
390 debug(DBG_DBG, "subjectaltnameregexp");
392 loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1);
396 ex = X509_get_ext(cert, loc);
397 alt = X509V3_EXT_d2i(ex);
401 n = sk_GENERAL_NAME_num(alt);
402 for (i = 0; i < n; i++) {
403 gn = sk_GENERAL_NAME_value(alt, i);
404 if (gn->type != type)
407 v = (char *)ASN1_STRING_data(gn->d.ia5);
408 l = ASN1_STRING_length(gn->d.ia5);
412 printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l);
415 if (memcmp(v, exact, l))
418 s = stringcopy((char *)v, l);
420 debug(DBG_ERR, "malloc failed");
423 if (regexec(regex, s, 0, NULL, 0)) {
432 GENERAL_NAMES_free(alt);
436 static int cnregexp(X509 *cert, char *exact, regex_t *regex) {
443 nm = X509_get_subject_name(cert);
446 loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc);
449 e = X509_NAME_get_entry(nm, loc);
450 t = X509_NAME_ENTRY_get_data(e);
451 v = (char *) ASN1_STRING_data(t);
452 l = ASN1_STRING_length(t);
456 if (l == strlen(exact) && !strncasecmp(exact, v, l))
459 s = stringcopy((char *)v, l);
461 debug(DBG_ERR, "malloc failed");
464 if (regexec(regex, s, 0, NULL, 0)) {
475 /* this is a bit sloppy, should not always accept match to any */
476 int certnamecheck(X509 *cert, struct list *hostports) {
477 struct list_node *entry;
478 struct hostportres *hp;
480 uint8_t type = 0; /* 0 for DNS, AF_INET for IPv4, AF_INET6 for IPv6 */
481 struct in6_addr addr;
483 for (entry = list_first(hostports); entry; entry = list_next(entry)) {
484 hp = (struct hostportres *)entry->data;
485 if (hp->prefixlen != 255) {
486 /* we disable the check for prefixes */
489 if (inet_pton(AF_INET, hp->host, &addr))
491 else if (inet_pton(AF_INET6, hp->host, &addr))
496 r = type ? subjectaltnameaddr(cert, type, &addr) : subjectaltnameregexp(cert, GEN_DNS, hp->host, NULL);
499 debug(DBG_DBG, "certnamecheck: Found subjectaltname matching %s %s", type ? "address" : "host", hp->host);
502 debug(DBG_WARN, "certnamecheck: No subjectaltname matching %s %s", type ? "address" : "host", hp->host);
504 if (cnregexp(cert, hp->host, NULL)) {
505 debug(DBG_DBG, "certnamecheck: Found cn matching host %s", hp->host);
508 debug(DBG_WARN, "certnamecheck: cn not matching host %s", hp->host);
514 int verifyconfcert(X509 *cert, struct clsrvconf *conf) {
515 if (conf->certnamecheck) {
516 if (!certnamecheck(cert, conf->hostports)) {
517 debug(DBG_WARN, "verifyconfcert: certificate name check failed");
520 debug(DBG_WARN, "verifyconfcert: certificate name check ok");
522 if (conf->certcnregex) {
523 if (cnregexp(cert, NULL, conf->certcnregex) < 1) {
524 debug(DBG_WARN, "verifyconfcert: CN not matching regex");
527 debug(DBG_DBG, "verifyconfcert: CN matching regex");
529 if (conf->certuriregex) {
530 if (subjectaltnameregexp(cert, GEN_URI, NULL, conf->certuriregex) < 1) {
531 debug(DBG_WARN, "verifyconfcert: subjectaltname URI not matching regex");
534 debug(DBG_DBG, "verifyconfcert: subjectaltname URI matching regex");
539 int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
541 long int expiry = LONG_MIN;
543 debug(DBG_DBG, "conftls_cb called for %s", block);
545 conf = malloc(sizeof(struct tls));
547 debug(DBG_ERR, "conftls_cb: malloc failed");
550 memset(conf, 0, sizeof(struct tls));
552 if (!getgenericconfig(cf, block,
553 "CACertificateFile", CONF_STR, &conf->cacertfile,
554 "CACertificatePath", CONF_STR, &conf->cacertpath,
555 "CertificateFile", CONF_STR, &conf->certfile,
556 "CertificateKeyFile", CONF_STR, &conf->certkeyfile,
557 "CertificateKeyPassword", CONF_STR, &conf->certkeypwd,
558 "CacheExpiry", CONF_LINT, &expiry,
559 "CRLCheck", CONF_BLN, &conf->crlcheck,
560 "PolicyOID", CONF_MSTR, &conf->policyoids,
563 debug(DBG_ERR, "conftls_cb: configuration error in block %s", val);
566 if (!conf->certfile || !conf->certkeyfile) {
567 debug(DBG_ERR, "conftls_cb: TLSCertificateFile and TLSCertificateKeyFile must be specified in block %s", val);
570 if (!conf->cacertfile && !conf->cacertpath) {
571 debug(DBG_ERR, "conftls_cb: CA Certificate file or path need to be specified in block %s", val);
574 if (expiry != LONG_MIN) {
576 debug(DBG_ERR, "error in block %s, value of option CacheExpiry is %ld, may not be negative", val, expiry);
579 conf->cacheexpiry = expiry;
582 conf->name = stringcopy(val, 0);
584 debug(DBG_ERR, "conftls_cb: malloc failed");
589 tlsconfs = hash_create();
590 if (!hash_insert(tlsconfs, val, strlen(val), conf)) {
591 debug(DBG_ERR, "conftls_cb: malloc failed");
594 if (!tlsgetctx(RAD_TLS, conf))
595 debug(DBG_ERR, "conftls_cb: error creating ctx for TLS block %s", val);
596 debug(DBG_DBG, "conftls_cb: added TLS block %s", val);
600 free(conf->cacertfile);
601 free(conf->cacertpath);
602 free(conf->certfile);
603 free(conf->certkeyfile);
604 free(conf->certkeypwd);
605 freegconfmstr(conf->policyoids);
610 int addmatchcertattr(struct clsrvconf *conf) {
614 if (!strncasecmp(conf->matchcertattr, "CN:/", 4)) {
615 r = &conf->certcnregex;
616 v = conf->matchcertattr + 4;
617 } else if (!strncasecmp(conf->matchcertattr, "SubjectAltName:URI:/", 20)) {
618 r = &conf->certuriregex;
619 v = conf->matchcertattr + 20;
624 /* regexp, remove optional trailing / if present */
625 if (v[strlen(v) - 1] == '/')
626 v[strlen(v) - 1] = '\0';
630 *r = malloc(sizeof(regex_t));
632 debug(DBG_ERR, "malloc failed");
635 if (regcomp(*r, v, REG_EXTENDED | REG_ICASE | REG_NOSUB)) {
638 debug(DBG_ERR, "failed to compile regular expression %s", v);
644 /* Just to makes file non-empty, should rather avoid compiling this file when not needed */
645 static void tlsdummy() {
649 /* Local Variables: */
650 /* c-file-style: "stroustrup" */