From: Stephen Blackwell Date: Wed, 15 Apr 2020 01:36:30 +0000 (-0400) Subject: Added tls functionality to curl and a SET_OPTION macro (#3376) X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ed363e456dd2aa5c3ab8765229dfd35b3e65ee3d;p=thirdparty%2Ffreeradius-server.git Added tls functionality to curl and a SET_OPTION macro (#3376) * Added fr_curl_easy_tls_init and fr_curl_response_certinfo to src/lib/curl/base.c and FR_CURL_SET_OPTION and fr_curl_tls_config to src/lib/curl/base.h * Fix whitespace Co-authored-by: Arran Cudbard-Bell --- diff --git a/src/lib/curl/base.c b/src/lib/curl/base.c index c2f39e08619..41b75c02874 100644 --- a/src/lib/curl/base.c +++ b/src/lib/curl/base.c @@ -30,7 +30,13 @@ #include static uint32_t instance_count = 0; +static fr_dict_t const *dict_freeradius; /*internal dictionary for server*/ +extern fr_dict_autoload_t rlm_imap_dict[]; +fr_dict_autoload_t rlm_imap_dict[] = { + { .out = &dict_freeradius, .proto = "freeradius" }, + { NULL } +}; /** Initialise global curl options * * libcurl is meant to performa reference counting, but still seems to @@ -81,6 +87,128 @@ void fr_curl_free(void) #ifdef WITH_TLS fr_openssl_free(); #endif - curl_global_cleanup(); } + +int fr_curl_easy_tls_init (fr_curl_io_request_t *randle, fr_curl_tls_t *conf) +{ + REQUEST *request = randle->request; + + if (conf->tls_certificate_file) FR_CURL_SET_OPTION(CURLOPT_SSLCERT, conf->tls_certificate_file); + if (conf->tls_private_key_file) FR_CURL_SET_OPTION(CURLOPT_SSLKEY, conf->tls_private_key_file); + if (conf->tls_private_key_password) FR_CURL_SET_OPTION(CURLOPT_KEYPASSWD, conf->tls_private_key_password); + if (conf->tls_ca_file) FR_CURL_SET_OPTION(CURLOPT_CAINFO, conf->tls_ca_file); + if (conf->tls_ca_issuer_file) FR_CURL_SET_OPTION(CURLOPT_ISSUERCERT, conf->tls_ca_issuer_file); + if (conf->tls_ca_path) FR_CURL_SET_OPTION(CURLOPT_CAPATH, conf->tls_ca_path); + if (conf->tls_random_file) FR_CURL_SET_OPTION(CURLOPT_RANDOM_FILE, conf->tls_random_file); + + FR_CURL_SET_OPTION(CURLOPT_SSL_VERIFYPEER, (conf->tls_check_cert == true) ? 1L : 0L); + FR_CURL_SET_OPTION(CURLOPT_SSL_VERIFYHOST, (conf->tls_check_cert_cn == true) ? 2L : 0L); + + return 0; +error: + return -1; +} + +int fr_curl_response_certinfo(REQUEST *request, fr_curl_io_request_t *randle) +{ + CURL *candle = randle->candle; + CURLcode ret; + int i; + char buffer[265]; + char *p , *q, *attr = buffer; + fr_cursor_t cursor, list; + VALUE_PAIR *cert_vps = NULL; + /* + * Examples and documentation show cert_info being + * a struct curl_certinfo *, but CPP checks require + * it to be a struct curl_slist *. + * + * https://curl.haxx.se/libcurl/c/certinfo.html + */ + union { + struct curl_slist *to_info; + struct curl_certinfo *to_certinfo; + } ptr; + ptr.to_info = NULL; + + fr_cursor_init(&list, &request->packet->vps); + + ret = curl_easy_getinfo(candle, CURLINFO_CERTINFO, &ptr.to_info); + if (ret != CURLE_OK) { + REDEBUG("Getting certificate info failed: %i - %s", ret, curl_easy_strerror(ret)); + + return -1; + } + + attr += strlcpy(attr, "TLS-Cert-", sizeof(buffer)); + + RDEBUG2("Chain has %i certificate(s)", ptr.to_certinfo->num_of_certs); + for (i = 0; i < ptr.to_certinfo->num_of_certs; i++) { + struct curl_slist *cert_attrs; + + RDEBUG2("Processing certificate %i",i); + fr_cursor_init(&cursor, &cert_vps); + + for (cert_attrs = ptr.to_certinfo->certinfo[i]; + cert_attrs; + cert_attrs = cert_attrs->next) { + VALUE_PAIR *vp; + fr_dict_attr_t const *da; + + q = strchr(cert_attrs->data, ':'); + if (!q) { + RWDEBUG("Malformed certinfo from libcurl: %s", cert_attrs->data); + continue; + } + + strlcpy(attr, cert_attrs->data, (q - cert_attrs->data) + 1); + for (p = attr; *p != '\0'; p++) if (*p == ' ') *p = '-'; + + da = fr_dict_attr_by_name(dict_freeradius, buffer); + if (!da) { + RDEBUG3("Skipping %s += '%s'", buffer, q + 1); + RDEBUG3("If this value is required, define attribute \"%s\"", buffer); + continue; + } + MEM(vp = fr_pair_afrom_da(request->packet, da)); + fr_pair_value_from_str(vp, q + 1, -1, '\0', true); + + fr_cursor_append(&cursor, vp); + } + /* + * Add a copy of the cert_vps to session state. + * + * Both PVS studio and Coverity detect the condition + * below as logically dead code unless we explicitly + * set cert_vps. This is because they're too dumb + * to realise that the cursor argument passed to + * tls_session_pairs_from_x509_cert contains a + * reference to cert_vps. + */ + cert_vps = fr_cursor_current(&cursor); + if (cert_vps) { + /* + * Print out all the pairs we have so far + */ + log_request_pair_list(L_DBG_LVL_2, request, cert_vps, NULL); + fr_cursor_merge(&list, &cursor); + cert_vps = NULL; + } + } + return 0; +} + +CONF_PARSER fr_curl_tls_config[] = { + { FR_CONF_OFFSET("ca_file", FR_TYPE_FILE_INPUT, fr_curl_tls_t, tls_ca_file) }, + { FR_CONF_OFFSET("ca_issuer_file", FR_TYPE_FILE_INPUT, fr_curl_tls_t, tls_ca_issuer_file) }, + { FR_CONF_OFFSET("ca_path", FR_TYPE_FILE_INPUT, fr_curl_tls_t, tls_ca_path) }, + { FR_CONF_OFFSET("certificate_file", FR_TYPE_FILE_INPUT, fr_curl_tls_t, tls_certificate_file) }, + { FR_CONF_OFFSET("private_key_file", FR_TYPE_FILE_INPUT, fr_curl_tls_t, tls_private_key_file) }, + { FR_CONF_OFFSET("private_key_password", FR_TYPE_STRING | FR_TYPE_SECRET, fr_curl_tls_t, tls_private_key_password) }, + { FR_CONF_OFFSET("random_file", FR_TYPE_STRING, fr_curl_tls_t, tls_random_file) }, + { FR_CONF_OFFSET("check_cert", FR_TYPE_BOOL, fr_curl_tls_t, tls_check_cert), .dflt = "yes" }, + { FR_CONF_OFFSET("check_cert_cn", FR_TYPE_BOOL, fr_curl_tls_t, tls_check_cert_cn), .dflt = "yes" }, + { FR_CONF_OFFSET("extract_cert_attrs", FR_TYPE_BOOL, fr_curl_tls_t, tls_extract_cert_attrs), .dflt = "no" }, + CONF_PARSER_TERMINATOR +}; diff --git a/src/lib/curl/base.h b/src/lib/curl/base.h index e910cd185b5..3999e54a94b 100644 --- a/src/lib/curl/base.h +++ b/src/lib/curl/base.h @@ -32,6 +32,20 @@ extern "C" { #include #include #include +#include + +DIAG_OPTIONAL +DIAG_OFF(disabled-macro-expansion) +#define FR_CURL_SET_OPTION(_x, _y)\ +do {\ + int _ret;\ + if ((_ret = curl_easy_setopt(randle->candle, _x, _y)) != CURLE_OK) {\ + char const *_option;\ + _option = STRINGIFY(_x);\ + ROPTIONAL(RERROR, ERROR, "Failed setting curl option %s: %s (%i)", _option, curl_easy_strerror(_ret), _ret);\ + goto error;\ + }\ +} while (0) /** Uctx data for timer and I/O functions * @@ -55,6 +69,21 @@ typedef struct { void *uctx; //!< Private data for the module using the API. } fr_curl_io_request_t; +typedef struct { + char const *tls_certificate_file; + char const *tls_private_key_file; + char const *tls_private_key_password; + char const *tls_ca_file; + char const *tls_ca_issuer_file; + char const *tls_ca_path; + char const *tls_random_file; + bool tls_check_cert; + bool tls_check_cert_cn; + bool tls_extract_cert_attrs; +} fr_curl_tls_t; + +extern CONF_PARSER fr_curl_tls_config[]; + int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, REQUEST *request, fr_curl_io_request_t *creq); @@ -66,6 +95,10 @@ int fr_curl_init(void); void fr_curl_free(void); +int fr_curl_response_certinfo(REQUEST *request, fr_curl_io_request_t *randle); + +int fr_curl_easy_tls_init (fr_curl_io_request_t *randle, fr_curl_tls_t *conf); + #ifdef __cplusplus } #endif