OpenSSL peer certificate subject,issuer details are passed to env

This commit is contained in:
Gerhard Rieger 2015-04-02 12:16:41 +02:00
parent c87979a7db
commit 241e5256ec
5 changed files with 191 additions and 11 deletions

View file

@ -18,7 +18,6 @@ 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.
corrections: corrections:
LISTEN based addresses applied some address options, e.g. so-keepalive, LISTEN based addresses applied some address options, e.g. so-keepalive,
to the listening file descriptor instead of the connected file to the listening file descriptor instead of the connected file
@ -282,6 +281,13 @@ new features:
feature of newer OpenSSL versions. Thanks to Michael Hanselmann for feature of newer OpenSSL versions. Thanks to Michael Hanselmann for
providing this contribution (sponsored by Google Inc.) providing this contribution (sponsored by Google Inc.)
OpenSSL addresses set couple of environment variables from values in
peer certificate, e.g.:
SOCAT_OPENSSL_X509_SUBJECT, SOCAT_OPENSSL_X509_ISSUER,
SOCAT_OPENSSL_X509_COMMONNAME,
SOCAT_OPENSSL_X509V3_SUBJECTALTNAME_DNS
Tests: ENV_OPENSSL_{CLIENT,SERVER}_X509_*
docu docu
minor corrections in docu (thanks to Paggas) minor corrections in docu (thanks to Paggas)

View file

@ -726,6 +726,42 @@ int xiosetenv2(const char *varname, const char *varname2, const char *value,
# undef XIO_ENVNAMELEN # undef XIO_ENVNAMELEN
} }
int xiosetenv3(const char *varname, const char *varname2, const char *varname3,
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);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
strncat(envname+l, varname2, XIO_ENVNAMELEN-l-1);
l += strlen(envname+l);
strncat(envname+l, "_", XIO_ENVNAMELEN-l-1);
l += 1;
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;
# undef XIO_ENVNAMELEN
}
/* like xiosetenv(), but uses an unsigned long value */ /* like xiosetenv(), but uses an unsigned long value */
int xiosetenvulong(const char *varname, unsigned long value, int overwrite) { int xiosetenvulong(const char *varname, unsigned long value, int overwrite) {

View file

@ -93,6 +93,9 @@ extern int xiosetenv(const char *varname, const char *value, int overwrite);
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);
extern int
xiosetenv3(const char *varname, const char *varname2, const char *varname3,
const char *value, int overwrite);
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,

101
test.sh
View file

@ -78,6 +78,9 @@ LOCALHOST6=[::1]
PROTO=$((144+RANDOM/2048)) PROTO=$((144+RANDOM/2048))
PORT=12002 PORT=12002
SOURCEPORT=2002 SOURCEPORT=2002
TESTCERT_CONF=testcert.conf
TESTCERT_SUBJECT="/C=XY"
TESTCERT_ISSUER="/C=XY"
CAT=cat CAT=cat
OD_C="od -c" OD_C="od -c"
# precision sleep; takes seconds with fractional part # precision sleep; takes seconds with fractional part
@ -2226,7 +2229,7 @@ gentestcert () {
local name="$1" local name="$1"
if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi
openssl genrsa $OPENSSL_RAND -out $name.key 768 >/dev/null 2>&1 openssl genrsa $OPENSSL_RAND -out $name.key 768 >/dev/null 2>&1
openssl req -new -config testcert.conf -key $name.key -x509 -days 3653 -out $name.crt >/dev/null 2>&1 openssl req -new -config $TESTCERT_CONF -key $name.key -x509 -days 3653 -out $name.crt >/dev/null 2>&1
cat $name.key $name.crt >$name.pem cat $name.key $name.crt >$name.pem
} }
@ -2237,7 +2240,7 @@ gentestdsacert () {
if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi if [ -s $name.key -a -s $name.crt -a -s $name.pem ]; then return; fi
openssl dsaparam -out $name-dsa.pem 512 >/dev/null 2>&1 openssl dsaparam -out $name-dsa.pem 512 >/dev/null 2>&1
openssl dhparam -dsaparam -out $name-dh.pem 512 >/dev/null 2>&1 openssl dhparam -dsaparam -out $name-dh.pem 512 >/dev/null 2>&1
openssl req -newkey dsa:$name-dsa.pem -keyout $name.key -nodes -x509 -days 3653 -config testcert.conf -out $name.crt >/dev/null 2>&1 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 cat $name-dsa.pem $name-dh.pem $name.key $name.crt >$name.pem
} }
@ -11833,6 +11836,100 @@ PORT=$((PORT+1))
N=$((N+1)) N=$((N+1))
# test: OPENSSL sets of environment variables with important values of peer certificate
while read ssldist MODE MODULE FIELD TESTADDRESS PEERADDRESS VALUE; do
if [ -z "$ssldist" ] || [[ "$ssldist" == \#* ]]; then continue; fi
#
SSLDIST=${ssldist^^*}
NAME="ENV_${SSLDIST}_${MODE}_${MODULE}_${FIELD}"
case "$TESTS" in
*%$N%*|*%functions%*|*%ip4%*|*%ipapp%*|*%tcp%*|*%$ssldist%*|*%envvar%*|*%$NAME%*)
TEST="$NAME: $SSLDIST sets env SOCAT_${SSLDIST}_${MODULE}_${FIELD}"
# have a server accepting a connection and invoking some shell code. The shell
# code extracts and prints the SOCAT related environment vars.
# outside code then checks if the environment contains the variables correctly
# describing the desired field.
if ! eval $NUMCOND; then :;
elif ! feat=$(testaddrs $FEAT); then
$PRINTF "test $F_n $TEST... ${YELLOW}$(echo "$feat" |tr a-z A-Z) not available${NORMAL}\n" $N
numCANT=$((numCANT+1))
else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
gentestcert testsrv
gentestcert testcli
test_proto=tcp4
case "$MODE" in
SERVER)
CMD0="$SOCAT $opts -u $TESTADDRESS system:\"echo SOCAT_${SSLDIST}_${MODULE}_${FIELD}=\\\$SOCAT_${SSLDIST}_${MODULE}_${FIELD}; sleep 1\""
CMD1="$SOCAT $opts -u /dev/null $PEERADDRESS"
printf "test $F_n $TEST... " $N
eval "$CMD0 2>\"${te}0\" >\"$tf\" &"
pid0=$!
wait${test_proto}port $PORT 1
$CMD1 2>"${te}1"
rc1=$?
waitfile "$tf" 2
kill $pid0 2>/dev/null; wait
;;
CLIENT)
CMD0="$SOCAT $opts -u /dev/null $PEERADDRESS"
CMD1="$SOCAT $opts -u $TESTADDRESS system:\"echo SOCAT_${SSLDIST}_${MODULE}_${FIELD}=\\\$SOCAT_${SSLDIST}_${MODULE}_${FIELD}; sleep 1\""
printf "test $F_n $TEST... " $N
$CMD0 2>"${te}0" &
pid0=$!
wait${test_proto}port $PORT 1
eval "$CMD1 2>\"${te}1\" >\"$tf\""
rc1=$?
waitfile "$tf" 2
kill $pid0 2>/dev/null; wait
;;
esac
if [ $rc1 != 0 ]; then
$PRINTF "$NO_RESULT (client failed):\n"
echo "$CMD0 &"
cat "${te}0"
echo "$CMD1"
cat "${te}1"
numCANT=$((numCANT+1))
elif effval="$(grep SOCAT_${SSLDIST}_${MODULE}_${FIELD} "${tf}" |sed -e 's/^[^=]*=//' |sed -e "s/[\"']//g")";
[ "$effval" = "$VALUE" ]; then
$PRINTF "$OK\n"
if [ "$debug" ]; then
echo "$CMD0 &"
cat "${te}0"
echo "$CMD1"
cat "${te}1"
fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "expected \"$VALUE\", got \"$effval\"" >&2
echo "$CMD0 &"
cat "${te}0"
echo "$CMD1"
cat "${te}1"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND, feats
;;
esac
N=$((N+1))
#
done <<<"
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 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 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 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
"
############################################################################### ###############################################################################
# tests: option umask with "passive" NAMED group addresses # tests: option umask with "passive" NAMED group addresses
while read addr fileopt addropts proto diropt ADDR2; do while read addr fileopt addropts proto diropt ADDR2; do

