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
Thanks to Peter Lobsinger for reporting and explaining this issue.
corrections:
LISTEN based addresses applied some address options, e.g. so-keepalive,
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
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
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
}
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 */
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
xiosetenv2(const char *varname, const char *varname2, const char *value,
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,
int overwrite);
extern int xiosetenvushort(const char *varname, unsigned short value,

101
test.sh
View file

@ -78,6 +78,9 @@ LOCALHOST6=[::1]
PROTO=$((144+RANDOM/2048))
PORT=12002
SOURCEPORT=2002
TESTCERT_CONF=testcert.conf
TESTCERT_SUBJECT="/C=XY"
TESTCERT_ISSUER="/C=XY"
CAT=cat
OD_C="od -c"
# precision sleep; takes seconds with fractional part
@ -2226,7 +2229,7 @@ gentestcert () {
local name="$1"
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 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
}
@ -2237,7 +2240,7 @@ gentestdsacert () {
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 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
}
@ -11833,6 +11836,100 @@ PORT=$((PORT+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
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_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;
case SSL_ERROR_SSL:
@ -1173,22 +1173,59 @@ 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;
}
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,
bool opt_ver, int level) {
X509 *peer_cert;
char *str;
char buff[2048]; /* hold peer certificate */
/*ASN1_TIME not_before, not_after;*/
int status;
/* SSL_CTX_add_extra_chain_cert
SSL_get_verify_result
*/
if ((peer_cert = SSL_get_peer_certificate(xfd->para.openssl.ssl)) != NULL) {
Debug("peer certificate:");
if ((str = X509_NAME_oneline(X509_get_subject_name(peer_cert), buff, sizeof(buff))) != NULL)
Debug1("\tsubject: %s", str); /*free (str); SIGSEGV*/
if ((str = X509_NAME_oneline(X509_get_issuer_name(peer_cert), buff, sizeof(buff))) != NULL)
Debug1("\tissuer: %s", str); /*free (str); SIGSEGV*/
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) {
@ -1346,6 +1383,7 @@ ssize_t xioread_openssl(struct single *pipe, void *buff, size_t bufsiz) {
case SSL_ERROR_NONE:
/* this is not an error, but I dare not continue for security reasons*/
Error("ok");
break;
case SSL_ERROR_ZERO_RETURN:
Error("connection closed by peer");
break;