Check OpenSSL peers commonName+subjectAltName; new option openssl-commonname

This commit is contained in:
Gerhard Rieger 2015-04-02 15:45:04 +02:00
parent 241e5256ec
commit cd164e7b58
13 changed files with 514 additions and 170 deletions

11
CHANGES
View file

@ -17,6 +17,17 @@ security:
Use sigaction() instead of signal() for better control
Turn off nested signal handler invocations
Thanks to Peter Lobsinger for reporting and explaining this issue.
Red Hat issue 1019975: add TLS host name checks
OpenSSL client checks if the server certificates names in
extensions/subjectAltName/DNS or in subject/commonName match the name
used to connect or the value of the openssl-commonname option.
Test: OPENSSL_CN_CLIENT_SECURITY
OpenSSL server checks if the client certificates names in
extensions/subjectAltNames/DNS or subject/commonName match the value of
the openssl-commonname option when it is used.
Test: OPENSSL_CN_SERVER_SECURITY
corrections:
LISTEN based addresses applied some address options, e.g. so-keepalive,

View file

@ -542,14 +542,23 @@ label(ADDRESS_OPENSSL_CONNECT)dit(bf(tt(OPENSSL:<host>:<port>)))
<host> [link(IP address)(TYPE_IP_ADDRESS)] using TCP/IP version 4 or 6
depending on address specification, name resolution, or option
link(pf)(OPTION_PROTOCOL_FAMILY).nl()
NOTE: The server certificate is only checked for validity against
link(cafile)(OPTION_OPENSSL_CAFILE) or link(capath)(OPTION_OPENSSL_CAPATH),
but not for match with the server's name or its IP address!nl()
NOTE: Up to version 1.7.2.4 and 2.0.0-b7
the server certificate was only checked for validity against the system
certificate store or link(cafile)(OPTION_OPENSSL_CAFILE) or
link(capath)(OPTION_OPENSSL_CAPATH),
but not for match with the server's name or its IP address.
Since version 1.7.3.0 and 2.0.0-b8 socat checks the peer certificate for match with the
<host> parameter or the value of the
link(openssl-commonname)(OPTION_OPENSSL_COMMONNAME) option.
Socat tries to match it against the certificates subject commonName,
and the certifications extension subjectAltName DNS names. Wildcards in the
certificate are supported.nl()
Option groups: link(FD)(GROUP_FD),link(SOCKET)(GROUP_SOCKET),link(IP4)(GROUP_IP4),link(IP6)(GROUP_IP6),link(TCP)(GROUP_TCP),link(OPENSSL)(GROUP_OPENSSL),link(RETRY)(GROUP_RETRY) nl()
Useful options:
link(cipher)(OPTION_OPENSSL_CIPHERLIST),
link(method)(OPTION_OPENSSL_METHOD),
link(verify)(OPTION_OPENSSL_VERIFY),
link(commonname)(OPTION_OPENSSL_COMMONNAME)
link(cafile)(OPTION_OPENSSL_CAFILE),
link(capath)(OPTION_OPENSSL_CAPATH),
link(certificate)(OPTION_OPENSSL_CERTIFICATE),
@ -577,6 +586,7 @@ label(ADDRESS_OPENSSL_LISTEN)dit(bf(tt(OPENSSL-LISTEN:<port>)))
link(cipher)(OPTION_OPENSSL_CIPHERLIST),
link(method)(OPTION_OPENSSL_METHOD),
link(verify)(OPTION_OPENSSL_VERIFY),
link(commonname)(OPTION_OPENSSL_COMMONNAME)
link(cafile)(OPTION_OPENSSL_CAFILE),
link(capath)(OPTION_OPENSSL_CAPATH),
link(certificate)(OPTION_OPENSSL_CERTIFICATE),
@ -2736,7 +2746,7 @@ label(OPTION_OPENSSL_PSEUDO)dit(bf(tt(pseudo)))
providing pseudo entropy. This is archieved by taking the current time in
microseconds for feeding the libc pseudo random number generator with an
initial value. openssl is then feeded with output from random\() calls.nl()
NOTE:This mechanism is not sufficient for generation of secure keys!
NOTE: This mechanism is not sufficient for generation of secure keys!
label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips)))
Enables FIPS mode if compiled in. For info about the FIPS encryption
implementation standard see lurl(http://oss-institute.org/fips-faq.html).
@ -2750,6 +2760,14 @@ label(OPTION_OPENSSL_COMPRESS)dit(bf(tt(compress)))
compression-related settings.
NOTE: Requires OpenSSL 0.9.8 or higher and disabling compression with
OpenSSL 0.9.8 affects all new connections in the process.
label(OPTION_OPENSSL_COMMONNAME)dit(bf(tt(commonname=<string>)))
Specify the commonname that the peer certificate must match. With
link(OPENSSL-CONNECT)(ADDRESS_OPENSSL_CONNECT) address this overrides the
given hostname or IP target address; with
link(OPENSSL-LISTEN)(ADDRESS_OPENSSL_LISTEN) this turns on check of peer
certificates commonname. This option has only meaning when option
link(verify)(OPTION_OPENSSL_VERIFY) is not disabled and the choosen cipher
provides a peer certificate.
enddit()
startdit()enddit()nl()
@ -3534,6 +3552,16 @@ dit(bf(SOCAT_IPV6_TCLASS) (output)) With all IPv6 based RECVFROM addresses
where address option link(ipv6-recvtclass)(OPTION_IPV6_RECVTCLASS) is applied,
socat() sets this variable to the transfer class of the received packet.
dit(bf(SOCAT_OPENSSL_X509_ISSUER) (output)) Issuer field from peer certificate
dit(bf(SOCAT_OPENSSL_X509_SUBJECT (output))) Subject field from peer certificate
dit(bf(SOCAT_OPENSSL_X509_COMMONNAME) (output)) commonName entries from peer certificates subject. Multiple values are separated by " // ".
dit(bf(SOCAT_OPENSSL_X509_*) (output)) all other entries from peer certificates subject
dit(bf(SOCAT_OPENSSL_X509V3_DNS) (output)) DNS entries from peer certificates extensions - subjectAltName field. Multiple values are separated by " // ".
dit(bf(HOSTNAME) (input)) Is used to determine the hostname for logging (see
link(-lh)(option_lh)).

View file

@ -275,7 +275,7 @@ int main(int argc, const char *argv[]) {
Info(copyright_ssleay);
#endif
Debug2("socat version %s on %s", socatversion, timestamp);
xiosetenv("VERSION", socatversion, 1); /* SOCAT_VERSION */
xiosetenv("VERSION", socatversion, 1, NULL); /* SOCAT_VERSION */
uname(&ubuf); /* ! here we circumvent internal tracing (Uname) */
Debug4("running on %s version %s, release %s, machine %s\n",
ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine);

View file

@ -666,24 +666,19 @@ int ifindex(const char *ifname, unsigned int *ifindex, int anysock) {
#endif /* WITH_IP4 || WITH_IP6 || WITH_INTERFACE */
/* constructs an environment variable whose name is built from socats uppercase
program name, and underscore and varname; if a variable of this name already
exists a non zero value of overwrite lets the old value be overwritten.
returns 0 on success or <0 if an error occurred. */
int xiosetenv(const char *varname, const char *value, int overwrite) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
size_t i, l;
progname = diag_get_string('p');
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1);
l = strlen(progname);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
if (Setenv(envname, value, overwrite) < 0) {
int _xiosetenv(const char *envname, const char *value, int overwrite, const char *sep) {
char *oldval;
char *newval;
if (overwrite >= 2 && (oldval = getenv(envname)) != NULL) {
size_t newlen = strlen(oldval)+strlen(sep)+strlen(value)+1;
if ((newval = Malloc(newlen+1)) == NULL) {
return -1;
}
snprintf(newval, newlen+1, "%s%s%s", oldval, sep, value);
} else {
newval = (char *)value;
}
if (Setenv(envname, newval, overwrite) < 0) {
Warn3("setenv(\"%s\", \"%s\", 1): %s",
envname, value, strerror(errno));
#if HAVE_UNSETENV
@ -692,11 +687,34 @@ int xiosetenv(const char *varname, const char *value, int overwrite) {
return -1;
}
return 0;
}
/* constructs an environment variable whose name is built from socats uppercase
program name, and underscore and varname;
if the variable of this name already exists arg overwrite determines:
0: keep old value
1: overwrite with new value
2: append to old value, separated by *sep
returns 0 on success or <0 if an error occurred. */
int xiosetenv(const char *varname, const char *value, int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
size_t i, l;
progname = diag_get_string('p');
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1);
l = strlen(envname);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
return _xiosetenv(envname, value, overwrite, sep);
# undef XIO_ENVNAMELEN
}
int xiosetenv2(const char *varname, const char *varname2, const char *value,
int overwrite) {
int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
@ -708,27 +726,19 @@ int xiosetenv2(const char *varname, const char *varname2, const char *value,
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
l += strlen(varname+l);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1);
l += strlen(varname+l);
l += strlen(envname+l);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
if (Setenv(envname, value, overwrite) < 0) {
Warn3("setenv(\"%s\", \"%s\", 1): %s",
envname, value, strerror(errno));
#if HAVE_UNSETENV
Unsetenv(envname); /* dont want to have a wrong value */
#endif
return -1;
}
return 0;
return _xiosetenv(envname, value, overwrite, sep);
# undef XIO_ENVNAMELEN
}
int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
const char *value,
int overwrite) {
int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envname[XIO_ENVNAMELEN];
@ -750,15 +760,7 @@ int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
strncat(envname+l, varname3, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
if (Setenv(envname, value, overwrite) < 0) {
Warn3("setenv(\"%s\", \"%s\", 1): %s",
envname, value, strerror(errno));
#if HAVE_UNSETENV
Unsetenv(envname); /* dont want to have a wrong value */
#endif
return -1;
}
return 0;
return _xiosetenv(envname, value, overwrite, sep);
# undef XIO_ENVNAMELEN
}
@ -769,7 +771,7 @@ int xiosetenvulong(const char *varname, unsigned long value, int overwrite) {
char envbuff[XIO_LONGLEN];
snprintf(envbuff, XIO_LONGLEN, "%lu", value);
return xiosetenv(varname, envbuff, overwrite);
return xiosetenv(varname, envbuff, overwrite, NULL);
# undef XIO_LONGLEN
}
@ -779,6 +781,6 @@ int xiosetenvushort(const char *varname, unsigned short value, int overwrite) {
char envbuff[XIO_SHORTLEN];
snprintf(envbuff, XIO_SHORTLEN, "%hu", value);
return xiosetenv(varname, envbuff, overwrite);
return xiosetenv(varname, envbuff, overwrite, NULL);
# undef XIO_SHORTLEN
}

View file

@ -89,13 +89,13 @@ extern int parseport(const char *portname, int proto);
extern int ifindexbyname(const char *ifname, int anysock);
extern int ifindex(const char *ifname, unsigned int *ifindex, int anysock);
extern int xiosetenv(const char *varname, const char *value, int overwrite);
extern int xiosetenv(const char *varname, const char *value, int overwrite, const char *sep);
extern int
xiosetenv2(const char *varname, const char *varname2, const char *value,
int overwrite);
int overwrite, const char *sep);
extern int
xiosetenv3(const char *varname, const char *varname2, const char *varname3,
const char *value, int overwrite);
const char *value, int overwrite, const char *sep);
extern int xiosetenvulong(const char *varname, unsigned long value,
int overwrite);
extern int xiosetenvushort(const char *varname, unsigned short value,

126
test.sh
View file

@ -70,8 +70,8 @@ else
fi
MCINTERFACE=lo # !!! Linux only
#LOCALHOST=192.168.58.1
#LOCALHOST=localhost
LOCALHOST=127.0.0.1
LOCALHOST=localhost
#LOCALHOST=127.0.0.1
LOCALHOST6=[::1]
#PROTO=$(awk '{print($2);}' /etc/protocols |sort -n |tail -n 1)
#PROTO=$(($PROTO+1))
@ -79,10 +79,24 @@ PROTO=$((144+RANDOM/2048))
PORT=12002
SOURCEPORT=2002
TESTCERT_CONF=testcert.conf
TESTCERT_SUBJECT="/C=XY"
TESTCERT_ISSUER="/C=XY"
TESTCERT6_CONF=testcert6.conf
# keep these values consistent with testcert.conf
TESTCERT_COMMONNAME="$LOCALHOST"
TESTCERT_COUNTRYNAME="$(grep ^countryName= testcert.conf)"; TESTCERT_COUNTRYNAME="${TESTCERT_COUNTRYNAME##*=}"
TESTCERT_LOCALITYNAME="$(grep ^L= testcert.conf)"; TESTCERT_LOCALITYNAME="${TESTCERT_LOCALITYNAME##*=}"
TESTCERT_ORGANIZATIONALUNITNAME="$(grep ^OU= testcert.conf)"; TESTCERT_ORGANIZATIONALUNITNAME="${TESTCERT_ORGANIZATIONALUNITNAME##*=}"
TESTCERT_ORGANIZATIONNAME="$(grep ^O= testcert.conf)"; TESTCERT_ORGANIZATIONNAME="${TESTCERT_ORGANIZATIONNAME##*=}"
TESTCERT_SUBJECT="C = XY, CN = localhost, O = dest-unreach, OU = socat, L = Lunar Base"
TESTCERT_ISSUER="C = XY, CN = localhost, O = dest-unreach, OU = socat, L = Lunar Base"
CAT=cat
OD_C="od -c"
# clean up from previous runs
rm -f testcli.{crt,key,pem}
rm -f testsrv.{crt,key,pem}
rm -f testcli6.{crt,key,pem}
rm -f testsrv6.{crt,key,pem}
# precision sleep; takes seconds with fractional part
psleep () {
local T="$1"
@ -2243,6 +2257,17 @@ gentestdsacert () {
openssl req -newkey dsa:$name-dsa.pem -keyout $name.key -nodes -x509 -days 3653 -config $TESTCERT_CONF -out $name.crt >/dev/null 2>&1
cat $name-dsa.pem $name-dh.pem $name.key $name.crt >$name.pem
}
gentestcert6 () {
local name="$1"
if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi
cat $TESTCERT_CONF |
{ echo "# automatically generated by $0"; cat; } |
sed 's/\(commonName\s*=\s*\).*/\1[::1]/' >$TESTCERT6_CONF
openssl genrsa $OPENSSL_RAND -out $name.key 768 >/dev/null 2>&1
openssl req -new -config $TESTCERT6_CONF -key $name.key -x509 -out $name.crt -days 3653 >/dev/null 2>&1
cat $name.key $name.crt >$name.pem
}
NAME=UNISTDIO
@ -4137,7 +4162,7 @@ TESTKEYW=${TESTADDR%%:*}
# does our address implementation support halfclose?
NAME=${NAMEKEYW}_HALFCLOSE
case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%halfclose%*|*%$NAME%*)
*%$N%*|*%functions%*|*%$FEAT%*|*%socket%*|*%halfclose%*|*%$NAME%*)
TEST="$NAME: $TESTKEYW half close"
# have a "peer" socat "peer" that executes "$OD_C" and see if EOF on the
# connecting socat brings the result of od
@ -5166,7 +5191,7 @@ N=$((N+1))
#!
NAME=OUTBOUNDIN
case "$TESTS" in
*%$N%*|*%functions%*|*%chain%*|*%proxy%*|*%$NAME%*)
*%$N%*|*%functions%|*%chain%**|*%openssl%*|*%proxy%*|*%$NAME%*)
TEST="$NAME: gender changer via SSL through HTTP proxy, oneshot"
if ! eval $NUMCOND; then :;
elif ! feat=$(testaddrs openssl proxy); then
@ -5254,7 +5279,7 @@ PORT=$((RANDOM+16184))
#!
NAME=INTRANETRIPPER
case "$TESTS" in
*%$N%*|*%functions%*|*%chain%*|*%proxy%*|*%$NAME%*)
*%$N%*|*%functions%*|*%chain%*|*%openssl%*|*%proxy%*|*%$NAME%*)
TEST="$NAME: gender changer via SSL through HTTP proxy, daemons"
if ! eval $NUMCOND; then :;
elif ! feat=$(testaddrs openssl proxy); then
@ -5387,7 +5412,6 @@ testserversec () {
local stat result
$PRINTF "test $F_n %s... " $N "$title"
#set -vx
# first: without security
# start server
$TRACE $SOCAT $opts "$arg1,$secopt0" echo 2>"${te}1" &
@ -5404,7 +5428,7 @@ testserversec () {
(echo "$da"; sleep $T) |$TRACE $SOCAT $opts - "$arg2" >"$tf" 2>"${te}2"
stat="$?"
kill $spid 2>/dev/null
#killall $TRACE $SOCAT 2>/dev/null
#killall $SOCAT 2>/dev/null
if [ "$stat" != 0 ]; then
$PRINTF "$NO_RESULT (ph.1 function fails): $TRACE $SOCAT:\n"
echo "$TRACE $SOCAT $opts \"$arg1,$secopt0\" echo &"
@ -5814,7 +5838,7 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "range=$SECONDADDR/32" "ssl:127.0.0.1:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "range=$SECONDADDR/32" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5830,8 +5854,8 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "sp=$PORT" "ssl:127.0.0.1:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "sp=$PORT" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
N=$((N+1))
@ -5846,7 +5870,7 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "lowport" "ssl:127.0.0.1:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "lowport" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5866,7 +5890,7 @@ ha="$td/hosts.allow"
hd="$td/hosts.deny"
$ECHO "socat: $SECONDADDR" >"$ha"
$ECHO "ALL: ALL" >"$hd"
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "tcpwrap-etc=$td" "ssl:127.0.0.1:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "tcpwrap-etc=$td" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5883,7 +5907,7 @@ elif ! testaddrs openssl >/dev/null; then
else
gentestcert testsrv
gentestcert testcli
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify,cert=testsrv.crt,key=testsrv.key" "cafile=testcli.crt" "cafile=testsrv.crt" "ssl:127.0.0.1:$PORT,cafile=testsrv.crt,cert=testcli.pem,$SOCAT_EGD" 4 tcp $PORT -1
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip4,reuseaddr,fork,retry=1,$SOCAT_EGD,verify,cert=testsrv.crt,key=testsrv.key" "cafile=testcli.crt" "cafile=testsrv.crt" "SSL:$LOCALHOST:$PORT,cafile=testsrv.crt,cert=testcli.pem,$SOCAT_EGD" 4 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5919,8 +5943,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "range=[::2/128]" "ssl:[::1]:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 6 tcp $PORT -1
gentestcert6 testsrv6
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv6.crt,key=testsrv6.key" "" "range=[::2/128]" "SSL:[::1]:$PORT,cafile=testsrv6.crt,$SOCAT_EGD" 6 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5938,8 +5962,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "sp=$PORT" "ssl:[::1]:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 6 tcp $PORT -1
gentestcert6 testsrv6
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv6.crt,key=testsrv6.key" "" "sp=$PORT" "SSL:[::1]:$PORT,cafile=testsrv6.crt,$SOCAT_EGD" 6 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5957,8 +5981,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
gentestcert testsrv
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "lowport" "ssl:[::1]:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 6 tcp $PORT -1
gentestcert6 testsrv6
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv6.crt,key=testsrv6.key" "" "lowport" "SSL:[::1]:$PORT,cafile=testsrv6.crt,$SOCAT_EGD" 6 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
@ -5973,17 +5997,65 @@ elif ! feat=$(testaddrs ip6 tcp libwrap openssl) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
gentestcert testsrv
gentestcert6 testsrv6
ha="$td/hosts.allow"
hd="$td/hosts.deny"
$ECHO "socat: [::2]" >"$ha"
$ECHO "ALL: ALL" >"$hd"
testserversec "$N" "$TEST" "$opts -s" "ssl-l:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv.crt,key=testsrv.key" "" "tcpwrap-etc=$td" "ssl:[::1]:$PORT,cafile=testsrv.crt,$SOCAT_EGD" 6 tcp $PORT -1
testserversec "$N" "$TEST" "$opts -s" "SSL-L:$PORT,pf=ip6,reuseaddr,fork,retry=1,$SOCAT_EGD,verify=0,cert=testsrv6.crt,key=testsrv6.key" "" "tcpwrap-etc=$td" "SSL:[::1]:$PORT,cafile=testsrv6.crt,$SOCAT_EGD" 6 tcp $PORT -1
fi ;; # NUMCOND, feats
esac
PORT=$((PORT+1))
N=$((N+1))
# test security with the openssl-commonname option on client side
NAME=OPENSSL_CN_CLIENT_SECURITY
case "$TESTS" in
*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
TEST="$NAME: security of client openssl-commonname option"
# connect using non matching server name/address with commonname
# options, this should succeed. Then without this option, should fail
if ! eval $NUMCOND; then :;
elif ! testaddrs openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
elif ! testaddrs 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))
else
gentestcert testsrv
gentestcert testcli
testserversec "$N" "$TEST" "$opts" "SSL:127.0.0.1:$PORT,fork,retry=2,verify,cafile=testsrv.crt" "commonname=$LOCALHOST" "" "SSL-L:$PORT,pf=ip4,reuseaddr,cert=testsrv.crt,key=testsrv.key,verify=0" 4 tcp "" 0
fi ;; # testaddrs, NUMCOND
esac
PORT=$((PORT+1))
N=$((N+1))
# test security with the openssl-commonname option on server side
NAME=OPENSSL_CN_SERVER_SECURITY
case "$TESTS" in
*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
TEST="$NAME: security of server openssl-commonname option"
# connect using with client certificate to server, this should succeed.
# Then use the server with a non matching openssl-commonname option,
# this must fail
if ! eval $NUMCOND; then :;
elif ! testaddrs openssl >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}OPENSSL not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
elif ! testaddrs 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))
else
gentestcert testsrv
gentestcert testcli
testserversec "$N" "$TEST" "$opts" "SSL-L:$PORT,pf=ip4,reuseaddr,cert=testsrv.crt,key=testsrv.key,cafile=testcli.crt" "" "commonname=onlyyou" "SSL:$LOCALHOST:$PORT,verify=0,cafile=testsrv.crt,cert=testcli.crt,key=testcli.key" 4 tcp "" 0
fi ;; # testaddrs, NUMCOND
esac
PORT=$((PORT+1))
N=$((N+1))
NAME=OPENSSL_FIPS_SECURITY
case "$TESTS" in
@ -9123,7 +9195,7 @@ NAME=OPENSSLREAD
# keeps there and is not transferred by socat until the socket indicates more
# data or EOF.
case "$TESTS" in
*%$N%*|*%functions%*|*%$NAME%*)
*%$N%*|*%functions%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: socat handles data buffered by openssl"
#idea: have a socat process (server) that gets an SSL block that is larger than
# socat transfer block size; keep the socket connection open and kill the
@ -10015,10 +10087,10 @@ N=$((N+1))
set +xv
#
done <<<"
TCP4 TCP $LOCALHOST $SECONDADDR $PORT $((PORT+1))
TCP4 TCP 127.0.0.1 $SECONDADDR $PORT $((PORT+1))
TCP6 IP6 [0000:0000:0000:0000:0000:0000:0000:0001] [0000:0000:0000:0000:0000:0000:0000:0001] $((PORT+2)) $((PORT+3))
UDP6 IP6 [0000:0000:0000:0000:0000:0000:0000:0001] [0000:0000:0000:0000:0000:0000:0000:0001] $((PORT+6)) $((PORT+7))
SCTP4 SCTP $LOCALHOST $SECONDADDR $((PORT+8)) $((PORT+9))
SCTP4 SCTP 127.0.0.1 $SECONDADDR $((PORT+8)) $((PORT+9))
SCTP6 SCTP [0000:0000:0000:0000:0000:0000:0000:0001] [0000:0000:0000:0000:0000:0000:0000:0001] $((PORT+10)) $((PORT+11))
UNIX UNIX $td/test\$N.server $td/test\$N.client , ,
"
@ -11737,7 +11809,7 @@ N=$((N+1))
# Linux) with "Invalid argument".
NAME=OPENSSL_CONNECT_BIND
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%ssl%*|*%$NAME%*)
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%ssl%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: test OPENSSL-CONNECT with bind option"
# have a simple SSL server that just echoes data.
# connect with socat using OPENSSL-CONNECT with bind, send data and check if the

