Checking for an error on the error queue is very tedious. You have to check both library and reason code separately, which often means you need to save the error code to a local variable. Make an ERR_equals function. I've gone ahead and just made it public API because even within BoringSSL, it seems to come up everywhere. Change-Id: Ia963c9ae743ac5c6939846f8f64bbc98b495ce0b Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81627 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com> Auto-Submit: David Benjamin <davidben@google.com>
369 lines
11 KiB
C++
369 lines
11 KiB
C++
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <openssl/asn1.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/err.h>
|
|
#include <openssl/mem.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/stack.h>
|
|
#include <openssl/x509.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
static int xname_cmp(const X509_NAME *const *a, const X509_NAME *const *b) {
|
|
return X509_NAME_cmp(*a, *b);
|
|
}
|
|
|
|
static int add_bio_cert_subjects_to_stack(STACK_OF(X509_NAME) *out, BIO *bio,
|
|
bool allow_empty) {
|
|
// This function historically sorted |out| after every addition and skipped
|
|
// duplicates. This implementation preserves that behavior, but only sorts at
|
|
// the end, to avoid a quadratic running time. Existing duplicates in |out|
|
|
// are preserved, but do not introduce new duplicates.
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> to_append(sk_X509_NAME_new(xname_cmp));
|
|
if (to_append == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
// Temporarily switch the comparison function for |out|.
|
|
struct RestoreCmpFunc {
|
|
~RestoreCmpFunc() { sk_X509_NAME_set_cmp_func(stack, old_cmp); }
|
|
STACK_OF(X509_NAME) *stack;
|
|
int (*old_cmp)(const X509_NAME *const *, const X509_NAME *const *);
|
|
};
|
|
RestoreCmpFunc restore = {out, sk_X509_NAME_set_cmp_func(out, xname_cmp)};
|
|
|
|
sk_X509_NAME_sort(out);
|
|
bool first = true;
|
|
for (;;) {
|
|
bssl::UniquePtr<X509> x509(
|
|
PEM_read_bio_X509(bio, nullptr, nullptr, nullptr));
|
|
if (x509 == nullptr) {
|
|
if (first && !allow_empty) {
|
|
return 0;
|
|
}
|
|
// TODO(davidben): This ignores PEM syntax errors. It should only succeed
|
|
// on |PEM_R_NO_START_LINE|.
|
|
ERR_clear_error();
|
|
break;
|
|
}
|
|
first = false;
|
|
|
|
X509_NAME *subject = X509_get_subject_name(x509.get());
|
|
// Skip if already present in |out|. Duplicates in |to_append| will be
|
|
// handled separately.
|
|
if (sk_X509_NAME_find(out, /*out_index=*/NULL, subject)) {
|
|
continue;
|
|
}
|
|
|
|
bssl::UniquePtr<X509_NAME> copy(X509_NAME_dup(subject));
|
|
if (copy == nullptr ||
|
|
!bssl::PushToStack(to_append.get(), std::move(copy))) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Append |to_append| to |stack|, skipping any duplicates.
|
|
sk_X509_NAME_sort(to_append.get());
|
|
size_t num = sk_X509_NAME_num(to_append.get());
|
|
for (size_t i = 0; i < num; i++) {
|
|
bssl::UniquePtr<X509_NAME> name(sk_X509_NAME_value(to_append.get(), i));
|
|
sk_X509_NAME_set(to_append.get(), i, nullptr);
|
|
if (i + 1 < num &&
|
|
X509_NAME_cmp(name.get(), sk_X509_NAME_value(to_append.get(), i + 1)) ==
|
|
0) {
|
|
continue;
|
|
}
|
|
if (!bssl::PushToStack(out, std::move(name))) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Sort |out| one last time, to preserve the historical behavior of
|
|
// maintaining the sorted list.
|
|
sk_X509_NAME_sort(out);
|
|
return 1;
|
|
}
|
|
|
|
int SSL_add_bio_cert_subjects_to_stack(STACK_OF(X509_NAME) *out, BIO *bio) {
|
|
return add_bio_cert_subjects_to_stack(out, bio, /*allow_empty=*/true);
|
|
}
|
|
|
|
STACK_OF(X509_NAME) *SSL_load_client_CA_file(const char *file) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
return nullptr;
|
|
}
|
|
bssl::UniquePtr<STACK_OF(X509_NAME)> ret(sk_X509_NAME_new_null());
|
|
if (ret == nullptr || //
|
|
!add_bio_cert_subjects_to_stack(ret.get(), in.get(),
|
|
/*allow_empty=*/false)) {
|
|
return nullptr;
|
|
}
|
|
return ret.release();
|
|
}
|
|
|
|
int SSL_add_file_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
|
|
const char *file) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
return 0;
|
|
}
|
|
return SSL_add_bio_cert_subjects_to_stack(out, in.get());
|
|
}
|
|
|
|
int SSL_use_certificate_file(SSL *ssl, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<X509> x;
|
|
if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
x.reset(d2i_X509_bio(in.get(), nullptr));
|
|
} else if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
x.reset(PEM_read_bio_X509(in.get(), nullptr,
|
|
ssl->ctx->default_passwd_callback,
|
|
ssl->ctx->default_passwd_callback_userdata));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (x == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
|
|
return SSL_use_certificate(ssl, x.get());
|
|
}
|
|
|
|
int SSL_use_RSAPrivateKey_file(SSL *ssl, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<RSA> rsa;
|
|
if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
rsa.reset(d2i_RSAPrivateKey_bio(in.get(), nullptr));
|
|
} else if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
rsa.reset(PEM_read_bio_RSAPrivateKey(
|
|
in.get(), nullptr, ssl->ctx->default_passwd_callback,
|
|
ssl->ctx->default_passwd_callback_userdata));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (rsa == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
return SSL_use_RSAPrivateKey(ssl, rsa.get());
|
|
}
|
|
|
|
int SSL_use_PrivateKey_file(SSL *ssl, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<EVP_PKEY> pkey;
|
|
if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
pkey.reset(PEM_read_bio_PrivateKey(
|
|
in.get(), nullptr, ssl->ctx->default_passwd_callback,
|
|
ssl->ctx->default_passwd_callback_userdata));
|
|
} else if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
pkey.reset(d2i_PrivateKey_bio(in.get(), nullptr));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (pkey == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
|
|
return SSL_use_PrivateKey(ssl, pkey.get());
|
|
}
|
|
|
|
int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<X509> x;
|
|
if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
x.reset(d2i_X509_bio(in.get(), nullptr));
|
|
} else if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
x.reset(PEM_read_bio_X509(in.get(), nullptr, ctx->default_passwd_callback,
|
|
ctx->default_passwd_callback_userdata));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (x == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
|
|
return SSL_CTX_use_certificate(ctx, x.get());
|
|
}
|
|
|
|
int SSL_CTX_use_RSAPrivateKey_file(SSL_CTX *ctx, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<RSA> rsa;
|
|
if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
rsa.reset(d2i_RSAPrivateKey_bio(in.get(), nullptr));
|
|
} else if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
rsa.reset(PEM_read_bio_RSAPrivateKey(
|
|
in.get(), nullptr, ctx->default_passwd_callback,
|
|
ctx->default_passwd_callback_userdata));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (rsa == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
return SSL_CTX_use_RSAPrivateKey(ctx, rsa.get());
|
|
}
|
|
|
|
int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *file, int type) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
int reason_code;
|
|
bssl::UniquePtr<EVP_PKEY> pkey;
|
|
if (type == SSL_FILETYPE_PEM) {
|
|
reason_code = ERR_R_PEM_LIB;
|
|
pkey.reset(PEM_read_bio_PrivateKey(in.get(), nullptr,
|
|
ctx->default_passwd_callback,
|
|
ctx->default_passwd_callback_userdata));
|
|
} else if (type == SSL_FILETYPE_ASN1) {
|
|
reason_code = ERR_R_ASN1_LIB;
|
|
pkey.reset(d2i_PrivateKey_bio(in.get(), nullptr));
|
|
} else {
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SSL_FILETYPE);
|
|
return 0;
|
|
}
|
|
|
|
if (pkey == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, reason_code);
|
|
return 0;
|
|
}
|
|
|
|
return SSL_CTX_use_PrivateKey(ctx, pkey.get());
|
|
}
|
|
|
|
// Read a file that contains our certificate in "PEM" format, possibly followed
|
|
// by a sequence of CA certificates that should be sent to the peer in the
|
|
// Certificate message.
|
|
int SSL_CTX_use_certificate_chain_file(SSL_CTX *ctx, const char *file) {
|
|
bssl::UniquePtr<BIO> in(BIO_new_file(file, "rb"));
|
|
if (in == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_BUF_LIB);
|
|
return 0;
|
|
}
|
|
|
|
bssl::UniquePtr<X509> x(
|
|
PEM_read_bio_X509_AUX(in.get(), nullptr, ctx->default_passwd_callback,
|
|
ctx->default_passwd_callback_userdata));
|
|
if (x == nullptr) {
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_PEM_LIB);
|
|
return 0;
|
|
}
|
|
|
|
if (!SSL_CTX_use_certificate(ctx, x.get())) {
|
|
return 0;
|
|
}
|
|
|
|
// If we could set up our certificate, now proceed to the CA certificates.
|
|
SSL_CTX_clear_chain_certs(ctx);
|
|
for (;;) {
|
|
bssl::UniquePtr<X509> ca(
|
|
PEM_read_bio_X509(in.get(), nullptr, ctx->default_passwd_callback,
|
|
ctx->default_passwd_callback_userdata));
|
|
if (ca == nullptr) {
|
|
break;
|
|
}
|
|
if (!SSL_CTX_add1_chain_cert(ctx, ca.get())) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// When the while loop ends, it's usually just EOF.
|
|
if (ERR_equals(ERR_peek_last_error(), ERR_LIB_PEM, PEM_R_NO_START_LINE)) {
|
|
ERR_clear_error();
|
|
return 1;
|
|
}
|
|
|
|
return 0; // Some real error.
|
|
}
|
|
|
|
void SSL_CTX_set_default_passwd_cb(SSL_CTX *ctx, pem_password_cb *cb) {
|
|
ctx->default_passwd_callback = cb;
|
|
}
|
|
|
|
pem_password_cb *SSL_CTX_get_default_passwd_cb(const SSL_CTX *ctx) {
|
|
return ctx->default_passwd_callback;
|
|
}
|
|
|
|
void SSL_CTX_set_default_passwd_cb_userdata(SSL_CTX *ctx, void *data) {
|
|
ctx->default_passwd_callback_userdata = data;
|
|
}
|
|
|
|
void *SSL_CTX_get_default_passwd_cb_userdata(const SSL_CTX *ctx) {
|
|
return ctx->default_passwd_callback_userdata;
|
|
}
|