diff -ruN openssh-3.9p1+x509g4/auth2-pubkey.c openssh-3.9p1+x509h/auth2-pubkey.c --- openssh-3.9p1+x509g4/auth2-pubkey.c 2004-08-18 09:06:01.000000000 +0300 +++ openssh-3.9p1+x509h/auth2-pubkey.c 2004-08-18 09:06:01.000000000 +0300 @@ -241,7 +241,12 @@ found_key = 1; debug("matching key found: file %s, line %lu", file, linenum); - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); + /* Variable key always contain public key or + * certificate. In case of X.509 certificate + * x509 attribute of Key structure "found" + * can contain only "Distinguished Name" ! + */ + fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); verbose("Found matching %s key: %s", key_type(found), fp); if ((key->type == KEY_X509_RSA) || diff -ruN openssh-3.9p1+x509g4/config.h.in openssh-3.9p1+x509h/config.h.in --- openssh-3.9p1+x509g4/config.h.in 2004-08-18 09:06:01.000000000 +0300 +++ openssh-3.9p1+x509h/config.h.in 2004-08-18 09:06:01.000000000 +0300 @@ -723,6 +723,9 @@ /* Define to 1 if you have the `nsleep' function. */ #undef HAVE_NSLEEP +/* Define to 1 if you have the `OCSP_sendreq_bio' function. */ +#undef HAVE_OCSP_SENDREQ_BIO + /* Define to 1 if you have the `ogetaddrinfo' function. */ #undef HAVE_OGETADDRINFO @@ -1086,6 +1089,9 @@ /* Specify location of ssh CA root */ #undef SSHCADIR +/* Define if you don't want to validate X.509 certificates with OCSP */ +#undef SSH_OCSP_ENABLED + /* Define if your openssl library don't support Email in X.509 'Distinguished Name' */ #undef SSH_OPENSSL_DN_WITHOUT_EMAIL diff -ruN openssh-3.9p1+x509g4/configure openssh-3.9p1+x509h/configure --- openssh-3.9p1+x509g4/configure 2004-08-18 09:06:02.000000000 +0300 +++ openssh-3.9p1+x509h/configure 2004-08-18 09:06:02.000000000 +0300 @@ -309,7 +309,7 @@ # include #endif" -ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os AWK CPP RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA AR CAT KILL PERL SED ENT TEST_MINUS_S_SH SH TEST_SHELL PATH_GROUPADD_PROG PATH_USERADD_PROG MAKE_PACKAGE_SUPPORTED LOGIN_PROGRAM_FALLBACK PATH_PASSWD_PROG LD EGREP LIBWRAP LIBPAM INSTALL_SSH_RAND_HELPER SSH_PRIVSEP_USER PROG_LS PROG_NETSTAT PROG_ARP PROG_IFCONFIG PROG_JSTAT PROG_PS PROG_SAR PROG_W PROG_WHO PROG_LAST PROG_LASTLOG PROG_DF PROG_VMSTAT PROG_UPTIME PROG_IPCS PROG_TAIL INSTALL_SSH_PRNG_CMDS OPENSC_CONFIG PRIVSEP_PATH xauth_path STRIP_OPT XAUTH_PATH NROFF MANTYPE mansubdir user_path sshcadir piddir LIBOBJS LTLIBOBJS' +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT build build_cpu build_vendor build_os host host_cpu host_vendor host_os AWK CPP RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA AR CAT KILL PERL SED ENT TEST_MINUS_S_SH SH TEST_SHELL PATH_GROUPADD_PROG PATH_USERADD_PROG MAKE_PACKAGE_SUPPORTED LOGIN_PROGRAM_FALLBACK PATH_PASSWD_PROG LD EGREP LIBWRAP LIBPAM INSTALL_SSH_RAND_HELPER SSH_PRIVSEP_USER PROG_LS PROG_NETSTAT PROG_ARP PROG_IFCONFIG PROG_JSTAT PROG_PS PROG_SAR PROG_W PROG_WHO PROG_LAST PROG_LASTLOG PROG_DF PROG_VMSTAT PROG_UPTIME PROG_IPCS PROG_TAIL INSTALL_SSH_PRNG_CMDS OPENSC_CONFIG PRIVSEP_PATH xauth_path STRIP_OPT XAUTH_PATH NROFF MANTYPE mansubdir user_path sshcadir OCSP_ON OCSP_OFF piddir LIBOBJS LTLIBOBJS' ac_subst_files='' # Initialize some variables set by options. @@ -848,6 +848,7 @@ --disable-strip Disable calling strip(1) on install --disable-etc-default-login Disable using PATH from /etc/default/login no --disable-x509store Disable X.509 store + --enable-ocsp Enable OCSP validation --disable-lastlog disable use of lastlog even if detected no --disable-utmp disable use of utmp even if detected no --disable-utmpx disable use of utmpx even if detected no @@ -23318,6 +23319,299 @@ fi +ssh_ocsp="no" +# Check whether --enable-ocsp or --disable-ocsp was given. +if test "${enable_ocsp+set}" = set; then + enableval="$enable_ocsp" + + if test "x$enableval" = "xyes"; then + if test "x$ssh_x509store" = "xyes"; then + ssh_ocsp="yes" + else + { { echo "$as_me:$LINENO: error: cannot enable OCSP when x509store is disabled" >&5 +echo "$as_me: error: cannot enable OCSP when x509store is disabled" >&2;} + { (exit 1); exit 1; }; } + fi + fi + + +fi; +if test "x$ssh_ocsp" = "xyes"; then + +for ac_func in OCSP_sendreq_bio +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + + if test "${ac_cv_header_openssl_ocsp_h+set}" = set; then + echo "$as_me:$LINENO: checking for openssl/ocsp.h" >&5 +echo $ECHO_N "checking for openssl/ocsp.h... $ECHO_C" >&6 +if test "${ac_cv_header_openssl_ocsp_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ocsp_h" >&5 +echo "${ECHO_T}$ac_cv_header_openssl_ocsp_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking openssl/ocsp.h usability" >&5 +echo $ECHO_N "checking openssl/ocsp.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking openssl/ocsp.h presence" >&5 +echo $ECHO_N "checking openssl/ocsp.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: openssl/ocsp.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: openssl/ocsp.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for openssl/ocsp.h" >&5 +echo $ECHO_N "checking for openssl/ocsp.h... $ECHO_C" >&6 +if test "${ac_cv_header_openssl_ocsp_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_openssl_ocsp_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_openssl_ocsp_h" >&5 +echo "${ECHO_T}$ac_cv_header_openssl_ocsp_h" >&6 + +fi +if test $ac_cv_header_openssl_ocsp_h = yes; then + : +else + + ssh_ocsp="no" + { { echo "$as_me:$LINENO: error: OCSP header not found" >&5 +echo "$as_me: error: OCSP header not found" >&2;} + { (exit 1); exit 1; }; } + +fi + + + +else + + ssh_ocsp="no" + { echo "$as_me:$LINENO: WARNING: Cannot find OCSP functions - OCSP is disabled" >&5 +echo "$as_me: WARNING: Cannot find OCSP functions - OCSP is disabled" >&2;} + +fi +done + +fi +if test "x$ssh_ocsp" = "xyes"; then + +cat >>confdefs.h <<_ACEOF +#define SSH_OCSP_ENABLED 1 +_ACEOF + + OCSP_ON='' + OCSP_OFF='#' +else + OCSP_ON='#' + OCSP_OFF='' +fi + + + + ssh_x509dn_email="yes" if test "x$ssh_x509store" = "xyes"; then # Check for Email in X.509 'Distinguished Name' @@ -24699,6 +24993,8 @@ s,@mansubdir@,$mansubdir,;t t s,@user_path@,$user_path,;t t s,@sshcadir@,$sshcadir,;t t +s,@OCSP_ON@,$OCSP_ON,;t t +s,@OCSP_OFF@,$OCSP_OFF,;t t s,@piddir@,$piddir,;t t s,@LIBOBJS@,$LIBOBJS,;t t s,@LTLIBOBJS@,$LTLIBOBJS,;t t @@ -25239,6 +25535,7 @@ echo " TCP Wrappers support: $TCPW_MSG" echo " MD5 password support: $MD5_MSG" echo " X.509 store support: $ssh_x509store" +echo " OCSP support: $ssh_ocsp" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" diff -ruN openssh-3.9p1+x509g4/configure.ac openssh-3.9p1+x509h/configure.ac --- openssh-3.9p1+x509g4/configure.ac 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/configure.ac 2004-08-18 09:06:00.000000000 +0300 @@ -2676,6 +2676,48 @@ fi +ssh_ocsp="no" +AC_ARG_ENABLE(ocsp, + [ --enable-ocsp Enable OCSP validation], + [ + if test "x$enableval" = "xyes"; then + if test "x$ssh_x509store" = "xyes"; then + ssh_ocsp="yes" + else + AC_MSG_ERROR([cannot enable OCSP when x509store is disabled]) + fi + fi + ] +) +if test "x$ssh_ocsp" = "xyes"; then + AC_CHECK_FUNCS(OCSP_sendreq_bio, + [ + AC_CHECK_HEADER(openssl/ocsp.h, + [], + [ + ssh_ocsp="no" + AC_MSG_ERROR([OCSP header not found]) + ]) + ], + [ + ssh_ocsp="no" + AC_MSG_WARN([Cannot find OCSP functions - OCSP is disabled]) + ]) +fi +if test "x$ssh_ocsp" = "xyes"; then + AC_DEFINE_UNQUOTED( + SSH_OCSP_ENABLED, 1, + [Define if you don't want to validate X.509 certificates with OCSP]) + OCSP_ON='' + OCSP_OFF='#' +else + OCSP_ON='#' + OCSP_OFF='' +fi +AC_SUBST(OCSP_ON) +AC_SUBST(OCSP_OFF) + + ssh_x509dn_email="yes" if test "x$ssh_x509store" = "xyes"; then # Check for Email in X.509 'Distinguished Name' @@ -3048,6 +3090,7 @@ echo " TCP Wrappers support: $TCPW_MSG" echo " MD5 password support: $MD5_MSG" echo " X.509 store support: $ssh_x509store" +echo " OCSP support: $ssh_ocsp" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" diff -ruN openssh-3.9p1+x509g4/Makefile.in openssh-3.9p1+x509h/Makefile.in --- openssh-3.9p1+x509g4/Makefile.in 2004-08-18 09:06:01.000000000 +0300 +++ openssh-3.9p1+x509h/Makefile.in 2004-08-18 09:06:01.000000000 +0300 @@ -61,7 +61,7 @@ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@ INSTALL_SSH_RAND_HELPER=@INSTALL_SSH_RAND_HELPER@ -X509_OBJS=ssh-x509.o x509store.o +X509_OBJS=ssh-x509.o x509store.o ssh-ocsp.o TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-agent$(EXEEXT) scp$(EXEEXT) ssh-rand-helper${EXEEXT} sftp-server$(EXEEXT) sftp$(EXEEXT) diff -ruN openssh-3.9p1+x509g4/readconf.c openssh-3.9p1+x509h/readconf.c --- openssh-3.9p1+x509g4/readconf.c 2004-08-18 09:06:01.000000000 +0300 +++ openssh-3.9p1+x509h/readconf.c 2004-08-18 09:06:01.000000000 +0300 @@ -136,6 +136,8 @@ oCARevocationFile, oCARevocationPath, oUserCACertificateFile, oUserCACertificatePath, oUserCARevocationFile, oUserCARevocationPath, + oVAType, oVACertificateFile, + oVAOCSPResponderURL, oDeprecated, oUnsupported } OpCodes; @@ -236,6 +238,9 @@ { "usercacertificatepath", oUserCACertificatePath }, { "usercarevocationfile", oUserCARevocationFile }, { "usercarevocationpath", oUserCARevocationPath }, + { "vatype", oVAType }, + { "vacertificatefile", oVACertificateFile }, + { "vaocspresponderurl", oVAOCSPResponderURL }, { NULL, oBadOption } }; @@ -884,6 +889,32 @@ break; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + case oVAType: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + options->va.type = ssh_get_vatype_s(arg); + if (options->va.type < 0) + fatal("config error: OCSP Responder type '%.30s' in file %s line %d.", arg, filename, linenum); + break; + + case oVACertificateFile: + case oVAOCSPResponderURL: + arg = strdelim(&s); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + switch (opcode) { + default: + break; + case oVACertificateFile: + options->va.certificate_file = xstrdup(arg); break; + case oVAOCSPResponderURL: + options->va.responder_url = xstrdup(arg); break; + } + break; +#endif /*def SSH_OCSP_ENABLED*/ + case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); @@ -899,6 +930,11 @@ case oUserCARevocationFile: case oUserCARevocationPath: #endif /*def SSH_X509STORE_DISABLED*/ +#ifndef SSH_OCSP_ENABLED + case oVAType: + case oVACertificateFile: + case oVAOCSPResponderURL: +#endif /*ndef SSH_OCSP_ENABLED*/ case oUnsupported: error("%s line %d: Unsupported option \"%s\"", filename, linenum, keyword); @@ -1049,6 +1085,11 @@ options->userca.revocation_file = NULL; options->userca.revocation_path = NULL; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + options->va.type = -1; + options->va.certificate_file = NULL; + options->va.responder_url = NULL; +#endif /*def SSH_OCSP_ENABLED*/ } #ifndef SSH_X509STORE_DISABLED @@ -1231,4 +1272,9 @@ ssh_x509store_init(options); #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + if (options->va.type == -1) + options->va.type = ssh_get_default_vatype(); + ssh_set_validator(&options->va); +#endif /*def SSH_OCSP_ENABLED*/ } diff -ruN openssh-3.9p1+x509g4/readconf.h openssh-3.9p1+x509h/readconf.h --- openssh-3.9p1+x509g4/readconf.h 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/readconf.h 2004-08-18 09:06:00.000000000 +0300 @@ -146,6 +146,10 @@ /* sshd PKI(X509) user store */ X509StoreOptions userca; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + /* ssh X.509 extra validation */ + VAOptions va; +#endif /*def SSH_OCSP_ENABLED*/ } Options; diff -ruN openssh-3.9p1+x509g4/README.x509v3 openssh-3.9p1+x509h/README.x509v3 --- openssh-3.9p1+x509g4/README.x509v3 2004-03-07 13:10:20.000000000 +0200 +++ openssh-3.9p1+x509h/README.x509v3 2004-03-20 12:56:57.000000000 +0200 @@ -1,6 +1,6 @@ Roumen Petrov Sofia, Bulgaria - Sat Mar 6 2004 + Sat Mar 20 2004 How to use X.509 certificates with OpenSSH? @@ -71,6 +71,12 @@ When you see this PLEASE send a EMAIL with "X509COMPAT" lines from log files. +1.1.5.) VAType none + Specifies whether `Online Certificate Status Protocol' (OCSP) is used + to validate client X.509 certificates. Specified value is used only + when OpenSSH is build with OCSP support. See sshd_config(5) man page + for allowed values and other VA* options. + 1.2.) user files on the server Append in USER_HOME/.ssh/authorized_keys a record with following format: @@ -159,6 +165,12 @@ Note: ssh-agent use only md5 digest for X.509 certificates. +2.2.4.) VAType none + Specifies whether `Online Certificate Status Protocol' (OCSP) is used + to validate server X.509 certificates. Specified value is used only + when OpenSSH is build with OCSP support. See ssh_config(5) man page + for allowed values and other VA* options. + 3.) test X.509 certificates. @@ -254,6 +266,21 @@ - Variables related to test certificates and CA. (only in config) +3.1.2.5.) OCSP responder: + Used only when OpenSSH is build with OCSP support! + Variables related to OCSP tests. + - SSH_VA_BASEPORT + (environment or config) + test script run one or more OCSP responders at same once. First + responder listen on specified port, second on port plus one and + etc. The default is 20080. + - SSH_OPENSLL_OCSP_TMOUT=60 + (config) + Wait specified number of seconds sockets opened by OCSP responders + to close. After this test script continue with next step. + This is work around for missing SO_REUSEADDR socket option in + OpenSSL OCSP responder. + 3.1.3.) Sample commands to run tests: $ OPENSSL=/usr/local/ssl/bin/openssl make check-certs $ SSHD_PORT=1122 SSH_X509TESTS="agent blob_auth" make check-certs @@ -370,6 +397,12 @@ connections from localhost, otherwise you can see in failed message text like this: "... connection closed by remote host ..." +4.3.) fail on "starting OCSP responder(XXX) on YYY:NNNNN failed" +- Ensure sequence of about six free ports and use SSH_VA_BASEPORT + to specify first of them. +- Ensure enough timeout previous running OCSP responders to free + ports. Increase value of SSH_OPENSLL_OCSP_TMOUT in test config file. + 5.) FAQ diff -ruN openssh-3.9p1+x509g4/servconf.c openssh-3.9p1+x509h/servconf.c --- openssh-3.9p1+x509g4/servconf.c 2004-08-18 09:06:01.000000000 +0300 +++ openssh-3.9p1+x509h/servconf.c 2004-08-18 09:06:01.000000000 +0300 @@ -133,6 +133,11 @@ options->ca.revocation_file = NULL; options->ca.revocation_path = NULL; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + options->va.type = -1; + options->va.certificate_file = NULL; + options->va.responder_url = NULL; +#endif /*def SSH_OCSP_ENABLED*/ /* Needs to be accessable in many places */ use_privsep = -1; @@ -279,6 +284,11 @@ options->ca.revocation_path = _PATH_CA_REVOCATION_PATH; ssh_x509store_addlocations(&options->ca); #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + if (options->va.type == -1) + options->va.type = ssh_get_default_vatype(); + ssh_set_validator(&options->va); +#endif /*def SSH_OCSP_ENABLED*/ /* Turn privilege separation on by default */ if (use_privsep == -1) @@ -324,6 +334,8 @@ sAllowedClientCertPurpose, sCACertificateFile, sCACertificatePath, sCARevocationFile, sCARevocationPath, + sVAType, sVACertificateFile, + sVAOCSPResponderURL, sDeprecated, sUnsupported } ServerOpCodes; @@ -430,6 +442,9 @@ { "cacertificatepath", sCACertificatePath }, { "carevocationfile", sCARevocationFile }, { "carevocationpath", sCARevocationPath }, + { "vatype", sVAType }, + { "vacertificatefile", sVACertificateFile }, + { "vaocspresponderurl", sVAOCSPResponderURL }, { NULL, sBadOption } }; @@ -1031,6 +1046,32 @@ break; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + case sVAType: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + options->va.type = ssh_get_vatype_s(arg); + if (options->va.type < 0) + fatal("config error: OCSP Responder type '%.30s' in file %s line %d.", arg, filename, linenum); + break; + + case sVACertificateFile: + case sVAOCSPResponderURL: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + switch (opcode) { + default: + break; + case sVACertificateFile: + options->va.certificate_file = xstrdup(arg); break; + case sVAOCSPResponderURL: + options->va.responder_url = xstrdup(arg); break; + } + break; +#endif /*def SSH_OCSP_ENABLED*/ + case sDeprecated: logit("%s line %d: Deprecated option %s", filename, linenum, arg); @@ -1044,6 +1085,11 @@ case sCARevocationFile: case sCARevocationPath: #endif /*def SSH_X509STORE_DISABLED*/ +#ifndef SSH_OCSP_ENABLED + case sVAType: + case sVACertificateFile: + case sVAOCSPResponderURL: +#endif /*ndef SSH_OCSP_ENABLED*/ case sUnsupported: logit("%s line %d: Unsupported option %s", filename, linenum, arg); diff -ruN openssh-3.9p1+x509g4/servconf.h openssh-3.9p1+x509h/servconf.h --- openssh-3.9p1+x509g4/servconf.h 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/servconf.h 2004-08-18 09:06:00.000000000 +0300 @@ -166,6 +166,10 @@ /* sshd PKI(X509) global store */ X509StoreOptions ca; #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_OCSP_ENABLED + /* ssh X.509 extra validation */ + VAOptions va; +#endif /*def SSH_OCSP_ENABLED*/ } ServerOptions; void initialize_server_options(ServerOptions *); diff -ruN openssh-3.9p1+x509g4/ssh_config openssh-3.9p1+x509h/ssh_config --- openssh-3.9p1+x509g4/ssh_config 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/ssh_config 2004-08-18 09:06:00.000000000 +0300 @@ -45,3 +45,4 @@ # UserCACertificatePath ~/.ssh/crt # UserCARevocationFile ~/.ssh/ca-bundle.crl # UserCARevocationPath ~/.ssh/crl +# VAType none diff -ruN openssh-3.9p1+x509g4/ssh_config.0 openssh-3.9p1+x509h/ssh_config.0 --- openssh-3.9p1+x509g4/ssh_config.0 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/ssh_config.0 2004-08-18 09:06:00.000000000 +0300 @@ -498,6 +498,29 @@ Specifies a file to use for the user host key database instead of $HOME/.ssh/known_hosts. + VACertificateFile + File with X.509 certificates in PEM format concatenated together. + In use when VAType is set to ``ocspspec''. The default value is + `' (empty). Certificates from that file explicitly trust `OCSP + Responder' public key. They are used as trusted certificates in + addition to certificates from CACertificateFile , + CACertificatePath , UserCACertificateFile and + UserCACertificatePath to verify responder certificate. + + VAType Specifies whether `Online Certificate Status Protocol' (OCSP) is + used to validate X.509 certificates. Accepted values are case + insensitive: + o `none' : do not use OCSP to validate certificates; + o `ocspcert' : validate only certificates that specify `OCSP + Service Locator' URL; + o `ocspspec' : use specified in the configuration `OCSP + Responder' to validate all certificates. + The default is ``none''. + + VAOCSPResponderURL + `Access Location' / `OCSP Service Locator' URL of the OCSP + provider. In use when VAType is set to ``ocspspec''. + VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. If this option is set to ``yes'', the client diff -ruN openssh-3.9p1+x509g4/ssh_config.5 openssh-3.9p1+x509h/ssh_config.5 --- openssh-3.9p1+x509g4/ssh_config.5 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/ssh_config.5 2004-08-18 09:06:00.000000000 +0300 @@ -842,6 +842,58 @@ Specifies a file to use for the user host key database instead of .Pa $HOME/.ssh/known_hosts . +.It Cm VACertificateFile +File with X.509 certificates in PEM format concatenated together. +In use when +.Cm VAType +is set to +.Dq ocspspec . +The default value is +.Sq +.. +(empty). +Certificates from that file explicitly trust +.Sq "OCSP Responder" +public key. +They are used as trusted certificates in addition to certificates from +.Cm CACertificateFile +, +.Cm CACertificatePath +, +.Cm UserCACertificateFile +and +.Cm UserCACertificatePath +to verify responder certificate. +.It Cm VAType +Specifies whether +.Sq "Online Certificate Status Protocol" +(OCSP) is used to validate X.509 certificates. +Accepted values are case insensitive: +.Bl -bullet -compact +.It +.Sq none +: do not use OCSP to validate certificates; +.It +.Sq ocspcert +: validate only certificates that specify +.Sq "OCSP Service Locator" +URL; +.It +.Sq ocspspec +: use specified in the configuration +.Sq "OCSP Responder" +to validate all certificates. +.El +The default is +.Dq none . +.It Cm VAOCSPResponderURL +.Sq "Access Location" +/ +.Sq "OCSP Service Locator" +URL of the OCSP provider. In use when +.Cm VAType +is set to +.Dq ocspspec . .It Cm VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. diff -ruN openssh-3.9p1+x509g4/sshd_config openssh-3.9p1+x509h/sshd_config --- openssh-3.9p1+x509g4/sshd_config 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/sshd_config 2004-08-18 09:06:00.000000000 +0300 @@ -50,6 +50,15 @@ # or have symbolic links to them of this form. #CARevocationPath /etc/ssh/ca/crl +# SSH can use "Online Certificate Status Protocol"(OCSP) +# to validate certificate. Set VAType to +# - none : do not use OCSP to validate certificates; +# - ocspcert: validate only certificates that specify `OCSP +# Service Locator' URL; +# - ocspspec: use specified in the configuration 'OCSP Responder' +# to validate all certificates. +#VAType none + # Lifetime and size of ephemeral version 1 server key #KeyRegenerationInterval 1h #ServerKeyBits 768 diff -ruN openssh-3.9p1+x509g4/sshd_config.0 openssh-3.9p1+x509h/sshd_config.0 --- openssh-3.9p1+x509g4/sshd_config.0 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/sshd_config.0 2004-08-18 09:06:00.000000000 +0300 @@ -443,6 +443,28 @@ taining any corruption within the unprivileged processes. The default is ``yes''. + VACertificateFile + File with X.509 certificates in PEM format concatenated together. + In use when VAType is set to ``ocspspec''. The default value is + `' (empty). Certificates from that file explicitly trust `OCSP + Responder' public key. They are used as trusted certificates in + addition to certificates from CACertificateFile and + CACertificatePath to verify responder certificate. + + VAType Specifies whether `Online Certificate Status Protocol' (OCSP) is + used to validate X.509 certificates. Accepted values are case + insensitive: + o `none' : do not use OCSP to validate certificates; + o `ocspcert' : validate only certificates that specify `OCSP + Service Locator' URL; + o `ocspspec' : use specified in the configuration `OCSP + Responder' to validate all certificates. + The default is ``none''. + + VAOCSPResponderURL + `Access Location' / `OCSP Service Locator' URL of the OCSP + provider. In use when VAType is set to ``ocspspec''. + X11DisplayOffset Specifies the first display number available for sshd's X11 for- warding. This prevents sshd from interfering with real X11 diff -ruN openssh-3.9p1+x509g4/sshd_config.5 openssh-3.9p1+x509h/sshd_config.5 --- openssh-3.9p1+x509g4/sshd_config.5 2004-08-18 09:06:00.000000000 +0300 +++ openssh-3.9p1+x509h/sshd_config.5 2004-08-18 09:06:00.000000000 +0300 @@ -734,6 +734,54 @@ escalation by containing any corruption within the unprivileged processes. The default is .Dq yes . +.It Cm VACertificateFile +File with X.509 certificates in PEM format concatenated together. +In use when +.Cm VAType +is set to +.Dq ocspspec . +The default value is +.Sq +.. +(empty). +Certificates from that file explicitly trust +.Sq "OCSP Responder" +public key. +They are used as trusted certificates in addition to certificates from +.Cm CACertificateFile +and +.Cm CACertificatePath +to verify responder certificate. +.It Cm VAType +Specifies whether +.Sq "Online Certificate Status Protocol" +(OCSP) is used to validate X.509 certificates. +Accepted values are case insensitive: +.Bl -bullet -compact +.It +.Sq none +: do not use OCSP to validate certificates; +.It +.Sq ocspcert +: validate only certificates that specify +.Sq "OCSP Service Locator" +URL; +.It +.Sq ocspspec +: use specified in the configuration +.Sq "OCSP Responder" +to validate all certificates. +.El +The default is +.Dq none . +.It Cm VAOCSPResponderURL +.Sq "Access Location" +/ +.Sq "OCSP Service Locator" +URL of the OCSP provider. In use when +.Cm VAType +is set to +.Dq ocspspec . .It Cm X11DisplayOffset Specifies the first display number available for .Nm sshd Ns 's diff -ruN openssh-3.9p1+x509g4/ssh-ocsp.c openssh-3.9p1+x509h/ssh-ocsp.c --- openssh-3.9p1+x509g4/ssh-ocsp.c 1970-01-01 02:00:00.000000000 +0200 +++ openssh-3.9p1+x509h/ssh-ocsp.c 2004-03-21 11:37:25.000000000 +0200 @@ -0,0 +1,1020 @@ +/* + * Copyright (c) 2004 Roumen Petrov. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "x509store.h" + +#ifdef SSH_OCSP_ENABLED +#if 0 +# define SSH_WITH_SSLOCSP +#endif + +#include "xmalloc.h" +#include "log.h" +#include +#include +#include +#ifdef SSH_WITH_SSLOCSP +# include +#endif + + +static VAOptions va = { SSHVA_NONE, NULL, NULL }; + +typedef struct va_type_map_s va_type_map; +struct va_type_map_s { + int id; + const char* code; +}; + +static va_type_map sshva_type_map[] = { + { SSHVA_NONE , "none" }, + { SSHVA_OCSP_CERT, "ocspcert" }, + { SSHVA_OCSP_SPEC, "ocspspec" }, +}; + + +int +ssh_get_default_vatype(void) { + return(SSHVA_NONE); +} + + +int +ssh_get_vatype_s(const char* type) { + int k, n; + + if (type == NULL) return(-1); + + n = sizeof(sshva_type_map) / sizeof(sshva_type_map[0]); + for (k = 0; k < n; k++) { + va_type_map *p = sshva_type_map + k; +logit("RUMEN: p->code=%s", p->code); + if (strcasecmp(type, p->code) == 0) return(p->id); + } + + return(-1); +} + + +static void +ssh_set_vatype(int type) { + switch (type) { + case SSHVA_NONE: + case SSHVA_OCSP_CERT: + case SSHVA_OCSP_SPEC: + va.type = type; + break; + default: + fatal("ssh_set_vatype: invalid type %d", type); + break; + } +} + + +void +ssh_set_validator(const VAOptions *_va) { + if (va.certificate_file != NULL) { + xfree((void*)va.certificate_file); + va.certificate_file = NULL; + } + if (va.responder_url != NULL) { + xfree((void*)va.responder_url); + va.responder_url = NULL; + } + if (_va == NULL) { + debug("ssh_set_validator: NULL options - set vatype to none"); + ssh_set_vatype(SSHVA_NONE); + return; + } + + ssh_set_vatype(_va->type); /*fatal on error*/ + if (_va->certificate_file != NULL) { + switch(va.type) { + case SSHVA_NONE: + case SSHVA_OCSP_CERT: + debug("ssh_set_validator: ingnore certificate file"); + break; + case SSHVA_OCSP_SPEC: + va.certificate_file = xstrdup(_va->certificate_file); /*fatal on error*/ + break; + } + } + switch(va.type) { + case SSHVA_NONE: + case SSHVA_OCSP_CERT: + debug("ssh_set_validator: ingnore responder url"); + break; + case SSHVA_OCSP_SPEC: + if (_va->responder_url == NULL) { + fatal("ssh_set_validator: responder url is mandatory"); + } + va.responder_url = xstrdup(_va->responder_url); /*fatal on error*/ + break; + } +} + + +static char* +openssl_errormsg(char *buf, size_t len) { + ERR_error_string_n(ERR_get_error(), buf, len); + + /* clear rest of errors in OpenSSL "error buffer" */ + ERR_clear_error(); + return(buf); +} + + +static char* +ssh_ASN1_GENERALIZEDTIME_2_string(ASN1_GENERALIZEDTIME *asn1_time) { + BIO *bio; + int k; + char *p = NULL; + + if (asn1_time == NULL) { + error("ssh_ASN1_GENERALIZEDTIME_2_string: asn1_time is NULL"); + return(NULL); + } + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + error("ssh_ASN1_GENERALIZEDTIME_2_string: BIO_new fail"); + return(NULL); + } + + ASN1_GENERALIZEDTIME_print(bio, asn1_time); + BIO_flush(bio); + + k = BIO_pending(bio); + p = xmalloc(k + 1); /*fatal on error*/ + k = BIO_read(bio, p, k); + p[k] = '\0'; + BIO_free_all(bio); + return(p); +} + + +static STACK_OF(X509)* +ssh_load_x509certs(const char *certs_file, const char* certs_descrip) { + STACK_OF(X509) *ret_certs = NULL; + BIO *fbio = NULL; + + if (certs_file == NULL) { + error("ssh_load_x509certs: file is NULL"); + goto exit; + } + + ret_certs = sk_X509_new_null(); + if (ret_certs == NULL) { + error("ssh_load_x509certs: sk_X509_new_null fail"); + goto exit; + } + + fbio = BIO_new(BIO_s_file()); + if (fbio == NULL) { + error("ssh_load_x509certs: BIO_new fail"); + goto exit; + } + + if (BIO_read_filename(fbio, certs_file) <= 0) { + char ebuf[512]; + error("ssh_load_x509certs:" + " File description is '%.512s'." + " BIO_read_filename(..., '%.512s')" + " fail with errormsg='%.512s'" + , certs_descrip + , certs_file + , openssl_errormsg(ebuf, sizeof(ebuf))); + goto exit; + } + + { + int k; + STACK_OF(X509_INFO) *data; + + data = PEM_X509_INFO_read_bio(fbio, NULL, NULL, NULL); + if (data == NULL) { + error("ssh_load_x509certs: no data."); + goto exit; + } + + for (k = 0; k < sk_X509_INFO_num(data); k++) { + X509_INFO *xi = sk_X509_INFO_value(data, k); + if (xi->x509) { + sk_X509_push(ret_certs, xi->x509); + xi->x509 = NULL; + } + } + sk_X509_INFO_pop_free(data, X509_INFO_free); + } + +exit: + if (fbio != NULL) BIO_free_all(fbio); + if (ret_certs != NULL) { + debug3("ssh_load_x509certs: return %d certs", (int)sk_X509_num(ret_certs)); + } else { + debug("ssh_load_x509certs: return NULL"); + } + return(ret_certs); +} + + +static int/*bool*/ +ssh_ocspreq_addcert( + X509 *cert, + X509_STORE* x509store, + OCSP_REQUEST *req, + STACK_OF(OCSP_CERTID) *ids, + STACK *subjs +) { + X509 *issuer = NULL; + OCSP_CERTID *id = NULL; + char subj[512]; + + if (cert == NULL) { + error("ssh_ocspreq_addcert: cert is NULL"); + return(0); + } + if (x509store == NULL) { + error("ssh_ocspreq_addcert: x509store is NULL"); + return(0); + } + if (req == NULL) { + error("ssh_ocspreq_addcert: req is NULL"); + return(0); + } + if (ids == NULL) { + error("ssh_ocspreq_addcert: ids is NULL"); + return(0); + } + if (subjs == NULL) { + error("ssh_ocspreq_addcert: subjs is NULL"); + return(0); + } + + { + X509_OBJECT xobj; + memset(&xobj, 0, sizeof(xobj)); + if (ssh_x509store_lookup(x509store, X509_LU_X509, X509_get_issuer_name(cert), &xobj) > 0) { + issuer = xobj.data.x509; + } + X509_OBJECT_free_contents(&xobj); + } + if (issuer == NULL) { + error("ssh_ocspreq_addcert: cannot found issuer certificate"); + return(0); + } + + id = OCSP_cert_to_id(NULL, cert, issuer); + if (id == NULL) { + error("ssh_ocspreq_addcert: OCSP_cert_to_id fail"); + return(0); + } + + if (!OCSP_request_add0_id(req, id)) { + error("ssh_ocspreq_addcert: OCSP_request_add0_id fail"); + return(0); + } + if (!sk_OCSP_CERTID_push(ids, id)) { + error("ssh_ocspreq_addcert: sk_OCSP_CERTID_push fail"); + return(0); + } + X509_NAME_oneline(X509_get_subject_name(cert), subj, sizeof(subj)); + if (!sk_push(subjs, subj)) { + error("ssh_ocspreq_addcert: sk_push(..., subj) fail"); + return(0); + } + + return(1); +} + + +struct ssh_ocsp_conn_s { + const char *url; + +#ifdef SSH_WITH_SSLOCSP + int use_ssl; +#endif + /*pointers inside data buffer*/ + /*const*/ char *host; + const char *port; + /*const*/ char *path; + + /*data buffer to hold all connection info*/ + char *data; +}; + +typedef struct ssh_ocsp_conn_s ssh_ocsp_conn; + + +static void +ssh_ocsp_conn_free(ssh_ocsp_conn **pconn) { + ssh_ocsp_conn *conn = *pconn; + + if (conn == NULL) return; + *pconn = NULL; + + if (conn->data != NULL) xfree(conn->data); + if (conn->url != NULL) xfree((void*)conn->url ); + xfree(conn); +} + + +static int/*bool*/ +ssh_ocsp_set_protocol(ssh_ocsp_conn *conn, const char *protocol) { + if (strcmp(protocol, "http") == 0) { +#ifdef SSH_WITH_SSLOCSP + conn->use_ssl = 0; +#endif + return(1); + } + +#ifdef SSH_WITH_SSLOCSP + if (strcmp(protocol, "https") == 0) { + conn->use_ssl = 1; + return(1); + } +#endif + +#ifdef SSH_WITH_SSLOCSP + conn->use_ssl = -1; +#endif + return(0); +} + + +static ssh_ocsp_conn* +ssh_ocsp_conn_new(const char *url) { + ssh_ocsp_conn *conn = NULL; + char *p = NULL; + char *q = NULL; + + if (url == NULL) { + error("ssh_ocsp_conn_new: url is NULL"); + return(NULL); + } + + conn = xmalloc(sizeof(*conn)); /*fatal on error*/ + memset(conn, 0, sizeof(*conn)); + + conn->url = xstrdup(url); /*fatal on error*/ + conn->data = xstrdup(url); /*fatal on error*/ + + /* chech for protocol */ + p = conn->data; + q = strchr(p, ':'); + if (q == NULL) goto error; + *q = '\x0'; + + if (!ssh_ocsp_set_protocol(conn, p)) { + error("ssh_ocsp_conn_new:" + " unsupported protocol '%.16s'" + , p); + goto error; + } + + p = q; + if (*++p != '/') { /*this symbol is inside data */ + error("ssh_ocsp_conn_new: expected first slash," + " got char with code %d" + , (int)*p); + goto error; + } + if (*++p != '/') { /*this symbol is inside data */ + error("ssh_ocsp_conn_new: expected second slash," + " got char with code %d" + , (int)*p); + goto error; + } + + /* chech for host and port */ + if (*++p == '\x0') { + error("ssh_ocsp_conn_new: missing host in url '%.512s'", url); + goto error; + } + conn->host = p; + q = strchr(p, '/'); + if (q != NULL) { + *q = '\x0'; + /* q+1 might point to path */ + } + /*else q is NULL !!!*/ + + /* chech for port */ + p = strrchr(conn->host, ':'); + if (p != NULL) { + *p = '\x0'; + if (*++p != '\x0') conn->port = p; + } + if (conn->port == NULL) { +#ifdef SSH_WITH_SSLOCSP + conn->port = conn->use_ssl ? "443" : "80"; +#else + conn->port = "80"; +#endif + } + + /* chech for path */ + p = q; + if (p == NULL) goto exit; + if (*++p == '\x0') goto exit; + conn->path = p; + +exit: + return(conn); +error: + ssh_ocsp_conn_free(&conn); + goto exit; +} + + +static OCSP_RESPONSE* +ssh_ocsp_get_response(const ssh_ocsp_conn *conn, OCSP_REQUEST *req) { + OCSP_RESPONSE *resp = NULL; + BIO *bio_conn = NULL; +#ifdef SSH_WITH_SSLOCSP + SSL_CTX *ctx = NULL; +#endif + + if (conn == NULL) { + error("ssh_ocsp_get_response: conn is NULL"); + return(NULL); + } + if (req == NULL) { + error("ssh_ocsp_get_response: req is NULL"); + return(NULL); + } + +#ifndef OPENSSL_NO_SOCK + bio_conn = BIO_new_connect(conn->host); + if (bio_conn == NULL) { + char ebuf[512]; + error("ssh_ocsp_get_response:" + " BIO_new_connect fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + goto exit; + } +#else + error("ssh_ocsp_get_response: sockets are not supported in OpenSSL"); + goto exit; +#endif + if (conn->port != NULL) { + BIO_set_conn_port(bio_conn, conn->port); + } + +#ifdef SSH_WITH_SSLOCSP + if (conn->use_ssl == 1) { + BIO *bio_sslconn; +#if !defined(OPENSSL_NO_SSL2) && !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv23_client_method()); +#elif !defined(OPENSSL_NO_SSL3) + ctx = SSL_CTX_new(SSLv3_client_method()); +#elif !defined(OPENSSL_NO_SSL2) + ctx = SSL_CTX_new(SSLv2_client_method()); +#else + error("ssh_ocsp_get_response: SSL is disabled"); + goto exit; +#endif + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + bio_sslconn = BIO_new_ssl(ctx, 1); + bio_conn = BIO_push(bio_sslconn, bio_conn); + } +#endif /*def SSH_WITH_SSLOCSP*/ + + if (BIO_do_connect(bio_conn) <= 0) { + char ebuf[512]; + error("ssh_ocsp_get_response:" + " BIO_do_connect fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + goto exit; + } + + resp = OCSP_sendreq_bio(bio_conn, conn->path, req); + if (resp == NULL) { + char ebuf[512]; + error("ssh_ocsp_get_response:" + " OCSP_sendreq_bio fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + } + +exit: + if (bio_conn != NULL) BIO_free_all(bio_conn); +#ifdef SSH_WITH_SSLOCSP + if (ctx != NULL) SSL_CTX_free(ctx); +#endif + + return(resp); +} + + +static OCSP_BASICRESP* +ssh_ocsp_get_basicresp( + OCSP_REQUEST *req, + OCSP_RESPONSE *resp, + STACK_OF(X509) *vacrts, + X509_STORE *x509store +) { + OCSP_BASICRESP *br = NULL; + unsigned long basic_verify_flags = 0/*NO:OCSP_NOEXPLICIT*/; + int flag; + + if (req == NULL) { + error("ssh_ocsp_get_basicresp: req is NULL"); + return(NULL); + } + if (resp == NULL) { + error("ssh_ocsp_get_basicresp: resp is NULL"); + return(NULL); + } + if (x509store == NULL) { + error("ssh_ocsp_get_basicresp: x509store is NULL"); + return(NULL); + } + + br = OCSP_response_get1_basic(resp); + if (br == NULL) { + char ebuf[512]; + error("ssh_ocsp_get_basicresp: " + " OCSP_response_get1_basic fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + return(NULL); + } + + flag = OCSP_check_nonce(req, br); + if (flag <= 0) { + if (flag == -1) { + logit("ssh_ocsp_get_basicresp: WARNING - no nonce in response"); + } else { + char ebuf[512]; + error("ssh_ocsp_get_basicresp: " + " OCSP_check_nonce fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + goto error; + } + } + +#ifdef SSHOCSPTEST +{ +int k; +logit("ssh_ocsp_get_basicresp: VA certs num=%d", sk_X509_num(vacrts)); +for (k = 0; k < sk_X509_num(vacrts); k++) { + char buf[512]; + X509 *x = sk_X509_value(vacrts, k); + X509_NAME_oneline(X509_get_subject_name(x), buf, sizeof(buf)); + logit("ssh_ocsp_get_basicresp: VA[%d] subject='%.512s'", k, buf); +} +} +#endif /*def SSHOCSPTEST*/ + +/* + * RFC2560: + * ... + * All definitive response messages SHALL be digitally signed. The key + * used to sign the response MUST belong to one of the following: + * + * -- the CA who issued the certificate in question + * -- a Trusted Responder whose public key is trusted by the requester + * -- a CA Designated Responder (Authorized Responder) who holds a + * specially marked certificate issued directly by the CA, indicating + * that the responder may issue OCSP responses for that CA + * ... + * + * TODO: to check OpenSLL implementation + */ + if ((vacrts == NULL) || (sk_X509_num(vacrts) <= 0)) { + flag = -1; + } else { + /* + * With flag OCSP_TRUSTOTHER: + * - we never get error 'without missing ocspsigning + * usage' for VA certificate !!! + * Without flag OCSP_TRUSTOTHER: + * - we can get OCSP_basic_verify error "root ca not trusted" + */ +#if 0 + flag = OCSP_basic_verify(br, vacrts, x509store, basic_verify_flags | OCSP_TRUSTOTHER); +#else + flag = OCSP_basic_verify(br, vacrts, x509store, basic_verify_flags); +#endif + } + if (flag < 0) { + flag = OCSP_basic_verify(br, NULL, x509store, basic_verify_flags); + } + if (flag <= 0) { + char ebuf[512]; + error("ssh_ocsp_get_basicresp:" + " flag=%d" + " OCSP_basic_verify fail with errormsg='%.512s'" + , flag + , openssl_errormsg(ebuf, sizeof(ebuf))); + goto error; + } + + debug3("ssh_ocsp_get_basicresp: OK"); + return(br); + +error: + debug3("ssh_ocsp_get_basicresp: FAIL"); + if (br != NULL) OCSP_BASICRESP_free(br); + return(NULL); +} + + +/* + * Method return: + * 1 - all cert.-s are good + * -1 - error or one cert. with status unknow + * 0 - otherwise, i.e. at least one cert. is revoked and rest are good + */ +static int +ssh_ocsp_check_validity( + OCSP_REQUEST *req, + OCSP_BASICRESP *br, + STACK_OF(OCSP_CERTID) *ids, + STACK *subjs +) { + int ret = 1; + /* Maximum leeway in validity period: default 5 minutes */ + const long nsec = (5 * 60); + const long maxage = -1; + + int k; + int status, reason; + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + if (req == NULL) { + error("ssh_ocsp_check_validity: req is NULL"); + return(-1); + } + if (br == NULL) { + error("ssh_ocsp_check_validity: br is NULL"); + return(-1); + } + if (sk_OCSP_CERTID_num(ids) <= 0) { + error("ssh_ocsp_check_validity:" + " number of ids is %d" + , k); + return(-1); + } + if (sk_OCSP_CERTID_num(subjs) <= 0) { + error("ssh_ocsp_check_validity:" + " number of subjs is %d" + , sk_OCSP_CERTID_num(subjs)); + return(-1); + } + if (sk_OCSP_CERTID_num(ids) != sk_OCSP_CERTID_num(subjs)) { + error("ssh_ocsp_check_validity:" + " ids(%d) != subjs(%d)" + , sk_OCSP_CERTID_num(ids) + , sk_OCSP_CERTID_num(subjs)); + return(-1); + } + + for (k = 0; k < sk_OCSP_CERTID_num(ids); k++) { + OCSP_CERTID *id = sk_OCSP_CERTID_value(ids, k); + + if (get_log_level() >= SYSLOG_LEVEL_DEBUG3) { + char *subject = sk_value(subjs, k); + debug3("ssh_ocsp_check_validity:" + " cert[%d]='%.512s'" + , k, subject); + } + + if (!OCSP_resp_find_status( + br, id, &status, &reason, + &rev, &thisupd, &nextupd) + ) { + ret = -1; + error("ssh_ocsp_check_validity: cannot found status"); + break; + } + + if (!OCSP_check_validity(thisupd, nextupd, nsec, maxage)) { + char ebuf[512]; + ret = -1; + logit("ssh_ocsp_check_validity: " + " WARNING-invalid status time." + " OCSP_check_validity fail with errormsg='%.512s'" + , openssl_errormsg(ebuf, sizeof(ebuf))); + break; + } + debug("ssh_ocsp_check_validity: status=%.32s", OCSP_cert_status_str(status)); + if (get_log_level() >= SYSLOG_LEVEL_DEBUG3) { + char *p = ssh_ASN1_GENERALIZEDTIME_2_string(thisupd); + debug3("ssh_ocsp_check_validity: This Update=%.128s", p); + xfree(p); + if (nextupd != NULL) { + p = ssh_ASN1_GENERALIZEDTIME_2_string(nextupd); + debug3("ssh_ocsp_check_validity: Next Update=%.128s", p); + xfree(p); + } + } + + if (status == V_OCSP_CERTSTATUS_GOOD) continue; + + if (status != V_OCSP_CERTSTATUS_REVOKED) { + ret = -1; + error("ssh_ocsp_check_validity: unknow certificate status"); + break; + } + + ret = 0; + if (get_log_level() >= SYSLOG_LEVEL_DEBUG3) { + char *p = ssh_ASN1_GENERALIZEDTIME_2_string(rev); + debug3("ssh_ocsp_check_validity: Revocation Time=%.128s", p); + xfree(p); + if (reason != -1) { + debug3("ssh_ocsp_check_validity:" + " Revocation Reason='%.128s'" + , OCSP_crl_reason_str(reason)); + } + } + break; + } + debug3("ssh_ocsp_check_validity: return %d", ret); + return(ret); +} + + +static int +ssh_ocsp_validate( + X509 *cert, + X509_STORE *x509store, + const ssh_ocsp_conn *ocsp +) { + int ret = -1; + int add_nonce = 0; + + STACK_OF(X509) *vacrts = NULL; + OCSP_REQUEST *req = OCSP_REQUEST_new(); + STACK_OF(OCSP_CERTID) *ids = sk_OCSP_CERTID_new_null(); + STACK *subjs = sk_new_null(); + OCSP_RESPONSE *resp = NULL; + OCSP_BASICRESP *br = NULL; + + if ((va.type == SSHVA_OCSP_SPEC) && + (va.certificate_file != NULL)) { + vacrts = ssh_load_x509certs(va.certificate_file, "'OCSP Responder' trusted certificates"); + if (vacrts == NULL) goto exit; + debug("ssh_ocsp_validate: VA certs num=%d", sk_X509_num(vacrts)); + } + + if (!ssh_ocspreq_addcert(cert, x509store, req, ids, subjs)) { + goto exit; + } + + if (req && add_nonce) { + OCSP_request_add1_nonce(req, NULL, -1); + } + + resp = ssh_ocsp_get_response(ocsp, req); + if (resp == NULL) goto exit; + + { /*check OCSP response status*/ + int flag = OCSP_response_status(resp); + if (flag != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + error("ssh_ocsp_validate:" + " responder error=%d(%.256s)" + , flag + , OCSP_response_status_str((long/*???*/)flag)); + goto exit; + } + } + + br = ssh_ocsp_get_basicresp(req, resp, vacrts, x509store); + if (br == NULL) goto exit; + + ret = ssh_ocsp_check_validity(req, br, ids, subjs); + +exit: + if (br != NULL) OCSP_BASICRESP_free(br); + if (resp != NULL) OCSP_RESPONSE_free(resp); + if (subjs != NULL) sk_free(subjs); + if (ids != NULL) sk_OCSP_CERTID_free(ids); + if (req != NULL) OCSP_REQUEST_free(req); + if (vacrts != NULL) sk_X509_pop_free(vacrts, X509_free); + + return(ret); +} + + +static AUTHORITY_INFO_ACCESS* +ssh_aia_get(X509_EXTENSION *ext) { + X509V3_EXT_METHOD *method = NULL; + void *ext_str = NULL; + unsigned char *p; + int len; + + if (ext == NULL) { + error("ssh_aia_get: ext is NULL"); + return(NULL); + } + + method = X509V3_EXT_get(ext); + if (method == NULL) { + debug("ssh_aia_get: cannot get method"); + return(NULL); + } + + p = ext->value->data; + len = ext->value->length; + if (method->it) { + ext_str = ASN1_item_d2i(NULL, &p, len, ASN1_ITEM_ptr(method->it)); + } else { + ext_str = method->d2i(NULL, &p, len); + } + if (ext_str == NULL) { + debug("ssh_aia_get: null ext_str!"); + return(NULL); + } + + return((AUTHORITY_INFO_ACCESS*)ext_str); +} + + +static void +ssh_aia_free(X509_EXTENSION *ext, AUTHORITY_INFO_ACCESS* aia) { + X509V3_EXT_METHOD *method = NULL; + + if (ext == NULL) { + error("ssh_aia_free: ext is NULL"); + return; + } + + method = X509V3_EXT_get(ext); + if (method == NULL) return; + + if (method->it) { + ASN1_item_free((void*)aia, ASN1_ITEM_ptr(method->it)); + } else { + method->ext_free(aia); + } +} + + +static int +ssh_aiaocsp_validate( + X509 *cert, + X509_STORE *x509store, + AUTHORITY_INFO_ACCESS *aia, + int *has_ocsp_url +) { + int ret = -1; + int k; + if (has_ocsp_url == NULL) { + fatal("ssh_aiaocsp_validate: has_ocsp_url is NULL"); + return(-1); /*;-)*/ + } + + *has_ocsp_url = 0; + for (k = 0; k < sk_ACCESS_DESCRIPTION_num(aia); k++) { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(aia, k); + GENERAL_NAME *gn; + ASN1_IA5STRING *uri; + ssh_ocsp_conn *conn; + + if (OBJ_obj2nid(ad->method) != + NID_id_pkix_OCSP_serviceLocator) continue; + + gn = ad->location; +#if 0 +{ +BIO *bio = BIO_new_fp(stderr, BIO_NOCLOSE); +if (bio != NULL) { + BIO_puts(bio, "gn->type:"); + switch (gn->type) { + case GEN_OTHERNAME : BIO_puts(bio, "GEN_OTHERNAME"); break; + case GEN_EMAIL : BIO_puts(bio, "GEN_EMAIL" ); break; + case GEN_DNS : BIO_puts(bio, "GEN_DNS" ); break; + case GEN_X400 : BIO_puts(bio, "GEN_X400" ); break; + case GEN_DIRNAME : BIO_puts(bio, "GEN_DIRNAME" ); break; + case GEN_EDIPARTY : BIO_puts(bio, "GEN_EDIPARTY" ); break; + case GEN_URI : BIO_puts(bio, "GEN_URI" ); break; + case GEN_IPADD : BIO_puts(bio, "GEN_IPADD" ); break; + case GEN_RID : BIO_puts(bio, "GEN_RID" ); break; + default : BIO_puts(bio, "[unsupported]"); break; + } + BIO_puts(bio, "\n"); + BIO_free(bio); +} +} +#endif + if (gn->type != GEN_URI) continue; + + uri = gn->d.uniformResourceIdentifier; + *has_ocsp_url = 1; + + conn = ssh_ocsp_conn_new((const char*)uri->data); + if (conn == NULL) { + debug("ssh_aiaocsp_validate: cannot create ocsp connection"); + continue; + } + ret = ssh_ocsp_validate(cert, x509store, conn); + ssh_ocsp_conn_free(&conn); + + if (ret >= 0) break; + } + + return(*has_ocsp_url ? ret : 1); +} + + +static int +ssh_x509_validate4cert(X509 *cert, X509_STORE *x509store) { + int found = 0; + int ret = -1; + int loc = -1; + + if (cert == NULL) return(0); + + for ( loc = X509_get_ext_by_NID(cert, NID_info_access, loc); + loc >= 0; + loc = X509_get_ext_by_NID(cert, NID_info_access, loc) + ) { + X509_EXTENSION *xe; + + xe = X509_get_ext(cert, loc); + if (xe == NULL) { + debug("ssh_x509_validate4cert: cannot get x509 extension"); + continue; + } + + {/*validate from AIA*/ + AUTHORITY_INFO_ACCESS *aia = ssh_aia_get(xe); + if (aia == NULL) continue; + + ret = ssh_aiaocsp_validate(cert, x509store, aia, &found); + + ssh_aia_free(xe, aia); + } + + if (ret >= 0) break; + } + + if (found) { + debug3("ssh_x509_validate4cert: validation result=%d", ret); + } else { + debug3("ssh_x509_validate4cert: no OCSP 'Service Locator' URL"); + } + return(found ? ret : 1); +} +#endif /*def SSH_OCSP_ENABLED*/ + + +int +ssh_x509_validate(X509 *cert, X509_STORE *x509store) { +#ifndef SSH_OCSP_ENABLED + return(1); +#else + int ret = -1; + ssh_ocsp_conn *conn = NULL; + + if (get_log_level() >= SYSLOG_LEVEL_DEBUG3) { + char buf[512]; + X509_NAME_oneline( X509_get_subject_name(cert), buf, sizeof(buf)); + debug3("ssh_x509_validate: for '%.512s'", buf); + } + + switch (va.type) { + default: + /*when something is missing*/ + fatal("ssh_x509_validate: invalid validator type %d", va.type); + break; /*;-)*/ + case SSHVA_NONE: + ret = 1; + break; + case SSHVA_OCSP_CERT: + ret = ssh_x509_validate4cert(cert, x509store); + break; + case SSHVA_OCSP_SPEC: + conn = ssh_ocsp_conn_new(va.responder_url); + if (conn != NULL) { + ret = ssh_ocsp_validate(cert, x509store, conn); + ssh_ocsp_conn_free(&conn); + } + break; + } + + return(ret); +#endif /*def SSH_OCSP_ENABLED*/ +} diff -ruN openssh-3.9p1+x509g4/tests/CA/1-cre_cadb.sh openssh-3.9p1+x509h/tests/CA/1-cre_cadb.sh --- openssh-3.9p1+x509g4/tests/CA/1-cre_cadb.sh 2004-03-03 16:42:59.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/1-cre_cadb.sh 2004-03-20 12:50:15.000000000 +0200 @@ -53,6 +53,31 @@ # === +# args: +# none +echo_CA_ocsp_options () { +if test "x$SSH_OCSP" = "xyes"; then +cat << EOF + +# OCSP Validator(Responder) URI +# Since OpenSSL OCSP responder support only one issuer certificate +# we should setup for the test cases many responders - each certificate +# type with responder on different port. +EOF + printf "authorityInfoAccess = " +( + port=${SSH_VA_BASEPORT} + for DIGEST in ${RSA_DIGEST_LIST}; do + printf "serviceLocator;URI:http://${SSHD_LISTENADDRESS}:${port}," + port=`expr ${port} + 1` + done + printf "serviceLocator;URI:http://${SSHD_LISTENADDRESS}:${port}" +) +fi +} + + +# === cre_config () { cat << EOF > "$1" [ ca ] @@ -116,6 +141,30 @@ challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 + + +[ ca_cert ] +# PKIX recommendation. + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +# Since we generate OpenSSH test CA we comment next line. +#basicConstraints=CA:true + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated OpenSSH Test CA Certificate" + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# Since we generate OpenSSH test CA we comment next line. +#keyUsage = cRLSign, keyCertSign + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always EOF @@ -132,13 +181,15 @@ # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Client Test Certificate" +nsComment = "OpenSSL Generated OpenSSH Test Client Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always EOF +echo_CA_ocsp_options >> "$1" + # X.509 extensions: SSH server certificates cat << EOF >> "$1" @@ -160,12 +211,42 @@ # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. -nsComment = "OpenSSL Generated Server Test Certificate" +nsComment = "OpenSSL Generated OpenSSH Test Server Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +EOF + +echo_CA_ocsp_options >> "$1" + + +# X.509 extensions: OCSP Validator certificates +if test "x$SSH_OCSP" = "xyes"; then +cat << EOF >> "$1" + + +[ ocsp_cert ] +# These extensions are added when 'ca' signs a request. +basicConstraints = CA:FALSE + +# Normal for validator certificate is: +nsCertType = objsign + +# This is typical in keyUsage for a validator certificate. +keyUsage = nonRepudiation, digitalSignature + +# This should present for a validator certificate. +extendedKeyUsage = OCSPSigning + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated OpenSSH Test OCSP Responder Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier = hash authorityKeyIdentifier = keyid,issuer:always EOF +fi for DIGEST in ${RSA_DIGEST_LIST}; do diff -ruN openssh-3.9p1+x509g4/tests/CA/2-cre_cakeys.sh openssh-3.9p1+x509h/tests/CA/2-cre_cakeys.sh --- openssh-3.9p1+x509g4/tests/CA/2-cre_cakeys.sh 2004-02-16 22:24:38.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/2-cre_cakeys.sh 2004-03-20 12:40:15.000000000 +0200 @@ -35,20 +35,16 @@ # === -SSH_DN_OU="OpenSSH Test CA Root" -SSH_DN_CN_BASE="OpenSSH Test CA key" - - echo_SSH_CA_DN () { cat <> "$OPENSSH_LOG" \ ; show_status $? "generating ${extd}TEST CA${norm} ${attn}rsa-${DIGEST}${norm} certificate" \ || return $? @@ -129,6 +126,7 @@ -passin pass:${KEY_PASS} \ -key "${TMPDIR}/${CAKEY_PREFIX}-dsa.key" \ -out "${TMPDIR}/${CAKEY_PREFIX}-dsa.crt" \ + -extensions ca_cert \ 2>> "$OPENSSH_LOG" \ ; show_status $? "generating ${extd}TEST CA${norm} ${attn}dsa-sha1${norm} certificate" \ || return $? @@ -186,7 +184,7 @@ create_empty_file "${TMPDIR}/${CACERTFILE}" && for type in ${SSH_SIGN_TYPES}; do F="${SSH_CACERTDIR}/${CAKEY_PREFIX}-${type}.crt.pem" - crt2bundle "$SSH_DN_OU" "${F}" >> "${TMPDIR}/${CACERTFILE}" || exit $? + crt2bundle "${SSH_DN_O}-${type}" "${F}" >> "${TMPDIR}/${CACERTFILE}" || exit $? done update_file "${TMPDIR}/${CACERTFILE}" "${SSH_CAROOT}/${CACERTFILE}" diff -ruN openssh-3.9p1+x509g4/tests/CA/3-cre_certs.sh openssh-3.9p1+x509h/tests/CA/3-cre_certs.sh --- openssh-3.9p1+x509g4/tests/CA/3-cre_certs.sh 2004-02-21 22:09:36.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/3-cre_certs.sh 2004-03-20 12:54:08.000000000 +0200 @@ -32,7 +32,7 @@ cat < -f[ile] [ssh]key_file_name - -t[ype] certificate type: client or server + -t[ype] certificate type: client, server, ocsp(if enabled) -n[ame] "base" common name EOF exit 1 @@ -74,6 +74,14 @@ server) SSH_X509V3_EXTENSIONS="srv_cert" ;; + ocsp) + if test "x$SSH_OCSP" = "xyes"; then + SSH_X509V3_EXTENSIONS="ocsp_cert" + else + echo "${warn}unsupported type${norm}" + usage + fi + ;; *) echo "${warn}wrong type${norm}" usage @@ -229,6 +237,8 @@ cre_csr && cre_crt || return $? + test "$SSH_X509V3_EXTENSIONS" = "ocsp_cert" && return 0 + cre_OpenSSH_Crt && cre_OpenSSH_PubKey && cre_P12_Crt diff -ruN openssh-3.9p1+x509g4/tests/CA/config openssh-3.9p1+x509h/tests/CA/config --- openssh-3.9p1+x509g4/tests/CA/config 2004-02-16 22:23:19.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/config 2004-03-20 12:02:17.000000000 +0200 @@ -65,6 +65,7 @@ dn_auth_path agent crl + ocsp " fi @@ -122,7 +123,7 @@ SSHSERVER_USEPRIVILEGESEPARATION="yes" SSHSERVER_SYSLOGFACILITY=AUTH -SSHSERVER_LOGLEVEL=INFO +SSHSERVER_LOGLEVEL=FATAL #SSHSERVER_SYSLOGFACILITY=LOCAL3 #SSHSERVER_LOGLEVEL=DEBUG3 @@ -152,3 +153,12 @@ SSH_DN_O="OpenSSH Test Team" SSH_DN_OU="OpenSSH Testers" SSH_DN_EM="email@not.set" + +# OpenSSL OCSP test responders listen on BASE, BASE+1, ... +if test -z "${SSH_VA_BASEPORT}"; then + SSH_VA_BASEPORT=20080 +fi + +# OpenSSL OCSP responder don't use SO_REUSEADDR :-(, so ocsp tests +# must wait socket to close. +SSH_OPENSLL_OCSP_TMOUT=60 diff -ruN openssh-3.9p1+x509g4/tests/CA/Makefile.in openssh-3.9p1+x509h/tests/CA/Makefile.in --- openssh-3.9p1+x509g4/tests/CA/Makefile.in 2004-02-21 22:11:12.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/Makefile.in 2004-03-09 21:37:04.000000000 +0200 @@ -1,4 +1,6 @@ srcdir=@srcdir@ +@OCSP_ON@SSH_OCSP=yes +@OCSP_OFF@SSH_OCSP=no all: @@ -7,9 +9,11 @@ clean: rm -f testhostkey_* rm -f testid_* + rm -f testocsp_* rm -fr ca-test/ rm -f openssh_ca-?.log* rm -f openssh_ca-3.*.log* + rm -f va-*.log rm -f sshd_x509.log distclean: clean @@ -17,7 +21,7 @@ # === -check-certs: ca_files hostkeys identities crl_files +check-certs: ca_files hostkeys identities ocsp_certs crl_files @echo $(SHELL) $(srcdir)/openssh_tests.sh @@ -28,6 +32,7 @@ #ca-test/catest.config: $(srcdir)/config ca-test/catest.config: @echo + SSH_OCSP=$(SSH_OCSP) \ $(SHELL) $(srcdir)/1-cre_cadb.sh ca-test/catest-bundle.crt: ca-test/catest.config @@ -84,6 +89,29 @@ # === +@OCSP_OFF@ocsp_certs: +@OCSP_ON@ocsp_certs: testocsp_rsa-rsa_md5.crt testocsp_dsa-rsa_md5.crt + +@OCSP_ON@testocsp_rsa-rsa_md5.crt: testocsp_rsa ca-test/catest-bundle.crt +@OCSP_ON@ @echo; echo "generating RSA ocsp responder certificates." +@OCSP_ON@ SSH_OCSP=$(SSH_OCSP) \ +@OCSP_ON@ $(SHELL) $(srcdir)/3-cre_certs.sh -f testocsp_rsa -t ocsp -n "validator RSA" + +@OCSP_ON@testocsp_rsa: +@OCSP_ON@ @echo; echo "generating RSA 'ocspkey'" +@OCSP_ON@ $(TEST_SSH_SSHKEYGEN) -t rsa -b 1024 -f $@ -N "" + +@OCSP_ON@testocsp_dsa-rsa_md5.crt: testocsp_dsa ca-test/catest-bundle.crt +@OCSP_ON@ @echo; echo "generating DSA ocsp responder certificates." +@OCSP_ON@ SSH_OCSP=$(SSH_OCSP) \ +@OCSP_ON@ $(SHELL) $(srcdir)/3-cre_certs.sh -f testocsp_dsa -t ocsp -n "validator DSA" + +@OCSP_ON@testocsp_dsa: +@OCSP_ON@ @echo; echo "generating DSA 'ocspkey'" +@OCSP_ON@ $(TEST_SSH_SSHKEYGEN) -t dsa -b 1024 -f $@ -N "" + + +# === crl_files: ca-test/catest-bundle.crl ca-test/catest-bundle.crl: testid_rsa-rsa_md5 testid_dsa-rsa_md5 diff -ruN openssh-3.9p1+x509g4/tests/CA/openssh_tests.sh openssh-3.9p1+x509h/tests/CA/openssh_tests.sh --- openssh-3.9p1+x509g4/tests/CA/openssh_tests.sh 2004-03-08 23:50:23.000000000 +0200 +++ openssh-3.9p1+x509h/tests/CA/openssh_tests.sh 2004-03-20 11:42:40.000000000 +0200 @@ -53,6 +53,13 @@ testid_dsa " +#OpenSSL OCSP limitation: only rsa keys +#TEST_OCSP_RESPKEYS="\ +# testocsp_rsa +# testocsp_dsa +#" +TEST_OCSP_RESPKEYS="testocsp_rsa" + #TEST_SSHD_HOSTKEY="${CWD}/testhostkey_rsa-rsa_md5" TEST_SSHD_HOSTKEY="${CWD}/testhostkey_rsa" @@ -72,6 +79,7 @@ cat > "$SSHD_CFG" < "${SSHD_LOG}" 2>&1 @@ -80,6 +88,11 @@ else SSH_X509STORE_DISABLED="no" fi +if grep 'Unsupported.*VAType' "${SSHD_LOG}" > /dev/null; then + SSH_OCSP_ENABLED="no" +else + SSH_OCSP_ENABLED="yes" +fi echo SSH_X509STORE_DISABLED=${SSH_X509STORE_DISABLED} if test "x${SSH_X509STORE_DISABLED}" = "xyes"; then @@ -89,6 +102,10 @@ -e 's|dn_auth_path||g' \ -e 's|crl||g'` fi +echo SSH_OCSP_ENABLED=${SSH_OCSP_ENABLED} +if test "x${SSH_OCSP_ENABLED}" = "xno"; then + SSH_X509TESTS=`echo "${SSH_X509TESTS}" | sed -e 's|ocsp||g'` +fi echo SSH_X509TESTS=$SSH_X509TESTS @@ -197,6 +214,7 @@ cat > "${SSH_CFG}" <&2 + while test ${count} -gt 0; do + printf 'O' + sleep 1 + count=`expr ${count} - 1` + done + printf 'X\n' +) +} + + +# === +killResponders() { +( + if test -z "${SSH_OPENSLL_OCSP_TMOUT}"; then + ( + has="" + for pidfile in va-*.pid; do + if test -r "${pidfile}"; then + kill `cat "${pidfile}"` > /dev/null 2>&1 + has="yes" + fi + done + if test -n "${has}"; then + openssl_ocsp_tmout + fi + ) + fi + ( + has="" + for pidfile in va-*.pid; do + if test -r "${pidfile}"; then + kill -9 `cat "${pidfile}"` > /dev/null 2>&1 + rm -f "${pidfile}" + has="yes" + fi + done + if test -n "${has}"; then + openssl_ocsp_tmout + fi + ) + : +) +} + + +# === +OCSPtestBREAK() { + echo + killResponders + testBREAK +} + +trap OCSPtestBREAK INT QUIT ABRT KILL TERM || exit 1 + + +# === +#args: +# $1 - port +#env. vars: +# OCSP_RESPKEY +# type +runResponder() { +( + port=$1 + + pidfile="va-${port}.pid" + test -r "${pidfile}" && exit 1 + + printf ' %s' "starting OCSP ${extd}responder${norm}(${attn}${type}${norm}) on ${attn}${SSHD_LISTENADDRESS}:${port}${norm}" + ( + ${OPENSSL} ocsp \ + -CA "${SSH_CACERTDIR}/${CAKEY_PREFIX}-${type}.crt.pem" \ + -rsigner "${OCSP_RESPKEY}-${type}.crt" \ + -rkey "${OCSP_RESPKEY}" \ + -index "${SSH_CAROOT}/index-${type}.txt" \ + -host ${SSHD_LISTENADDRESS} \ + -port ${port} 2> va-${type}.log & + pid=$! + echo ${pid} > "${pidfile}" + wait ${pid} + rm -f "${pidfile}" + ) 2> /dev/null & + + sleep 1 + test -r "${pidfile}"; show_status $? +) +} + + +# === +test_ocsp_cert () { +( + printSeparator + echo "Begin test ${extd}against${norm} OCSP provider from ${attn}certificate${norm} ..." + + retval=1 + for OCSP_RESPKEY in ${TEST_OCSP_RESPKEYS}; do + printSeparator + echo " respoder key base ${attn}${OCSP_RESPKEY}${norm} ..." + + creTestSSHDcfgFile + cat >> "$SSHD_CFG" <> "$SSHD_CFG" < 0) { +/* + * OpenSSH implementation first verify and validate certificate by + * "X.509 store" with certs and crls from file system. It is fast + * check. After this when certificate chain is correct and + * certificate is not revoked we send a status request to an OCSP + * responder if configured. + * + * RFC2560(OCSP): + * ... + * 2.7 CA Key Compromise + * If an OCSP responder knows that a particular CA's private key + * has been compromised, it MAY return the revoked state for all + * certificates issued by that CA. + * ... + * 5. Security Considerations + * For this service to be effective, certificate using systems must + * connect to the certificate status service provider. In the event + * such a connection cannot be obtained, certificate-using systems + * could implement CRL processing logic as a fall-back position. + * ... + * RFC2560(OCSP)^ + * + * About OpenSSH implementation: + * 1.) We preffer to delegate validation of issuer certificates to + * 'OCSP Provider'. It is easy and simple to configure an OCSP + * responder to return revoked state for all certificates issued + * by a CA. Usually 'OCSP Provider' admins shall be first informed + * for certificates with changed state. In each case this simplify + * 'OCSP client'. + * 2.) To conform to RFC2560 we should use OCSP to check status of + * all certificates in the chain. Since this is network request it + * is good to implement a cache and to save status with lifetime. + * Might is good to have an OCSP cache server ;-). + * + * To minimize network latency and keeping in mind 1.) we send + * 'OCSP request' only for the last certificate in the chain, i.e. + * sended client or server certificate. + * + * Therefore instead to send OCSP request in ssh_x509revoked_cb() + * we do this here. + */ + ret = ssh_x509_validate(_cert, x509store); + } #else /*def SSH_X509STORE_DISABLED*/ if (sshpurpose.index >=0) { @@ -498,7 +542,7 @@ { EVP_PKEY *pkey = X509_get_pubkey(cert); if (pkey == NULL) { - error("ssh_check_crl:unable to devode public key"); + error("ssh_check_crl:unable to decode public key"); X509_STORE_CTX_set_error(_ctx, X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY); return(0); } @@ -572,8 +616,8 @@ static int ssh_x509revoked_cb(int ok, X509_STORE_CTX *ctx) { - X509 *cert; - X509_OBJECT xobj; + X509 *cert; + X509_OBJECT xobj; if (!ok) return(0); if (x509revoked == NULL) diff -ruN openssh-3.9p1+x509g4/x509store.h openssh-3.9p1+x509h/x509store.h --- openssh-3.9p1+x509g4/x509store.h 2004-02-22 00:00:47.000000000 +0200 +++ openssh-3.9p1+x509h/x509store.h 2004-03-21 11:09:12.000000000 +0200 @@ -51,7 +51,43 @@ int ssh_x509store_addlocations(const X509StoreOptions *_locations); +int ssh_x509_validate(X509 *cert, X509_STORE *x509store); + #endif /*ndef SSH_X509STORE_DISABLED*/ +#ifdef SSH_X509STORE_DISABLED +#ifdef SSH_OCSP_ENABLED +# include "cannot enable OCSP when x509store is disabled" +#endif /*def SSH_OCSP_ENABLED*/ +#endif /*def SSH_X509STORE_DISABLED*/ + + +#ifdef SSH_OCSP_ENABLED + +enum va_type { + SSHVA_NONE, + SSHVA_OCSP_CERT, + SSHVA_OCSP_SPEC +}; + + +typedef struct { + int type; /*allowed values from enum va_type*/ + + /* file with additional trusted certificates */ + const char *certificate_file; + + /* ssh OCSP Provider(Respoder) URL */ + const char *responder_url; +} VAOptions; + +int ssh_get_default_vatype(void); +int ssh_get_vatype_s(const char* type); + +void ssh_set_validator(const VAOptions *_va); /*fatal on error*/ + +#endif /*def SSH_OCSP_ENABLED*/ + + #endif /* X509STORE_H */