View file

@ -5,5 +5,9 @@ default_bits = 768
distinguished_name=Test
[ Test ]
countryName = XY
countryName=XY
commonName=localhost
O=dest-unreach
OU=socat
L=Lunar Base

View file

@ -67,7 +67,7 @@ const struct wordent *keyw(const struct wordent *keywds, const char *name, unsig
return NULL;
}
/* Linux: setenv(), AIX: putenv() */
/* Linux: setenv(), AIX (4.3?): putenv() */
#if !HAVE_SETENV
int setenv(const char *name, const char *value, int overwrite) {
int result;

View file

@ -6,6 +6,9 @@
#include "xiosysincludes.h"
#if WITH_OPENSSL /* make this address configure dependend */
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include "xioopen.h"
#include "xio-fd.h"
@ -45,11 +48,14 @@ static int xioopen_openssl_listen(int argc, const char *argv[], struct opt *opts
int xioflags, xiofile_t *fd, unsigned groups,
int dummy1, int dummy2, int dummy3);
static int openssl_SSL_ERROR_SSL(int level, const char *funcname);
static int openssl_handle_peer_certificate(struct single *xfd, bool opt_ver,
static int openssl_handle_peer_certificate(struct single *xfd,
const char *peername,
bool opt_ver,
int level);
static int xioSSL_set_fd(struct single *xfd, int level);
static int xioSSL_connect(struct single *xfd, bool opt_ver, int level);
static int xioSSL_connect(struct single *xfd, const char *opt_commonname, bool opt_ver, int level);
static int openssl_delete_cert_info(void);
/* description record for inter-address ssl connect with 0 parameters */
static const struct xioaddr_inter_desc xiointer_openssl_connect0 = {
@ -183,6 +189,7 @@ const struct optdesc opt_openssl_compress = { "openssl-compress", "compress
#if WITH_FIPS
const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC };
#endif
const struct optdesc opt_openssl_commonname = { "openssl-commonname", "cn", OPT_OPENSSL_COMMONNAME, GROUP_OPENSSL, PH_SPEC, TYPE_STRING, OFUNC_SPEC };
/* If FIPS is compiled in, we need to track if the user asked for FIPS mode.
@ -263,6 +270,7 @@ static int
/*0 SSL_CTX* ctx;*/
bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */
const char *opt_commonname = NULL; /* for checking peer certificate */
int result;
if (!(xioflags & XIO_MAYCONVERT)) {
@ -278,6 +286,12 @@ static int
if (argc == 3) {
hostname = argv[1];
portname = argv[2];
if (hostname[0] == '\0') {
/* we catch this explicitely because empty commonname (peername) disables
commonName check of peer certificate */
Error1("%s: empty host name", argv[0]);
return STAT_NORETRY;
}
/* a "terminal" form where we build a tcp connection to given host and
port */
@ -288,6 +302,11 @@ static int
retropt_bool(opts, OPT_FORK, &dofork);
retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert);
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
if (opt_commonname == NULL) {
opt_commonname = hostname;
}
result =
_xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert,
@ -314,6 +333,7 @@ static int
retropt_bool(opts, OPT_FORK, &dofork);
retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert);
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
result =
_xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert,
@ -378,7 +398,7 @@ static int
}
result =
_xioopen_openssl_connect(xfd, opt_ver, xfd->para.openssl.ctx, level);
_xioopen_openssl_connect(xfd, opt_ver, opt_commonname, xfd->para.openssl.ctx, level);
switch (result) {
case STAT_OK: break;
#if WITH_RETRY
@ -449,6 +469,7 @@ static int
SSL connection from an FD and a CTX. */
int _xioopen_openssl_connect(struct single *xfd,
bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx,
int level) {
SSL *ssl;
@ -473,14 +494,15 @@ static int
return result;
}
result = xioSSL_connect(xfd, opt_ver, level);
result = xioSSL_connect(xfd, opt_commonname, opt_ver, level);
if (result != STAT_OK) {
sycSSL_free(xfd->para.openssl.ssl);
xfd->para.openssl.ssl = NULL;
return result;
}
result = openssl_handle_peer_certificate(xfd, opt_ver, level);
result = openssl_handle_peer_certificate(xfd, opt_commonname,
opt_ver, level);
if (result != STAT_OK) {
sycSSL_free(xfd->para.openssl.ssl);
xfd->para.openssl.ssl = NULL;
@ -522,6 +544,7 @@ static int
/*0 SSL_CTX* ctx;*/
bool opt_ver = true; /* verify peer certificate - changed with 1.6.0 */
char *opt_cert = NULL; /* file name of server certificate */
const char *opt_commonname = NULL; /* for checking peer certificate */
int result;
if (!(xioflags & XIO_MAYCONVERT)) {
@ -549,6 +572,8 @@ static int
Warn("no certificate given; consider option \"cert\"");
}
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
result =
_xioopen_openssl_prepare(opts, xfd, true, &opt_ver, opt_cert,
&xfd->para.openssl.ctx);
@ -576,6 +601,8 @@ static int
Warn("no certificate given; consider option \"cert\"");
}
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
applyopts(-1, opts, PH_EARLY);
result =
@ -634,7 +661,7 @@ static int
xfd->wfd = xfd->rfd;
}
result =
_xioopen_openssl_listen(xfd, opt_ver, xfd->para.openssl.ctx, level);
_xioopen_openssl_listen(xfd, opt_ver, opt_commonname, xfd->para.openssl.ctx, level);
switch (result) {
case STAT_OK: break;
#if WITH_RETRY
@ -668,6 +695,7 @@ static int
int _xioopen_openssl_listen(struct single *xfd,
bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx,
int level) {
char error_string[120];
@ -748,7 +776,7 @@ int _xioopen_openssl_listen(struct single *xfd,
return STAT_RETRYLATER;
}
if (openssl_handle_peer_certificate(xfd, opt_ver, E_ERROR/*!*/) < 0) {
if (openssl_handle_peer_certificate(xfd, opt_commonname, opt_ver, E_ERROR/*!*/) < 0) {
return STAT_NORETRY;
}
@ -852,7 +880,6 @@ int
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
retropt_string(opts, OPT_OPENSSL_COMPRESS, &opt_compress);
#endif
#if WITH_FIPS
if (opt_fips) {
if (!sycFIPS_mode_set(1)) {
@ -865,6 +892,8 @@ int
}
#endif
openssl_delete_cert_info();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
@ -1104,19 +1133,21 @@ int
static int openssl_SSL_ERROR_SSL(int level, const char *funcname) {
unsigned long e;
char buf[120]; /* this value demanded by "man ERR_error_string" */
int stat = STAT_OK;
e = ERR_get_error();
Debug1("ERR_get_error(): %lx", e);
if (e == ((ERR_LIB_RAND<<24)|
(RAND_F_SSLEAY_RAND_BYTES<<12)|
(RAND_R_PRNG_NOT_SEEDED)) /*0x24064064*/) {
Error("too few entropy; use options \"egd\" or \"pseudo\"");
return STAT_NORETRY;
} else {
Msg2(level, "%s(): %s", funcname, ERR_error_string(e, buf));
return level==E_ERROR ? STAT_NORETRY : STAT_RETRYLATER;
while (e = ERR_get_error()) {
Debug1("ERR_get_error(): %lx", e);
if (e == ((ERR_LIB_RAND<<24)|
(RAND_F_SSLEAY_RAND_BYTES<<12)|
(RAND_R_PRNG_NOT_SEEDED)) /*0x24064064*/) {
Error("too few entropy; use options \"egd\" or \"pseudo\"");
stat = STAT_NORETRY;
} else {
Msg2(level, "%s(): %s", funcname, ERR_error_string(e, buf));
stat = level==E_ERROR ? STAT_NORETRY : STAT_RETRYLATER;
}
}
return STAT_OK;
return stat;
}
static const char *openssl_verify_messages[] = {
@ -1173,87 +1204,171 @@ static const char *openssl_verify_messages[] = {
/* 50 */ "application verification failure",
} ;
static int openssl_extract_cert_info(const char *field, X509_NAME *name) {
int n, i;
{
BIO *bio = BIO_new(BIO_s_mem());
char *buf = NULL, *str;
size_t len;
X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE&~ASN1_STRFLGS_ESC_MSB); /* rc not documented */
len = BIO_get_mem_data (bio, &buf);
if ((str = Malloc(len+1)) == NULL) {
BIO_free(bio);
return -1;
/* delete all environment variables whose name begins with SOCAT_OPENSSL_
resp. <progname>_OPENSSL_ */
static int openssl_delete_cert_info(void) {
# define XIO_ENVNAMELEN 256
const char *progname;
char envprefix[XIO_ENVNAMELEN];
char envname[XIO_ENVNAMELEN];
size_t i, l;
const char **entry;
progname = diag_get_string('p');
envprefix[0] = '\0'; strncat(envprefix, progname, XIO_ENVNAMELEN-1);
l = strlen(envprefix);
for (i = 0; i < l; ++i) envprefix[i] = toupper(envprefix[i]);
strncat(envprefix+l, "_OPENSSL_", XIO_ENVNAMELEN-l-1);
entry = (const char **)environ;
while (*entry != NULL) {
if (!strncmp(*entry, envprefix, strlen(envprefix))) {
const char *eq = strchr(*entry, '=');
if (eq == NULL) eq = *entry + strlen(*entry);
envname[0] = '\0'; strncat(envname, *entry, eq-*entry);
Unsetenv(envname);
} else {
++entry;
}
str[len] = '\0';
Info2("SSL peer cert %s: \"%s\"", field, buf);
xiosetenv2("OPENSSL_X509", field, buf, 1);
free(str);
BIO_free(bio);
}
return 0;
}
/* compares the peername used/provided by the client to cn as extracted from
the peer certificate.
supports wildcard cn like *.domain which matches domain and
host.domain
returns true on match */
static bool openssl_check_name(const char *cn, const char *peername) {
const char *dotp;
if (peername == NULL) {
Info1("commonName \"%s\": no peername", cn);
return false;
} else if (peername[0] == '\0') {
Info1("commonName \"%s\": matched by empty peername", cn);
return true;
}
if (! (cn[0] == '*' && cn[1] == '.')) {
/* normal server name - this is simple */
Debug1("commonName \"%s\" has no wildcard", cn);
if (strcmp(cn, peername) == 0) {
Debug2("commonName \"%s\" matches peername \"%s\"", cn, peername);
return true;
} else {
Info2("commonName \"%s\" does not match peername \"%s\"", cn, peername);
return false;
}
}
/* wildcard cert */
Debug1("commonName \"%s\" is a wildcard name", cn);
/* case: just the base domain */
if (strcmp(cn+2, peername) == 0) {
Debug2("wildcard commonName \"%s\" matches base domain \"%s\"", cn, peername);
return true;
}
/* case: subdomain; only one level! */
dotp = strchr(peername, '.');
if (dotp == NULL) {
Info2("peername \"%s\" is not a subdomain, thus is not matched by wildcard commonName \"%s\"",
peername, cn);
return false;
}
if (strcmp(cn+1, dotp) != 0) {
Info2("commonName \"%s\" does not match subdomain peername \"%s\"", cn, peername);
return false;
}
Debug2("commonName \"%s\" matches subdomain peername \"%s\"", cn, peername);
return true;
}
/* retrieves the commonName field and compares it to the peername
returns true on match, false otherwise */
static bool openssl_check_peername(X509_NAME *name, const char *peername) {
int ind = -1;
X509_NAME_ENTRY *entry;
ASN1_STRING *data;
unsigned char *text;
ind = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
if (ind < 0) {
Info("no COMMONNAME field in peer certificate");
return false;
}
entry = X509_NAME_get_entry(name, ind);
data = X509_NAME_ENTRY_get_data(entry);
text = ASN1_STRING_data(data);
return openssl_check_name((const char *)text, peername);
}
/* retrieves certificate provided by peer, sets env vars containing
/* retrieves certificate provided by peer, sets env vars containing
certificates field values, and checks peername if provided by
calling function */
/* parts of this code were copied from Gene Spaffords C/C++ Secure Programming at Etutorials.org:
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.
*/
/* read in the "name" information (from field "issuer" or "subject") and
create environment variable with complete info, eg:
SOCAT_OPENSSL_X509_SUBJECT */
static int openssl_setenv_cert_name(const char *field, X509_NAME *name) {
BIO *bio = BIO_new(BIO_s_mem());
char *buf = NULL, *str;
size_t len;
X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE&~ASN1_STRFLGS_ESC_MSB); /* rc not documented */
len = BIO_get_mem_data (bio, &buf);
if ((str = Malloc(len+1)) == NULL) {
BIO_free(bio);
return -1;
}
str[len] = '\0';
Info2("SSL peer cert %s: \"%s\"", field, buf);
xiosetenv2("OPENSSL_X509", field, buf, 1, NULL);
free(str);
BIO_free(bio);
return 0;
}
/* read in the "name" information (from field "issuer" or "subject") and
create environment variables with the fields, eg:
SOCAT_OPENSSL_X509_COMMONNAME
*/
static int openssl_setenv_cert_fields(const char *field, X509_NAME *name) {
int n, i;
n = X509_NAME_entry_count(name);
/* extract fields of cert name */
for (i = 0; i < n; ++i) {
X509_NAME_ENTRY *entry;
char *text;
ASN1_STRING *data;
ASN1_OBJECT *obj;
ASN1_STRING *data;
unsigned char *text;
int nid;
entry = X509_NAME_get_entry(name, i);
data = X509_NAME_ENTRY_get_data(entry);
obj = X509_NAME_ENTRY_get_object(entry);
data = X509_NAME_ENTRY_get_data(entry);
nid = OBJ_obj2nid(obj);
text = (char *)ASN1_STRING_data(data);
Debug3("SSL peer cert %s entry: %s=\"%s\"", field, OBJ_nid2ln(nid), text);
xiosetenv3("OPENSSL_X509", field, OBJ_nid2ln(nid), text, 0);
text = ASN1_STRING_data(data);
Debug3("SSL peer cert %s entry: %s=\"%s\"", (field[0]?field:"subject"), OBJ_nid2ln(nid), text);
if (field != NULL && field[0] != '\0') {
xiosetenv3("OPENSSL_X509", field, OBJ_nid2ln(nid), (const char *)text, 2, " // ");
} else {
xiosetenv2("OPENSSL_X509", OBJ_nid2ln(nid), (const char *)text, 2, " // ");
}
}
return 0;
}
static int openssl_handle_peer_certificate(struct single *xfd,
const char *peername,
bool opt_ver, int level) {
X509 *peer_cert;
X509_NAME *subjectname, *issuername;
/*ASN1_TIME not_before, not_after;*/
int extcount, i, ok = 0;
int status;
/* SSL_CTX_add_extra_chain_cert
SSL_get_verify_result
*/
if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) != NULL) {
X509_NAME *name;
if ((name = X509_get_subject_name(peer_cert)) != NULL)
openssl_extract_cert_info("subject", name);
if ((name = X509_get_issuer_name(peer_cert)) != NULL)
openssl_extract_cert_info("issuer", name);
/* I'd like to provide dates too; see
http://markmail.org/message/yi4vspp7aeu3xwtu#query:+page:1+mid:jhnl4wklif3pgzqf+state:results */
}
if (peer_cert) {
if (opt_ver) {
long verify_result;
if ((verify_result = sycSSL_get_verify_result(xfd->para.openssl.ssl)) == X509_V_OK) {
Info("accepted peer certificate");
status = STAT_OK;
} else {
const char *message = NULL;
if (verify_result >= 0 &&
(size_t)verify_result <
sizeof(openssl_verify_messages)/sizeof(char*))
{
message = openssl_verify_messages[verify_result];
}
if (message) {
Msg1(level, "%s", message);
} else {
Msg1(level, "rejected peer certificate with error %ld", verify_result);
}
status = STAT_RETRYLATER;
}
} else {
Notice("no check of certificate");
status = STAT_OK;
}
} else {
if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) == NULL) {
if (opt_ver) {
Msg(level, "no peer certificate");
status = STAT_RETRYLATER;
@ -1261,8 +1376,112 @@ static int openssl_handle_peer_certificate(struct single *xfd,
Notice("no peer certificate and no check");
status = STAT_OK;
}
return status;
}
/* verify peer certificate (trust, signature, validity dates) */
if (opt_ver) {
long verify_result;
if ((verify_result = sycSSL_get_verify_result(xfd->para.openssl.ssl)) != X509_V_OK) {
const char *message = NULL;
if (verify_result >= 0 &&
(size_t)verify_result <
sizeof(openssl_verify_messages)/sizeof(char*)) {
message = openssl_verify_messages[verify_result];
}
if (message) {
Msg1(level, "%s", message);
} else {
Msg1(level, "rejected peer certificate with error %ld", verify_result);
}
status = STAT_RETRYLATER;
X509_free(peer_cert);
return STAT_RETRYLATER;
}
Info("peer certificate is trusted");
}
/* set env vars from cert's subject and issuer values */
if ((subjectname = X509_get_subject_name(peer_cert)) != NULL) {
openssl_setenv_cert_name("subject", subjectname);
openssl_setenv_cert_fields("", subjectname);
/*! I'd like to provide dates too; see
http://markmail.org/message/yi4vspp7aeu3xwtu#query:+page:1+mid:jhnl4wklif3pgzqf+state:results */
}
if ((issuername = X509_get_issuer_name(peer_cert)) != NULL) {
openssl_setenv_cert_name("issuer", issuername);
}
/* check peername against cert's subjectAltName DNS entries */
/* this code is based on example from Gerhard Gappmeier in
http://openssl.6102.n7.nabble.com/How-to-extract-subjectAltName-td17236.html
*/
if ((extcount = X509_get_ext_count(peer_cert)) > 0) {
for (i = 0; !ok && i < extcount; ++i) {
const char *extstr;
X509_EXTENSION *ext;
const X509V3_EXT_METHOD *meth;
ext = X509_get_ext(peer_cert, i);
extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
if (!strcasecmp(extstr, "subjectAltName")) {
void *names;
if (!(meth = X509V3_EXT_get(ext))) break;
names = X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL);
if (names) {
int numalts;
int i;
/* 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 );
/* loop through all alternatives */
for ( i=0; ( i<numalts ); i++ ) {
/* get a handle to alternative name number i */
const GENERAL_NAME *pName = sk_GENERAL_NAME_value (names, i );
unsigned char *pBuffer;
switch ( pName->type ) {
case GEN_DNS:
ASN1_STRING_to_UTF8(&pBuffer, pName->d.ia5);
xiosetenv("OPENSSL_X509V3_SUBJECTALTNAME_DNS", (char *)pBuffer, 2, " // ");
if (peername != NULL &&
openssl_check_name((char *)pBuffer, /*const char*/peername)) {
ok = 1;
}
OPENSSL_free(pBuffer);
break;
default: continue;
}
}
}
}
}
}
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) {
Notice("trusting certificate, commonName matches");
X509_free(peer_cert);
return STAT_OK;
}
/* here: all envs set; opt_ver, cert verified, no subjAltName match -> check subject CN */
if (!openssl_check_peername(/*X509_NAME*/subjectname, /*const char*/peername)) {
Error("certificate is valid but its commonName does not match hostname");
status = STAT_NORETRY;
} else {
Notice("trusting certificate, commonName matches");
status = STAT_OK;
}
X509_free(peer_cert);
return status;
}
@ -1310,7 +1529,8 @@ static int xioSSL_set_fd(struct single *xfd, int level) {
in case of an error condition, this function check forever and retry
options and ev. sleeps an interval. It returns NORETRY when the caller
should not retry for any reason. */
static int xioSSL_connect(struct single *xfd, bool opt_ver, int level) {
static int xioSSL_connect(struct single *xfd, const char *opt_commonname,
bool opt_ver, int level) {
char error_string[120];
int errint, status, ret;
unsigned long err;
@ -1355,7 +1575,7 @@ static int xioSSL_connect(struct single *xfd, bool opt_ver, int level) {
break;
case SSL_ERROR_SSL:
status = openssl_SSL_ERROR_SSL(level, "SSL_connect");
if (openssl_handle_peer_certificate(xfd, opt_ver, level/*!*/) < 0) {
if (openssl_handle_peer_certificate(xfd, opt_commonname, opt_ver, level/*!*/) < 0) {
return STAT_RETRYLATER;
}
break;

View file

@ -1,5 +1,5 @@
/* source: xio-openssl.h */
/* Copyright Gerhard Rieger 2002-2012 */
/* Copyright Gerhard Rieger */
/* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xio_openssl_included
@ -29,6 +29,7 @@ extern const struct optdesc opt_openssl_compress;
#if WITH_FIPS
extern const struct optdesc opt_openssl_fips;
#endif
extern const struct optdesc opt_openssl_commonname;
extern int
_xioopen_openssl_prepare(struct opt *opts, struct single *xfd,
@ -36,9 +37,11 @@ extern int
SSL_CTX **ctx);
extern int
_xioopen_openssl_connect(struct single *xfd, bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx, int level);
extern int
_xioopen_openssl_listen(struct single *xfd, bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx, int level);
extern int xioclose_openssl(xiofile_t *xfd);
extern int xioshutdown_openssl(xiofile_t *xfd, int how);

View file

@ -1741,11 +1741,11 @@ int xiodopacketinfo(struct msghdr *msgh, bool withlog, bool withenv) {
}
if (withenv) {
if (*envp) {
xiosetenv(envp, valp, 1);
xiosetenv(envp, valp, 1, NULL);
} else if (!strcasecmp(typp+strlen(typp)-strlen(namp), namp)) {
xiosetenv(typp, valp, 1);
xiosetenv(typp, valp, 1, NULL);
} else {
xiosetenv2(typp, namp, valp, 1);
xiosetenv2(typp, namp, valp, 1, NULL);
}
}
if (++i == num) break;
@ -2132,7 +2132,7 @@ int xiosetsockaddrenv(const char *lr,
xiosetsockaddrenv_unix(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->un, salen, proto);
xiosetenv(namebuff, valuebuff, 1);
xiosetenv(namebuff, valuebuff, 1, NULL);
break;
#endif /* WITH_UNIX */
#if WITH_IP4
@ -2142,7 +2142,7 @@ int xiosetsockaddrenv(const char *lr,
xiosetsockaddrenv_ip4(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->ip4, proto);
xiosetenv(namebuff, valuebuff, 1);
xiosetenv(namebuff, valuebuff, 1, NULL);
namebuff[strlen(lr)] = '\0'; ++idx;
} while (result > 0);
break;
@ -2155,7 +2155,7 @@ int xiosetsockaddrenv(const char *lr,
xiosetsockaddrenv_ip6(idx, strchr(namebuff, '\0'), XIOSOCKADDRENVLEN-strlen(lr),
valuebuff, XIOSOCKADDRENVLEN,
&sau->ip6, proto);
xiosetenv(namebuff, valuebuff, 1);
xiosetenv(namebuff, valuebuff, 1, NULL);
namebuff[strlen(lr)] = '\0'; ++idx;
} while (result > 0);
break;

View file

@ -296,6 +296,8 @@ const struct optname optionnames[] = {
IF_TERMIOS("clocal", &opt_clocal)
IF_ANY ("cloexec", &opt_cloexec)
IF_ANY ("close", &opt_end_close)
IF_OPENSSL("cn", &opt_openssl_commonname)
IF_OPENSSL("commonname", &opt_openssl_commonname)
IF_EXEC ("commtype", &opt_commtype)
#if WITH_EXT2 && defined(EXT2_COMPR_FL)
IF_ANY ("compr", &opt_ext2_compr)
@ -1058,6 +1060,7 @@ const struct optname optionnames[] = {
IF_OPENSSL("openssl-capath", &opt_openssl_capath)
IF_OPENSSL("openssl-certificate", &opt_openssl_certificate)
IF_OPENSSL("openssl-cipherlist", &opt_openssl_cipherlist)
IF_OPENSSL("openssl-commonname", &opt_openssl_commonname)
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
IF_OPENSSL("openssl-compress", &opt_openssl_compress)
#endif

View file

@ -1,5 +1,5 @@
/* source: xioopts.h */
/* Copyright Gerhard Rieger 2001-2012 */
/* Copyright Gerhard Rieger */
/* Published under the GNU General Public License V.2, see file COPYING */
#ifndef __xioopts_h_included
@ -477,6 +477,7 @@ enum e_optcode {
OPT_OPENSSL_CAPATH,
OPT_OPENSSL_CERTIFICATE,
OPT_OPENSSL_CIPHERLIST,
OPT_OPENSSL_COMMONNAME,
#if OPENSSL_VERSION_NUMBER >= 0x00908000L
OPT_OPENSSL_COMPRESS,
#endif