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

This commit is contained in:
Gerhard Rieger 2015-01-12 23:34:47 +01:00
parent 05afec429d
commit 2f40a439cb
13 changed files with 535 additions and 195 deletions

13
CHANGES
View File

@ -18,6 +18,17 @@ security:
Turn off nested signal handler invocations Turn off nested signal handler invocations
Thanks to Peter Lobsinger for reporting and explaining this issue. 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
new features: new features:
OpenSSL addresses set couple of environment variables from values in OpenSSL addresses set couple of environment variables from values in
peer certificate, e.g.: peer certificate, e.g.:
@ -1033,7 +1044,7 @@ further corrections:
ftp.sh script supports proxy address ftp.sh script supports proxy address
man page no longer installed with execute permissions (thanks to Peter man page no longer installed with execute permissions (thanks to Peter
Bray) Bray)
fixed a malloc call bug that could cause SIGSEGV or false "out of fixed a malloc call bug that could cause SIGSEGV or false "out of
memory" errors on EXEC and SYSTEM, depending on program name length and memory" errors on EXEC and SYSTEM, depending on program name length and

View File

@ -492,14 +492,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 <host> [link(IP address)(TYPE_IP_ADDRESS)] using TCP/IP version 4 or 6
depending on address specification, name resolution, or option depending on address specification, name resolution, or option
link(pf)(OPTION_PROTOCOL_FAMILY).nl() link(pf)(OPTION_PROTOCOL_FAMILY).nl()
NOTE: The server certificate is only checked for validity against NOTE: Up to version 1.7.2.4
link(cafile)(OPTION_OPENSSL_CAFILE) or link(capath)(OPTION_OPENSSL_CAPATH), the server certificate was only checked for validity against the system
but not for match with the server's name or its IP address!nl() 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 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() 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: Useful options:
link(cipher)(OPTION_OPENSSL_CIPHERLIST), link(cipher)(OPTION_OPENSSL_CIPHERLIST),
link(method)(OPTION_OPENSSL_METHOD), link(method)(OPTION_OPENSSL_METHOD),
link(verify)(OPTION_OPENSSL_VERIFY), link(verify)(OPTION_OPENSSL_VERIFY),
link(commonname)(OPTION_OPENSSL_COMMONNAME)
link(cafile)(OPTION_OPENSSL_CAFILE), link(cafile)(OPTION_OPENSSL_CAFILE),
link(capath)(OPTION_OPENSSL_CAPATH), link(capath)(OPTION_OPENSSL_CAPATH),
link(certificate)(OPTION_OPENSSL_CERTIFICATE), link(certificate)(OPTION_OPENSSL_CERTIFICATE),
@ -528,6 +537,7 @@ label(ADDRESS_OPENSSL_LISTEN)dit(bf(tt(OPENSSL-LISTEN:<port>)))
link(cipher)(OPTION_OPENSSL_CIPHERLIST), link(cipher)(OPTION_OPENSSL_CIPHERLIST),
link(method)(OPTION_OPENSSL_METHOD), link(method)(OPTION_OPENSSL_METHOD),
link(verify)(OPTION_OPENSSL_VERIFY), link(verify)(OPTION_OPENSSL_VERIFY),
link(commonname)(OPTION_OPENSSL_COMMONNAME)
link(cafile)(OPTION_OPENSSL_CAFILE), link(cafile)(OPTION_OPENSSL_CAFILE),
link(capath)(OPTION_OPENSSL_CAPATH), link(capath)(OPTION_OPENSSL_CAPATH),
link(certificate)(OPTION_OPENSSL_CERTIFICATE), link(certificate)(OPTION_OPENSSL_CERTIFICATE),
@ -2675,6 +2685,14 @@ label(OPTION_OPENSSL_COMPRESS)dit(bf(tt(compress)))
compression-related settings. compression-related settings.
NOTE: Requires OpenSSL 0.9.8 or higher and disabling compression with NOTE: Requires OpenSSL 0.9.8 or higher and disabling compression with
OpenSSL 0.9.8 affects all new connections in the process. 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.
label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips))) label(OPTION_OPENSSL_FIPS)dit(bf(tt(fips)))
Enables FIPS mode if compiled in. For info about the FIPS encryption Enables FIPS mode if compiled in. For info about the FIPS encryption
implementation standard see lurl(http://oss-institute.org/fips-faq.html). implementation standard see lurl(http://oss-institute.org/fips-faq.html).
@ -3440,6 +3458,16 @@ dit(bf(SOCAT_IPV6_TCLASS) (output)) With all IPv6 based RECVFROM addresses
where address option link(ipv6-recvtclass)(OPTION_IPV6_RECVTCLASS) is applied, where address option link(ipv6-recvtclass)(OPTION_IPV6_RECVTCLASS) is applied,
socat sets this variable to the transfer class of the received packet. 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 dit(bf(HOSTNAME) (input)) Is used to determine the hostname for logging (see
link(-lh)(option_lh)). link(-lh)(option_lh)).

