OpenSSL client checks SubjectAltName IP addresses

This commit is contained in:
Gerhard Rieger 2020-12-31 12:06:32 +01:00
parent 6635e159c4
commit 6128ea36ac
5 changed files with 305 additions and 43 deletions

10
CHANGES
View file

@ -120,22 +120,24 @@ Testing:
* renamed testaddrs() to testfeats(), and introduced new testaddrs() * renamed testaddrs() to testfeats(), and introduced new testaddrs()
New features: New features:
<<<<<<< HEAD
GOPEN and UNIX-CLIENT addresses now support sockets of type SEQPACKET. GOPEN and UNIX-CLIENT addresses now support sockets of type SEQPACKET.
Test: GOPENUNIXSEQPACKET Test: GOPENUNIXSEQPACKET
Feature suggested by vi0oss. Feature suggested by vi0oss.
Features:
The generic setsockopt-int and related options are, in case of The generic setsockopt-int and related options are, in case of
listening/accepting addresses, applied to the connected socket(s). To enable listening/accepting addresses, applied to the connected socket(s). To enable
setting options on the listening socket, a new option setsockopt-listen setting options on the listening socket, a new option setsockopt-listen
has been implemented. See the documentation for info on data types. has been implemented. See the documentation for info on data types.
Tests: SETSOCKOPT SETSOCKOPT_LISTEN Tests: SETSOCKOPT SETSOCKOPT_LISTEN
Thanks to Steven Danna and Korian Edeline for reporting this issue. Thanks to Steven Danna and Korian Edeline for reporting this issue.
=======
Filan option -S gives short description like -s but with improved Filan option -S gives short description like -s but with improved
format format
>>>>>>> 7cd82d9... Fixed filan -s, added -S
Socat OpenSSL client, when server was specified using IP address, did
not verify connection on certificates SubjectAltName IP entries.
Tests: OPENSSL_SERVERALTAUTH OPENSSL_SERVERALTIP4AUTH OPENSSL_SERVERALTIP6AUTH
Fixes Red Hat bug 1805132
####################### V 1.7.3.4: ####################### V 1.7.3.4:

206
test.sh
View file

