Brian Waters | 13d9601 | 2017-12-08 16:53:31 -0600 | [diff] [blame] | 1 | /********************************************************************************************************* |
| 2 | * Software License Agreement (BSD License) * |
| 3 | * Author: Sebastien Decugis <sdecugis@freediameter.net> * |
| 4 | * * |
| 5 | * Copyright (c) 2015, WIDE Project and NICT * |
| 6 | * All rights reserved. * |
| 7 | * * |
| 8 | * Redistribution and use of this software in source and binary forms, with or without modification, are * |
| 9 | * permitted provided that the following conditions are met: * |
| 10 | * * |
| 11 | * * Redistributions of source code must retain the above * |
| 12 | * copyright notice, this list of conditions and the * |
| 13 | * following disclaimer. * |
| 14 | * * |
| 15 | * * Redistributions in binary form must reproduce the above * |
| 16 | * copyright notice, this list of conditions and the * |
| 17 | * following disclaimer in the documentation and/or other * |
| 18 | * materials provided with the distribution. * |
| 19 | * * |
| 20 | * * Neither the name of the WIDE Project or NICT nor the * |
| 21 | * names of its contributors may be used to endorse or * |
| 22 | * promote products derived from this software without * |
| 23 | * specific prior written permission of WIDE Project and * |
| 24 | * NICT. * |
| 25 | * * |
| 26 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * |
| 27 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * |
| 28 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * |
| 29 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * |
| 30 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * |
| 31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * |
| 32 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * |
| 33 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * |
| 34 | *********************************************************************************************************/ |
| 35 | |
| 36 | #include "fdcore-internal.h" |
| 37 | #include <sys/stat.h> |
| 38 | |
| 39 | /* Configuration management */ |
| 40 | |
| 41 | #ifndef GNUTLS_DEFAULT_PRIORITY |
| 42 | # define GNUTLS_DEFAULT_PRIORITY "NORMAL" |
| 43 | #endif /* GNUTLS_DEFAULT_PRIORITY */ |
| 44 | #ifndef GNUTLS_DEFAULT_DHBITS |
| 45 | # define GNUTLS_DEFAULT_DHBITS 1024 |
| 46 | #endif /* GNUTLS_DEFAULT_DHBITS */ |
| 47 | |
| 48 | /* Initialize the fd_g_config structure to default values -- it should already have been initialized to all-0 */ |
| 49 | int fd_conf_init() |
| 50 | { |
| 51 | TRACE_ENTRY(); |
| 52 | |
| 53 | fd_g_config->cnf_eyec = EYEC_CONFIG; |
| 54 | |
| 55 | fd_g_config->cnf_timer_tc = 30; |
| 56 | fd_g_config->cnf_timer_tw = 30; |
| 57 | |
| 58 | fd_g_config->cnf_port = DIAMETER_PORT; |
| 59 | fd_g_config->cnf_port_tls = DIAMETER_SECURE_PORT; |
| 60 | fd_g_config->cnf_sctp_str = 30; |
| 61 | fd_g_config->cnf_thr_srv = 5; |
| 62 | fd_g_config->cnf_dispthr = 4; |
| 63 | fd_list_init(&fd_g_config->cnf_endpoints, NULL); |
| 64 | fd_list_init(&fd_g_config->cnf_apps, NULL); |
| 65 | #ifdef DISABLE_SCTP |
| 66 | fd_g_config->cnf_flags.no_sctp = 1; |
| 67 | #endif /* DISABLE_SCTP */ |
| 68 | |
| 69 | fd_g_config->cnf_orstateid = (uint32_t) time(NULL); |
| 70 | |
| 71 | CHECK_FCT( fd_dict_init(&fd_g_config->cnf_dict) ); |
| 72 | CHECK_FCT( fd_fifo_new(&fd_g_config->cnf_main_ev, 0) ); |
| 73 | |
| 74 | /* TLS parameters */ |
| 75 | CHECK_GNUTLS_DO( gnutls_certificate_allocate_credentials (&fd_g_config->cnf_sec_data.credentials), return ENOMEM ); |
| 76 | CHECK_GNUTLS_DO( gnutls_dh_params_init (&fd_g_config->cnf_sec_data.dh_cache), return ENOMEM ); |
| 77 | #ifdef GNUTLS_VERSION_300 |
| 78 | CHECK_GNUTLS_DO( gnutls_x509_trust_list_init(&fd_g_config->cnf_sec_data.trustlist, 0), return ENOMEM ); |
| 79 | #endif /* GNUTLS_VERSION_300 */ |
| 80 | |
| 81 | return 0; |
| 82 | } |
| 83 | |
| 84 | DECLARE_FD_DUMP_PROTOTYPE(fd_conf_dump) |
| 85 | { |
| 86 | FD_DUMP_HANDLE_OFFSET(); |
| 87 | |
| 88 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "freeDiameter configuration:\n"), return NULL); |
| 89 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Default trace level .... : %+d\n", fd_g_debug_lvl), return NULL); |
| 90 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Configuration file ..... : %s\n", fd_g_config->cnf_file), return NULL); |
| 91 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Identity ...... : %s (l:%zi)\n", fd_g_config->cnf_diamid, fd_g_config->cnf_diamid_len), return NULL); |
| 92 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Diameter Realm ......... : %s (l:%zi)\n", fd_g_config->cnf_diamrlm, fd_g_config->cnf_diamrlm_len), return NULL); |
| 93 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tc Timer ............... : %u\n", fd_g_config->cnf_timer_tc), return NULL); |
| 94 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Tw Timer ............... : %u\n", fd_g_config->cnf_timer_tw), return NULL); |
| 95 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local port ............. : %hu\n", fd_g_config->cnf_port), return NULL); |
| 96 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local secure port ...... : %hu\n", fd_g_config->cnf_port_tls), return NULL); |
| 97 | if (fd_g_config->cnf_port_3436) { |
| 98 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local SCTP TLS port .... : %hu\n", fd_g_config->cnf_port_3436), return NULL); |
| 99 | } |
| 100 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of SCTP streams . : %hu\n", fd_g_config->cnf_sctp_str), return NULL); |
| 101 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of clients thr .. : %d\n", fd_g_config->cnf_thr_srv), return NULL); |
| 102 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Number of app threads .. : %hu\n", fd_g_config->cnf_dispthr), return NULL); |
| 103 | if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) { |
| 104 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : Default (use all available)\n"), return NULL); |
| 105 | } else { |
| 106 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local endpoints ........ : "), return NULL); |
| 107 | CHECK_MALLOC_DO( fd_ep_dump( FD_DUMP_STD_PARAMS, 0, 0, &fd_g_config->cnf_endpoints ), return NULL); |
| 108 | } |
| 109 | if (FD_IS_LIST_EMPTY(&fd_g_config->cnf_apps)) { |
| 110 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : (none)"), return NULL); |
| 111 | } else { |
| 112 | struct fd_list * li = fd_g_config->cnf_apps.next; |
| 113 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Local applications ..... : "), return NULL); |
| 114 | while (li != &fd_g_config->cnf_apps) { |
| 115 | struct fd_app * app = (struct fd_app *)li; |
| 116 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "App: %u,%s%s,Vnd:%u\t", |
| 117 | app->appid, |
| 118 | app->flags.auth ? "Au" : "--", |
| 119 | app->flags.acct ? "Ac" : "--", |
| 120 | app->vndid), return NULL); |
| 121 | li = li->next; |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, "\n Flags : - IP ........... : %s\n", fd_g_config->cnf_flags.no_ip4 ? "DISABLED" : "Enabled"), return NULL); |
| 126 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - IPv6 ......... : %s\n", fd_g_config->cnf_flags.no_ip6 ? "DISABLED" : "Enabled"), return NULL); |
| 127 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Relay app .... : %s\n", fd_g_config->cnf_flags.no_fwd ? "DISABLED" : "Enabled"), return NULL); |
| 128 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TCP .......... : %s\n", fd_g_config->cnf_flags.no_tcp ? "DISABLED" : "Enabled"), return NULL); |
| 129 | #ifdef DISABLE_SCTP |
| 130 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : DISABLED (at compilation)\n"), return NULL); |
| 131 | #else /* DISABLE_SCTP */ |
| 132 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - SCTP ......... : %s\n", fd_g_config->cnf_flags.no_sctp ? "DISABLED" : "Enabled"), return NULL); |
| 133 | #endif /* DISABLE_SCTP */ |
| 134 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Pref. proto .. : %s\n", fd_g_config->cnf_flags.pr_tcp ? "TCP" : "SCTP"), return NULL); |
| 135 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - TLS method ... : %s\n", fd_g_config->cnf_flags.tls_alg ? "INBAND" : "Separate port"), return NULL); |
| 136 | |
| 137 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " TLS : - Certificate .. : %s\n", fd_g_config->cnf_sec_data.cert_file ?: "(NONE)"), return NULL); |
| 138 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Private key .. : %s\n", fd_g_config->cnf_sec_data.key_file ?: "(NONE)"), return NULL); |
| 139 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CA (trust) ... : %s (%d certs)\n", fd_g_config->cnf_sec_data.ca_file ?: "(none)", fd_g_config->cnf_sec_data.ca_file_nr), return NULL); |
| 140 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - CRL .......... : %s\n", fd_g_config->cnf_sec_data.crl_file ?: "(none)"), return NULL); |
| 141 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - Priority ..... : %s\n", fd_g_config->cnf_sec_data.prio_string ?: "(default: '" GNUTLS_DEFAULT_PRIORITY "')"), return NULL); |
| 142 | if (fd_g_config->cnf_sec_data.dh_file) { |
| 143 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH file ...... : %s\n", fd_g_config->cnf_sec_data.dh_file), return NULL); |
| 144 | } else { |
| 145 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " - DH bits ...... : %d\n", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS), return NULL); |
| 146 | } |
| 147 | |
| 148 | CHECK_MALLOC_DO( fd_dump_extend( FD_DUMP_STD_PARAMS, " Origin-State-Id ........ : %u", fd_g_config->cnf_orstateid), return NULL); |
| 149 | |
| 150 | return *buf; |
| 151 | } |
| 152 | |
| 153 | /* read contents of a file opened in "rb" mode and alloc this data into a gnutls_datum_t (must be freed afterwards) */ |
| 154 | int fd_conf_stream_to_gnutls_datum(FILE * pemfile, gnutls_datum_t *out) |
| 155 | { |
| 156 | size_t alloc = 0; |
| 157 | |
| 158 | CHECK_PARAMS( pemfile && out ); |
| 159 | memset(out, 0, sizeof(gnutls_datum_t)); |
| 160 | |
| 161 | do { |
| 162 | uint8_t * realloced = NULL; |
| 163 | size_t read = 0; |
| 164 | |
| 165 | if (alloc < out->size + BUFSIZ + 1) { |
| 166 | alloc += alloc / 2 + BUFSIZ + 1; |
| 167 | CHECK_MALLOC_DO( realloced = realloc(out->data, alloc), |
| 168 | { |
| 169 | free(out->data); |
| 170 | return ENOMEM; |
| 171 | } ) |
| 172 | out->data = realloced; |
| 173 | } |
| 174 | |
| 175 | read = fread( out->data + out->size, 1, alloc - out->size - 1, pemfile ); |
| 176 | out->size += read; |
| 177 | |
| 178 | if (ferror(pemfile)) { |
| 179 | int err = errno; |
| 180 | TRACE_DEBUG(INFO, "An error occurred while reading file: %s", strerror(err)); |
| 181 | return err; |
| 182 | } |
| 183 | } while (!feof(pemfile)); |
| 184 | |
| 185 | out->data[out->size] = '\0'; |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | #ifdef GNUTLS_VERSION_300 |
| 190 | /* inspired from GnuTLS manual */ |
| 191 | static int fd_conf_print_details_func (gnutls_x509_crt_t cert, |
| 192 | gnutls_x509_crt_t issuer, gnutls_x509_crl_t crl, |
| 193 | unsigned int verification_output) |
| 194 | { |
| 195 | char name[512]; |
| 196 | char issuer_name[512]; |
| 197 | size_t name_size; |
| 198 | size_t issuer_name_size; |
| 199 | |
| 200 | if (!TRACE_BOOL(GNUTLS_DBG_LEVEL)) |
| 201 | return 0; |
| 202 | |
| 203 | issuer_name_size = sizeof (issuer_name); |
| 204 | gnutls_x509_crt_get_issuer_dn (cert, issuer_name, &issuer_name_size); |
| 205 | |
| 206 | name_size = sizeof (name); |
| 207 | gnutls_x509_crt_get_dn (cert, name, &name_size); |
| 208 | |
| 209 | fd_log_debug("\tSubject: %s", name); |
| 210 | fd_log_debug("\tIssuer: %s", issuer_name); |
| 211 | |
| 212 | if (issuer != NULL) |
| 213 | { |
| 214 | issuer_name_size = sizeof (issuer_name); |
| 215 | gnutls_x509_crt_get_dn (issuer, issuer_name, &issuer_name_size); |
| 216 | |
| 217 | fd_log_debug("\tVerified against: %s", issuer_name); |
| 218 | } |
| 219 | |
| 220 | if (crl != NULL) |
| 221 | { |
| 222 | issuer_name_size = sizeof (issuer_name); |
| 223 | gnutls_x509_crl_get_issuer_dn (crl, issuer_name, &issuer_name_size); |
| 224 | |
| 225 | fd_log_debug("\tVerified against CRL of: %s", issuer_name); |
| 226 | } |
| 227 | |
| 228 | fd_log_debug("\tVerification output: %x", verification_output); |
| 229 | |
| 230 | return 0; |
| 231 | } |
| 232 | #endif /* GNUTLS_VERSION_300 */ |
| 233 | |
| 234 | #ifndef GNUTLS_VERSION_300 |
| 235 | GCC_DIAG_OFF("-Wdeprecated-declarations") |
| 236 | #endif /* !GNUTLS_VERSION_300 */ |
| 237 | /* Parse the configuration file (using the yacc parser) */ |
| 238 | int fd_conf_parse() |
| 239 | { |
| 240 | extern FILE * fddin; |
| 241 | const char * orig = NULL; |
| 242 | |
| 243 | /* Attempt to find the configuration file */ |
| 244 | if (!fd_g_config->cnf_file) |
| 245 | fd_g_config->cnf_file = FD_DEFAULT_CONF_FILENAME; |
| 246 | |
| 247 | fddin = fopen(fd_g_config->cnf_file, "r"); |
| 248 | if ((fddin == NULL) && (*fd_g_config->cnf_file != '/')) { |
| 249 | char * new_cnf = NULL; |
| 250 | /* We got a relative path, attempt to add the default directory prefix */ |
| 251 | orig = fd_g_config->cnf_file; |
| 252 | CHECK_MALLOC( new_cnf = malloc(strlen(orig) + strlen(DEFAULT_CONF_PATH) + 2) ); /* we will not free it, but not important */ |
| 253 | sprintf( new_cnf, DEFAULT_CONF_PATH "/%s", orig ); |
| 254 | fd_g_config->cnf_file = new_cnf; |
| 255 | fddin = fopen(fd_g_config->cnf_file, "r"); |
| 256 | } |
| 257 | if (fddin == NULL) { |
| 258 | int ret = errno; |
| 259 | LOG_F("Unable to open configuration file for reading; tried the following locations: %s%s%s; Error: %s", |
| 260 | orig ?: "", orig? " and " : "", fd_g_config->cnf_file, strerror(ret)); |
| 261 | return ret; |
| 262 | } |
| 263 | |
| 264 | /* call yacc parser */ |
| 265 | TRACE_DEBUG (FULL, "Parsing configuration file: %s", fd_g_config->cnf_file); |
| 266 | CHECK_FCT( fddparse(fd_g_config) ); |
| 267 | |
| 268 | /* close the file */ |
| 269 | fclose(fddin); |
| 270 | |
| 271 | /* Check that TLS private key was given */ |
| 272 | if (! fd_g_config->cnf_sec_data.key_file) { |
| 273 | /* If TLS is not enabled, we allow empty TLS configuration */ |
| 274 | if ((fd_g_config->cnf_port_tls == 0) && (fd_g_config->cnf_flags.tls_alg == 0)) { |
| 275 | LOG_N("TLS is disabled, this is *NOT* a recommended practice! Diameter protocol conveys highly sensitive information on your users."); |
| 276 | fd_g_config->cnf_sec_data.tls_disabled = 1; |
| 277 | } else { |
| 278 | LOG_F( "Missing private key configuration for TLS. Please provide the TLS_cred configuration directive."); |
| 279 | return EINVAL; |
| 280 | } |
| 281 | } |
| 282 | |
| 283 | /* Resolve hostname if not provided */ |
| 284 | if (fd_g_config->cnf_diamid == NULL) { |
| 285 | char buf[HOST_NAME_MAX + 1]; |
| 286 | struct addrinfo hints, *info; |
| 287 | int ret; |
| 288 | |
| 289 | /* local host name */ |
| 290 | CHECK_SYS(gethostname(buf, sizeof(buf))); |
| 291 | |
| 292 | /* get FQDN */ |
| 293 | memset(&hints, 0, sizeof hints); |
| 294 | hints.ai_flags = AI_CANONNAME; |
| 295 | |
| 296 | ret = getaddrinfo(buf, NULL, &hints, &info); |
| 297 | if (ret != 0) { |
| 298 | TRACE_ERROR( "Error resolving local FQDN : '%s' : %s" |
| 299 | ". Please provide Identity in configuration file.", |
| 300 | buf, gai_strerror(ret)); |
| 301 | return EINVAL; |
| 302 | } |
| 303 | fd_g_config->cnf_diamid = info->ai_canonname; |
| 304 | CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 1) ); |
| 305 | freeaddrinfo(info); |
| 306 | } else { |
| 307 | CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamid, &fd_g_config->cnf_diamid_len, 0) ); |
| 308 | } |
| 309 | |
| 310 | /* Handle the realm part */ |
| 311 | if (fd_g_config->cnf_diamrlm == NULL) { |
| 312 | char * start = NULL; |
| 313 | |
| 314 | /* Check the diameter identity is a fqdn */ |
| 315 | start = strchr(fd_g_config->cnf_diamid, '.'); |
| 316 | if ((start == NULL) || (start[1] == '\0')) { |
| 317 | TRACE_ERROR( "Unable to extract realm from the Identity '%s'." |
| 318 | " Please fix your Identity setting or provide Realm.", |
| 319 | fd_g_config->cnf_diamid); |
| 320 | return EINVAL; |
| 321 | } |
| 322 | |
| 323 | fd_g_config->cnf_diamrlm = start + 1; |
| 324 | CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 1) ); |
| 325 | } else { |
| 326 | CHECK_FCT( fd_os_validate_DiameterIdentity(&fd_g_config->cnf_diamrlm, &fd_g_config->cnf_diamrlm_len, 0) ); |
| 327 | } |
| 328 | |
| 329 | /* Validate some flags */ |
| 330 | if (fd_g_config->cnf_flags.no_ip4 && fd_g_config->cnf_flags.no_ip6) { |
| 331 | TRACE_ERROR( "IP and IPv6 cannot be disabled at the same time."); |
| 332 | return EINVAL; |
| 333 | } |
| 334 | if (fd_g_config->cnf_flags.no_tcp && fd_g_config->cnf_flags.no_sctp) { |
| 335 | TRACE_ERROR( "TCP and SCTP cannot be disabled at the same time."); |
| 336 | return EINVAL; |
| 337 | } |
| 338 | |
| 339 | /* Validate local endpoints */ |
| 340 | if ((!FD_IS_LIST_EMPTY(&fd_g_config->cnf_endpoints)) && (fd_g_config->cnf_flags.no_ip4 || fd_g_config->cnf_flags.no_ip6)) { |
| 341 | struct fd_list * li; |
| 342 | for ( li = fd_g_config->cnf_endpoints.next; li != &fd_g_config->cnf_endpoints; li = li->next) { |
| 343 | struct fd_endpoint * ep = (struct fd_endpoint *)li; |
| 344 | if ( (fd_g_config->cnf_flags.no_ip4 && (ep->sa.sa_family == AF_INET)) |
| 345 | ||(fd_g_config->cnf_flags.no_ip6 && (ep->sa.sa_family == AF_INET6)) ) { |
| 346 | char sa_buf[sSA_DUMP_STRLEN];; |
| 347 | li = li->prev; |
| 348 | fd_list_unlink(&ep->chain); |
| 349 | fd_sa_sdump_numeric(sa_buf, &ep->sa); |
| 350 | LOG_N("Info: Removing local address conflicting with the flags no_IP / no_IP6 : %s", sa_buf); |
| 351 | free(ep); |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* Configure TLS default parameters */ |
| 357 | if ((!fd_g_config->cnf_sec_data.tls_disabled) && (!fd_g_config->cnf_sec_data.prio_string)) { |
| 358 | const char * err_pos = NULL; |
| 359 | CHECK_GNUTLS_DO( gnutls_priority_init( |
| 360 | &fd_g_config->cnf_sec_data.prio_cache, |
| 361 | GNUTLS_DEFAULT_PRIORITY, |
| 362 | &err_pos), |
| 363 | { TRACE_ERROR("Error in priority string at position : %s", err_pos); return EINVAL; } ); |
| 364 | } |
| 365 | |
| 366 | /* Verify that our certificate is valid -- otherwise remote peers will reject it */ |
| 367 | if (!fd_g_config->cnf_sec_data.tls_disabled) { |
| 368 | int ret = 0, i; |
| 369 | |
| 370 | gnutls_datum_t certfile; |
| 371 | |
| 372 | gnutls_x509_crt_t * certs = NULL; |
| 373 | unsigned int cert_max = 0; |
| 374 | |
| 375 | |
| 376 | /* Read the certificate file */ |
| 377 | FILE *stream = fopen (fd_g_config->cnf_sec_data.cert_file, "rb"); |
| 378 | if (!stream) { |
| 379 | int err = errno; |
| 380 | TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.cert_file, strerror(err)); |
| 381 | return err; |
| 382 | } |
| 383 | CHECK_FCT( fd_conf_stream_to_gnutls_datum(stream, &certfile) ); |
| 384 | fclose(stream); |
| 385 | |
| 386 | /* Import the certificate(s) */ |
| 387 | GNUTLS_TRACE( ret = gnutls_x509_crt_list_import(NULL, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED) ); |
| 388 | if (ret != GNUTLS_E_SHORT_MEMORY_BUFFER) { |
| 389 | CHECK_GNUTLS_DO(ret, return EINVAL); |
| 390 | } |
| 391 | |
| 392 | CHECK_MALLOC( certs = calloc(cert_max, sizeof(gnutls_x509_crt_t)) ); |
| 393 | CHECK_GNUTLS_DO( gnutls_x509_crt_list_import(certs, &cert_max, &certfile, GNUTLS_X509_FMT_PEM, |
| 394 | #ifdef GNUTLS_VERSION_300 |
| 395 | GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED |
| 396 | #else /* GNUTLS_VERSION_300 */ |
| 397 | 0 |
| 398 | #endif /* GNUTLS_VERSION_300 */ |
| 399 | ), |
| 400 | { |
| 401 | TRACE_ERROR("Failed to import the data from file '%s'", fd_g_config->cnf_sec_data.cert_file); |
| 402 | free(certfile.data); |
| 403 | return EINVAL; |
| 404 | } ); |
| 405 | free(certfile.data); |
| 406 | |
| 407 | ASSERT(cert_max >= 1); |
| 408 | |
| 409 | /* Now, verify the list against the local CA and CRL */ |
| 410 | |
| 411 | #ifdef GNUTLS_VERSION_300 |
| 412 | |
| 413 | /* We use the trust list for this purpose */ |
| 414 | { |
| 415 | unsigned int output; |
| 416 | |
| 417 | gnutls_x509_trust_list_verify_named_crt ( |
| 418 | fd_g_config->cnf_sec_data.trustlist, |
| 419 | certs[0], |
| 420 | fd_g_config->cnf_diamid, |
| 421 | fd_g_config->cnf_diamid_len, |
| 422 | 0, |
| 423 | &output, |
| 424 | fd_conf_print_details_func); |
| 425 | |
| 426 | /* if this certificate is not explicitly trusted verify against CAs |
| 427 | */ |
| 428 | if (output != 0) |
| 429 | { |
| 430 | gnutls_x509_trust_list_verify_crt ( |
| 431 | fd_g_config->cnf_sec_data.trustlist, |
| 432 | certs, |
| 433 | cert_max, |
| 434 | 0, |
| 435 | &output, |
| 436 | fd_conf_print_details_func); |
| 437 | } |
| 438 | |
| 439 | if (output & GNUTLS_CERT_INVALID) |
| 440 | { |
| 441 | fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file); |
| 442 | if (output & GNUTLS_CERT_SIGNER_NOT_FOUND) |
| 443 | TRACE_ERROR(" - The certificate hasn't got a known issuer. Did you forget to specify TLS_CA ?"); |
| 444 | if (output & GNUTLS_CERT_SIGNER_NOT_CA) |
| 445 | TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints."); |
| 446 | if (output & GNUTLS_CERT_NOT_ACTIVATED) |
| 447 | TRACE_ERROR(" - The certificate is not yet activated."); |
| 448 | if (output & GNUTLS_CERT_EXPIRED) |
| 449 | TRACE_ERROR(" - The certificate is expired."); |
| 450 | return EINVAL; |
| 451 | } |
| 452 | |
| 453 | /* Now check the subject matches our hostname */ |
| 454 | if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) |
| 455 | { |
| 456 | TRACE_ERROR("TLS: The certificate owner does not match the hostname '%s'", fd_g_config->cnf_diamid); |
| 457 | return EINVAL; |
| 458 | } |
| 459 | |
| 460 | } |
| 461 | |
| 462 | |
| 463 | #else /* GNUTLS_VERSION_300 */ |
| 464 | |
| 465 | /* GnuTLS 2.x way of checking certificates */ |
| 466 | { |
| 467 | gnutls_x509_crt_t * CA_list; |
| 468 | int CA_list_length; |
| 469 | |
| 470 | gnutls_x509_crl_t * CRL_list; |
| 471 | int CRL_list_length; |
| 472 | |
| 473 | unsigned int verify; |
| 474 | time_t now; |
| 475 | GNUTLS_TRACE( gnutls_certificate_get_x509_cas (fd_g_config->cnf_sec_data.credentials, &CA_list, (unsigned int *) &CA_list_length) ); |
| 476 | GNUTLS_TRACE( gnutls_certificate_get_x509_crls (fd_g_config->cnf_sec_data.credentials, &CRL_list, (unsigned int *) &CRL_list_length) ); |
| 477 | CHECK_GNUTLS_DO( gnutls_x509_crt_list_verify(certs, cert_max, CA_list, CA_list_length, CRL_list, CRL_list_length, 0, &verify), |
| 478 | { |
| 479 | TRACE_ERROR("Failed to verify the local certificate '%s' against local credentials. Please check your certificate is valid.", fd_g_config->cnf_sec_data.cert_file); |
| 480 | return EINVAL; |
| 481 | } ); |
| 482 | |
| 483 | if (verify) { |
| 484 | fd_log_debug("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file); |
| 485 | if (verify & GNUTLS_CERT_INVALID) |
| 486 | TRACE_ERROR(" - The certificate is not trusted (unknown CA? expired?)"); |
| 487 | if (verify & GNUTLS_CERT_REVOKED) |
| 488 | TRACE_ERROR(" - The certificate has been revoked."); |
| 489 | if (verify & GNUTLS_CERT_SIGNER_NOT_FOUND) |
| 490 | TRACE_ERROR(" - The certificate hasn't got a known issuer."); |
| 491 | if (verify & GNUTLS_CERT_SIGNER_NOT_CA) |
| 492 | TRACE_ERROR(" - The certificate signer is not a CA, or uses version 1, or 3 without basic constraints."); |
| 493 | if (verify & GNUTLS_CERT_INSECURE_ALGORITHM) |
| 494 | TRACE_ERROR(" - The certificate signature uses a weak algorithm."); |
| 495 | return EINVAL; |
| 496 | } |
| 497 | |
| 498 | /* Check the local Identity is valid with the certificate */ |
| 499 | if (!gnutls_x509_crt_check_hostname (certs[0], fd_g_config->cnf_diamid)) { |
| 500 | TRACE_ERROR("TLS: Local certificate '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file); |
| 501 | TRACE_ERROR(" - The certificate hostname does not match '%s'", fd_g_config->cnf_diamid); |
| 502 | return EINVAL; |
| 503 | } |
| 504 | |
| 505 | /* Check validity of all the certificates in the chain */ |
| 506 | now = time(NULL); |
| 507 | for (i = 0; i < cert_max; i++) |
| 508 | { |
| 509 | time_t deadline; |
| 510 | |
| 511 | GNUTLS_TRACE( deadline = gnutls_x509_crt_get_expiration_time(certs[i]) ); |
| 512 | if ((deadline != (time_t)-1) && (deadline < now)) { |
| 513 | TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file); |
| 514 | TRACE_ERROR(" - The certificate %d in the chain is expired", i); |
| 515 | return EINVAL; |
| 516 | } |
| 517 | |
| 518 | GNUTLS_TRACE( deadline = gnutls_x509_crt_get_activation_time(certs[i]) ); |
| 519 | if ((deadline != (time_t)-1) && (deadline > now)) { |
| 520 | TRACE_ERROR("TLS: Local certificate chain '%s' is invalid :", fd_g_config->cnf_sec_data.cert_file); |
| 521 | TRACE_ERROR(" - The certificate %d in the chain is not yet activated", i); |
| 522 | return EINVAL; |
| 523 | } |
| 524 | } |
| 525 | } |
| 526 | #endif /* GNUTLS_VERSION_300 */ |
| 527 | |
| 528 | /* Everything checked OK, free the certificate list */ |
| 529 | for (i = 0; i < cert_max; i++) |
| 530 | { |
| 531 | GNUTLS_TRACE( gnutls_x509_crt_deinit (certs[i]) ); |
| 532 | } |
| 533 | free(certs); |
| 534 | |
| 535 | #ifdef GNUTLS_VERSION_300 |
| 536 | /* Use certificate verification during the handshake */ |
| 537 | gnutls_certificate_set_verify_function (fd_g_config->cnf_sec_data.credentials, fd_tls_verify_credentials_2); |
| 538 | #endif /* GNUTLS_VERSION_300 */ |
| 539 | |
| 540 | } |
| 541 | |
| 542 | /* gnutls_certificate_set_verify_limits -- so far the default values are fine... */ |
| 543 | |
| 544 | /* DH */ |
| 545 | if (!fd_g_config->cnf_sec_data.tls_disabled) { |
| 546 | if (fd_g_config->cnf_sec_data.dh_file) { |
| 547 | gnutls_datum_t dhparams = { NULL, 0 }; |
| 548 | size_t alloc = 0; |
| 549 | FILE *stream = fopen (fd_g_config->cnf_sec_data.dh_file, "rb"); |
| 550 | if (!stream) { |
| 551 | int err = errno; |
| 552 | TRACE_DEBUG(INFO, "An error occurred while opening '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err)); |
| 553 | return err; |
| 554 | } |
| 555 | do { |
| 556 | uint8_t * realloced = NULL; |
| 557 | size_t read = 0; |
| 558 | |
| 559 | if (alloc < dhparams.size + BUFSIZ + 1) { |
| 560 | alloc += alloc / 2 + BUFSIZ + 1; |
| 561 | CHECK_MALLOC_DO( realloced = realloc(dhparams.data, alloc), |
| 562 | { |
| 563 | free(dhparams.data); |
| 564 | return ENOMEM; |
| 565 | } ) |
| 566 | dhparams.data = realloced; |
| 567 | } |
| 568 | |
| 569 | read = fread( dhparams.data + dhparams.size, 1, alloc - dhparams.size - 1, stream ); |
| 570 | dhparams.size += read; |
| 571 | |
| 572 | if (ferror(stream)) { |
| 573 | int err = errno; |
| 574 | TRACE_DEBUG(INFO, "An error occurred while reading '%s': %s", fd_g_config->cnf_sec_data.dh_file, strerror(err)); |
| 575 | return err; |
| 576 | } |
| 577 | } while (!feof(stream)); |
| 578 | dhparams.data[dhparams.size] = '\0'; |
| 579 | fclose(stream); |
| 580 | CHECK_GNUTLS_DO( gnutls_dh_params_import_pkcs3( |
| 581 | fd_g_config->cnf_sec_data.dh_cache, |
| 582 | &dhparams, |
| 583 | GNUTLS_X509_FMT_PEM), |
| 584 | { TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } ); |
| 585 | free(dhparams.data); |
| 586 | |
| 587 | } else { |
| 588 | LOG_D( "Generating fresh Diffie-Hellman parameters of size %d (this takes some time)... ", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); |
| 589 | CHECK_GNUTLS_DO( gnutls_dh_params_generate2( |
| 590 | fd_g_config->cnf_sec_data.dh_cache, |
| 591 | fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS), |
| 592 | { TRACE_ERROR("Error in DH bits value : %d", fd_g_config->cnf_sec_data.dh_bits ?: GNUTLS_DEFAULT_DHBITS); return EINVAL; } ); |
| 593 | } |
| 594 | |
| 595 | } |
| 596 | |
| 597 | return 0; |
| 598 | } |
| 599 | #ifndef GNUTLS_VERSION_300 |
| 600 | GCC_DIAG_ON("-Wdeprecated-declarations") |
| 601 | #endif /* !GNUTLS_VERSION_300 */ |
| 602 | |
| 603 | |
| 604 | /* Destroy contents of fd_g_config structure */ |
| 605 | int fd_conf_deinit() |
| 606 | { |
| 607 | TRACE_ENTRY(); |
| 608 | |
| 609 | if (!fd_g_config) |
| 610 | return 0; |
| 611 | |
| 612 | /* Free the TLS parameters */ |
| 613 | #ifdef GNUTLS_VERSION_300 |
| 614 | gnutls_x509_trust_list_deinit(fd_g_config->cnf_sec_data.trustlist, 1); |
| 615 | #endif /* GNUTLS_VERSION_300 */ |
| 616 | gnutls_priority_deinit(fd_g_config->cnf_sec_data.prio_cache); |
| 617 | gnutls_dh_params_deinit(fd_g_config->cnf_sec_data.dh_cache); |
| 618 | gnutls_certificate_free_credentials(fd_g_config->cnf_sec_data.credentials); |
| 619 | |
| 620 | free(fd_g_config->cnf_sec_data.cert_file); fd_g_config->cnf_sec_data.cert_file = NULL; |
| 621 | free(fd_g_config->cnf_sec_data.key_file); fd_g_config->cnf_sec_data.key_file = NULL; |
| 622 | free(fd_g_config->cnf_sec_data.ca_file); fd_g_config->cnf_sec_data.ca_file = NULL; |
| 623 | free(fd_g_config->cnf_sec_data.crl_file); fd_g_config->cnf_sec_data.crl_file = NULL; |
| 624 | free(fd_g_config->cnf_sec_data.prio_string); fd_g_config->cnf_sec_data.prio_string = NULL; |
| 625 | free(fd_g_config->cnf_sec_data.dh_file); fd_g_config->cnf_sec_data.dh_file = NULL; |
| 626 | |
| 627 | /* Destroy dictionary */ |
| 628 | CHECK_FCT_DO( fd_dict_fini(&fd_g_config->cnf_dict), ); |
| 629 | |
| 630 | /* Destroy the main event queue */ |
| 631 | CHECK_FCT_DO( fd_fifo_del(&fd_g_config->cnf_main_ev), ); |
| 632 | |
| 633 | /* Destroy the local endpoints and applications */ |
| 634 | CHECK_FCT_DO(fd_ep_filter(&fd_g_config->cnf_endpoints, 0 ), ); |
| 635 | CHECK_FCT_DO(fd_app_empty(&fd_g_config->cnf_apps ), ); |
| 636 | |
| 637 | /* Destroy the local identity */ |
| 638 | free(fd_g_config->cnf_diamid); fd_g_config->cnf_diamid = NULL; |
| 639 | free(fd_g_config->cnf_diamrlm); fd_g_config->cnf_diamrlm = NULL; |
| 640 | |
| 641 | return 0; |
| 642 | } |
| 643 | |
| 644 | |