+ if (r <= 0 || r >= (int)sizeof(buf))
+ goto err;
+ if (!SSL_add_file_cert_subjects_to_stack(stack, buf))
+ goto err;
+ }
+
+ if (errno) {
+ SYSerr(SYS_F_OPENDIR, get_last_sys_error());
+ ERR_add_error_data(3, "OPENSSL_DIR_read(&ctx, '", dir, "')");
+ SSLerr(SSL_F_SSL_ADD_DIR_CERT_SUBJECTS_TO_STACK, ERR_R_SYS_LIB);
+ goto err;
+ }
+
+ ret = 1;
+
+ err:
+ if (d)
+ OPENSSL_DIR_end(&d);
+ CRYPTO_w_unlock(CRYPTO_LOCK_READDIR);
+ return ret;
+}
+
+/* Add a certificate to a BUF_MEM structure */
+
+static int ssl_add_cert_to_buf(BUF_MEM *buf, unsigned long *l, X509 *x)
+{
+ int n;
+ unsigned char *p;
+
+ n = i2d_X509(x, NULL);
+ if (!BUF_MEM_grow_clean(buf, (int)(n + (*l) + 3))) {
+ SSLerr(SSL_F_SSL_ADD_CERT_TO_BUF, ERR_R_BUF_LIB);
+ return 0;
+ }
+ p = (unsigned char *)&(buf->data[*l]);
+ l2n3(n, p);
+ i2d_X509(x, &p);
+ *l += n + 3;
+
+ return 1;
+}
+
+/* Add certificate chain to internal SSL BUF_MEM strcuture */
+int ssl_add_cert_chain(SSL *s, CERT_PKEY *cpk, unsigned long *l)
+{
+ BUF_MEM *buf = s->init_buf;
+ int i;
+
+ X509 *x;
+ STACK_OF(X509) *extra_certs;
+ X509_STORE *chain_store;
+
+ /* TLSv1 sends a chain with nothing in it, instead of an alert */
+ if (!BUF_MEM_grow_clean(buf, 10)) {
+ SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_BUF_LIB);
+ return 0;
+ }
+
+ if (!cpk || !cpk->x509)
+ return 1;
+
+ x = cpk->x509;
+
+ /*
+ * If we have a certificate specific chain use it, else use parent ctx.
+ */
+ if (cpk->chain)
+ extra_certs = cpk->chain;
+ else
+ extra_certs = s->ctx->extra_certs;
+
+ if ((s->mode & SSL_MODE_NO_AUTO_CHAIN) || extra_certs)
+ chain_store = NULL;
+ else if (s->cert->chain_store)
+ chain_store = s->cert->chain_store;
+ else
+ chain_store = s->ctx->cert_store;
+
+ if (chain_store) {
+ X509_STORE_CTX xs_ctx;
+
+ if (!X509_STORE_CTX_init(&xs_ctx, chain_store, x, NULL)) {
+ SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, ERR_R_X509_LIB);
+ return (0);
+ }
+ X509_verify_cert(&xs_ctx);
+ /* Don't leave errors in the queue */
+ ERR_clear_error();
+ i = ssl_security_cert_chain(s, xs_ctx.chain, NULL, 0);
+ if (i != 1) {
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
+ return 0;
+ }
+ for (i = 0; i < sk_X509_num(xs_ctx.chain); i++) {
+ x = sk_X509_value(xs_ctx.chain, i);
+
+ if (!ssl_add_cert_to_buf(buf, l, x)) {
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ return 0;
+ }
+ }
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ } else {
+ i = ssl_security_cert_chain(s, extra_certs, x, 0);
+ if (i != 1) {
+ SSLerr(SSL_F_SSL_ADD_CERT_CHAIN, i);
+ return 0;
+ }
+ if (!ssl_add_cert_to_buf(buf, l, x))
+ return 0;
+ for (i = 0; i < sk_X509_num(extra_certs); i++) {
+ x = sk_X509_value(extra_certs, i);
+ if (!ssl_add_cert_to_buf(buf, l, x))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/* Build a certificate chain for current certificate */
+int ssl_build_cert_chain(SSL *s, SSL_CTX *ctx, int flags)
+{
+ CERT *c = s ? s->cert : ctx->cert;
+ CERT_PKEY *cpk = c->key;
+ X509_STORE *chain_store = NULL;
+ X509_STORE_CTX xs_ctx;
+ STACK_OF(X509) *chain = NULL, *untrusted = NULL;
+ X509 *x;
+ int i, rv = 0;
+ unsigned long error;
+
+ if (!cpk->x509) {
+ SSLerr(SSL_F_SSL_BUILD_CERT_CHAIN, SSL_R_NO_CERTIFICATE_SET);
+ goto err;
+ }
+ /* Rearranging and check the chain: add everything to a store */
+ if (flags & SSL_BUILD_CHAIN_FLAG_CHECK) {
+ chain_store = X509_STORE_new();
+ if (!chain_store)
+ goto err;
+ for (i = 0; i < sk_X509_num(cpk->chain); i++) {
+ x = sk_X509_value(cpk->chain, i);
+ if (!X509_STORE_add_cert(chain_store, x)) {
+ error = ERR_peek_last_error();
+ if (ERR_GET_LIB(error) != ERR_LIB_X509 ||
+ ERR_GET_REASON(error) !=
+ X509_R_CERT_ALREADY_IN_HASH_TABLE)
+ goto err;
+ ERR_clear_error();
+ }
+ }
+ /* Add EE cert too: it might be self signed */
+ if (!X509_STORE_add_cert(chain_store, cpk->x509)) {
+ error = ERR_peek_last_error();
+ if (ERR_GET_LIB(error) != ERR_LIB_X509 ||
+ ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE)
+ goto err;
+ ERR_clear_error();
+ }
+ } else {
+ if (c->chain_store)
+ chain_store = c->chain_store;
+ else if (s)
+ chain_store = s->ctx->cert_store;
+ else
+ chain_store = ctx->cert_store;
+
+ if (flags & SSL_BUILD_CHAIN_FLAG_UNTRUSTED)
+ untrusted = cpk->chain;
+ }
+
+ if (!X509_STORE_CTX_init(&xs_ctx, chain_store, cpk->x509, untrusted)) {
+ SSLerr(SSL_F_SSL_BUILD_CERT_CHAIN, ERR_R_X509_LIB);
+ goto err;
+ }
+ /* Set suite B flags if needed */
+ X509_STORE_CTX_set_flags(&xs_ctx,
+ c->cert_flags & SSL_CERT_FLAG_SUITEB_128_LOS);
+
+ i = X509_verify_cert(&xs_ctx);
+ if (i <= 0 && flags & SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR) {
+ if (flags & SSL_BUILD_CHAIN_FLAG_CLEAR_ERROR)
+ ERR_clear_error();
+ i = 1;
+ rv = 2;
+ }
+ if (i > 0)
+ chain = X509_STORE_CTX_get1_chain(&xs_ctx);
+ if (i <= 0) {
+ SSLerr(SSL_F_SSL_BUILD_CERT_CHAIN, SSL_R_CERTIFICATE_VERIFY_FAILED);
+ i = X509_STORE_CTX_get_error(&xs_ctx);
+ ERR_add_error_data(2, "Verify error:",
+ X509_verify_cert_error_string(i));
+
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ goto err;
+ }
+ X509_STORE_CTX_cleanup(&xs_ctx);
+ /* Remove EE certificate from chain */
+ x = sk_X509_shift(chain);
+ X509_free(x);
+ if (flags & SSL_BUILD_CHAIN_FLAG_NO_ROOT) {
+ if (sk_X509_num(chain) > 0) {
+ /* See if last cert is self signed */
+ x = sk_X509_value(chain, sk_X509_num(chain) - 1);
+ X509_check_purpose(x, -1, 0);
+ if (x->ex_flags & EXFLAG_SS) {
+ x = sk_X509_pop(chain);
+ X509_free(x);
+ }
+ }
+ }
+ /*
+ * Check security level of all CA certificates: EE will have been checked
+ * already.
+ */
+ for (i = 0; i < sk_X509_num(chain); i++) {
+ x = sk_X509_value(chain, i);
+ rv = ssl_security_cert(s, ctx, x, 0, 0);
+ if (rv != 1) {
+ SSLerr(SSL_F_SSL_BUILD_CERT_CHAIN, rv);
+ sk_X509_pop_free(chain, X509_free);
+ rv = 0;
+ goto err;
+ }
+ }
+ if (cpk->chain)
+ sk_X509_pop_free(cpk->chain, X509_free);
+ cpk->chain = chain;
+ if (rv == 0)
+ rv = 1;
+ err:
+ if (flags & SSL_BUILD_CHAIN_FLAG_CHECK)
+ X509_STORE_free(chain_store);
+
+ return rv;
+}
+
+int ssl_cert_set_cert_store(CERT *c, X509_STORE *store, int chain, int ref)
+{
+ X509_STORE **pstore;
+ if (chain)
+ pstore = &c->chain_store;
+ else
+ pstore = &c->verify_store;
+ if (*pstore)
+ X509_STORE_free(*pstore);
+ *pstore = store;
+ if (ref && store)
+ CRYPTO_add(&store->references, 1, CRYPTO_LOCK_X509_STORE);
+ return 1;
+}
+
+static int ssl_security_default_callback(SSL *s, SSL_CTX *ctx, int op,
+ int bits, int nid, void *other,
+ void *ex)
+{
+ int level, minbits;
+ static const int minbits_table[5] = { 80, 112, 128, 192, 256 };
+ if (ctx)
+ level = SSL_CTX_get_security_level(ctx);
+ else
+ level = SSL_get_security_level(s);
+ /* Level 0: anything goes */
+ if (level <= 0)
+ return 1;
+ if (level > 5)
+ level = 5;
+ minbits = minbits_table[level - 1];
+ switch (op) {
+ case SSL_SECOP_CIPHER_SUPPORTED:
+ case SSL_SECOP_CIPHER_SHARED:
+ case SSL_SECOP_CIPHER_CHECK:
+ {
+ const SSL_CIPHER *c = other;
+ /* No ciphers below security level */
+ if (bits < minbits)
+ return 0;
+ /* No unauthenticated ciphersuites */
+ if (c->algorithm_auth & SSL_aNULL)
+ return 0;
+ /* No MD5 mac ciphersuites */
+ if (c->algorithm_mac & SSL_MD5)
+ return 0;
+ /* SHA1 HMAC is 160 bits of security */
+ if (minbits > 160 && c->algorithm_mac & SSL_SHA1)
+ return 0;
+ /* Level 2: no RC4 */
+ if (level >= 2 && c->algorithm_enc == SSL_RC4)
+ return 0;
+ /* Level 3: forward secure ciphersuites only */
+ if (level >= 3 && !(c->algorithm_mkey & (SSL_kEDH | SSL_kEECDH)))
+ return 0;
+ break;
+ }
+ case SSL_SECOP_VERSION:
+ /* SSLv3 not allowed on level 2 */
+ if (nid <= SSL3_VERSION && level >= 2)
+ return 0;
+ /* TLS v1.1 and above only for level 3 */
+ if (nid <= TLS1_VERSION && level >= 3)
+ return 0;
+ /* TLS v1.2 only for level 4 and above */
+ if (nid <= TLS1_1_VERSION && level >= 4)
+ return 0;
+ break;
+
+ case SSL_SECOP_COMPRESSION:
+ if (level >= 2)
+ return 0;
+ break;
+ case SSL_SECOP_TICKET:
+ if (level >= 3)
+ return 0;
+ break;
+ default:
+ if (bits < minbits)
+ return 0;
+ }
+ return 1;
+}
+
+int ssl_security(SSL *s, int op, int bits, int nid, void *other)
+{
+ return s->cert->sec_cb(s, NULL, op, bits, nid, other, s->cert->sec_ex);
+}
+
+int ssl_ctx_security(SSL_CTX *ctx, int op, int bits, int nid, void *other)
+{
+ return ctx->cert->sec_cb(NULL, ctx, op, bits, nid, other,
+ ctx->cert->sec_ex);
+}