@ -99,6 +99,7 @@ REUSEADDR=reuseaddr # use this with LISTEN addresses and bind options
# SSL certificate contents # SSL certificate contents
TESTCERT_CONF=testcert.conf TESTCERT_CONF=testcert.conf
TESTCERT6_CONF=testcert6.conf TESTCERT6_CONF=testcert6.conf
TESTALT_CONF=testalt.conf
# #
TESTCERT_COMMONNAME="$LOCALHOST" TESTCERT_COMMONNAME="$LOCALHOST"
TESTCERT_COMMONNAME6="$LOCALHOST6" TESTCERT_COMMONNAME6="$LOCALHOST6"
@ -123,6 +124,7 @@ commonName=$TESTCERT_COMMONNAME
O=$TESTCERT_ORGANIZATIONNAME O=$TESTCERT_ORGANIZATIONNAME
OU=$TESTCERT_ORGANIZATIONALUNITNAME OU=$TESTCERT_ORGANIZATIONALUNITNAME
L=$TESTCERT_LOCALITYNAME L=$TESTCERT_LOCALITYNAME
EOF EOF
cat >$TESTCERT6_CONF <<EOF cat >$TESTCERT6_CONF <<EOF
@ -138,6 +140,36 @@ commonName=$TESTCERT_COMMONNAME6
O=$TESTCERT_ORGANIZATIONNAME O=$TESTCERT_ORGANIZATIONNAME
OU=$TESTCERT_ORGANIZATIONALUNITNAME OU=$TESTCERT_ORGANIZATIONALUNITNAME
L=$TESTCERT_LOCALITYNAME L=$TESTCERT_LOCALITYNAME
EOF
cat >$TESTALT_CONF <<EOF
# config for generation of self signed certificate with IP addresses in
# SubjectAltNames
prompt=no
[ req ]
default_bits = $RSABITS
distinguished_name = subject
x509_extensions = x509_ext
[ subject ]
countryName=$TESTCERT_COUNTRYNAME
commonName=servername
O=$TESTCERT_ORGANIZATIONNAME
OU=$TESTCERT_ORGANIZATIONALUNITNAME
L=$TESTCERT_LOCALITYNAME
[ x509_ext ]
subjectAltName = @alternate_names
[ alternate_names ]
DNS.1 = localhost
DNS.2 = localhost4
DNS.3 = localhost6
IP.1 = 127.0.0.1
IP.2 = ::1
EOF EOF
# clean up from previous runs # clean up from previous runs
@ -145,7 +177,7 @@ rm -f testcli.{crt,key,pem}
rm -f testsrv.{crt,key,pem} rm -f testsrv.{crt,key,pem}
rm -f testcli6.{crt,key,pem} rm -f testcli6.{crt,key,pem}
rm -f testsrv6.{crt,key,pem} rm -f testsrv6.{crt,key,pem}
rm -f testalt.{crt,key,pem}
CAT=cat CAT=cat
OD_C="od -c" OD_C="od -c"
@ -2402,6 +2434,7 @@ gentestcert () {
fi fi
if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi
openssl genrsa $OPENSSL_RAND -out $name.key $RSABITS >/dev/null 2>&1 openssl genrsa $OPENSSL_RAND -out $name.key $RSABITS >/dev/null 2>&1
#openssl req -new -config $TESTCERT_CONF -key $name.key -x509 -out $name.crt -days 3653 -extensions v3_ca >/dev/null 2>&1
openssl req -new -config $TESTCERT_CONF -key $name.key -x509 -out $name.crt -days 3653 >/dev/null 2>&1 openssl req -new -config $TESTCERT_CONF -key $name.key -x509 -out $name.crt -days 3653 >/dev/null 2>&1
cat $name.key $name.crt testcert.dh >$name.pem cat $name.key $name.crt testcert.dh >$name.pem
} }
@ -2437,6 +2470,18 @@ gentestcert6 () {
cat $name.key $name.crt >$name.pem cat $name.key $name.crt >$name.pem
} }
# generate a server certificate and key with SubjectAltName
gentestaltcert () {
local name="$1"
if ! [ -f testcert.dh ]; then
openssl dhparam -out testcert.dh $RSABITS
fi
if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi
openssl genrsa $OPENSSL_RAND -out $name.key $RSABITS >/dev/null 2>&1
openssl req -new -config $TESTALT_CONF -key $name.key -x509 -out $name.crt -days 3653 >/dev/null 2>&1
cat $name.key $name.crt testcert.dh >$name.pem
}
NAME=UNISTDIO NAME=UNISTDIO
case "$TESTS " in case "$TESTS " in
@ -4209,7 +4254,6 @@ te="$td/test$N.stderr"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM" da="test$N $(date) $RANDOM"
CMD2="$TRACE $SOCAT $opts exec:'openssl s_server -accept "$PORT" -quiet -cert testsrv.pem' pipe" CMD2="$TRACE $SOCAT $opts exec:'openssl s_server -accept "$PORT" -quiet -cert testsrv.pem' pipe"
#! CMD="$TRACE $SOCAT $opts - openssl:$LOCALHOST:$PORT"
CMD="$TRACE $SOCAT $opts - openssl:$LOCALHOST:$PORT,pf=ip4,verify=0,$SOCAT_EGD" CMD="$TRACE $SOCAT $opts - openssl:$LOCALHOST:$PORT,pf=ip4,verify=0,$SOCAT_EGD"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
eval "$CMD2 2>\"${te}1\" &" eval "$CMD2 2>\"${te}1\" &"
@ -4413,7 +4457,7 @@ OPENSSL6SERVER OPENSSL tcp6 OPENSSL-LISTEN:\$PORT,pf=ip6,$SOCAT_EGD,cert=tests
NAME=OPENSSL_SERVERAUTH NAME=OPENSSL_SERVERAUTH
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
TEST="$NAME: openssl server authentication" TEST="$NAME: OpenSSL server authentication (hostname)"
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! testfeats openssl >/dev/null; then elif ! testfeats openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
@ -4430,19 +4474,19 @@ tf="$td/test$N.stdout"
te="$td/test$N.stderr" te="$td/test$N.stderr"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM" da="test$N $(date) $RANDOM"
CMD2="$TRACE $SOCAT $opts OPENSSL-LISTEN:$PORT,$REUSEADDR,$SOCAT_EGD,cert=testsrv.crt,key=testsrv.key,verify=0 pipe" CMD0="$TRACE $SOCAT $opts OPENSSL-LISTEN:$PORT,$REUSEADDR,$SOCAT_EGD,cert=testsrv.crt,key=testsrv.key,verify=0 pipe"
CMD="$TRACE $SOCAT $opts - openssl:$LOCALHOST:$PORT,verify=1,cafile=testsrv.crt,$SOCAT_EGD" CMD1="$TRACE $SOCAT $opts - OPENSSL:$LOCALHOST:$PORT,verify=1,cafile=testsrv.crt,$SOCAT_EGD"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
eval "$CMD2 2>\"${te}1\" &" eval "$CMD0 2>\"${te}0\" &"
pid=$! # background process id pid=$! # background process id
waittcp4port $PORT waittcp4port $PORT
echo "$da" |$CMD >$tf 2>"${te}2" echo "$da" |$CMD1 >$tf 2>"${te}1"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n" $PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD2 &" echo "$CMD0 &"
echo "$CMD" cat "${te}0"
echo "$CMD1"
cat "${te}1" cat "${te}1"
cat "${te}2"
cat "$tdiff" cat "$tdiff"
numFAIL=$((numFAIL+1)) numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
@ -14069,6 +14113,148 @@ PORT=$((PORT+1))
N=$((N+1)) N=$((N+1))
NAME=OPENSSL_SERVERALTAUTH
case "$TESTS" in
*%$N%*|*%functions%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
TEST="$NAME: OpenSSL server authentication with SubjectAltName (hostname)"
if ! eval $NUMCOND; then :;
elif ! testfeats openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! testfeats listen tcp ip4 >/dev/null || ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv4 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
gentestaltcert testalt
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts OPENSSL-LISTEN:$PORT,$REUSEADDR,$SOCAT_EGD,cert=testalt.crt,key=testalt.key,verify=0 pipe"
CMD1="$TRACE $SOCAT $opts - OPENSSL:$LOCALHOST:$PORT,verify=1,cafile=testalt.crt,$SOCAT_EGD"
printf "test $F_n $TEST... " $N
eval "$CMD0 2>\"${te}0\" &"
pid=$! # background process id
waittcp4port $PORT
echo "$da" |$CMD1 >$tf 2>"${te}1"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &" >&2
cat "${te}0" >&2
echo "$CMD1" >&2
cat "${te}1" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi
numOK=$((numOK+1))
fi
kill $pid 2>/dev/null
wait
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
N=$((N+1))
NAME=OPENSSL_SERVERALTIP4AUTH
case "$TESTS" in
*%$N%*|*%functions%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
TEST="$NAME: OpenSSL server authentication with SubjectAltName (IPv4 address)"
if ! eval $NUMCOND; then :;
elif ! testfeats openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! testfeats listen tcp ip4 openssl >/dev/null || ! runsip4 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv4 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
gentestaltcert testalt
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts OPENSSL-LISTEN:$PORT,$REUSEADDR,$SOCAT_EGD,cert=testalt.crt,key=testalt.key,verify=0 pipe"
CMD1="$TRACE $SOCAT $opts - OPENSSL:127.0.0.1:$PORT,verify=1,cafile=testalt.crt,$SOCAT_EGD"
printf "test $F_n $TEST... " $N
eval "$CMD0 2>\"${te}0\" &"
pid=$! # background process id
waittcp4port $PORT
echo "$da" |$CMD1 >$tf 2>"${te}1"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &" >&2
cat "${te}0" >&2
echo "$CMD1" >&2
cat "${te}1" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi
numOK=$((numOK+1))
fi
kill $pid 2>/dev/null
wait
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
N=$((N+1))
NAME=OPENSSL_SERVERALTIP6AUTH
case "$TESTS" in
*%$N%*|*%functions%*|*%openssl%*|*%tcp%*|*%tcp6%*|*%ip6%*|*%$NAME%*)
TEST="$NAME: OpenSSL server authentication with SubjectAltName (IPv6 address)"
if ! eval $NUMCOND; then :;
elif ! testfeats openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
elif ! testfeats listen tcp ip6 openssl >/dev/null || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP/IPv6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
listCANT="$listCANT $N"
else
gentestaltcert testalt
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts OPENSSL-LISTEN:$PORT,pf=ip6,$REUSEADDR,$SOCAT_EGD,cert=testalt.crt,key=testalt.key,verify=0 pipe"
CMD1="$TRACE $SOCAT $opts - OPENSSL:[::1]:$PORT,verify=1,cafile=testalt.crt,$SOCAT_EGD"
printf "test $F_n $TEST... " $N
eval "$CMD0 2>\"${te}0\" &"
pid=$! # background process id
waittcp6port $PORT
echo "$da" |$CMD1 >$tf 2>"${te}1"
if ! echo "$da" |diff - "$tf" >"$tdiff"; then
$PRINTF "$FAILED: $TRACE $SOCAT:\n"
echo "$CMD0 &" >&2
cat "${te}0" >&2
echo "$CMD1" >&2
cat "${te}1" >&2
cat "$tdiff" >&2
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
else
$PRINTF "$OK\n"
if [ -n "$debug" ]; then cat "${te}1" "${te}2"; fi
numOK=$((numOK+1))
fi
kill $pid 2>/dev/null
wait
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
N=$((N+1))
################################################################################## ##################################################################################
#================================================================================= #=================================================================================
# here come tests that might affect your systems integrity. Put normal tests # here come tests that might affect your systems integrity. Put normal tests

