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
|
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)
|
||||||
|
|
||||||
|
|
36
sysutils.c
36
sysutils.c
|
@ -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) {
|
||||||
|
|
|
@ -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
101
test.sh
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue