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;
	}