View file

@ -734,7 +734,7 @@ int _xioopen_openssl_listen(struct single *xfd,
ERR_lib_error_string(err), ERR_func_error_string(err), ERR_lib_error_string(err), ERR_func_error_string(err),
ERR_reason_error_string(err)); ERR_reason_error_string(err));
} }
/* Msg1(level, "SSL_connect(): %s", ERR_error_string(e, buf));*/ /* Msg1(level, "SSL_accept(): %s", ERR_error_string(e, buf));*/
} }
break; break;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
@ -1173,22 +1173,59 @@ 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;
{
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);
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;
}
static int openssl_handle_peer_certificate(struct single *xfd, static int openssl_handle_peer_certificate(struct single *xfd,
bool opt_ver, int level) { bool opt_ver, int level) {
X509 *peer_cert; X509 *peer_cert;
char *str; /*ASN1_TIME not_before, not_after;*/
char buff[2048]; /* hold peer certificate */
int status; int status;
/* SSL_CTX_add_extra_chain_cert /* SSL_CTX_add_extra_chain_cert
SSL_get_verify_result SSL_get_verify_result
*/ */
if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) != NULL) { if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) != NULL) {
Debug("peer certificate:"); X509_NAME *name;
if ((str = X509_NAME_oneline(X509_get_subject_name(peer_cert), buff, sizeof(buff))) != NULL) if ((name = X509_get_subject_name(peer_cert)) != NULL)
Debug1("\tsubject: %s", str); /*free (str); SIGSEGV*/ openssl_extract_cert_info("subject", name);
if ((str = X509_NAME_oneline(X509_get_issuer_name(peer_cert), buff, sizeof(buff))) != NULL) if ((name = X509_get_issuer_name(peer_cert)) != NULL)
Debug1("\tissuer: %s", str); /*free (str); SIGSEGV*/ 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 (peer_cert) {
@ -1346,6 +1383,7 @@ ssize_t xioread_openssl(struct single *pipe, void *buff, size_t bufsiz) {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
/* this is not an error, but I dare not continue for security reasons*/ /* this is not an error, but I dare not continue for security reasons*/
Error("ok"); Error("ok");
break;
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
Error("connection closed by peer"); Error("connection closed by peer");
break; break;