View File

@ -274,7 +274,7 @@ int main(int argc, const char *argv[]) {
Info(copyright_ssleay); Info(copyright_ssleay);
#endif #endif
Debug2("socat version %s on %s", socatversion, timestamp); 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) */ uname(&ubuf); /* ! here we circumvent internal tracing (Uname) */
Debug4("running on %s version %s, release %s, machine %s\n", Debug4("running on %s version %s, release %s, machine %s\n",
ubuf.sysname, ubuf.version, ubuf.release, ubuf.machine); 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 */ #endif /* WITH_IP4 || WITH_IP6 || WITH_INTERFACE */
/* constructs an environment variable whose name is built from socats uppercase int _xiosetenv(const char *envname, const char *value, int overwrite, const char *sep) {
program name, and underscore and varname; if a variable of this name already char *oldval;
exists a non zero value of overwrite lets the old value be overwritten. char *newval;
returns 0 on success or <0 if an error occurred. */ if (overwrite >= 2 && (oldval = getenv(envname)) != NULL) {
int xiosetenv(const char *varname, const char *value, int overwrite) { size_t newlen = strlen(oldval)+strlen(sep)+strlen(value)+1;
# define XIO_ENVNAMELEN 256 if ((newval = Malloc(newlen+1)) == NULL) {
const char *progname; return -1;
char envname[XIO_ENVNAMELEN]; }
size_t i, l; snprintf(newval, newlen+1, "%s%s%s", oldval, sep, value);
} else {
progname = diag_get_string('p'); newval = (char *)value;
envname[0] = '\0'; strncat(envname, progname, XIO_ENVNAMELEN-1); }
l = strlen(progname); if (Setenv(envname, newval, overwrite) < 0) {
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) {
Warn3("setenv(\"%s\", \"%s\", 1): %s", Warn3("setenv(\"%s\", \"%s\", 1): %s",
envname, value, strerror(errno)); envname, value, strerror(errno));
#if HAVE_UNSETENV #if HAVE_UNSETENV
@ -692,11 +687,35 @@ int xiosetenv(const char *varname, const char *value, int overwrite) {
return -1; return -1;
} }
return 0; return 0;
# undef XIO_ENVNAMELEN }
/* 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
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, 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 ENVNAMELEN
} }
int xiosetenv2(const char *varname, const char *varname2, const char *value, int xiosetenv2(const char *varname, const char *varname2, const char *value,
int overwrite) { int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256 # define XIO_ENVNAMELEN 256
const char *progname; const char *progname;
char envname[XIO_ENVNAMELEN]; char envname[XIO_ENVNAMELEN];
@ -714,21 +733,13 @@ int xiosetenv2(const char *varname, const char *varname2, const char *value,
strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1); strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l); l += strlen(envname+l);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]); for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
if (Setenv(envname, value, overwrite) < 0) { return _xiosetenv(envname, value, overwrite, sep);
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;
# undef XIO_ENVNAMELEN # undef XIO_ENVNAMELEN
} }
int xiosetenv3(const char *varname, const char *varname2, const char *varname3, int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
const char *value, const char *value,
int overwrite) { int overwrite, const char *sep) {
# define XIO_ENVNAMELEN 256 # define XIO_ENVNAMELEN 256
const char *progname; const char *progname;
char envname[XIO_ENVNAMELEN]; char envname[XIO_ENVNAMELEN];
@ -750,15 +761,7 @@ int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
strncat(envname+l, varname3, XIO_ENVNAMELEN-l-1); strncat(envname+l, varname3, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l); l += strlen(envname+l);
for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]); for (i = 0; i < l; ++i) envname[i] = toupper(envname[i]);
if (Setenv(envname, value, overwrite) < 0) { return _xiosetenv(envname, value, overwrite, sep);
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;
# undef XIO_ENVNAMELEN # undef XIO_ENVNAMELEN
} }
@ -769,7 +772,7 @@ int xiosetenvulong(const char *varname, unsigned long value, int overwrite) {
char envbuff[XIO_LONGLEN]; char envbuff[XIO_LONGLEN];
snprintf(envbuff, XIO_LONGLEN, "%lu", value); snprintf(envbuff, XIO_LONGLEN, "%lu", value);
return xiosetenv(varname, envbuff, overwrite); return xiosetenv(varname, envbuff, overwrite, NULL);
# undef XIO_LONGLEN # undef XIO_LONGLEN
} }
@ -779,6 +782,6 @@ int xiosetenvushort(const char *varname, unsigned short value, int overwrite) {
char envbuff[XIO_SHORTLEN]; char envbuff[XIO_SHORTLEN];
snprintf(envbuff, XIO_SHORTLEN, "%hu", value); snprintf(envbuff, XIO_SHORTLEN, "%hu", value);
return xiosetenv(varname, envbuff, overwrite); return xiosetenv(varname, envbuff, overwrite, NULL);
# undef XIO_SHORTLEN # undef XIO_SHORTLEN
} }

View File

@ -88,13 +88,13 @@ extern int parseport(const char *portname, int proto);
extern int ifindexbyname(const char *ifname, int anysock); extern int ifindexbyname(const char *ifname, int anysock);
extern int ifindex(const char *ifname, unsigned int *ifindex, 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 extern int
xiosetenv2(const char *varname, const char *varname2, const char *value, xiosetenv2(const char *varname, const char *varname2, const char *value,
int overwrite); int overwrite, const char *sep);
extern int extern int
xiosetenv3(const char *varname, const char *varname2, const char *varname3, 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, extern int xiosetenvulong(const char *varname, unsigned long value,
int overwrite); int overwrite);
extern int xiosetenvushort(const char *varname, unsigned short value, extern int xiosetenvushort(const char *varname, unsigned short value,

161
test.sh
View File

@ -70,8 +70,8 @@ else
fi fi
MCINTERFACE=lo # !!! Linux only MCINTERFACE=lo # !!! Linux only
#LOCALHOST=192.168.58.1 #LOCALHOST=192.168.58.1
#LOCALHOST=localhost LOCALHOST=localhost
LOCALHOST=127.0.0.1 #LOCALHOST=127.0.0.1
LOCALHOST6=[::1] LOCALHOST6=[::1]
#PROTO=$(awk '{print($2);}' /etc/protocols |sort -n |tail -n 1) #PROTO=$(awk '{print($2);}' /etc/protocols |sort -n |tail -n 1)
#PROTO=$(($PROTO+1)) #PROTO=$(($PROTO+1))
@ -79,10 +79,24 @@ PROTO=$((144+RANDOM/2048))
PORT=12002 PORT=12002
SOURCEPORT=2002 SOURCEPORT=2002
TESTCERT_CONF=testcert.conf TESTCERT_CONF=testcert.conf
TESTCERT_SUBJECT="/C=XY" TESTCERT6_CONF=testcert6.conf
TESTCERT_ISSUER="/C=XY" # 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 CAT=cat
OD_C="od -c" 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 # precision sleep; takes seconds with fractional part
psleep () { psleep () {
local T="$1" local T="$1"
@ -1578,7 +1592,6 @@ testecho () {
listFAIL="$listFAIL $N" listFAIL="$listFAIL $N"
fi fi
fi # NUMCOND fi # NUMCOND
#set +vx
} }
# test if call to od and throughput of data works - with graceful shutdown and # test if call to od and throughput of data works - with graceful shutdown and
@ -2221,6 +2234,17 @@ gentestdsacert () {
cat $name-dsa.pem $name-dh.pem $name.key $name.crt >$name.pem 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 NAME=UNISTDIO
case "$TESTS " in case "$TESTS " in
@ -4018,7 +4042,7 @@ TESTKEYW=${TESTADDR%%:*}
# does our address implementation support halfclose? # does our address implementation support halfclose?
NAME=${NAMEKEYW}_HALFCLOSE NAME=${NAMEKEYW}_HALFCLOSE
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%socket%*|*%halfclose%*|*%$NAME%*) *%$N%*|*%functions%*|*%$FEAT%*|*%socket%*|*%halfclose%*|*%$NAME%*)
TEST="$NAME: $TESTKEYW half close" TEST="$NAME: $TESTKEYW half close"
# have a "peer" socat "peer" that executes "$OD_C" and see if EOF on the # have a "peer" socat "peer" that executes "$OD_C" and see if EOF on the
# connecting socat brings the result of od # connecting socat brings the result of od
@ -5043,7 +5067,7 @@ N=$((N+1))
#! #!
NAME=OUTBOUNDIN NAME=OUTBOUNDIN
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%proxy%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%$NAME%*)
TEST="$NAME: gender changer via SSL through HTTP proxy, oneshot" TEST="$NAME: gender changer via SSL through HTTP proxy, oneshot"
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! feat=$(testaddrs openssl proxy); then elif ! feat=$(testaddrs openssl proxy); then
@ -5129,7 +5153,7 @@ PORT=$((RANDOM+16184))
#! #!
NAME=INTRANETRIPPER NAME=INTRANETRIPPER
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%proxy%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%proxy%*|*%$NAME%*)
TEST="$NAME: gender changer via SSL through HTTP proxy, daemons" TEST="$NAME: gender changer via SSL through HTTP proxy, daemons"
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! feat=$(testaddrs openssl proxy); then elif ! feat=$(testaddrs openssl proxy); then
@ -5257,7 +5281,6 @@ testserversec () {
local stat result local stat result
$PRINTF "test $F_n %s... " $N "$title" $PRINTF "test $F_n %s... " $N "$title"
#set -vx
# first: without security # first: without security
# start server # start server
$TRACE $SOCAT $opts "$arg1,$secopt0" echo 2>"${te}1" & $TRACE $SOCAT $opts "$arg1,$secopt0" echo 2>"${te}1" &
@ -5327,8 +5350,7 @@ testserversec () {
da="test$N.2 $(date) $RANDOM" da="test$N.2 $(date) $RANDOM"
(echo "$da"; sleep $T) |$TRACE $SOCAT $opts - "$arg2" >"$tf" 2>"${te}4" (echo "$da"; sleep $T) |$TRACE $SOCAT $opts - "$arg2" >"$tf" 2>"${te}4"
stat=$? stat=$?
kill $spid kill $spid 2>/dev/null
#kill $spid 2>/dev/null
#set +vx #set +vx
#killall $TRACE $SOCAT 2>/dev/null #killall $TRACE $SOCAT 2>/dev/null
if [ "$stat" != 0 ]; then if [ "$stat" != 0 ]; then
@ -5683,7 +5705,7 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5699,7 +5721,7 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv 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 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5715,7 +5737,7 @@ elif ! testaddrs openssl >/dev/null; then
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5735,7 +5757,7 @@ ha="$td/hosts.allow"
hd="$td/hosts.deny" hd="$td/hosts.deny"
$ECHO "socat: $SECONDADDR" >"$ha" $ECHO "socat: $SECONDADDR" >"$ha"
$ECHO "ALL: ALL" >"$hd" $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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5752,7 +5774,7 @@ elif ! testaddrs openssl >/dev/null; then
else else
gentestcert testsrv gentestcert testsrv
gentestcert testcli 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5788,8 +5810,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv gentestcert6 testsrv6
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 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5807,8 +5829,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv gentestcert6 testsrv6
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 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5826,8 +5848,8 @@ elif ! feat=$(testaddrs tcp ip6) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}TCP6 not available${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv gentestcert6 testsrv6
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 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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
@ -5842,18 +5864,66 @@ elif ! feat=$(testaddrs ip6 tcp libwrap openssl) || ! runsip6 >/dev/null; then
$PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N $PRINTF "test $F_n $TEST... ${YELLOW}$feat not available${NORMAL}\n" $N
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
else else
gentestcert testsrv gentestcert6 testsrv6
ha="$td/hosts.allow" ha="$td/hosts.allow"
hd="$td/hosts.deny" hd="$td/hosts.deny"
$ECHO "socat: [::2]" >"$ha" $ECHO "socat: [::2]" >"$ha"
$ECHO "ALL: ALL" >"$hd" $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 fi ;; # NUMCOND, feats
esac esac
PORT=$((PORT+1)) PORT=$((PORT+1))
N=$((N+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 NAME=OPENSSL_FIPS_SECURITY
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%security%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*) *%$N%*|*%functions%*|*%security%*|*%openssl%*|*%fips%*|*%tcp%*|*%tcp4%*|*%ip4%*|*%$NAME%*)
@ -6138,7 +6208,7 @@ N=$((N+1))
NAME=OPENSSLLISTENDSA NAME=OPENSSLLISTENDSA
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: openssl listen with DSA certificate" TEST="$NAME: openssl listen with DSA certificate"
if ! eval $NUMCOND; then :; if ! eval $NUMCOND; then :;
elif ! testaddrs openssl >/dev/null; then elif ! testaddrs openssl >/dev/null; then
@ -6438,7 +6508,6 @@ SCTP4 $LOCALHOST PORT
SCTP6 $LOCALHOST6 PORT SCTP6 $LOCALHOST6 PORT
UNIX FILE , UNIX FILE ,
" "
#set +vx
NAME=UNIXTOSTREAM NAME=UNIXTOSTREAM
@ -8965,7 +9034,7 @@ NAME=OPENSSLREAD
# keeps there and is not transferred by socat until the socket indicates more # keeps there and is not transferred by socat until the socket indicates more
# data or EOF. # data or EOF.
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%$NAME%*)
TEST="$NAME: socat handles data buffered by openssl" TEST="$NAME: socat handles data buffered by openssl"
#idea: have a socat process (server) that gets an SSL block that is larger than #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 # socat transfer block size; keep the socket connection open and kill the
@ -9862,10 +9931,10 @@ N=$((N+1))
#set +xv #set +xv
# #
done <<<" 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)) 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)) 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)) 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 , , UNIX UNIX $td/test\$N.server $td/test\$N.client , ,
" "
@ -11352,7 +11421,7 @@ N=$((N+1))
# Linux) with "Invalid argument". # Linux) with "Invalid argument".
NAME=OPENSSL_CONNECT_BIND NAME=OPENSSL_CONNECT_BIND
case "$TESTS" in case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%socket%*|*%ssl%*|*%$NAME%*) *%$N%*|*%functions%*|*%openssl%*|*%bugs%*|*%socket%*|*%ssl%*|*%$NAME%*)
TEST="$NAME: test OPENSSL-CONNECT with bind option" TEST="$NAME: test OPENSSL-CONNECT with bind option"
# have a simple SSL server that just echoes data. # have a simple SSL server that just echoes data.
# connect with socat using OPENSSL-CONNECT with bind, send data and check if the # connect with socat using OPENSSL-CONNECT with bind, send data and check if the
@ -11451,17 +11520,15 @@ PORT=$((PORT+1))
N=$((N+1)) N=$((N+1))
#set -xv
# test: OPENSSL sets of environment variables with important values of peer certificate # test: OPENSSL sets of environment variables with important values of peer certificate
unset SOCAT_OPENSSL_X509_SUBJECT SOCAT_OPENSSL_X509_ISSUER while read ssldist MODE MODULE FIELD TESTADDRESS PEERADDRESS VALUE; do
while read SSLDIST MODE MODULE FIELD VALUE TESTADDRESS PEERADDRESS; do if [ -z "$ssldist" ] || [[ "$ssldist" == \#* ]]; then continue; fi
if [ -z "$SSLDIST" ] || [[ "$SSLDIST" == \#* ]]; then continue; fi
# #
test_proto="$(echo "$KEYW" |tr A-Z a-z)" SSLDIST=${ssldist^^*}
NAME="ENV_${SSLDIST}_${MODE}_${MODULE}_${FIELD}" NAME="ENV_${SSLDIST}_${MODE}_${MODULE}_${FIELD}"
case "$TESTS" in case "$TESTS" in
*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$SSLDIST%*|*%envvar%*|*%$NAME%*) *%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$ssldist%*|*%envvar%*|*%$NAME%*)
TEST="$NAME: $SSLDIST generates environment variable SOCAT_${SSLDIST}_${MODULE}_${FIELD} with correct value" TEST="$NAME: $SSLDIST sets env SOCAT_${SSLDIST}_${MODULE}_${FIELD}"
# have a server accepting a connection and invoking some shell code. The shell # have a server accepting a connection and invoking some shell code. The shell
# code extracts and prints the SOCAT related environment vars. # code extracts and prints the SOCAT related environment vars.
# outside code then checks if the environment contains the variables correctly # outside code then checks if the environment contains the variables correctly
@ -11475,7 +11542,6 @@ tf="$td/test$N.stdout"
te="$td/test$N.stderr" te="$td/test$N.stderr"
gentestcert testsrv gentestcert testsrv
gentestcert testcli gentestcert testcli
#CMD0="$SOCAT $opts -u ${SSLDIST}-LISTEN:$PORT,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 system:\"echo SOCAT_${SSLDIST}_${MODULE}_${FIELD}=\\\$SOCAT_${SSLDIST}_${MODULE}_${FIELD}; sleep 1\""
test_proto=tcp4 test_proto=tcp4
case "$MODE" in case "$MODE" in
SERVER) SERVER)
@ -11510,7 +11576,8 @@ if [ $rc1 != 0 ]; then
echo "$CMD1" echo "$CMD1"
cat "${te}1" cat "${te}1"
numCANT=$((numCANT+1)) numCANT=$((numCANT+1))
elif [ "$(grep SOCAT_${SSLDIST}_${MODULE}_${FIELD} "${tf}" |sed -e 's/^[^=]*=//' |sed -e "s/[\"']//g")" = "$VALUE" ]; then elif effval="$(grep SOCAT_${SSLDIST}_${MODULE}_${FIELD} "${tf}" |sed -e 's/^[^=]*=//' |sed -e "s/[\"']//g")";
[ "$effval" = "$VALUE" ]; then
$PRINTF "$OK\n" $PRINTF "$OK\n"
if [ "$debug" ]; then if [ "$debug" ]; then
echo "$CMD0 &" echo "$CMD0 &"
@ -11521,6 +11588,7 @@ elif [ "$(grep SOCAT_${SSLDIST}_${MODULE}_${FIELD} "${tf}" |sed -e 's/^[^=]*=//'
numOK=$((numOK+1)) numOK=$((numOK+1))
else else
$PRINTF "$FAILED\n" $PRINTF "$FAILED\n"
echo "expected \"$VALUE\", got \"$effval\"" >&2
echo "$CMD0 &" echo "$CMD0 &"
cat "${te}0" cat "${te}0"
echo "$CMD1" echo "$CMD1"
@ -11532,15 +11600,18 @@ fi # NUMCOND, feats
;; ;;
esac esac
N=$((N+1)) N=$((N+1))
#set +xv
# #
done <<<" done <<<"
OPENSSL SERVER X509 SUBJECT $TESTCERT_SUBJECT OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 openssl SERVER X509 ISSUER OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_ISSUER
OPENSSL SERVER X509 ISSUER $TESTCERT_ISSUER OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 openssl SERVER X509 SUBJECT OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_SUBJECT
OPENSSL CLIENT X509 SUBJECT $TESTCERT_SUBJECT OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 openssl SERVER X509 COMMONNAME OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_COMMONNAME
OPENSSL CLIENT X509 ISSUER $TESTCERT_ISSUER OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 openssl SERVER X509 COUNTRYNAME OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_COUNTRYNAME
openssl SERVER X509 LOCALITYNAME OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_LOCALITYNAME
openssl SERVER X509 ORGANIZATIONALUNITNAME OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_ORGANIZATIONALUNITNAME
openssl SERVER X509 ORGANIZATIONNAME OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 $TESTCERT_ORGANIZATIONNAME
openssl CLIENT X509 SUBJECT OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 $TESTCERT_SUBJECT
openssl CLIENT X509 ISSUER OPENSSL-CONNECT:$LOCALHOST:$PORT,cert=testcli.pem,cafile=testsrv.crt,verify=1 OPENSSL-LISTEN:$PORT,so-reuseaddr,bind=$LOCALHOST,cert=testsrv.pem,cafile=testcli.crt,verify=1 $TESTCERT_ISSUER
" "
set +xv
############################################################################### ###############################################################################

View File

@ -5,5 +5,9 @@ default_bits = 768
distinguished_name=Test distinguished_name=Test
[ 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; return NULL;
} }
/* Linux: setenv(), AIX: putenv() */ /* Linux: setenv(), AIX (4.3?): putenv() */
#if !HAVE_SETENV #if !HAVE_SETENV
int setenv(const char *name, const char *value, int overwrite) { int setenv(const char *name, const char *value, int overwrite) {
int result; int result;

View File

@ -6,6 +6,9 @@
#include "xiosysincludes.h" #include "xiosysincludes.h"
#if WITH_OPENSSL /* make this address configure dependend */ #if WITH_OPENSSL /* make this address configure dependend */
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include "xioopen.h" #include "xioopen.h"
#include "xio-fd.h" #include "xio-fd.h"
@ -45,10 +48,13 @@ static int xioopen_openssl_listen(int argc, const char *argv[], struct opt *opts
int xioflags, xiofile_t *fd, unsigned groups, int xioflags, xiofile_t *fd, unsigned groups,
int dummy1, int dummy2, int dummy3); int dummy1, int dummy2, int dummy3);
static int openssl_SSL_ERROR_SSL(int level, const char *funcname); 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); int level);
static int xioSSL_set_fd(struct single *xfd, 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 ssl connect */ /* description record for ssl connect */
@ -110,6 +116,7 @@ const struct optdesc opt_openssl_compress = { "openssl-compress", "compress
#if WITH_FIPS #if WITH_FIPS
const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC }; const struct optdesc opt_openssl_fips = { "openssl-fips", "fips", OPT_OPENSSL_FIPS, GROUP_OPENSSL, PH_SPEC, TYPE_BOOL, OFUNC_SPEC };
#endif #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. /* If FIPS is compiled in, we need to track if the user asked for FIPS mode.
@ -189,6 +196,7 @@ static int
SSL_CTX* ctx; SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate */ bool opt_ver = true; /* verify peer certificate */
char *opt_cert = NULL; /* file name of client certificate */ char *opt_cert = NULL; /* file name of client certificate */
const char *opt_commonname = NULL; /* for checking peer certificate */
int result; int result;
if (!(xioflags & XIO_MAYCONVERT)) { if (!(xioflags & XIO_MAYCONVERT)) {
@ -203,6 +211,12 @@ static int
} }
hostname = argv[1]; hostname = argv[1];
portname = argv[2]; 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;
}
xfd->howtoend = END_SHUTDOWN; xfd->howtoend = END_SHUTDOWN;
if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1; if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
@ -211,6 +225,11 @@ static int
retropt_bool(opts, OPT_FORK, &dofork); retropt_bool(opts, OPT_FORK, &dofork);
retropt_string(opts, OPT_OPENSSL_CERTIFICATE, &opt_cert); 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 = result =
_xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert, &ctx); _xioopen_openssl_prepare(opts, xfd, false, &opt_ver, opt_cert, &ctx);
@ -270,7 +289,7 @@ static int
return result; return result;
} }
result = _xioopen_openssl_connect(xfd, opt_ver, ctx, level); result = _xioopen_openssl_connect(xfd, opt_ver, opt_commonname, ctx, level);
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
@ -338,6 +357,7 @@ static int
SSL connection from an FD and a CTX. */ SSL connection from an FD and a CTX. */
int _xioopen_openssl_connect(struct single *xfd, int _xioopen_openssl_connect(struct single *xfd,
bool opt_ver, bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx, SSL_CTX *ctx,
int level) { int level) {
SSL *ssl; SSL *ssl;
@ -362,14 +382,15 @@ int _xioopen_openssl_connect(struct single *xfd,
return result; return result;
} }
result = xioSSL_connect(xfd, opt_ver, level); result = xioSSL_connect(xfd, opt_commonname, opt_ver, level);
if (result != STAT_OK) { if (result != STAT_OK) {
sycSSL_free(xfd->para.openssl.ssl); sycSSL_free(xfd->para.openssl.ssl);
xfd->para.openssl.ssl = NULL; xfd->para.openssl.ssl = NULL;
return result; 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) { if (result != STAT_OK) {
sycSSL_free(xfd->para.openssl.ssl); sycSSL_free(xfd->para.openssl.ssl);
xfd->para.openssl.ssl = NULL; xfd->para.openssl.ssl = NULL;
@ -411,6 +432,7 @@ static int
SSL_CTX* ctx; SSL_CTX* ctx;
bool opt_ver = true; /* verify peer certificate - changed with 1.6.0 */ bool opt_ver = true; /* verify peer certificate - changed with 1.6.0 */
char *opt_cert = NULL; /* file name of server certificate */ char *opt_cert = NULL; /* file name of server certificate */
const char *opt_commonname = NULL; /* for checking peer certificate */
int result; int result;
if (!(xioflags & XIO_MAYCONVERT)) { if (!(xioflags & XIO_MAYCONVERT)) {
@ -443,6 +465,8 @@ static int
Warn("no certificate given; consider option \"cert\""); Warn("no certificate given; consider option \"cert\"");
} }
retropt_string(opts, OPT_OPENSSL_COMMONNAME, (char **)&opt_commonname);
applyopts(-1, opts, PH_EARLY); applyopts(-1, opts, PH_EARLY);
result = result =
@ -501,7 +525,7 @@ static int
return result; return result;
} }
result = _xioopen_openssl_listen(xfd, opt_ver, ctx, level); result = _xioopen_openssl_listen(xfd, opt_ver, opt_commonname, ctx, level);
switch (result) { switch (result) {
case STAT_OK: break; case STAT_OK: break;
#if WITH_RETRY #if WITH_RETRY
@ -535,6 +559,7 @@ static int
int _xioopen_openssl_listen(struct single *xfd, int _xioopen_openssl_listen(struct single *xfd,
bool opt_ver, bool opt_ver,
const char *opt_commonname,
SSL_CTX *ctx, SSL_CTX *ctx,
int level) { int level) {
char error_string[120]; char error_string[120];
@ -616,7 +641,7 @@ int _xioopen_openssl_listen(struct single *xfd,
return STAT_RETRYLATER; 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; return STAT_NORETRY;
} }
@ -721,7 +746,6 @@ int
#if OPENSSL_VERSION_NUMBER >= 0x00908000L #if OPENSSL_VERSION_NUMBER >= 0x00908000L
retropt_string(opts, OPT_OPENSSL_COMPRESS, &opt_compress); retropt_string(opts, OPT_OPENSSL_COMPRESS, &opt_compress);
#endif #endif
#if WITH_FIPS #if WITH_FIPS
if (opt_fips) { if (opt_fips) {
if (!sycFIPS_mode_set(1)) { if (!sycFIPS_mode_set(1)) {
@ -734,6 +758,8 @@ int
} }
#endif #endif
openssl_delete_cert_info();
OpenSSL_add_all_algorithms(); OpenSSL_add_all_algorithms();
OpenSSL_add_all_ciphers(); OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests(); OpenSSL_add_all_digests();
@ -973,19 +999,21 @@ int
static int openssl_SSL_ERROR_SSL(int level, const char *funcname) { static int openssl_SSL_ERROR_SSL(int level, const char *funcname) {
unsigned long e; unsigned long e;
char buf[120]; /* this value demanded by "man ERR_error_string" */ char buf[120]; /* this value demanded by "man ERR_error_string" */
int stat = STAT_OK;
e = ERR_get_error(); while (e = ERR_get_error()) {
Debug1("ERR_get_error(): %lx", e); Debug1("ERR_get_error(): %lx", e);
if (e == ((ERR_LIB_RAND<<24)| if (e == ((ERR_LIB_RAND<<24)|
(RAND_F_SSLEAY_RAND_BYTES<<12)| (RAND_F_SSLEAY_RAND_BYTES<<12)|
(RAND_R_PRNG_NOT_SEEDED)) /*0x24064064*/) { (RAND_R_PRNG_NOT_SEEDED)) /*0x24064064*/) {
Error("too few entropy; use options \"egd\" or \"pseudo\""); Error("too few entropy; use options \"egd\" or \"pseudo\"");
return STAT_NORETRY; stat = STAT_NORETRY;
} else { } else {
Msg2(level, "%s(): %s", funcname, ERR_error_string(e, buf)); Msg2(level, "%s(): %s", funcname, ERR_error_string(e, buf));
return level==E_ERROR ? STAT_NORETRY : STAT_RETRYLATER; stat = level==E_ERROR ? STAT_NORETRY : STAT_RETRYLATER;
}
} }
return STAT_OK; return stat;
} }
static const char *openssl_verify_messages[] = { static const char *openssl_verify_messages[] = {
@ -1042,87 +1070,169 @@ static const char *openssl_verify_messages[] = {
/* 50 */ "application verification failure", /* 50 */ "application verification failure",
} ; } ;
static int openssl_extract_cert_info(const char *field, X509_NAME *name) {
int n, i; /* delete all environment variables whose name begins with SOCAT_OPENSSL_
{ resp. <progname>_OPENSSL_ */
BIO *bio = BIO_new(BIO_s_mem()); static int openssl_delete_cert_info(void) {
char *buf = NULL, *str; # define XIO_ENVNAMELEN 256
size_t len; const char *progname;
X509_NAME_print_ex(bio, name, 0, XN_FLAG_ONELINE&~ASN1_STRFLGS_ESC_MSB); /* rc not documented */ char envprefix[XIO_ENVNAMELEN];
len = BIO_get_mem_data (bio, &buf); char envname[XIO_ENVNAMELEN];
if ((str = Malloc(len+1)) == NULL) { size_t i, l;
BIO_free(bio); const char **entry;
return -1;
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);
}
n = X509_NAME_entry_count(name);
for (i = 0; i < n; ++i) {
X509_NAME_ENTRY *entry;
char *text;
ASN1_STRING *data;
ASN1_OBJECT *obj;
int nid;
entry = X509_NAME_get_entry(name, i);
data = X509_NAME_ENTRY_get_data(entry);
obj = X509_NAME_ENTRY_get_object(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);
} }
return 0; return 0;
} }
/* 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;
ASN1_OBJECT *obj;
ASN1_STRING *data;
unsigned char *text;
int nid;
entry = X509_NAME_get_entry(name, i);
obj = X509_NAME_ENTRY_get_object(entry);
data = X509_NAME_ENTRY_get_data(entry);
nid = OBJ_obj2nid(obj);
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;
}
/* 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
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.
*/
static int openssl_handle_peer_certificate(struct single *xfd, static int openssl_handle_peer_certificate(struct single *xfd,
const char *peername,
bool opt_ver, int level) { bool opt_ver, int level) {
X509 *peer_cert; X509 *peer_cert;
X509_NAME *subjectname, *issuername;
/*ASN1_TIME not_before, not_after;*/ /*ASN1_TIME not_before, not_after;*/
int extcount, i, ok = 0;
int status; int status;
/* SSL_CTX_add_extra_chain_cert if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) == NULL) {
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 (opt_ver) { if (opt_ver) {
Msg(level, "no peer certificate"); Msg(level, "no peer certificate");
status = STAT_RETRYLATER; status = STAT_RETRYLATER;
@ -1130,8 +1240,113 @@ static int openssl_handle_peer_certificate(struct single *xfd,
Notice("no peer certificate and no check"); Notice("no peer certificate and no check");
status = STAT_OK; 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); X509_free(peer_cert);
return status; return status;
} }
@ -1156,7 +1371,8 @@ static int xioSSL_set_fd(struct single *xfd, int level) {
in case of an error condition, this function check forever and retry in case of an error condition, this function check forever and retry
options and ev. sleeps an interval. It returns NORETRY when the caller options and ev. sleeps an interval. It returns NORETRY when the caller
should not retry for any reason. */ 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]; char error_string[120];
int errint, status, ret; int errint, status, ret;
unsigned long err; unsigned long err;
@ -1201,7 +1417,7 @@ static int xioSSL_connect(struct single *xfd, bool opt_ver, int level) {
break; break;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
status = openssl_SSL_ERROR_SSL(level, "SSL_connect"); 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; return STAT_RETRYLATER;
} }
break; break;

View File

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

View File

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

View File

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

View File

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