2 * Copyright (C) 2006-2008 Stig Venaas <venaas@uninett.no>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
9 #if defined(RADPROT_TLS) || defined(RADPROT_DTLS)
11 #include <sys/socket.h>
12 #include <netinet/in.h>
21 #include <sys/types.h>
22 #include <sys/select.h>
25 #include <arpa/inet.h>
29 #include <openssl/ssl.h>
30 #include <openssl/rand.h>
31 #include <openssl/err.h>
32 #include <openssl/md5.h>
33 #include <openssl/hmac.h>
34 #include <openssl/x509v3.h>
40 #include "radsecproxy.h"
54 X509_VERIFY_PARAM *vpm;
59 static struct hash *tlsconfs = NULL;
61 static int pem_passwd_cb(char *buf, int size, int rwflag, void *userdata) {
62 int pwdlen = strlen(userdata);
63 if (rwflag != 0 || pwdlen > size) /* not for decryption or too large */
65 memcpy(buf, userdata, pwdlen);
69 static int verify_cb(int ok, X509_STORE_CTX *ctx) {
74 err_cert = X509_STORE_CTX_get_current_cert(ctx);
75 err = X509_STORE_CTX_get_error(ctx);
76 depth = X509_STORE_CTX_get_error_depth(ctx);
78 if (depth > MAX_CERT_DEPTH) {
80 err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
81 X509_STORE_CTX_set_error(ctx, err);
86 buf = X509_NAME_oneline(X509_get_subject_name(err_cert), NULL, 0);
87 debug(DBG_WARN, "verify error: num=%d:%s:depth=%d:%s", err, X509_verify_cert_error_string(err), depth, buf ? buf : "");
92 case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
94 buf = X509_NAME_oneline(X509_get_issuer_name(err_cert), NULL, 0);
96 debug(DBG_WARN, "\tIssuer=%s", buf);
102 case X509_V_ERR_CERT_NOT_YET_VALID:
103 case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
104 debug(DBG_WARN, "\tCertificate not yet valid");
106 case X509_V_ERR_CERT_HAS_EXPIRED:
107 debug(DBG_WARN, "Certificate has expired");
109 case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
110 debug(DBG_WARN, "Certificate no longer valid (after notAfter)");
112 case X509_V_ERR_NO_EXPLICIT_POLICY:
113 debug(DBG_WARN, "No Explicit Certificate Policy");
118 printf("certificate verify returns %d\n", ok);
124 static void ssl_info_callback(const SSL *ssl, int where, int ret) {
128 w = where & ~SSL_ST_MASK;
130 if (w & SSL_ST_CONNECT)
132 else if (w & SSL_ST_ACCEPT)
137 if (where & SSL_CB_LOOP)
138 debug(DBG_DBG, "%s:%s\n", s, SSL_state_string_long(ssl));
139 else if (where & SSL_CB_ALERT) {
140 s = (where & SSL_CB_READ) ? "read" : "write";
141 debug(DBG_DBG, "SSL3 alert %s:%s:%s\n", s, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
143 else if (where & SSL_CB_EXIT) {
145 debug(DBG_DBG, "%s:failed in %s\n", s, SSL_state_string_long(ssl));
147 debug(DBG_DBG, "%s:error in %s\n", s, SSL_state_string_long(ssl));
152 static X509_VERIFY_PARAM *createverifyparams(char **poids) {
153 X509_VERIFY_PARAM *pm;
154 ASN1_OBJECT *pobject;
157 pm = X509_VERIFY_PARAM_new();
161 for (i = 0; poids[i]; i++) {
162 pobject = OBJ_txt2obj(poids[i], 0);
164 X509_VERIFY_PARAM_free(pm);
167 X509_VERIFY_PARAM_add0_policy(pm, pobject);
170 X509_VERIFY_PARAM_set_flags(pm, X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY);
174 static int tlsaddcacrl(SSL_CTX *ctx, struct tls *conf) {
175 STACK_OF(X509_NAME) *calist;
179 if (!SSL_CTX_load_verify_locations(ctx, conf->cacertfile, conf->cacertpath)) {
180 while ((error = ERR_get_error()))
181 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
182 debug(DBG_ERR, "tlsaddcacrl: Error updating TLS context %s", conf->name);
186 calist = conf->cacertfile ? SSL_load_client_CA_file(conf->cacertfile) : NULL;
187 if (!conf->cacertfile || calist) {
188 if (conf->cacertpath) {
190 calist = sk_X509_NAME_new_null();
191 if (!SSL_add_dir_cert_subjects_to_stack(calist, conf->cacertpath)) {
192 sk_X509_NAME_free(calist);
198 while ((error = ERR_get_error()))
199 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
200 debug(DBG_ERR, "tlsaddcacrl: Error adding CA subjects in TLS context %s", conf->name);
203 ERR_clear_error(); /* add_dir_cert_subj returns errors on success */
204 SSL_CTX_set_client_CA_list(ctx, calist);
206 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
207 SSL_CTX_set_verify_depth(ctx, MAX_CERT_DEPTH + 1);
209 if (conf->crlcheck || conf->vpm) {
210 x509_s = SSL_CTX_get_cert_store(ctx);
212 X509_STORE_set_flags(x509_s, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
214 X509_STORE_set1_param(x509_s, conf->vpm);
217 debug(DBG_DBG, "tlsaddcacrl: updated TLS context %s", conf->name);
221 static SSL_CTX *tlscreatectx(uint8_t type, struct tls *conf) {
228 ctx = SSL_CTX_new(TLSv1_method());
230 SSL_CTX_set_info_callback(ctx, ssl_info_callback);
236 ctx = SSL_CTX_new(DTLSv1_method());
238 SSL_CTX_set_info_callback(ctx, ssl_info_callback);
240 SSL_CTX_set_read_ahead(ctx, 1);
245 debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name);
249 if (conf->certkeypwd) {
250 SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->certkeypwd);
251 SSL_CTX_set_default_passwd_cb(ctx, pem_passwd_cb);
253 if (!SSL_CTX_use_certificate_chain_file(ctx, conf->certfile) ||
254 !SSL_CTX_use_PrivateKey_file(ctx, conf->certkeyfile, SSL_FILETYPE_PEM) ||
255 !SSL_CTX_check_private_key(ctx)) {
256 while ((error = ERR_get_error()))
257 debug(DBG_ERR, "SSL: %s", ERR_error_string(error, NULL));
258 debug(DBG_ERR, "tlscreatectx: Error initialising SSL/TLS in TLS context %s", conf->name);
263 if (conf->policyoids) {
265 conf->vpm = createverifyparams(conf->policyoids);
267 debug(DBG_ERR, "tlscreatectx: Failed to add policyOIDs in TLS context %s", conf->name);
274 if (!tlsaddcacrl(ctx, conf)) {
276 X509_VERIFY_PARAM_free(conf->vpm);
283 debug(DBG_DBG, "tlscreatectx: created TLS context %s", conf->name);
287 struct tls *tlsgettls(char *alt1, char *alt2) {
290 t = hash_read(tlsconfs, alt1, strlen(alt1));
292 t = hash_read(tlsconfs, alt2, strlen(alt2));
296 SSL_CTX *tlsgetctx(uint8_t type, struct tls *t) {
301 gettimeofday(&now, NULL);
306 if (t->tlsexpiry && t->tlsctx) {
307 if (t->tlsexpiry < now.tv_sec) {
308 t->tlsexpiry = now.tv_sec + t->cacheexpiry;
309 tlsaddcacrl(t->tlsctx, t);
313 t->tlsctx = tlscreatectx(RAD_TLS, t);
315 t->tlsexpiry = now.tv_sec + t->cacheexpiry;
321 if (t->dtlsexpiry && t->dtlsctx) {
322 if (t->dtlsexpiry < now.tv_sec) {
323 t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
324 tlsaddcacrl(t->dtlsctx, t);
328 t->dtlsctx = tlscreatectx(RAD_DTLS, t);
330 t->dtlsexpiry = now.tv_sec + t->cacheexpiry;
338 int conftls_cb(struct gconffile **cf, void *arg, char *block, char *opt, char *val) {
340 long int expiry = LONG_MIN;
342 debug(DBG_DBG, "conftls_cb called for %s", block);
344 conf = malloc(sizeof(struct tls));
346 debug(DBG_ERR, "conftls_cb: malloc failed");
349 memset(conf, 0, sizeof(struct tls));
351 if (!getgenericconfig(cf, block,
352 "CACertificateFile", CONF_STR, &conf->cacertfile,
353 "CACertificatePath", CONF_STR, &conf->cacertpath,
354 "CertificateFile", CONF_STR, &conf->certfile,
355 "CertificateKeyFile", CONF_STR, &conf->certkeyfile,
356 "CertificateKeyPassword", CONF_STR, &conf->certkeypwd,
357 "CacheExpiry", CONF_LINT, &expiry,
358 "CRLCheck", CONF_BLN, &conf->crlcheck,
359 "PolicyOID", CONF_MSTR, &conf->policyoids,
362 debug(DBG_ERR, "conftls_cb: configuration error in block %s", val);
365 if (!conf->certfile || !conf->certkeyfile) {
366 debug(DBG_ERR, "conftls_cb: TLSCertificateFile and TLSCertificateKeyFile must be specified in block %s", val);
369 if (!conf->cacertfile && !conf->cacertpath) {
370 debug(DBG_ERR, "conftls_cb: CA Certificate file or path need to be specified in block %s", val);
373 if (expiry != LONG_MIN) {
375 debug(DBG_ERR, "error in block %s, value of option CacheExpiry is %ld, may not be negative", val, expiry);
378 conf->cacheexpiry = expiry;
381 conf->name = stringcopy(val, 0);
383 debug(DBG_ERR, "conftls_cb: malloc failed");
388 tlsconfs = hash_create();
389 if (!hash_insert(tlsconfs, val, strlen(val), conf)) {
390 debug(DBG_ERR, "conftls_cb: malloc failed");
393 if (!tlsgetctx(RAD_TLS, conf))
394 debug(DBG_ERR, "conftls_cb: error creating ctx for TLS block %s", val);
395 debug(DBG_DBG, "conftls_cb: added TLS block %s", val);
399 free(conf->cacertfile);
400 free(conf->cacertpath);
401 free(conf->certfile);
402 free(conf->certkeyfile);
403 free(conf->certkeypwd);
404 freegconfmstr(conf->policyoids);
409 /* Just to makes file non-empty, should rather avoid compiling this file when not needed */
410 static void tlsdummy() {