mirror of
https://repo.or.cz/socat.git
synced 2024-12-22 23:42:34 +00:00
OpenSSL peer certificate subject,issuer details are passed to env
This commit is contained in:
parent
c87979a7db
commit
241e5256ec
5 changed files with 191 additions and 11 deletions
8
CHANGES
8
CHANGES
|
@ -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)
|
||||
|
||||
|
|
36
sysutils.c
36
sysutils.c
|
@ -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) {
|
||||
|
|
|
@ -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
101
test.sh
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue