static void load_also_cas(starter_ca_t *ca, also_t *also, starter_config_t *cfg) { while (also != NULL) { kw_list_t *kw = find_also_ca(also->name, ca, cfg); if (kw == NULL) { DBG1(DBG_APP, " ca '%s' cannot include '%s'", ca->name, also->name); } else { DBG2(DBG_APP, "ca '%s' includes '%s'", ca->name, also->name); /* only load if no error occurred in the first round */ if (cfg->err == 0) load_ca(ca, kw, cfg); } also = also->next; } }
/* * initialize tls virtual domains */ int init_tls_domains(struct tls_domain *d) { struct tls_domain *dom; dom = d; while (d) { if (d->name.len) { LM_INFO("Processing TLS domain '%.*s'\n", d->name.len, ZSW(d->name.s)); } else { LM_INFO("Processing TLS domain [%s:%d]\n", ip_addr2a(&d->addr), d->port); } /* * set method */ if (d->method == TLS_METHOD_UNSPEC) { LM_DBG("no method for tls[%s:%d], using default\n", ip_addr2a(&d->addr), d->port); d->method = tls_method; } /* * create context */ d->ctx = SSL_CTX_new(ssl_methods[d->method - 1]); if (d->ctx == NULL) { LM_ERR("cannot create ssl context for " "tls[%s:%d]\n", ip_addr2a(&d->addr), d->port); return -1; } if (init_ssl_ctx_behavior( d ) < 0) return -1; /* * load certificate */ if (!d->cert_file) { LM_NOTICE("no certificate for tls[%s:%d] defined, using default" "'%s'\n", ip_addr2a(&d->addr), d->port, tls_cert_file); d->cert_file = tls_cert_file; } if (load_certificate(d->ctx, d->cert_file) < 0) return -1; /* * load ca */ if (!d->ca_file) { LM_NOTICE("no CA for tls[%s:%d] defined, " "using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_ca_file); d->ca_file = tls_ca_file; } if (d->ca_file && load_ca(d->ctx, d->ca_file) < 0) return -1; /* * load ca from directory */ if (!d->ca_directory) { LM_NOTICE("no CA for tls[%s:%d] defined, " "using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_ca_dir); d->ca_directory = tls_ca_dir; } if (d->ca_directory && load_ca_dir(d->ctx, d->ca_directory) < 0) return -1; d = d->next; } /* * load all private keys as the last step (may prompt for password) */ d = dom; while (d) { if (!d->pkey_file) { LM_NOTICE("no private key for tls[%s:%d] defined, using default" "'%s'\n", ip_addr2a(&d->addr), d->port, tls_pkey_file); d->pkey_file = tls_pkey_file; } if (load_private_key(d->ctx, d->pkey_file) < 0) return -1; d = d->next; } return 0; }
static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **options) { TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; int ret; if ((ret = ff_tls_open_underlying(s, h, uri, options)) < 0) goto fail; c->ssl_context = SSLCreateContext(NULL, s->listen ? kSSLServerSide : kSSLClientSide, kSSLStreamType); if (!c->ssl_context) { av_log(h, AV_LOG_ERROR, "Unable to create SSL context\n"); ret = AVERROR(ENOMEM); goto fail; } if (s->ca_file) { if ((ret = load_ca(h)) < 0) goto fail; } if (s->ca_file || !s->verify) CHECK_ERROR(SSLSetSessionOption, c->ssl_context, kSSLSessionOptionBreakOnServerAuth, true); if (s->cert_file) if ((ret = load_cert(h)) < 0) goto fail; CHECK_ERROR(SSLSetPeerDomainName, c->ssl_context, s->host, strlen(s->host)); CHECK_ERROR(SSLSetIOFuncs, c->ssl_context, tls_read_cb, tls_write_cb); CHECK_ERROR(SSLSetConnection, c->ssl_context, h); while (1) { OSStatus status = SSLHandshake(c->ssl_context); if (status == errSSLServerAuthCompleted) { SecTrustRef peerTrust; SecTrustResultType trustResult; if (!s->verify) continue; if (SSLCopyPeerTrust(c->ssl_context, &peerTrust) != noErr) { ret = AVERROR(ENOMEM); goto fail; } if (SecTrustSetAnchorCertificates(peerTrust, c->ca_array) != noErr) { ret = AVERROR_UNKNOWN; goto fail; } if (SecTrustEvaluate(peerTrust, &trustResult) != noErr) { ret = AVERROR_UNKNOWN; goto fail; } if (trustResult == kSecTrustResultProceed || trustResult == kSecTrustResultUnspecified) { // certificate is trusted status = errSSLWouldBlock; // so we call SSLHandshake again } else if (trustResult == kSecTrustResultRecoverableTrustFailure) { // not trusted, for some reason other than being expired status = errSSLXCertChainInvalid; } else { // cannot use this certificate (fatal) status = errSSLBadCert; } if (peerTrust) CFRelease(peerTrust); } if (status == noErr) break; av_log(h, AV_LOG_ERROR, "Unable to negotiate TLS/SSL session: %i\n", (int)status); ret = AVERROR(EIO); goto fail; } return 0; fail: tls_close(h); return ret; }
/* * called once from main.c (main process) */ int init_tls(void) { struct tls_domain *d; DBG("init_tls: Entered\n"); #if OPENSSL_VERSION_NUMBER < 0x00907000L LOG(L_ERR, "WARNING! You are using an old version of OpenSSL (< 0.9.7). Upgrade!\n"); #endif LOG(L_ALERT, "WARNING! TLS is considered as an EXPERIMENTAL module\n" ); /* * this has to be called before any function calling CRYPTO_malloc, * CRYPTO_malloc will set allow_customize in openssl to 0 */ if (!CRYPTO_set_mem_functions(ser_malloc, ser_realloc, ser_free)) { LOG(L_ERR, "init_tls: Unable to set the memory allocation functions\n"); return -1; } SSL_library_init(); SSL_load_error_strings(); init_ssl_methods(); /* * initialize default context first */ default_ctx = SSL_CTX_new(ssl_methods[tls_method - 1]); if (default_ctx == NULL) { LOG(L_ERR, "init_tls: Cannot create default ssl context\n"); return -1; } init_ssl_ctx_behavior( default_ctx ); if (load_certificate(default_ctx, tls_cert_file) < 0) return -1; if (tls_ca_file && load_ca(default_ctx, tls_ca_file) < 0) return -1; if (load_private_key(default_ctx, tls_pkey_file) < 0) return -1; /* * now initialize tls virtual domains */ d = tls_domains; while (d) { DBG("init_tls: Processing TLS domain [%s:%d]\n", ip_addr2a(&d->addr), d->port); /* * create context */ if (d->method == TLS_METHOD_UNSPEC) { DBG("init_tls: No method for tls[%s:%d], using default\n", ip_addr2a(&d->addr), d->port); d->method = tls_method; } d->ctx = SSL_CTX_new(ssl_methods[d->method - 1]); if (d->ctx == NULL) { LOG(L_ERR, "init_tls: Cannot create ssl context for tls[%s:%d]\n", ip_addr2a(&d->addr), d->port); return -1; } init_ssl_ctx_behavior( d->ctx ); /* * load certificate */ if (!d->cert_file) { LOG(L_NOTICE, "init_tls: No certificate for tls[%s:%d] defined, using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_cert_file); d->cert_file = tls_cert_file; } if (load_certificate(d->ctx, d->cert_file) < 0) return -1; /* * load ca */ if (!d->ca_file) { LOG(L_NOTICE, "init_tls: No CA for tls[%s:%d] defined, using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_ca_file); d->ca_file = tls_ca_file; } if (d->ca_file && load_ca(d->ctx, d->ca_file) < 0) return -1; d = d->next; } /* * load all private keys as the last step (may prompt for password) */ d = tls_domains; while (d) { if (!d->pkey_file) { LOG(L_NOTICE, "init_tls: No private key for tls[%s:%d] defined, using default '%s'\n", ip_addr2a(&d->addr), d->port, tls_pkey_file); d->pkey_file = tls_pkey_file; } if (load_private_key(d->ctx, d->pkey_file) < 0) return -1; d = d->next; } /* * we are all set */ return 0; }
/* * load and parse an IPsec configuration file */ starter_config_t* confread_load(const char *file) { starter_config_t *cfg = NULL; config_parsed_t *cfgp; section_list_t *sconn, *sca; starter_conn_t *conn; starter_ca_t *ca; u_int total_err; u_int visit = 0; /* load IPSec configuration file */ cfgp = parser_load_conf(file); if (!cfgp) { return NULL; } cfg = malloc_thing(starter_config_t); /* set default values */ default_values(cfg); /* load config setup section */ load_setup(cfg, cfgp); /* in the first round parse also statements */ cfg->parse_also = TRUE; /* find %default ca section */ for (sca = cfgp->ca_first; sca; sca = sca->next) { if (streq(sca->name, "%default")) { DBG2(DBG_APP, "Loading ca %%default"); load_ca(&cfg->ca_default, sca->kw, cfg); } } /* parameters defined in ca %default sections can be overloads */ cfg->ca_default.seen = SEEN_NONE; /* load other ca sections */ for (sca = cfgp->ca_first; sca; sca = sca->next) { u_int previous_err; /* skip %default ca section */ if (streq(sca->name, "%default")) continue; DBG2(DBG_APP, "Loading ca '%s'", sca->name); ca = malloc_thing(starter_ca_t); ca_default(sca->name, ca, &cfg->ca_default); ca->kw = sca->kw; ca->next = NULL; previous_err = cfg->err; load_ca(ca, ca->kw, cfg); if (cfg->err > previous_err) { /* errors occurred - free the ca */ confread_free_ca(ca); cfg->non_fatal_err += cfg->err - previous_err; cfg->err = previous_err; } else { /* success - insert the ca into the chained list */ if (cfg->ca_last) cfg->ca_last->next = ca; cfg->ca_last = ca; if (!cfg->ca_first) cfg->ca_first = ca; } } for (ca = cfg->ca_first; ca; ca = ca->next) { also_t *also = ca->also; while (also != NULL) { kw_list_t *kw = find_also_ca(also->name, cfg->ca_first, cfg); load_ca(ca, kw, cfg); also = also->next; } if (ca->startup != STARTUP_NO) ca->state = STATE_TO_ADD; } /* find %default conn sections */ for (sconn = cfgp->conn_first; sconn; sconn = sconn->next) { if (streq(sconn->name, "%default")) { DBG2(DBG_APP, "Loading conn %%default"); load_conn(&cfg->conn_default, sconn->kw, cfg); } } /* parameters defined in conn %default sections can be overloaded */ cfg->conn_default.seen = SEEN_NONE; cfg->conn_default.right.seen = SEEN_NONE; cfg->conn_default.left.seen = SEEN_NONE; /* load other conn sections */ for (sconn = cfgp->conn_first; sconn; sconn = sconn->next) { u_int previous_err; /* skip %default conn section */ if (streq(sconn->name, "%default")) continue; DBG2(DBG_APP, "Loading conn '%s'", sconn->name); conn = malloc_thing(starter_conn_t); conn_default(sconn->name, conn, &cfg->conn_default); conn->kw = sconn->kw; conn->next = NULL; previous_err = cfg->err; load_conn(conn, conn->kw, cfg); if (cfg->err > previous_err) { /* error occurred - free the conn */ confread_free_conn(conn); cfg->non_fatal_err += cfg->err - previous_err; cfg->err = previous_err; } else { /* success - insert the conn into the chained list */ if (cfg->conn_last) cfg->conn_last->next = conn; cfg->conn_last = conn; if (!cfg->conn_first) cfg->conn_first = conn; } } /* in the second round do not parse also statements */ cfg->parse_also = FALSE; for (ca = cfg->ca_first; ca; ca = ca->next) { ca->visit = ++visit; load_also_cas(ca, ca->also, cfg); if (ca->startup != STARTUP_NO) ca->state = STATE_TO_ADD; } for (conn = cfg->conn_first; conn; conn = conn->next) { conn->visit = ++visit; load_also_conns(conn, conn->also, cfg); if (conn->startup != STARTUP_NO) conn->state = STATE_TO_ADD; } parser_free_conf(cfgp); total_err = cfg->err + cfg->non_fatal_err; if (total_err > 0) { DBG1(DBG_APP, "### %d parsing error%s (%d fatal) ###", total_err, (total_err > 1)?"s":"", cfg->err); } return cfg; }
/* * Revoke one certificate at a time * No check performed to see if certificate already revoked. */ void revoke_cert(char * ca_name, char * name) { char filename[FIELD_SZ+5]; FILE * f ; X509_CRL * crl ; X509 * cert ; ASN1_INTEGER * r_serial ; ASN1_INTEGER * crlnum ; X509_REVOKED * rev ; ASN1_TIME * tm ; identity ca ; BIO * out ; BIGNUM * b_crlnum ; /* Find requested certificate by name */ sprintf(filename, "%s.crt", name); if ((f=fopen(filename, "r"))==NULL) { fprintf(stderr, "Cannot find: %s\n", filename); return ; } cert = PEM_read_X509(f, NULL, NULL, NULL); fclose(f); /* Get certificate serial number */ r_serial = X509_get_serialNumber(cert); /* Find out if if was already revoked */ /* Make a revoked object with that serial */ rev = X509_REVOKED_new(); X509_REVOKED_set_serialNumber(rev, r_serial); X509_free(cert); /* Set reason to unspecified */ rev->reason = ASN1_ENUMERATED_get(CRL_REASON_UNSPECIFIED); /* Load or create new CRL */ if ((crl = load_crl(ca_name))==NULL) { crl = X509_CRL_new(); X509_CRL_set_version(crl, 1); /* Set CRL number */ crlnum = ASN1_INTEGER_new(); ASN1_INTEGER_set(crlnum, 1); X509_CRL_add1_ext_i2d(crl, NID_crl_number, crlnum, 0, 0); ASN1_INTEGER_free(crlnum); } else { crlnum = X509_CRL_get_ext_d2i(crl, NID_crl_number, 0, 0); b_crlnum = ASN1_INTEGER_to_BN(crlnum, NULL); BN_add_word(b_crlnum, 1); BN_to_ASN1_INTEGER(b_crlnum, crlnum); BN_free(b_crlnum); X509_CRL_add1_ext_i2d(crl, NID_crl_number, crlnum, 0, X509V3_ADD_REPLACE_EXISTING); ASN1_INTEGER_free(crlnum); } /* What time is it? */ tm = ASN1_TIME_new(); X509_gmtime_adj(tm, 0); X509_REVOKED_set_revocationDate(rev, tm); X509_CRL_set_lastUpdate(crl, tm); /* Set CRL next update to a year from now */ X509_gmtime_adj(tm, 365*24*60*60); X509_CRL_set_nextUpdate(crl, tm); ASN1_TIME_free(tm); /* Add revoked to CRL */ X509_CRL_add0_revoked(crl, rev); X509_CRL_sort(crl); /* Load root key to sign CRL */ if (load_ca(ca_name, &ca)!=0) { fprintf(stderr, "Cannot find CA key/crt\n"); return ; } X509_CRL_set_issuer_name(crl, X509_get_subject_name(ca.cert)); X509_free(ca.cert); /* Sign CRL */ X509_CRL_sign(crl, ca.key, EVP_sha256()); EVP_PKEY_free(ca.key); /* Dump CRL */ sprintf(filename, "%s.crl", ca_name); if ((f = fopen(filename, "wb"))==NULL) { fprintf(stderr, "Cannot write %s: aborting\n", filename); X509_CRL_free(crl); return ; } out = BIO_new(BIO_s_file()); BIO_set_fp(out, f, BIO_NOCLOSE); PEM_write_bio_X509_CRL(out, crl); BIO_free_all(out); fclose(f); X509_CRL_free(crl); return ; }
/* * Create identity */ int build_identity(void) { EVP_PKEY * pkey ; RSA * rsa ; EC_KEY * ecc ; X509 * cert ; X509_NAME * name ; identity ca ; char filename[FIELD_SZ+5]; FILE * pem ; /* Check before overwriting */ sprintf(filename, "%s.crt", certinfo.cn); if (access(filename, F_OK)!=-1) { fprintf(stderr, "identity named %s already exists in this directory. Exiting now\n", filename); return -1 ; } sprintf(filename, "%s.key", certinfo.cn); if (access(filename, F_OK)!=-1) { fprintf(stderr, "identity named %s already exists in this directory. Exiting now\n", filename); return -1 ; } switch (certinfo.profile) { case PROFILE_ROOT_CA: strcpy(certinfo.ou, "Root"); break; case PROFILE_SUB_CA: strcpy(certinfo.ou, "Sub"); break; case PROFILE_SERVER: strcpy(certinfo.ou, "Server"); break; case PROFILE_CLIENT: strcpy(certinfo.ou, "Client"); break; case PROFILE_WWW: strcpy(certinfo.ou, "Server"); break; default: fprintf(stderr, "Unknown profile: aborting\n"); return -1 ; } if (certinfo.ec_name[0] && certinfo.profile!=PROFILE_CLIENT) { fprintf(stderr, "ECC keys are only supported for clients\n"); return -1 ; } if (certinfo.profile != PROFILE_ROOT_CA) { /* Need to load signing CA */ if (load_ca(certinfo.signing_ca, &ca)!=0) { fprintf(stderr, "Cannot find CA key or certificate\n"); return -1 ; } /* Organization is the same as root */ X509_NAME_get_text_by_NID(X509_get_subject_name(ca.cert), NID_organizationName, certinfo.o, FIELD_SZ); } /* Generate key pair */ if (certinfo.ec_name[0]) { printf("Generating EC key [%s]\n", certinfo.ec_name); ecc = EC_KEY_new_by_curve_name(OBJ_txt2nid(certinfo.ec_name)); if (!ecc) { fprintf(stderr, "Unknown curve: [%s]\n", certinfo.ec_name); return -1 ; } EC_KEY_set_asn1_flag(ecc, OPENSSL_EC_NAMED_CURVE); EC_KEY_generate_key(ecc); pkey = EVP_PKEY_new(); EVP_PKEY_assign_EC_KEY(pkey, ecc); } else { printf("Generating RSA-%d key\n", certinfo.rsa_keysz); pkey = EVP_PKEY_new(); rsa = RSA_generate_key(certinfo.rsa_keysz, RSA_F4, progress, 0); EVP_PKEY_assign_RSA(pkey, rsa); } /* Assign all certificate fields */ cert = X509_new(); X509_set_version(cert, 2); set_serial128(cert); X509_gmtime_adj(X509_get_notBefore(cert), 0); X509_gmtime_adj(X509_get_notAfter(cert), certinfo.days * 24*60*60); X509_set_pubkey(cert, pkey); name = X509_get_subject_name(cert); if (certinfo.c[0]) { X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char*)certinfo.c, -1, -1, 0); } X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char*)certinfo.o, -1, -1, 0); X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char*)certinfo.cn, -1, -1, 0); X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char*)certinfo.ou, -1, -1, 0); if (certinfo.l[0]) { X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)certinfo.l, -1, -1, 0); } if (certinfo.st[0]) { X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)certinfo.st, -1, -1, 0); } /* Set extensions according to profile */ switch (certinfo.profile) { case PROFILE_ROOT_CA: /* CA profiles can issue certs and sign CRLS */ set_extension(cert, cert, NID_basic_constraints, "critical,CA:TRUE"); set_extension(cert, cert, NID_key_usage, "critical,keyCertSign,cRLSign"); set_extension(cert, cert, NID_subject_key_identifier, "hash"); set_extension(cert, cert, NID_authority_key_identifier, "keyid:always"); break ; case PROFILE_SUB_CA: /* CA profiles can issue certs and sign CRLS */ set_extension(ca.cert, cert, NID_basic_constraints, "critical,CA:TRUE"); set_extension(ca.cert, cert, NID_key_usage, "critical,keyCertSign,cRLSign"); set_extension(ca.cert, cert, NID_subject_key_identifier, "hash"); set_extension(ca.cert, cert, NID_authority_key_identifier, "keyid:always"); break; case PROFILE_CLIENT: if (certinfo.san[0]) { set_extension(ca.cert, cert, NID_subject_alt_name, certinfo.san); } set_extension(ca.cert, cert, NID_basic_constraints, "CA:FALSE"); set_extension(ca.cert, cert, NID_anyExtendedKeyUsage, "clientAuth"); set_extension(ca.cert, cert, NID_key_usage, "digitalSignature"); set_extension(ca.cert, cert, NID_subject_key_identifier, "hash"); set_extension(ca.cert, cert, NID_authority_key_identifier, "issuer:always,keyid:always"); break ; case PROFILE_SERVER: if (certinfo.san[0]) { set_extension(ca.cert, cert, NID_subject_alt_name, certinfo.san); } set_extension(ca.cert, cert, NID_basic_constraints, "CA:FALSE"); set_extension(ca.cert, cert, NID_netscape_cert_type, "server"); set_extension(ca.cert, cert, NID_anyExtendedKeyUsage, "serverAuth"); set_extension(ca.cert, cert, NID_key_usage, "digitalSignature,keyEncipherment"); set_extension(ca.cert, cert, NID_subject_key_identifier, "hash"); set_extension(ca.cert, cert, NID_authority_key_identifier, "issuer:always,keyid:always"); break ; case PROFILE_WWW: if (certinfo.san[0]) { set_extension(ca.cert, cert, NID_subject_alt_name, certinfo.san); } set_extension(ca.cert, cert, NID_basic_constraints, "CA:FALSE"); set_extension(ca.cert, cert, NID_netscape_cert_type, "server"); set_extension(ca.cert, cert, NID_anyExtendedKeyUsage, "serverAuth,clientAuth"); set_extension(ca.cert, cert, NID_key_usage, "digitalSignature,keyEncipherment"); set_extension(ca.cert, cert, NID_subject_key_identifier, "hash"); set_extension(ca.cert, cert, NID_authority_key_identifier, "issuer:always,keyid:always"); break; case PROFILE_UNKNOWN: default: break ; } /* Set issuer */ if (certinfo.profile==PROFILE_ROOT_CA) { /* Self-signed */ X509_set_issuer_name(cert, name); X509_sign(cert, pkey, EVP_sha256()); } else { /* Signed by parent CA */ X509_set_issuer_name(cert, X509_get_subject_name(ca.cert)); X509_sign(cert, ca.key, EVP_sha256()); } printf("Saving results to %s.[crt|key]\n", certinfo.cn); pem = fopen(filename, "wb"); PEM_write_PrivateKey(pem, pkey, NULL, NULL, 0, NULL, NULL); fclose(pem); sprintf(filename, "%s.crt", certinfo.cn); pem = fopen(filename, "wb"); PEM_write_X509(pem, cert); fclose(pem); X509_free(cert); EVP_PKEY_free(pkey); if (certinfo.profile!=PROFILE_ROOT_CA) { X509_free(ca.cert); EVP_PKEY_free(ca.key); } printf("done\n"); return 0; }