iostream-openssl-common.c revision cca4ba2a504d70a9fe9fee37f8433997359de52c
* Copyright (c) 2009-2013 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "iostream-openssl.h"
#include <openssl/x509v3.h>
enum {
DOVECOT_SSL_PROTO_SSLv2 = 0x01,
DOVECOT_SSL_PROTO_SSLv3 = 0x02,
DOVECOT_SSL_PROTO_TLSv1 = 0x04,
DOVECOT_SSL_PROTO_ALL = 0x07
};
int openssl_get_protocol_options(const char *protocols)
{
const char *const *tmp;
int proto, op = 0, include = 0, exclude = 0;
bool neg;
tmp = t_strsplit_spaces(protocols, " ");
for (; *tmp != NULL; tmp++) {
const char *name = *tmp;
if (*name != '!')
neg = FALSE;
else {
name++;
neg = TRUE;
}
if (strcasecmp(name, SSL_TXT_SSLV2) == 0)
proto = DOVECOT_SSL_PROTO_SSLv2;
else if (strcasecmp(name, SSL_TXT_SSLV3) == 0)
proto = DOVECOT_SSL_PROTO_SSLv3;
else if (strcasecmp(name, SSL_TXT_TLSV1) == 0)
proto = DOVECOT_SSL_PROTO_TLSv1;
else {
i_fatal("Invalid ssl_protocols setting: "
"Unknown protocol '%s'", name);
}
if (neg)
exclude |= proto;
else
include |= proto;
}
if (include != 0) {
/* exclude everything, except those that are included
(and let excludes still override those) */
exclude |= DOVECOT_SSL_PROTO_ALL & ~include;
}
if ((exclude & DOVECOT_SSL_PROTO_SSLv2) != 0) op |= SSL_OP_NO_SSLv2;
if ((exclude & DOVECOT_SSL_PROTO_SSLv3) != 0) op |= SSL_OP_NO_SSLv3;
if ((exclude & DOVECOT_SSL_PROTO_TLSv1) != 0) op |= SSL_OP_NO_TLSv1;
return op;
}
static const char *asn1_string_to_c(ASN1_STRING *asn_str)
{
const char *cstr;
unsigned int len;
len = ASN1_STRING_length(asn_str);
cstr = t_strndup(ASN1_STRING_data(asn_str), len);
if (strlen(cstr) != len) {
/* NULs in the name - could be some MITM attack.
never allow. */
return "";
}
return cstr;
}
static const char *get_general_dns_name(const GENERAL_NAME *name)
{
if (ASN1_STRING_type(name->d.ia5) != V_ASN1_IA5STRING)
return "";
return asn1_string_to_c(name->d.ia5);
}
static const char *get_cname(X509 *cert)
{
X509_NAME *name;
X509_NAME_ENTRY *entry;
ASN1_STRING *str;
int cn_idx;
name = X509_get_subject_name(cert);
if (name == NULL)
return "";
cn_idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
if (cn_idx == -1)
return "";
entry = X509_NAME_get_entry(name, cn_idx);
i_assert(entry != NULL);
str = X509_NAME_ENTRY_get_data(entry);
i_assert(str != NULL);
return asn1_string_to_c(str);
}
int openssl_cert_match_name(SSL *ssl, const char *verify_name)
{
X509 *cert;
STACK_OF(GENERAL_NAME) *gnames;
const GENERAL_NAME *gn;
const char *dnsname;
bool dns_names = FALSE;
unsigned int i, count;
int ret;
cert = SSL_get_peer_certificate(ssl);
i_assert(cert != NULL);
/* verify against SubjectAltNames */
gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames);
for (i = 0; i < count; i++) {
gn = sk_GENERAL_NAME_value(gnames, i);
if (gn->type == GEN_DNS) {
dns_names = TRUE;
dnsname = get_general_dns_name(gn);
if (strcmp(dnsname, verify_name) == 0)
break;
}
}
sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free);
/* verify against CommonName only when there wasn't any DNS
SubjectAltNames */
if (dns_names)
ret = i < count ? 0 : -1;
else
ret = strcmp(get_cname(cert), verify_name) == 0 ? 0 : -1;
X509_free(cert);
return ret;
}