View file

@ -75,6 +75,32 @@ const struct optdesc opt_ipv6_recvtclass = { "ipv6-recvtclass", "recvtclass", OP
const struct optdesc opt_ipv6_recvpathmtu = { "ipv6-recvpathmtu", "recvpathmtu", OPT_IPV6_RECVPATHMTU, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPATHMTU }; const struct optdesc opt_ipv6_recvpathmtu = { "ipv6-recvpathmtu", "recvpathmtu", OPT_IPV6_RECVPATHMTU, GROUP_SOCK_IP6, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_IPV6, IPV6_RECVPATHMTU };
#endif #endif
/* Returns canonical form of IPv6 address.
IPv6 address may bei enclose in brackets.
Returns STAT_OK on success, STAT_NORETRY on failure. */
int xioip6_pton(const char *src, struct in6_addr *dst) {
union sockaddr_union sockaddr;
socklen_t sockaddrlen = sizeof(sockaddr);
if (src[0] == '[') {
char plainaddr[INET6_ADDRSTRLEN];
char *clos;
strncpy(plainaddr, src+1, INET6_ADDRSTRLEN);
plainaddr[INET6_ADDRSTRLEN-1] = '\0';
if ((clos = strchr(plainaddr, ']')) != NULL)
*clos = '\0';
return xioip6_pton(plainaddr, dst);
}
if (xiogetaddrinfo(src, NULL, PF_INET6, 0, 0, &sockaddr, &sockaddrlen,
0, 0)
!= STAT_OK) {
return STAT_NORETRY;
}
*dst = sockaddr.ip6.sin6_addr;
return STAT_OK;
}
int xioparsenetwork_ip6(const char *rangename, struct xiorange *range) { int xioparsenetwork_ip6(const char *rangename, struct xiorange *range) {
char *delimpos; /* absolute address of delimiter */ char *delimpos; /* absolute address of delimiter */
size_t delimind; /* index of delimiter in string */ size_t delimind; /* index of delimiter in string */

View file

@ -7,6 +7,10 @@
#if WITH_IP6 #if WITH_IP6
#ifndef INET6_ADDRSTRLEN
# define INET6_ADDRSTRLEN 46
#endif
extern const struct optdesc opt_ipv6_v6only; extern const struct optdesc opt_ipv6_v6only;
extern const struct optdesc opt_ipv6_join_group; extern const struct optdesc opt_ipv6_join_group;
extern const struct optdesc opt_ipv6_pktinfo; extern const struct optdesc opt_ipv6_pktinfo;
@ -27,6 +31,7 @@ extern const struct optdesc opt_ipv6_tclass;
extern const struct optdesc opt_ipv6_recvtclass; extern const struct optdesc opt_ipv6_recvtclass;
extern const struct optdesc opt_ipv6_recvpathmtu; extern const struct optdesc opt_ipv6_recvpathmtu;
extern int xioip6_pton(const char *src, struct in6_addr *dst);
extern extern
int xioparsenetwork_ip6(const char *rangename, struct xiorange *range); int xioparsenetwork_ip6(const char *rangename, struct xiorange *range);
extern int xiorange_ip6andmask(struct xiorange *range); extern int xiorange_ip6andmask(struct xiorange *range);

View file

@ -16,6 +16,8 @@
#include "xio-listen.h" #include "xio-listen.h"
#include "xio-udp.h" #include "xio-udp.h"
#include "xio-ipapp.h" #include "xio-ipapp.h"
#include "xio-ip6.h"
#include "xio-openssl.h" #include "xio-openssl.h"
/* the openssl library requires a file descriptor for external communications. /* the openssl library requires a file descriptor for external communications.
@ -1534,31 +1536,30 @@ static int openssl_setenv_cert_fields(const char *field, X509_NAME *name) {
supports wildcard cn like *.domain which matches domain and supports wildcard cn like *.domain which matches domain and
host.domain host.domain
returns true on match */ returns true on match */
static bool openssl_check_name(const char *cn, const char *peername) { static bool openssl_check_name(const char *nametype, const char *cn, const char *peername) {
const char *dotp; const char *dotp;
if (peername == NULL) { if (peername == NULL) {
Info1("commonName \"%s\": no peername", cn); Info2("%s \"%s\": no peername", nametype, cn);
return false; return false;
} else if (peername[0] == '\0') { } else if (peername[0] == '\0') {
Info1("commonName \"%s\": matched by empty peername", cn); Info2("%s \"%s\": matched by empty peername", nametype, cn);
return true; return true;
} }
if (! (cn[0] == '*' && cn[1] == '.')) { if (! (cn[0] == '*' && cn[1] == '.')) {
/* normal server name - this is simple */ /* normal server name - this is simple */
Debug1("commonName \"%s\" has no wildcard", cn);
if (strcmp(cn, peername) == 0) { if (strcmp(cn, peername) == 0) {
Debug2("commonName \"%s\" matches peername \"%s\"", cn, peername); Debug3("%s \"%s\" matches peername \"%s\"", nametype, cn, peername);
return true; return true;
} else { } else {
Info2("commonName \"%s\" does not match peername \"%s\"", cn, peername); Info3("%s \"%s\" does not match peername \"%s\"", nametype, cn, peername);
return false; return false;
} }
} }
/* wildcard cert */ /* wildcard cert */
Debug1("commonName \"%s\" is a wildcard name", cn); Debug2("%s \"%s\" is a wildcard name", nametype, cn);
/* case: just the base domain */ /* case: just the base domain */
if (strcmp(cn+2, peername) == 0) { if (strcmp(cn+2, peername) == 0) {
Debug2("wildcard commonName \"%s\" matches base domain \"%s\"", cn, peername); Debug3("wildcard %s \"%s\" matches base domain \"%s\"", nametype, cn, peername);
return true; return true;
} }
/* case: subdomain; only one level! */ /* case: subdomain; only one level! */
@ -1569,10 +1570,10 @@ static bool openssl_check_name(const char *cn, const char *peername) {
return false; return false;
} }
if (strcmp(cn+1, dotp) != 0) { if (strcmp(cn+1, dotp) != 0) {
Info2("commonName \"%s\" does not match subdomain peername \"%s\"", cn, peername); Info3("%s \"%s\" does not match subdomain peername \"%s\"", nametype, cn, peername);
return false; return false;
} }
Debug2("commonName \"%s\" matches subdomain peername \"%s\"", cn, peername); Debug3("%s \"%s\" matches subdomain peername \"%s\"", nametype, cn, peername);
return true; return true;
} }
@ -1595,7 +1596,7 @@ static bool openssl_check_peername(X509_NAME *name, const char *peername) {
#else #else
text = ASN1_STRING_data(data); text = ASN1_STRING_data(data);
#endif #endif
return openssl_check_name((const char *)text, peername); return openssl_check_name("commonName", (const char *)text, peername);
} }
/* retrieves certificate provided by peer, sets env vars containing /* retrieves certificate provided by peer, sets env vars containing
@ -1605,6 +1606,9 @@ static bool openssl_check_peername(X509_NAME *name, const char *peername) {
http://etutorials.org/Programming/secure+programming/Chapter+10.+Public+Key+Infrastructure/10.8+Adding+Hostname+Checking+to+Certificate+Verification/ http://etutorials.org/Programming/secure+programming/Chapter+10.+Public+Key+Infrastructure/10.8+Adding+Hostname+Checking+to+Certificate+Verification/
The code examples in this tutorial do not seem to have explicit license restrictions. The code examples in this tutorial do not seem to have explicit license restrictions.
*/ */
/* peername is, with OpenSSL client, the server name, or the value of option
commonname if provided;
With OpenSSL server, it is the value of option commonname */
static int openssl_handle_peer_certificate(struct single *xfd, static int openssl_handle_peer_certificate(struct single *xfd,
const char *peername, const char *peername,
bool opt_ver, int level) { bool opt_ver, int level) {
@ -1658,9 +1662,17 @@ static int openssl_handle_peer_certificate(struct single *xfd,
openssl_setenv_cert_name("issuer", issuername); openssl_setenv_cert_name("issuer", issuername);
} }
if (!opt_ver) {
Notice("option openssl-verify disabled, no check of certificate");
X509_free(peer_cert);
return STAT_OK;
}
/* check peername against cert's subjectAltName DNS entries */ /* check peername against cert's subjectAltName DNS entries */
/* this code is based on example from Gerhard Gappmeier in /* this code is based on example from Gerhard Gappmeier in
http://openssl.6102.n7.nabble.com/How-to-extract-subjectAltName-td17236.html http://openssl.6102.n7.nabble.com/How-to-extract-subjectAltName-td17236.html
and the GEN_IPADD from
http://openssl.6102.n7.nabble.com/reading-IP-addresses-from-Subject-Alternate-Name-extension-td29245.html
*/ */
if ((extcount = X509_get_ext_count(peer_cert)) > 0) { if ((extcount = X509_get_ext_count(peer_cert)) > 0) {
for (i = 0; !ok && i < extcount; ++i) { for (i = 0; !ok && i < extcount; ++i) {
@ -1680,50 +1692,81 @@ static int openssl_handle_peer_certificate(struct single *xfd,
/* get amount of alternatives, RFC2459 claims there MUST be at least one, but we don't depend on it... */ /* get amount of alternatives, RFC2459 claims there MUST be at least one, but we don't depend on it... */
numalts = sk_GENERAL_NAME_num ( names ); numalts = sk_GENERAL_NAME_num ( names );
/* loop through all alternatives */ /* loop through all alternatives */
for ( i=0; ( i<numalts ); i++ ) { for (i = 0; i < numalts; ++i) {
/* get a handle to alternative name number i */ /* get a handle to alternative name number i */
const GENERAL_NAME *pName = sk_GENERAL_NAME_value (names, i); const GENERAL_NAME *pName = sk_GENERAL_NAME_value (names, i);
unsigned char *pBuffer; unsigned char *pBuffer;
switch (pName->type) { switch (pName->type) {
case GEN_DNS: case GEN_DNS:
ASN1_STRING_to_UTF8(&pBuffer, ASN1_STRING_to_UTF8(&pBuffer, pName->d.ia5);
pName->d.ia5);
xiosetenv("OPENSSL_X509V3_SUBJECTALTNAME_DNS", (char *)pBuffer, 2, " // "); xiosetenv("OPENSSL_X509V3_SUBJECTALTNAME_DNS", (char *)pBuffer, 2, " // ");
if (peername != NULL && if (peername != NULL &&
openssl_check_name((char *)pBuffer, /*const char*/peername)) { openssl_check_name("subjectAltName", (char *)pBuffer, /*const char*/peername)) {
ok = 1; ok = 1;
} }
OPENSSL_free(pBuffer); OPENSSL_free(pBuffer);
break; break;
default: continue; case GEN_IPADD:
{
/* binary address format */
const unsigned char *data = pName->d.iPAddress->data;
size_t len = pName->d.iPAddress->length;
char aBuffer[INET6_ADDRSTRLEN]; /* canonical peername */
struct in6_addr ip6bin;
switch (len) {
case 4: /* IPv4 */
snprintf(aBuffer, sizeof(aBuffer), "%u.%u.%u.%u", data[0], data[1], data[2], data[3]);
if (peername != NULL &&
openssl_check_name("subjectAltName", aBuffer, /*const char*/peername)) {
ok = 1;
} }
break;
case 16: /* IPv6 */
inet_ntop(AF_INET6, data, aBuffer, sizeof(aBuffer));
xioip6_pton(peername, &ip6bin);
if (memcmp(data, &ip6bin, sizeof(ip6bin)) == 0) {
Debug2("subjectAltName \"%s\" matches peername \"%s\"",
aBuffer, peername);
ok = 1;
} else {
Info2("subjectAltName \"%s\" does not match peername \"%s\"",
aBuffer, peername);
}
break;
}
xiosetenv("OPENSSL_X509V3_SUBJECTALTNAME_IPADD", (char *)aBuffer, 2, " // ");
}
break;
default: Warn3("Unknown subject type %d (GEN_DNS=%d, GEN_IPADD=%d",
pName->type, GEN_DNS, GEN_IPADD);
continue;
}
if (ok) { break; }
} }
} }
} }
} }
} }
if (!opt_ver) {
Notice("option openssl-verify disabled, no check of certificate");
X509_free(peer_cert);
return STAT_OK;
}
if (peername == NULL || peername[0] == '\0') {
Notice("trusting certificate, no check of commonName");
X509_free(peer_cert);
return STAT_OK;
}
if (ok) { if (ok) {
Notice("trusting certificate, commonName matches"); Notice("trusting certificate, commonName matches");
X509_free(peer_cert); X509_free(peer_cert);
return STAT_OK; return STAT_OK;
} }
if (peername == NULL || peername[0] == '\0') {
Notice("trusting certificate, no check of commonName");
X509_free(peer_cert);
return STAT_OK;
}
/* here: all envs set; opt_ver, cert verified, no subjAltName match -> check subject CN */ /* here: all envs set; opt_ver, cert verified, no subjAltName match -> check subject CN */
if (!openssl_check_peername(/*X509_NAME*/subjectname, /*const char*/peername)) { if (!openssl_check_peername(/*X509_NAME*/subjectname, /*const char*/peername)) {
Error("certificate is valid but its commonName does not match hostname"); Error1("certificate is valid but its commonName does not match hostname \"%s\"",
peername);
status = STAT_NORETRY; status = STAT_NORETRY;
} else { } else {
Notice("trusting certificate, commonName matches"); Notice("trusting certificate, commonName matches");