diff --git a/README.FIPS b/README.FIPS index 473a0f4..0f21716 100644 --- a/README.FIPS +++ b/README.FIPS @@ -0,0 +1,64 @@ +Notes on FIPS mode and OpenSSH + +--- + +SUSE OpenSSH comes with FIPS 140-2 support, and certain versions have been +certified as FIPS compliant by NIST. Apart from other things, this standard +puts restrictions on cryptographic algorithms that may be used. + +Important notice: FIPS is not only a matter of functionality. If you want to +claim having a FIPS certified service, you *must* use the certified binaries. +Even binaries built from the same sources in the same environment and running +on a certified system, yet from a package lacking the certification, are +formally not considered to be fulfilling the requirements. + +The certified binaries (ssh, sshd, sftp-server) perform mandatory selfcheck at +startup and proceed only when the checks succeed (non-certified binaries may +skip the check). These checks require the cryptographic hashes contained in the +openssh-fips subpackage. + +The FIPS mode for OpenSSH is enabled in two ways - either: + +1) /proc/sys/crypto/fips_enabled contains a single character '1' - this is a +system-wide setting controlled bu the fips kernel parameter; or + +2) the environment variable SSH_FORCE_FIPS - if set (to any value), the +binaries behave as if they were running on a system in FIPS mode. + +Since FIPS 140-2 only allows use of certain cryptographic algorithms, both the +client and server will fail if they are requested to use non-approved +algorithms while in FIPS mode. This means that working configurations for FIPS +mode form a proper subset of all working (generic) configurations. Some +configurations may even prevent the binaries from starting at all. + +This however should be viewed in the context of FIPS being a security policy +tool - it is not of much use to run the same system both in FIPS mode and +outside of it, since that would defeat the main purpose of FIPS having +guaranteeing standardised minimum restrictions on cryptographic algorithms +(and thus on the overall security of the system). + +Unless you specify what cryptographic algorithms you wish to use, both the +client and server should work out of the box in FIPS mode. + +For sshd, you can use the `-t` option to check whether the configuration file +is working. Setting the above mentioned environment variable allows testing of +behaviour in FIPS mode (checksum files for both OpenSSH and OpenSSL must be +installed). + +In addition to cryptographic algorithms restrictions, sshd performs periodic +PRNG re-seeding. The seed is read from entropy source either /dev/urandom or +/dev/random. By default, the former is used, unless the environment variable +SSH_USE_STRONG_RNG is set to a non-zero value or the binary is running in FIPS +mode. This has two important implications: + +1) the selected entropy source must be available, i.e. when running in a +changeroot the device files need to be present there. + +2) /dev/random is a blocking interface - unless enough randomness is available, +the process stops until the entropy pool is replenished. Thus on systems where +a long running processes are expected, one should make sure there is always +enough entropy for sshd. Sporadically this may also cause sshd to aborted, +since some versions of OpenSSL (the underlying cryptographic engine) don't +handle gracefully being interrupted while trying to read entropy from the +system source. + diff --git a/openssh-7.2p2-IPv6_X_forwarding.patch b/openssh-7.2p2-IPv6_X_forwarding.patch new file mode 100644 index 0000000..067f644 --- /dev/null +++ b/openssh-7.2p2-IPv6_X_forwarding.patch @@ -0,0 +1,72 @@ +# HG changeset patch +# Parent 8c4cb20b9633595de68131224b2d434e8dc41e17 +Correctly parse DISPLAY variable for cases where it contains an IPv6 address +(which should - but not always is - in (square) brackets). + +bnc#847710 - https://bugzilla.novell.com/show_bug.cgi?id=847710 + +diff --git a/openssh-7.2p2/channels.c b/openssh-7.2p2/channels.c +--- a/openssh-7.2p2/channels.c ++++ b/openssh-7.2p2/channels.c +@@ -4049,18 +4049,19 @@ x11_connect_display(void) + /* OK, we now have a connection to the display. */ + return sock; + } + #endif + /* + * Check if it is a unix domain socket. Unix domain displays are in + * one of the following formats: unix:d[.s], :d[.s], ::d[.s] + */ ++ cp = strrchr(display, ':'); + if (strncmp(display, "unix:", 5) == 0 || +- display[0] == ':') { ++ (display[0] == ':' && ((cp - display) < 2)) ) { + /* Connect to the unix domain socket. */ + if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + return -1; + } + /* Create a socket. */ + sock = connect_local_xsocket(display_number); +@@ -4068,30 +4069,39 @@ x11_connect_display(void) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } + /* + * Connect to an inet socket. The DISPLAY value is supposedly + * hostname:d[.s], where hostname may also be numeric IP address. ++ * Note that IPv6 numberic addresses contain colons (e.g. ::1:0) + */ + strlcpy(buf, display, sizeof(buf)); +- cp = strchr(buf, ':'); ++ cp = strrchr(buf, ':'); + if (!cp) { + error("Could not find ':' in DISPLAY: %.100s", display); + return -1; + } + *cp = 0; + /* buf now contains the host name. But first we parse the display number. */ + if (sscanf(cp + 1, "%u", &display_number) != 1) { + error("Could not parse display number from DISPLAY: %.100s", + display); + return -1; + } ++ ++ /* Remove brackets surrounding IPv6 addresses if there are any. */ ++ if (buf[0] == '[' && (cp = strchr(buf, ']'))) { ++ *cp = 0; ++ cp = buf + 1; ++ } else { ++ cp = buf; ++ } + + /* Look up the host address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%u", 6000 + display_number); + if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host. (%s)", buf, diff --git a/openssh-7.2p2-X11_trusted_forwarding.patch b/openssh-7.2p2-X11_trusted_forwarding.patch index 795433e..95b8d04 100644 --- a/openssh-7.2p2-X11_trusted_forwarding.patch +++ b/openssh-7.2p2-X11_trusted_forwarding.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 48bbbfeff186061b7fd4795bff15f15f571e2c8f +# Parent 7197d7a6b7c90566c68e980b5f8b937c183e79d0 # enable trusted X11 forwarding by default in both sshd and sshsystem-wide # configuration # bnc#50836 (was suse #35836) diff --git a/openssh-7.2p2-X_forward_with_disabled_ipv6.patch b/openssh-7.2p2-X_forward_with_disabled_ipv6.patch new file mode 100644 index 0000000..6b83f3b --- /dev/null +++ b/openssh-7.2p2-X_forward_with_disabled_ipv6.patch @@ -0,0 +1,34 @@ +# HG changeset patch +# Parent 28e8840bbf49c6e603bf2b55a08ed9050a60f9fb +Do not throw away already open sockets for X11 forwarding if another socket +family is not available for bind() + +diff --git a/openssh-7.2p2/channels.c b/openssh-7.2p2/channels.c +--- a/openssh-7.2p2/channels.c ++++ b/openssh-7.2p2/channels.c +@@ -3937,22 +3937,24 @@ x11_create_display_inet(int x11_display_ + } + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); + if (x11_use_localhost) + channel_set_reuseaddr(sock); + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug2("bind port %d: %.100s", port, strerror(errno)); + close(sock); +- ++ continue; ++ /* do not remove successfully opened sockets + for (n = 0; n < num_socks; n++) { + close(socks[n]); + } + num_socks = 0; + break; ++ */ + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; + } + freeaddrinfo(aitop); + if (num_socks > 0) + break; diff --git a/openssh-7.2p2-additional_seccomp_archs.patch b/openssh-7.2p2-additional_seccomp_archs.patch new file mode 100644 index 0000000..a410f8d --- /dev/null +++ b/openssh-7.2p2-additional_seccomp_archs.patch @@ -0,0 +1,56 @@ +# HG changeset patch +# Parent e7bdbc5ea8971599466becf01bff12b9fcb5df3e +Enable the seccomp-bpf sandbox on more architectures + +upstream commit: b9c50614eba9d90939b2b119b6e1b7e03b462278 (7.3p1) +Author: Damien Miller +Date: Fri Jul 8 13:59:13 2016 +1000 + + whitelist more architectures for seccomp-bpf + + bz#2590 - testing and patch from Jakub Jelen + +diff --git a/openssh-7.2p2/configure.ac b/openssh-7.2p2/configure.ac +--- a/openssh-7.2p2/configure.ac ++++ b/openssh-7.2p2/configure.ac +@@ -818,16 +818,40 @@ main() { if (NSVersionOfRunTimeLibrary(" + seccomp_audit_arch=AUDIT_ARCH_I386 + ;; + arm*-*) + seccomp_audit_arch=AUDIT_ARCH_ARM + ;; + aarch64*-*) + seccomp_audit_arch=AUDIT_ARCH_AARCH64 + ;; ++ s390x-*) ++ seccomp_audit_arch=AUDIT_ARCH_S390X ++ ;; ++ s390-*) ++ seccomp_audit_arch=AUDIT_ARCH_S390 ++ ;; ++ powerpc64-*) ++ seccomp_audit_arch=AUDIT_ARCH_PPC64 ++ ;; ++ powerpc64le-*) ++ seccomp_audit_arch=AUDIT_ARCH_PPC64LE ++ ;; ++ mips-*) ++ seccomp_audit_arch=AUDIT_ARCH_MIPS ++ ;; ++ mipsel-*) ++ seccomp_audit_arch=AUDIT_ARCH_MIPSEL ++ ;; ++ mips64-*) ++ seccomp_audit_arch=AUDIT_ARCH_MIPS64 ++ ;; ++ mips64el-*) ++ seccomp_audit_arch=AUDIT_ARCH_MIPSEL64 ++ ;; + esac + if test "x$seccomp_audit_arch" != "x" ; then + AC_MSG_RESULT(["$seccomp_audit_arch"]) + AC_DEFINE_UNQUOTED([SECCOMP_AUDIT_ARCH], [$seccomp_audit_arch], + [Specify the system call convention in use]) + else + AC_MSG_RESULT([architecture not supported]) + fi diff --git a/openssh-7.2p2-allow_DSS_by_default.patch b/openssh-7.2p2-allow_DSS_by_default.patch index c1a8166..ad19e35 100644 --- a/openssh-7.2p2-allow_DSS_by_default.patch +++ b/openssh-7.2p2-allow_DSS_by_default.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 2730f36bee0d6e141d8391b414a702e1add5a853 +# Parent d33bce122aa351a56ce457be35feda52171f9088 Enable DSS authentication by default to maintain compatibility with older versions. diff --git a/openssh-7.2p2-allow_root_password_login.patch b/openssh-7.2p2-allow_root_password_login.patch index 884f785..9500ee5 100644 --- a/openssh-7.2p2-allow_root_password_login.patch +++ b/openssh-7.2p2-allow_root_password_login.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 8cf6984812ab2211ce60c0a9156892b3a7ee3aaf +# Parent c43ae523939377778762e81743b77b3c75eb4bd1 Allow root login with password by default. While less secure than upstream default of forbidding access to the root account with a password, we are temporarily introducing this change to keep the default used in older OpenSSH diff --git a/openssh-7.2p2-audit.patch b/openssh-7.2p2-audit.patch new file mode 100644 index 0000000..d8c570a --- /dev/null +++ b/openssh-7.2p2-audit.patch @@ -0,0 +1,3241 @@ +# HG changeset patch +# Parent af5c4026e36e7aa181c164d2eca72b7e2a8a897a +Extended auditing through the Linux Auditing subsystem + +diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in +--- a/openssh-7.2p2/Makefile.in ++++ b/openssh-7.2p2/Makefile.in +@@ -88,17 +88,18 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + ssh-pkcs11.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o kexgssc.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o kexgsss.o \ + platform-pledge.o \ +- fips.o ++ fips.o \ ++ auditstub.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o mux.o + + SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ + audit.o audit-bsm.o audit-linux.o platform.o \ + sshpty.o sshlogin.o servconf.o serverloop.o \ + auth.o auth1.o auth2.o auth-options.o session.o \ +diff --git a/openssh-7.2p2/audit-bsm.c b/openssh-7.2p2/audit-bsm.c +--- a/openssh-7.2p2/audit-bsm.c ++++ b/openssh-7.2p2/audit-bsm.c +@@ -370,34 +370,53 @@ audit_connection_from(const char *host, + /* this is used on IPv4-only machines */ + tid->port = (dev_t)port; + tid->machine = inet_addr(host); + snprintf(buf, sizeof(buf), "%08x", tid->machine); + debug3("BSM audit: machine ID %s", buf); + #endif + } + +-void ++int + audit_run_command(const char *command) + { + /* not implemented */ ++ return 0; ++} ++ ++void ++audit_end_command(int handle, const char *command) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_count_session_open(void) ++{ ++ /* not necessary */ + } + + void + audit_session_open(struct logininfo *li) + { + /* not implemented */ + } + + void + audit_session_close(struct logininfo *li) + { + /* not implemented */ + } + ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ /* not implemented */ ++} ++ + void + audit_event(ssh_audit_event_t event) + { + char textbuf[BSM_TEXTBUFSZ]; + static int logged_in = 0; + const char *user = the_authctxt ? the_authctxt->user : "(unknown user)"; + + if (cannot_audit(0)) +@@ -449,9 +468,45 @@ audit_event(ssh_audit_event_t event) + case SSH_AUTH_FAIL_KBDINT: + bsm_audit_bad_login("interactive password entry"); + break; + + default: + debug("%s: unhandled event %d", __func__, event); + } + } ++ ++void ++audit_unsupported_body(int what) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ /* not implemented */ ++} ++ ++void ++audit_generate_ephemeral_server_key(const char *fp) ++{ ++ /* not implemented */ ++} + #endif /* BSM */ +diff --git a/openssh-7.2p2/audit-linux.c b/openssh-7.2p2/audit-linux.c +--- a/openssh-7.2p2/audit-linux.c ++++ b/openssh-7.2p2/audit-linux.c +@@ -30,97 +30,381 @@ + #include "includes.h" + #if defined(USE_LINUX_AUDIT) + #include + #include + #include + + #include "log.h" + #include "audit.h" ++#include "key.h" ++#include "hostfile.h" ++#include "auth.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" + #include "canohost.h" ++#include "packet.h" ++#include "cipher.h" + ++#define AUDIT_LOG_SIZE 256 ++ ++extern ServerOptions options; ++extern Authctxt *the_authctxt; ++extern u_int utmp_len; + const char* audit_username(void); + +-int +-linux_audit_record_event(int uid, const char *username, +- const char *hostname, const char *ip, const char *ttyn, int success) ++static void ++linux_audit_user_logxxx(int uid, const char *username, ++ const char *hostname, const char *ip, const char *ttyn, int success, int event) + { + int audit_fd, rc, saved_errno; + + audit_fd = audit_open(); + if (audit_fd < 0) { + if (errno == EINVAL || errno == EPROTONOSUPPORT || + errno == EAFNOSUPPORT) +- return 1; /* No audit support in kernel */ ++ return; /* No audit support in kernel */ + else +- return 0; /* Must prevent login */ ++ goto fatal_report; /* Must prevent login */ + } +- rc = audit_log_acct_message(audit_fd, AUDIT_USER_LOGIN, ++ rc = audit_log_acct_message(audit_fd, event, + NULL, "login", username ? username : "(unknown)", + username == NULL ? uid : -1, hostname, ip, ttyn, success); + saved_errno = errno; + close(audit_fd); + /* + * Do not report error if the error is EPERM and sshd is run as non + * root user. + */ + if ((rc == -EPERM) && (geteuid() != 0)) + rc = 0; + errno = saved_errno; +- return (rc >= 0); ++ if (rc < 0) { ++fatal_report: ++ fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ } + } + ++static void ++linux_audit_user_auth(int uid, const char *username, ++ const char *hostname, const char *ip, const char *ttyn, int success, int event) ++{ ++ int audit_fd, rc, saved_errno; ++ static const char *event_name[] = { ++ "maxtries exceeded", ++ "root denied", ++ "success", ++ "none", ++ "password", ++ "challenge-response", ++ "pubkey", ++ "hostbased", ++ "gssapi", ++ "invalid user", ++ "nologin", ++ "connection closed", ++ "connection abandoned", ++ "unknown" ++ }; ++ ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return; /* No audit support in kernel */ ++ else ++ goto fatal_report; /* Must prevent login */ ++ } ++ ++ if ((event < 0) || (event > SSH_AUDIT_UNKNOWN)) ++ event = SSH_AUDIT_UNKNOWN; ++ ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, ++ NULL, event_name[event], username ? username : "(unknown)", ++ username == NULL ? uid : -1, hostname, ip, ttyn, success); ++ saved_errno = errno; ++ close(audit_fd); ++ /* ++ * Do not report error if the error is EPERM and sshd is run as non ++ * root user. ++ */ ++ if ((rc == -EPERM) && (geteuid() != 0)) ++ rc = 0; ++ errno = saved_errno; ++ if (rc < 0) { ++fatal_report: ++ fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ } ++} ++ ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, rc, saved_errno; ++ ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return 1; /* No audit support in kernel */ ++ else ++ return 0; /* Must prevent login */ ++ } ++ snprintf(buf, sizeof(buf), "%s_auth rport=%d", host_user ? "pubkey" : "hostbased", get_remote_port()); ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, ++ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); ++ if ((rc < 0) && ((rc != -1) || (getuid() == 0))) ++ goto out; ++ /* is the fingerprint_prefix() still needed? ++ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s%s rport=%d", ++ type, bits, sshkey_fingerprint_prefix(), fp, get_remote_port()); ++ */ ++ snprintf(buf, sizeof(buf), "key algo=%s size=%d fp=%s rport=%d", ++ type, bits, fp, get_remote_port()); ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, ++ buf, audit_username(), -1, NULL, get_remote_ipaddr(), NULL, rv); ++out: ++ saved_errno = errno; ++ audit_close(audit_fd); ++ errno = saved_errno; ++ /* do not report error if the error is EPERM and sshd is run as non root user */ ++ return (rc >= 0) || ((rc == -EPERM) && (getuid() != 0)); ++} ++ ++static int user_login_count = 0; ++ + /* Below is the sshd audit API code */ + + void + audit_connection_from(const char *host, int port) + { ++ /* not implemented */ + } +- /* not implemented */ ++ ++int ++audit_run_command(const char *command) ++{ ++ if (!user_login_count++) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_START); ++ return 0; ++} + + void +-audit_run_command(const char *command) ++audit_end_command(int handle, const char *command) + { +- /* not implemented */ ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGOUT); ++} ++ ++void ++audit_count_session_open(void) ++{ ++ user_login_count++; + } + + void + audit_session_open(struct logininfo *li) + { +- if (linux_audit_record_event(li->uid, NULL, li->hostname, +- NULL, li->line, 1) == 0) +- fatal("linux_audit_write_entry failed: %s", strerror(errno)); ++ if (!user_login_count++) ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_START); + } + + void + audit_session_close(struct logininfo *li) + { +- /* not implemented */ ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ NULL, li->line, 1, AUDIT_USER_LOGOUT); + } + + void + audit_event(ssh_audit_event_t event) + { + switch(event) { + case SSH_AUTH_SUCCESS: +- case SSH_CONNECTION_CLOSE: +- case SSH_NOLOGIN: +- case SSH_LOGIN_EXCEED_MAXTRIES: +- case SSH_LOGIN_ROOT_DENIED: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 1, event); + break; + ++ case SSH_NOLOGIN: ++ case SSH_LOGIN_ROOT_DENIED: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, event); ++ linux_audit_user_logxxx(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); ++ break; ++ ++ case SSH_LOGIN_EXCEED_MAXTRIES: + case SSH_AUTH_FAIL_NONE: + case SSH_AUTH_FAIL_PASSWD: + case SSH_AUTH_FAIL_KBDINT: + case SSH_AUTH_FAIL_PUBKEY: + case SSH_AUTH_FAIL_HOSTBASED: + case SSH_AUTH_FAIL_GSSAPI: ++ linux_audit_user_auth(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, event); ++ break; ++ ++ case SSH_CONNECTION_CLOSE: ++ if (user_login_count) { ++ while (user_login_count--) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_END); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, get_remote_name_or_ip(utmp_len, options.use_dns), ++ NULL, "ssh", 1, AUDIT_USER_LOGOUT); ++ } ++ break; ++ ++ case SSH_CONNECTION_ABANDON: + case SSH_INVALID_USER: +- linux_audit_record_event(-1, audit_username(), NULL, +- get_remote_ipaddr(), "sshd", 0); ++ linux_audit_user_logxxx(-1, audit_username(), NULL, ++ get_remote_ipaddr(), "ssh", 0, AUDIT_USER_LOGIN); + break; + + default: + debug("%s: unhandled event %d", __func__, event); + } + } + ++void ++audit_unsupported_body(int what) ++{ ++#ifdef AUDIT_CRYPTO_SESSION ++ char buf[AUDIT_LOG_SIZE]; ++ const static char *name[] = { "cipher", "mac", "comp" }; ++ char *s; ++ int audit_fd; ++ ++ snprintf(buf, sizeof(buf), "op=unsupported-%s direction=? cipher=? ksize=? rport=%d laddr=%s lport=%d ", ++ name[what], get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), ++ get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) ++ /* no problem, the next instruction will be fatal() */ ++ return; ++ audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, ++ buf, NULL, get_remote_ipaddr(), NULL, 0); ++ audit_close(audit_fd); ++#endif ++} ++ ++const static char *direction[] = { "from-server", "from-client", "both" }; ++ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++ uid_t uid) ++{ ++#ifdef AUDIT_CRYPTO_SESSION ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ const struct sshcipher *cipher = cipher_by_name(enc); ++ char *s; ++ ++ snprintf(buf, sizeof(buf), "op=start direction=%s cipher=%s ksize=%d mac=%s pfs=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", ++ direction[ctos], enc, cipher ? 8 * cipher->key_len : 0, mac, pfs, ++ (intmax_t)pid, (intmax_t)uid, ++ get_remote_port(), (s = get_local_ipaddr(packet_get_connection_in())), get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return; /* No audit support in kernel */ ++ else ++ fatal("cannot open audit"); /* Must prevent login */ ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, ++ buf, NULL, get_remote_ipaddr(), NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ fatal("cannot write into audit"); /* Must prevent login */ ++#endif ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ char *s; ++ ++ snprintf(buf, sizeof(buf), "op=destroy kind=session fp=? direction=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", ++ direction[ctos], (intmax_t)pid, (intmax_t)uid, ++ get_remote_port(), ++ (s = get_local_ipaddr(packet_get_connection_in())), ++ get_local_port()); ++ free(s); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, get_remote_ipaddr(), NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} ++ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ ++ snprintf(buf, sizeof(buf), "op=destroy kind=server fp=%s direction=? spid=%jd suid=%jd ", ++ fp, (intmax_t)pid, (intmax_t)uid); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, ++ listening_for_clients() ? NULL : get_remote_ipaddr(), ++ NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} ++ ++void ++audit_generate_ephemeral_server_key(const char *fp) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ ++ snprintf(buf, sizeof(buf), "op=create kind=server fp=%s direction=? ", fp); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, ++ buf, NULL, 0, NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} + #endif /* USE_LINUX_AUDIT */ +diff --git a/openssh-7.2p2/audit.c b/openssh-7.2p2/audit.c +--- a/openssh-7.2p2/audit.c ++++ b/openssh-7.2p2/audit.c +@@ -23,31 +23,38 @@ + * (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 "includes.h" + + #include + #include ++#include + + #ifdef SSH_AUDIT_EVENTS + + #include "audit.h" + #include "log.h" + #include "key.h" + #include "hostfile.h" + #include "auth.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "xmalloc.h" ++#include "misc.h" ++#include "servconf.h" + + /* + * Care must be taken when using this since it WILL NOT be initialized when + * audit_connection_from() is called and MAY NOT be initialized when + * audit_event(CONNECTION_ABANDON) is called. Test for NULL before using. + */ + extern Authctxt *the_authctxt; ++extern ServerOptions options; + + /* Maybe add the audit class to struct Authmethod? */ + ssh_audit_event_t + audit_classify_auth(const char *method) + { + if (strcmp(method, "none") == 0) + return SSH_AUTH_FAIL_NONE; + else if (strcmp(method, "password") == 0) +@@ -66,23 +73,20 @@ audit_classify_auth(const char *method) + else + return SSH_AUDIT_UNKNOWN; + } + + /* helper to return supplied username */ + const char * + audit_username(void) + { +- static const char unknownuser[] = "(unknown user)"; +- static const char invaliduser[] = "(invalid user)"; ++ static const char unknownuser[] = "(unknown)"; + +- if (the_authctxt == NULL || the_authctxt->user == NULL) ++ if (the_authctxt == NULL || the_authctxt->user == NULL || !the_authctxt->valid) + return (unknownuser); +- if (!the_authctxt->valid) +- return (invaliduser); + return (the_authctxt->user); + } + + const char * + audit_event_lookup(ssh_audit_event_t ev) + { + int i; + static struct event_lookup_struct { +@@ -106,16 +110,50 @@ audit_event_lookup(ssh_audit_event_t ev) + }; + + for (i = 0; event_lookup[i].event != SSH_AUDIT_UNKNOWN; i++) + if (event_lookup[i].event == ev) + break; + return(event_lookup[i].name); + } + ++void ++audit_key(int host_user, int *rv, const Key *key) ++{ ++ char *fp; ++ const char *crypto_name; ++ ++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); ++ if (key->type == KEY_RSA1) ++ crypto_name = "ssh-rsa1"; ++ else ++ crypto_name = key_ssh_name(key); ++ if (audit_keyusage(host_user, crypto_name, key_size(key), fp, *rv) == 0) ++ *rv = 0; ++ free(fp); ++} ++ ++void ++audit_unsupported(int what) ++{ ++ PRIVSEP(audit_unsupported_body(what)); ++} ++ ++void ++audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++{ ++ PRIVSEP(audit_kex_body(ctos, enc, mac, comp, pfs, getpid(), getuid())); ++} ++ ++void ++audit_session_key_free(int ctos) ++{ ++ PRIVSEP(audit_session_key_free_body(ctos, getpid(), getuid())); ++} ++ + # ifndef CUSTOM_SSH_AUDIT_EVENTS + /* + * Null implementations of audit functions. + * These get used if SSH_AUDIT_EVENTS is defined but no audit module is enabled. + */ + + /* + * Called after a connection has been accepted but before any authentication +@@ -135,16 +173,27 @@ audit_connection_from(const char *host, + void + audit_event(ssh_audit_event_t event) + { + debug("audit event euid %d user %s event %d (%s)", geteuid(), + audit_username(), event, audit_event_lookup(event)); + } + + /* ++ * Called when a child process has called, or will soon call, ++ * audit_session_open. ++ */ ++void ++audit_count_session_open(void) ++{ ++ debug("audit count session open euid %d user %s", geteuid(), ++ audit_username()); ++} ++ ++/* + * Called when a user session is started. Argument is the tty allocated to + * the session, or NULL if no tty was allocated. + * + * Note that this may be called multiple times if multiple sessions are used + * within a single connection. + */ + void + audit_session_open(struct logininfo *li) +@@ -169,18 +218,96 @@ audit_session_close(struct logininfo *li + + debug("audit session close euid %d user %s tty name %s", geteuid(), + audit_username(), t); + } + + /* + * This will be called when a user runs a non-interactive command. Note that + * it may be called multiple times for a single connection since SSH2 allows +- * multiple sessions within a single connection. ++ * multiple sessions within a single connection. Returns a "handle" for ++ * audit_end_command. + */ +-void ++int + audit_run_command(const char *command) + { + debug("audit run command euid %d user %s command '%.200s'", geteuid(), + audit_username(), command); ++ return 0; ++} ++ ++/* ++ * This will be called when the non-interactive command finishes. Note that ++ * it may be called multiple times for a single connection since SSH2 allows ++ * multiple sessions within a single connection. "handle" should come from ++ * the corresponding audit_run_command. ++ */ ++void ++audit_end_command(int handle, const char *command) ++{ ++ debug("audit end nopty exec euid %d user %s command '%.200s'", geteuid(), ++ audit_username(), command); ++} ++ ++/* ++ * This will be called when user is successfully autherized by the RSA1/RSA/DSA key. ++ * ++ * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key. ++ */ ++int ++audit_keyusage(int host_user, const char *type, unsigned bits, char *fp, int rv) ++{ ++ debug("audit %s key usage euid %d user %s key type %s key length %d fingerprint %s%s, result %d", ++ host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), type, bits, ++ sshkey_fingerprint_prefix(), fp, rv); ++} ++ ++/* ++ * This will be called when the protocol negotiation fails. ++ */ ++void ++audit_unsupported_body(int what) ++{ ++ debug("audit unsupported protocol euid %d type %d", geteuid(), what); ++} ++ ++/* ++ * This will be called on succesfull protocol negotiation. ++ */ ++void ++audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++ uid_t uid) ++{ ++ debug("audit protocol negotiation euid %d direction %d cipher %s mac %s compresion %s pfs %s from pid %ld uid %u", ++ (unsigned)geteuid(), ctos, enc, mac, compress, pfs, (long)pid, ++ (unsigned)uid); ++} ++ ++/* ++ * This will be called on succesfull session key discard ++ */ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ debug("audit session key discard euid %u direction %d from pid %ld uid %u", ++ (unsigned)geteuid(), ctos, (long)pid, (unsigned)uid); ++} ++ ++/* ++ * This will be called on destroy private part of the server key ++ */ ++void ++audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ debug("audit destroy sensitive data euid %d fingerprint %s from pid %ld uid %u", ++ geteuid(), fp, (long)pid, (unsigned)uid); ++} ++ ++/* ++ * This will be called on generation of the ephemeral server key ++ */ ++void ++audit_generate_ephemeral_server_key(const char *) ++{ ++ debug("audit create ephemeral server key euid %d fingerprint %s", geteuid(), fp); + } + # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ + #endif /* SSH_AUDIT_EVENTS */ +diff --git a/openssh-7.2p2/audit.h b/openssh-7.2p2/audit.h +--- a/openssh-7.2p2/audit.h ++++ b/openssh-7.2p2/audit.h +@@ -23,16 +23,17 @@ + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + #ifndef _SSH_AUDIT_H + # define _SSH_AUDIT_H + + #include "loginrec.h" ++#include "key.h" + + enum ssh_audit_event_type { + SSH_LOGIN_EXCEED_MAXTRIES, + SSH_LOGIN_ROOT_DENIED, + SSH_AUTH_SUCCESS, + SSH_AUTH_FAIL_NONE, + SSH_AUTH_FAIL_PASSWD, + SSH_AUTH_FAIL_KBDINT, /* keyboard-interactive or challenge-response */ +@@ -40,18 +41,38 @@ enum ssh_audit_event_type { + SSH_AUTH_FAIL_HOSTBASED, /* ssh2 hostbased or ssh1 rhostsrsa */ + SSH_AUTH_FAIL_GSSAPI, + SSH_INVALID_USER, + SSH_NOLOGIN, /* denied by /etc/nologin, not implemented */ + SSH_CONNECTION_CLOSE, /* closed after attempting auth or session */ + SSH_CONNECTION_ABANDON, /* closed without completing auth */ + SSH_AUDIT_UNKNOWN + }; ++ ++enum ssh_audit_kex { ++ SSH_AUDIT_UNSUPPORTED_CIPHER, ++ SSH_AUDIT_UNSUPPORTED_MAC, ++ SSH_AUDIT_UNSUPPORTED_COMPRESSION ++}; + typedef enum ssh_audit_event_type ssh_audit_event_t; + ++int listening_for_clients(void); ++ + void audit_connection_from(const char *, int); + void audit_event(ssh_audit_event_t); ++void audit_count_session_open(void); + void audit_session_open(struct logininfo *); + void audit_session_close(struct logininfo *); +-void audit_run_command(const char *); ++int audit_run_command(const char *); ++void audit_end_command(int, const char *); + ssh_audit_event_t audit_classify_auth(const char *); ++int audit_keyusage(int, const char *, unsigned, char *, int); ++void audit_key(int, int *, const Key *); ++void audit_unsupported(int); ++void audit_kex(int, char *, char *, char *, char *); ++void audit_unsupported_body(int); ++void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); ++void audit_session_key_free(int ctos); ++void audit_session_key_free_body(int ctos, pid_t, uid_t); ++void audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++void audit_generate_ephemeral_server_key(const char *); + + #endif /* _SSH_AUDIT_H */ +diff --git a/openssh-7.2p2/auditstub.c b/openssh-7.2p2/auditstub.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/auditstub.c +@@ -0,0 +1,50 @@ ++/* $Id: auditstub.c,v 1.1 jfch Exp $ */ ++ ++/* ++ * Copyright 2010 Red Hat, Inc. All rights reserved. ++ * Use is subject to license terms. ++ * ++ * 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. ++ * ++ * Red Hat author: Jan F. Chadima ++ */ ++ ++#include ++ ++void ++audit_unsupported(int n) ++{ ++} ++ ++void ++audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++{ ++} ++ ++void ++audit_session_key_free(int ctos) ++{ ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++} +diff --git a/openssh-7.2p2/auth-rsa.c b/openssh-7.2p2/auth-rsa.c +--- a/openssh-7.2p2/auth-rsa.c ++++ b/openssh-7.2p2/auth-rsa.c +@@ -96,16 +96,20 @@ int + auth_rsa_verify_response(Key *key, BIGNUM *challenge, + u_char response[SSH_DIGEST_MAX_LENGTH]) + { + u_char buf[2 * SSH_DIGEST_MAX_LENGTH], mdbuf[SSH_DIGEST_MAX_LENGTH]; + struct ssh_digest_ctx *md; + int len; + int dgst; + size_t dgst_len; ++ int rv; ++#ifdef SSH_AUDIT_EVENTS ++ char *fp; ++#endif + + /* don't allow short keys */ + if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + error("%s: RSA modulus too small: %d < minimum %d bits", + __func__, + BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); + return (0); + } +@@ -123,22 +127,28 @@ auth_rsa_verify_response(Key *key, BIGNU + if ((md = ssh_digest_start(dgst)) == NULL || + ssh_digest_update(md, buf, 2 * dgst_len) < 0 || + ssh_digest_update(md, session_id, dgst_len) < 0 || + ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) + fatal("%s: md5 failed", __func__); + ssh_digest_free(md); + + /* Verify that the response is the original challenge. */ +- if (timingsafe_bcmp(response, mdbuf, dgst_len) != 0) { +- /* Wrong answer. */ +- return (0); +- } +- /* Correct answer. */ +- return (1); ++ rv = (timingsafe_bcmp(response, mdbuf, dgst_len) == 0); ++ ++#ifdef SSH_AUDIT_EVENTS ++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); ++ if (audit_keyusage(1, "ssh-rsa1", RSA_size(key->rsa) * 8, fp, rv) == 0) { ++ debug("unsuccessful audit"); ++ rv = 0; ++ } ++ free(fp); ++#endif ++ ++ return rv; + } + + /* + * Performs the RSA authentication challenge-response dialog with the client, + * and returns true (non-zero) if the client gave the correct answer to + * our challenge; returns zero if the client gives a wrong answer. + */ + +diff --git a/openssh-7.2p2/auth.c b/openssh-7.2p2/auth.c +--- a/openssh-7.2p2/auth.c ++++ b/openssh-7.2p2/auth.c +@@ -640,19 +640,16 @@ getpwnamallow(const char *user) + #endif + if (pw == NULL) { + logit("Invalid user %.100s from %.100s", + user, get_remote_ipaddr()); + #ifdef CUSTOM_FAILED_LOGIN + record_failed_login(user, + get_canonical_hostname(options.use_dns), "ssh"); + #endif +-#ifdef SSH_AUDIT_EVENTS +- audit_event(SSH_INVALID_USER); +-#endif /* SSH_AUDIT_EVENTS */ + return (NULL); + } + if (!allowed_user(pw)) + return (NULL); + #ifdef HAVE_LOGIN_CAP + if ((lc = login_getclass(pw->pw_class)) == NULL) { + debug("unable to get login class: %s", user); + return (NULL); +diff --git a/openssh-7.2p2/auth.h b/openssh-7.2p2/auth.h +--- a/openssh-7.2p2/auth.h ++++ b/openssh-7.2p2/auth.h +@@ -187,16 +187,17 @@ int allowed_user(struct passwd *); + struct passwd * getpwnamallow(const char *user); + + char *get_challenge(Authctxt *); + int verify_response(Authctxt *, const char *); + void abandon_challenge_response(Authctxt *); + + char *expand_authorized_keys(const char *, struct passwd *pw); + char *authorized_principals_file(struct passwd *); ++int user_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); + + FILE *auth_openkeyfile(const char *, struct passwd *, int); + FILE *auth_openprincipals(const char *, struct passwd *, int); + int auth_key_is_revoked(Key *); + + HostStatus + check_key_in_hostfiles(struct passwd *, Key *, const char *, + const char *, const char *); +@@ -205,16 +206,17 @@ check_key_in_hostfiles(struct passwd *, + Key *get_hostkey_by_index(int); + Key *get_hostkey_public_by_index(int, struct ssh *); + Key *get_hostkey_public_by_type(int, int, struct ssh *); + Key *get_hostkey_private_by_type(int, int, struct ssh *); + int get_hostkey_index(Key *, int, struct ssh *); + int ssh1_session_key(BIGNUM *); + int sshd_hostkey_sign(Key *, Key *, u_char **, size_t *, + const u_char *, size_t, const char *, u_int); ++int hostbased_key_verify(const Key *, const u_char *, u_int, const u_char *, u_int); + + /* debug messages during authentication */ + void auth_debug_add(const char *fmt,...) __attribute__((format(printf, 1, 2))); + void auth_debug_send(void); + void auth_debug_reset(void); + + struct passwd *fakepw(void); + +diff --git a/openssh-7.2p2/auth2-hostbased.c b/openssh-7.2p2/auth2-hostbased.c +--- a/openssh-7.2p2/auth2-hostbased.c ++++ b/openssh-7.2p2/auth2-hostbased.c +@@ -133,33 +133,45 @@ userauth_hostbased(Authctxt *authctxt) + #endif + + pubkey_auth_info(authctxt, key, + "client user \"%.100s\", client host \"%.100s\"", cuser, chost); + + /* test for allowed key and correct signature */ + authenticated = 0; + if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && +- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), ++ PRIVSEP(hostbased_key_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b))) == 1) + authenticated = 1; + + buffer_free(&b); + done: + debug2("userauth_hostbased: authenticated %d", authenticated); + if (key != NULL) + key_free(key); + free(pkalg); + free(pkblob); + free(cuser); + free(chost); + free(sig); + return authenticated; + } + ++int ++hostbased_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) ++{ ++ int rv; ++ ++ rv = key_verify(key, sig, slen, data, datalen); ++#ifdef SSH_AUDIT_EVENTS ++ audit_key(0, &rv, key); ++#endif ++ return rv; ++} ++ + /* return 1 if given hostkey is allowed */ + int + hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, + Key *key) + { + const char *resolvedname, *ipaddr, *lookup, *reason; + HostStatus host_status; + int len; +diff --git a/openssh-7.2p2/auth2-pubkey.c b/openssh-7.2p2/auth2-pubkey.c +--- a/openssh-7.2p2/auth2-pubkey.c ++++ b/openssh-7.2p2/auth2-pubkey.c +@@ -171,17 +171,17 @@ userauth_pubkey(Authctxt *authctxt) + #ifdef DEBUG_PK + buffer_dump(&b); + #endif + pubkey_auth_info(authctxt, key, NULL); + + /* test for correct signature */ + authenticated = 0; + if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && +- PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), ++ PRIVSEP(user_key_verify(key, sig, slen, buffer_ptr(&b), + buffer_len(&b))) == 1) { + authenticated = 1; + /* Record the successful key to prevent reuse */ + auth2_record_userkey(authctxt, key); + key = NULL; /* Don't free below */ + } + buffer_free(&b); + free(sig); +@@ -251,16 +251,28 @@ pubkey_auth_info(Authctxt *authctxt, con + auth_info(authctxt, "%s %s%s%s", key_type(key), + fp == NULL ? "(null)" : fp, + extra == NULL ? "" : ", ", extra == NULL ? "" : extra); + free(fp); + } + free(extra); + } + ++int ++user_key_verify(const Key *key, const u_char *sig, u_int slen, const u_char *data, u_int datalen) ++{ ++ int rv; ++ ++ rv = key_verify(key, sig, slen, data, datalen); ++#ifdef SSH_AUDIT_EVENTS ++ audit_key(1, &rv, key); ++#endif ++ return rv; ++} ++ + /* + * Splits 's' into an argument vector. Handles quoted string and basic + * escape characters (\\, \", \'). Caller must free the argument vector + * and its members. + */ + static int + split_argv(const char *s, int *argcp, char ***argvp) + { +diff --git a/openssh-7.2p2/auth2.c b/openssh-7.2p2/auth2.c +--- a/openssh-7.2p2/auth2.c ++++ b/openssh-7.2p2/auth2.c +@@ -236,19 +236,16 @@ input_userauth_request(int type, u_int32 + authctxt->pw = PRIVSEP(getpwnamallow(user)); + authctxt->user = xstrdup(user); + if (authctxt->pw && strcmp(service, "ssh-connection")==0) { + authctxt->valid = 1; + debug2("input_userauth_request: setting up authctxt for %s", user); + } else { + logit("input_userauth_request: invalid user %s", user); + authctxt->pw = fakepw(); +-#ifdef SSH_AUDIT_EVENTS +- PRIVSEP(audit_event(SSH_INVALID_USER)); +-#endif + } + #ifdef USE_PAM + if (options.use_pam) + PRIVSEP(start_pam(authctxt)); + #endif + setproctitle("%s%s", authctxt->valid ? user : "unknown", + use_privsep ? " [net]" : ""); + authctxt->service = xstrdup(service); +diff --git a/openssh-7.2p2/cipher.c b/openssh-7.2p2/cipher.c +--- a/openssh-7.2p2/cipher.c ++++ b/openssh-7.2p2/cipher.c +@@ -55,36 +55,16 @@ + #include "log.h" + + #ifdef WITH_SSH1 + extern const EVP_CIPHER *evp_ssh1_bf(void); + extern const EVP_CIPHER *evp_ssh1_3des(void); + extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); + #endif + +-struct sshcipher { +- char *name; +- int number; /* for ssh1 only */ +- u_int block_size; +- u_int key_len; +- u_int iv_len; /* defaults to block_size */ +- u_int auth_len; +- u_int discard_len; +- u_int flags; +-#define CFLAG_CBC (1<<0) +-#define CFLAG_CHACHAPOLY (1<<1) +-#define CFLAG_AESCTR (1<<2) +-#define CFLAG_NONE (1<<3) +-#ifdef WITH_OPENSSL +- const EVP_CIPHER *(*evptype)(void); +-#else +- void *ignored; +-#endif +-}; +- + static const struct sshcipher ciphers_all[] = { + #ifdef WITH_SSH1 + { "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, + { "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, + { "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf }, + #endif /* WITH_SSH1 */ + #ifdef WITH_OPENSSL + { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, +diff --git a/openssh-7.2p2/cipher.h b/openssh-7.2p2/cipher.h +--- a/openssh-7.2p2/cipher.h ++++ b/openssh-7.2p2/cipher.h +@@ -57,17 +57,36 @@ + #define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ + #define SSH_CIPHER_BLOWFISH 6 + #define SSH_CIPHER_RESERVED 7 + #define SSH_CIPHER_MAX 31 + + #define CIPHER_ENCRYPT 1 + #define CIPHER_DECRYPT 0 + +-struct sshcipher; ++struct sshcipher { ++ char *name; ++ int number; /* for ssh1 only */ ++ u_int block_size; ++ u_int key_len; ++ u_int iv_len; /* defaults to block_size */ ++ u_int auth_len; ++ u_int discard_len; ++ u_int flags; ++#define CFLAG_CBC (1<<0) ++#define CFLAG_CHACHAPOLY (1<<1) ++#define CFLAG_AESCTR (1<<2) ++#define CFLAG_NONE (1<<3) ++#ifdef WITH_OPENSSL ++ const EVP_CIPHER *(*evptype)(void); ++#else ++ void *ignored; ++#endif ++}; ++ + struct sshcipher_ctx { + int plaintext; + int encrypt; + EVP_CIPHER_CTX evp; + struct chachapoly_ctx cp_ctx; /* XXX union with evp? */ + struct aesctr_ctx ac_ctx; /* XXX union with evp? */ + const struct sshcipher *cipher; + }; +diff --git a/openssh-7.2p2/kex.c b/openssh-7.2p2/kex.c +--- a/openssh-7.2p2/kex.c ++++ b/openssh-7.2p2/kex.c +@@ -48,16 +48,17 @@ + #include "match.h" + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" ++#include "audit.h" + + #include "fips.h" + + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + + #if OPENSSL_VERSION_NUMBER >= 0x00907000L +@@ -684,18 +685,22 @@ kex_start_rekex(struct ssh *ssh) + return kex_send_kexinit(ssh); + } + + static int + choose_enc(struct sshenc *enc, char *client, char *server) + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(SSH_AUDIT_UNSUPPORTED_CIPHER); ++#endif + return SSH_ERR_NO_CIPHER_ALG_MATCH; ++ } + if ((enc->cipher = cipher_by_name(name)) == NULL) + return SSH_ERR_INTERNAL_ERROR; + enc->name = name; + enc->enabled = 0; + enc->iv = NULL; + enc->iv_len = cipher_ivlen(enc->cipher); + enc->key = NULL; + enc->key_len = cipher_keylen(enc->cipher); +@@ -703,36 +708,44 @@ choose_enc(struct sshenc *enc, char *cli + return 0; + } + + static int + choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(SSH_AUDIT_UNSUPPORTED_MAC); ++#endif + return SSH_ERR_NO_MAC_ALG_MATCH; ++ } + if (mac_setup(mac, name) < 0) + return SSH_ERR_INTERNAL_ERROR; + /* truncate the key */ + if (ssh->compat & SSH_BUG_HMAC) + mac->key_len = 16; + mac->name = name; + mac->key = NULL; + mac->enabled = 0; + return 0; + } + + static int + choose_comp(struct sshcomp *comp, char *client, char *server) + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(SSH_AUDIT_UNSUPPORTED_COMPRESSION); ++#endif + return SSH_ERR_NO_COMPRESS_ALG_MATCH; ++ } + if (strcmp(name, "zlib@openssh.com") == 0) { + comp->type = COMP_DELAYED; + } else if (strcmp(name, "zlib") == 0) { + comp->type = COMP_ZLIB; + } else if (strcmp(name, "none") == 0) { + comp->type = COMP_NONE; + } else { + return SSH_ERR_INTERNAL_ERROR; +@@ -893,16 +906,20 @@ kex_choose_conf(struct ssh *ssh) + need = MAX(need, newkeys->enc.key_len); + need = MAX(need, newkeys->enc.block_size); + need = MAX(need, newkeys->enc.iv_len); + need = MAX(need, newkeys->mac.key_len); + dh_need = MAX(dh_need, cipher_seclen(newkeys->enc.cipher)); + dh_need = MAX(dh_need, newkeys->enc.block_size); + dh_need = MAX(dh_need, newkeys->enc.iv_len); + dh_need = MAX(dh_need, newkeys->mac.key_len); ++ debug("kex: %s need=%d dh_need=%d", kex->name, need, dh_need); ++#ifdef SSH_AUDIT_EVENTS ++ audit_kex(mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); ++#endif + } + /* XXX need runden? */ + kex->we_need = need; + kex->dh_need = dh_need; + + /* ignore the next message if the proposals do not match */ + if (first_kex_follows && !proposals_match(my, peer) && + !(ssh->compat & SSH_BUG_FIRSTKEX)) +@@ -1069,8 +1086,38 @@ derive_ssh1_session_id(BIGNUM *host_modu + #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) + void + dump_digest(char *msg, u_char *digest, int len) + { + fprintf(stderr, "%s\n", msg); + sshbuf_dump_data(digest, len, stderr); + } + #endif ++ ++static void ++enc_destroy(struct sshenc *enc) ++{ ++ if (enc == NULL) ++ return; ++ ++ if (enc->key) { ++ memset(enc->key, 0, enc->key_len); ++ free(enc->key); ++ } ++ ++ if (enc->iv) { ++ memset(enc->iv, 0, enc->iv_len); ++ free(enc->iv); ++ } ++ ++ memset(enc, 0, sizeof(*enc)); ++} ++ ++void ++newkeys_destroy(struct newkeys *newkeys) ++{ ++ if (newkeys == NULL) ++ return; ++ ++ enc_destroy(&newkeys->enc); ++ mac_destroy(&newkeys->mac); ++ memset(&newkeys->comp, 0, sizeof(newkeys->comp)); ++} +diff --git a/openssh-7.2p2/kex.h b/openssh-7.2p2/kex.h +--- a/openssh-7.2p2/kex.h ++++ b/openssh-7.2p2/kex.h +@@ -199,16 +199,18 @@ int kexecdh_server(struct ssh *); + int kexc25519_client(struct ssh *); + int kexc25519_server(struct ssh *); + + #ifdef GSSAPI + int kexgss_client(struct ssh *); + int kexgss_server(struct ssh *); + #endif + ++void newkeys_destroy(struct newkeys *newkeys); ++ + int kex_dh_hash(const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); + + int kexgex_hash(int, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + int, int, int, + const BIGNUM *, const BIGNUM *, const BIGNUM *, +diff --git a/openssh-7.2p2/key.h b/openssh-7.2p2/key.h +--- a/openssh-7.2p2/key.h ++++ b/openssh-7.2p2/key.h +@@ -45,16 +45,17 @@ typedef struct sshkey Key; + #define key_ssh_name_plain sshkey_ssh_name_plain + #define key_type_from_name sshkey_type_from_name + #define key_ecdsa_nid_from_name sshkey_ecdsa_nid_from_name + #define key_type_is_cert sshkey_type_is_cert + #define key_size sshkey_size + #define key_ecdsa_bits_to_nid sshkey_ecdsa_bits_to_nid + #define key_ecdsa_key_to_nid sshkey_ecdsa_key_to_nid + #define key_is_cert sshkey_is_cert ++#define key_is_private sshkey_is_private + #define key_type_plain sshkey_type_plain + #define key_curve_name_to_nid sshkey_curve_name_to_nid + #define key_curve_nid_to_bits sshkey_curve_nid_to_bits + #define key_curve_nid_to_name sshkey_curve_nid_to_name + #define key_ec_nid_to_hash_alg sshkey_ec_nid_to_hash_alg + #define key_dump_ec_point sshkey_dump_ec_point + #define key_dump_ec_key sshkey_dump_ec_key + #endif +diff --git a/openssh-7.2p2/mac.c b/openssh-7.2p2/mac.c +--- a/openssh-7.2p2/mac.c ++++ b/openssh-7.2p2/mac.c +@@ -259,16 +259,30 @@ mac_clear(struct sshmac *mac) + if (mac->umac_ctx != NULL) + umac128_delete(mac->umac_ctx); + } else if (mac->hmac_ctx != NULL) + ssh_hmac_free(mac->hmac_ctx); + mac->hmac_ctx = NULL; + mac->umac_ctx = NULL; + } + ++void ++mac_destroy(struct sshmac *mac) ++{ ++ if (mac == NULL) ++ return; ++ ++ if (mac->key) { ++ memset(mac->key, 0, mac->key_len); ++ free(mac->key); ++ } ++ ++ memset(mac, 0, sizeof(*mac)); ++} ++ + /* XXX copied from ciphers_valid */ + #define MAC_SEP "," + int + mac_valid(const char *names) + { + char *maclist, *cp, *p; + + if (names == NULL || strcmp(names, "") == 0) +diff --git a/openssh-7.2p2/mac.h b/openssh-7.2p2/mac.h +--- a/openssh-7.2p2/mac.h ++++ b/openssh-7.2p2/mac.h +@@ -42,10 +42,11 @@ struct sshmac { + + int mac_valid(const char *); + char *mac_alg_list(char); + int mac_setup(struct sshmac *, char *); + int mac_init(struct sshmac *); + int mac_compute(struct sshmac *, u_int32_t, const u_char *, int, + u_char *, size_t); + void mac_clear(struct sshmac *); ++void mac_destroy(struct sshmac *); + + #endif /* SSHMAC_H */ +diff --git a/openssh-7.2p2/monitor.c b/openssh-7.2p2/monitor.c +--- a/openssh-7.2p2/monitor.c ++++ b/openssh-7.2p2/monitor.c +@@ -96,31 +96,34 @@ + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" + #include "monitor_fdpass.h" + #include "compat.h" + #include "ssh2.h" + #include "authfd.h" ++#include "audit.h" + #include "match.h" + #include "ssherr.h" + + #ifdef GSSAPI + static Gssctxt *gsscontext = NULL; + #endif + + /* Imports */ + extern ServerOptions options; + extern u_int utmp_len; + extern u_char session_id[]; + extern Buffer auth_debug; + extern int auth_debug_init; + extern Buffer loginmsg; + ++extern void destroy_sensitive_data(int); ++ + /* State exported from the child */ + static struct sshbuf *child_state; + + /* Functions on the monitor that answer unprivileged requests */ + + int mm_answer_moduli(int, Buffer *); + int mm_answer_sign(int, Buffer *); + int mm_answer_pwnamallow(int, Buffer *); +@@ -158,16 +161,21 @@ int mm_answer_gss_userok(int, Buffer *); + int mm_answer_gss_checkmic(int, Buffer *); + int mm_answer_gss_sign(int, Buffer *); + int mm_answer_gss_updatecreds(int, Buffer *); + #endif + + #ifdef SSH_AUDIT_EVENTS + int mm_answer_audit_event(int, Buffer *); + int mm_answer_audit_command(int, Buffer *); ++int mm_answer_audit_end_command(int, Buffer *); ++int mm_answer_audit_unsupported_body(int, Buffer *); ++int mm_answer_audit_kex_body(int, Buffer *); ++int mm_answer_audit_session_key_free_body(int, Buffer *); ++int mm_answer_audit_server_key_free(int, Buffer *); + #endif + + static int monitor_read_log(struct monitor *); + + static Authctxt *authctxt; + + #ifdef WITH_SSH1 + static BIGNUM *ssh1_challenge = NULL; /* used for ssh1 rsa auth */ +@@ -214,16 +222,20 @@ struct mon_table mon_dispatch_proto20[] + {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, + {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, + {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, + {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond}, + {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, + #endif + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #ifdef BSD_AUTH + {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, + {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, + #endif + #ifdef SKEY + {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, + {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, +@@ -252,16 +264,21 @@ struct mon_table mon_dispatch_postauth20 + #endif + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, + {MONITOR_REQ_TERM, 0, mm_answer_term}, + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, + {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, ++ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_proto15[] = { + #ifdef WITH_SSH1 + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_SESSKEY, MON_ONCE, mm_answer_sesskey}, +@@ -284,29 +301,38 @@ struct mon_table mon_dispatch_proto15[] + {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, + {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, + {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, + {MONITOR_REQ_PAM_RESPOND, MON_ISAUTH, mm_answer_pam_respond}, + {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, + #endif + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #endif /* WITH_SSH1 */ + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_postauth15[] = { + #ifdef WITH_SSH1 + {MONITOR_REQ_PTY, MON_ONCE, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, MON_ONCE, mm_answer_pty_cleanup}, + {MONITOR_REQ_TERM, 0, mm_answer_term}, + #ifdef SSH_AUDIT_EVENTS + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, + {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT|MON_ONCE, mm_answer_audit_command}, ++ {MONITOR_REQ_AUDIT_END_COMMAND, MON_PERMIT, mm_answer_audit_end_command}, ++ {MONITOR_REQ_AUDIT_UNSUPPORTED, MON_PERMIT, mm_answer_audit_unsupported_body}, ++ {MONITOR_REQ_AUDIT_KEX, MON_PERMIT, mm_answer_audit_kex_body}, ++ {MONITOR_REQ_AUDIT_SESSION_KEY_FREE, MON_PERMIT, mm_answer_audit_session_key_free_body}, ++ {MONITOR_REQ_AUDIT_SERVER_KEY_FREE, MON_PERMIT, mm_answer_audit_server_key_free}, + #endif + #endif /* WITH_SSH1 */ + {0, 0, NULL} + }; + + struct mon_table *mon_dispatch; + + /* Specifies if a certain message is allowed at the moment */ +@@ -1423,26 +1449,30 @@ monitor_valid_hostbasedblob(u_char *data + } + + int + mm_answer_keyverify(int sock, Buffer *m) + { + Key *key; + u_char *signature, *data, *blob; + u_int signaturelen, datalen, bloblen; ++ int type = 0; + int verified = 0; + int valid_data = 0; + ++ type = buffer_get_int(m); + blob = buffer_get_string(m, &bloblen); + signature = buffer_get_string(m, &signaturelen); + data = buffer_get_string(m, &datalen); + + if (hostbased_cuser == NULL || hostbased_chost == NULL || + !monitor_allowed_key(blob, bloblen)) + fatal("%s: bad key, not previously allowed", __func__); ++ if (type != key_blobtype) ++ fatal("%s: bad key type", __func__); + + key = key_from_blob(blob, bloblen); + if (key == NULL) + fatal("%s: bad public key blob", __func__); + + switch (key_blobtype) { + case MM_USERKEY: + valid_data = monitor_valid_userblob(data, datalen); +@@ -1453,17 +1483,27 @@ mm_answer_keyverify(int sock, Buffer *m) + break; + default: + valid_data = 0; + break; + } + if (!valid_data) + fatal("%s: bad signature data blob", __func__); + +- verified = key_verify(key, signature, signaturelen, data, datalen); ++ switch (key_blobtype) { ++ case MM_USERKEY: ++ verified = user_key_verify(key, signature, signaturelen, data, datalen); ++ break; ++ case MM_HOSTKEY: ++ verified = hostbased_key_verify(key, signature, signaturelen, data, datalen); ++ break; ++ default: ++ verified = 0; ++ break; ++ } + debug3("%s: key %p signature %s", + __func__, key, (verified == 1) ? "verified" : "unverified"); + + /* If auth was successful then record key to ensure it isn't reused */ + if (verified == 1 && key_blobtype == MM_USERKEY) + auth2_record_userkey(authctxt, key); + else + key_free(key); +@@ -1514,16 +1554,22 @@ mm_record_login(Session *s, struct passw + static void + mm_session_close(Session *s) + { + debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid); + if (s->ttyfd != -1) { + debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); + session_pty_cleanup2(s); + } ++#ifdef SSH_AUDIT_EVENTS ++ if (s->command != NULL) { ++ debug3("%s: command %d", __func__, s->command_handle); ++ session_end_command2(s); ++ } ++#endif + session_unused(s->self); + } + + int + mm_answer_pty(int sock, Buffer *m) + { + extern struct monitor *pmonitor; + Session *s; +@@ -1796,16 +1842,18 @@ mm_answer_term(int sock, Buffer *req) + /* The child is terminating */ + session_destroy_all(&mm_session_close); + + #ifdef USE_PAM + if (options.use_pam) + sshpam_cleanup(); + #endif + ++ destroy_sensitive_data(0); ++ + while (waitpid(pmonitor->m_pid, &status, 0) == -1) + if (errno != EINTR) + exit(1); + + res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; + + /* Terminate process */ + exit(res); +@@ -1838,21 +1886,53 @@ mm_answer_audit_event(int socket, Buffer + return (0); + } + + int + mm_answer_audit_command(int socket, Buffer *m) + { + u_int len; + char *cmd; ++ Session *s; + + debug3("%s entering", __func__); + cmd = buffer_get_string(m, &len); ++ + /* sanity check command, if so how? */ +- audit_run_command(cmd); ++ s = session_new(); ++ if (s == NULL) ++ fatal("%s: error allocating a session", __func__); ++ s->command = cmd; ++ s->command_handle = audit_run_command(cmd); ++ ++ buffer_clear(m); ++ buffer_put_int(m, s->self); ++ ++ mm_request_send(socket, MONITOR_ANS_AUDIT_COMMAND, m); ++ ++ return (0); ++} ++ ++int ++mm_answer_audit_end_command(int socket, Buffer *m) ++{ ++ int handle; ++ u_int len; ++ char *cmd; ++ Session *s; ++ ++ debug3("%s entering", __func__); ++ handle = buffer_get_int(m); ++ cmd = buffer_get_string(m, &len); ++ ++ s = session_by_id(handle); ++ if (s == NULL || s->ttyfd != -1 || s->command == NULL || ++ strcmp(s->command, cmd) != 0) ++ fatal("%s: invalid handle", __func__); ++ mm_session_close(s); + free(cmd); + return (0); + } + #endif /* SSH_AUDIT_EVENTS */ + + void + monitor_apply_keystate(struct monitor *pmonitor) + { +@@ -1899,23 +1979,39 @@ monitor_apply_keystate(struct monitor *p + } + } + + /* This function requries careful sanity checking */ + + void + mm_get_keystate(struct monitor *pmonitor) + { ++ Buffer m; + debug3("%s: Waiting for new keys", __func__); + + if ((child_state = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, + child_state); + debug3("%s: GOT new keys", __func__); ++ ++#ifdef SSH_AUDIT_EVENTS ++ if (compat20) { ++ buffer_init(&m); ++ mm_request_receive_expect(pmonitor->m_sendfd, ++ MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); ++ mm_answer_audit_session_key_free_body(pmonitor->m_sendfd, &m); ++ buffer_free(&m); ++ } ++#endif ++ ++ /* Drain any buffered messages from the child */ ++ while (pmonitor->m_log_recvfd >= 0 && monitor_read_log(pmonitor) == 0) ++ ; ++ + } + + + /* XXX */ + + #define FD_CLOSEONEXEC(x) do { \ + if (fcntl(x, F_SETFD, FD_CLOEXEC) == -1) \ + fatal("fcntl(%d, F_SETFD)", x); \ +@@ -2157,8 +2253,91 @@ mm_answer_gss_updatecreds(int socket, Bu + + mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); + + return(0); + } + + #endif /* GSSAPI */ + ++#ifdef SSH_AUDIT_EVENTS ++int ++mm_answer_audit_unsupported_body(int sock, Buffer *m) ++{ ++ int what; ++ ++ what = buffer_get_int(m); ++ ++ audit_unsupported_body(what); ++ ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_UNSUPPORTED, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_kex_body(int sock, Buffer *m) ++{ ++ int ctos, len; ++ char *cipher, *mac, *compress, *pfs; ++ pid_t pid; ++ uid_t uid; ++ ++ ctos = buffer_get_int(m); ++ cipher = buffer_get_string(m, &len); ++ mac = buffer_get_string(m, &len); ++ compress = buffer_get_string(m, &len); ++ pfs = buffer_get_string(m, &len); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); ++ ++ free(cipher); ++ free(mac); ++ free(compress); ++ free(pfs); ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_KEX, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_session_key_free_body(int sock, Buffer *m) ++{ ++ int ctos; ++ pid_t pid; ++ uid_t uid; ++ ++ ctos = buffer_get_int(m); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_session_key_free_body(ctos, pid, uid); ++ ++ buffer_clear(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_server_key_free(int sock, Buffer *m) ++{ ++ int len; ++ char *fp; ++ pid_t pid; ++ uid_t uid; ++ ++ fp = buffer_get_string(m, &len); ++ pid = buffer_get_int64(m); ++ uid = buffer_get_int64(m); ++ ++ audit_destroy_sensitive_data(fp, pid, uid); ++ ++ free(fp); ++ buffer_clear(m); ++ ++ return 0; ++} ++#endif /* SSH_AUDIT_EVENTS */ +diff --git a/openssh-7.2p2/monitor.h b/openssh-7.2p2/monitor.h +--- a/openssh-7.2p2/monitor.h ++++ b/openssh-7.2p2/monitor.h +@@ -58,17 +58,23 @@ enum monitor_reqtype { + MONITOR_REQ_TERM = 50, + + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, + MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105, + MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, + MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, +- MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, ++ MONITOR_REQ_AUDIT_EVENT = 112, ++ MONITOR_REQ_AUDIT_COMMAND = 114, MONITOR_ANS_AUDIT_COMMAND = 115, ++ MONITOR_REQ_AUDIT_END_COMMAND = 116, ++ MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, ++ MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, ++ MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, ++ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124, + + MONITOR_REQ_GSSSIGN = 201, MONITOR_ANS_GSSSIGN = 202, + MONITOR_REQ_GSSUPCREDS = 203, MONITOR_ANS_GSSUPCREDS = 204, + + }; + + struct mm_master; + struct monitor { +diff --git a/openssh-7.2p2/monitor_wrap.c b/openssh-7.2p2/monitor_wrap.c +--- a/openssh-7.2p2/monitor_wrap.c ++++ b/openssh-7.2p2/monitor_wrap.c +@@ -438,30 +438,31 @@ mm_key_allowed(enum mm_keytype type, cha + + /* + * This key verify needs to send the key type along, because the + * privileged parent makes the decision if the key is allowed + * for authentication. + */ + + int +-mm_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++mm_key_verify(enum mm_keytype type, Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) + { + Buffer m; + u_char *blob; + u_int len; + int verified = 0; + + debug3("%s entering", __func__); + + /* Convert the key to a blob and the pass it over */ + if (!key_to_blob(key, &blob, &len)) + return (0); + + buffer_init(&m); ++ buffer_put_int(&m, type); + buffer_put_string(&m, blob, len); + buffer_put_string(&m, sig, siglen); + buffer_put_string(&m, data, datalen); + free(blob); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_KEYVERIFY, &m); + + debug3("%s: waiting for MONITOR_ANS_KEYVERIFY", __func__); +@@ -469,16 +470,28 @@ mm_key_verify(Key *key, u_char *sig, u_i + + verified = buffer_get_int(&m); + + buffer_free(&m); + + return (verified); + } + ++int ++mm_hostbased_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++{ ++ return mm_key_verify(MM_HOSTKEY, key, sig, siglen, data, datalen); ++} ++ ++int ++mm_user_key_verify(Key *key, u_char *sig, u_int siglen, u_char *data, u_int datalen) ++{ ++ return mm_key_verify(MM_USERKEY, key, sig, siglen, data, datalen); ++} ++ + void + mm_send_keystate(struct monitor *monitor) + { + struct ssh *ssh = active_state; /* XXX */ + struct sshbuf *m; + int r; + + if ((m = sshbuf_new()) == NULL) +@@ -981,27 +994,48 @@ mm_audit_event(ssh_audit_event_t event) + + buffer_init(&m); + buffer_put_int(&m, event); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_EVENT, &m); + buffer_free(&m); + } + +-void ++int + mm_audit_run_command(const char *command) + { + Buffer m; ++ int handle; + + debug3("%s entering command %s", __func__, command); + + buffer_init(&m); + buffer_put_cstring(&m, command); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_COMMAND, &m); ++ ++ handle = buffer_get_int(&m); ++ buffer_free(&m); ++ ++ return (handle); ++} ++ ++void ++mm_audit_end_command(int handle, const char *command) ++{ ++ Buffer m; ++ ++ debug3("%s entering command %s", __func__, command); ++ ++ buffer_init(&m); ++ buffer_put_int(&m, handle); ++ buffer_put_cstring(&m, command); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, &m); + buffer_free(&m); + } + #endif /* SSH_AUDIT_EVENTS */ + + #ifdef GSSAPI + OM_uint32 + mm_ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID goid) + { +@@ -1127,8 +1161,119 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc + + buffer_free(&m); + + return (ok); + } + + #endif /* GSSAPI */ + ++#ifdef SSH_AUDIT_EVENTS ++void ++mm_audit_unsupported_body(int what) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, what); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_UNSUPPORTED, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_UNSUPPORTED, ++ &m); ++ ++ buffer_free(&m); ++} ++ ++void ++mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, ++ uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, ctos); ++ buffer_put_cstring(&m, cipher); ++ buffer_put_cstring(&m, (mac ? mac : "")); ++ buffer_put_cstring(&m, compress); ++ buffer_put_cstring(&m, fps); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_KEX, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_KEX, ++ &m); ++ ++ buffer_free(&m); ++} ++ ++void ++mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_int(&m, ctos); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SESSION_KEY_FREE, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, ++ &m); ++ buffer_free(&m); ++} ++ ++void ++mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ Buffer m; ++ ++ buffer_init(&m); ++ buffer_put_cstring(&m, fp); ++ buffer_put_int64(&m, pid); ++ buffer_put_int64(&m, uid); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, &m); ++ buffer_free(&m); ++} ++ ++int mm_forward_audit_messages(int fdin) ++{ ++ u_char buf[4]; ++ u_int blen, msg_len; ++ Buffer m; ++ int ret = 0; ++ ++ debug3("%s: entering", __func__); ++ buffer_init(&m); ++ do { ++ blen = atomicio(read, fdin, buf, sizeof(buf)); ++ if (blen == 0) /* closed pipe */ ++ break; ++ if (blen != sizeof(buf)) { ++ error("%s: Failed to read the buffer from child", __func__); ++ ret = -1; ++ break; ++ } ++ ++ msg_len = get_u32(buf); ++ if (msg_len > 256 * 1024) ++ fatal("%s: read: bad msg_len %d", __func__, msg_len); ++ buffer_clear(&m); ++ buffer_append_space(&m, msg_len); ++ if (atomicio(read, fdin, buffer_ptr(&m), msg_len) != msg_len) { ++ error("%s: Failed to read the the buffer conent from the child", __func__); ++ ret = -1; ++ break; ++ } ++ if (atomicio(vwrite, pmonitor->m_recvfd, buf, blen) != blen || ++ atomicio(vwrite, pmonitor->m_recvfd, buffer_ptr(&m), msg_len) != msg_len) { ++ error("%s: Failed to write the messag to the monitor", __func__); ++ ret = -1; ++ break; ++ } ++ } while (1); ++ buffer_free(&m); ++ return ret; ++} ++void mm_set_monitor_pipe(int fd) ++{ ++ pmonitor->m_recvfd = fd; ++} ++#endif /* SSH_AUDIT_EVENTS */ +diff --git a/openssh-7.2p2/monitor_wrap.h b/openssh-7.2p2/monitor_wrap.h +--- a/openssh-7.2p2/monitor_wrap.h ++++ b/openssh-7.2p2/monitor_wrap.h +@@ -44,17 +44,18 @@ int mm_key_sign(Key *, u_char **, u_int + void mm_inform_authserv(char *, char *); + struct passwd *mm_getpwnamallow(const char *); + char *mm_auth2_read_banner(void); + int mm_auth_password(struct Authctxt *, char *); + int mm_key_allowed(enum mm_keytype, char *, char *, Key *, int); + int mm_user_key_allowed(struct passwd *, Key *, int); + int mm_hostbased_key_allowed(struct passwd *, char *, char *, Key *); + int mm_auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *); +-int mm_key_verify(Key *, u_char *, u_int, u_char *, u_int); ++int mm_hostbased_key_verify(Key *, u_char *, u_int, u_char *, u_int); ++int mm_user_key_verify(Key *, u_char *, u_int, u_char *, u_int); + int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); + int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *); + BIGNUM *mm_auth_rsa_generate_challenge(Key *); + + #ifdef GSSAPI + OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); + OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); +@@ -71,17 +72,24 @@ void *mm_sshpam_init_ctx(struct Authctxt + int mm_sshpam_query(void *, char **, char **, u_int *, char ***, u_int **); + int mm_sshpam_respond(void *, u_int, char **); + void mm_sshpam_free_ctx(void *); + #endif + + #ifdef SSH_AUDIT_EVENTS + #include "audit.h" + void mm_audit_event(ssh_audit_event_t); +-void mm_audit_run_command(const char *); ++int mm_audit_run_command(const char *); ++void mm_audit_end_command(int, const char *); ++void mm_audit_unsupported_body(int); ++void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); ++void mm_audit_session_key_free_body(int, pid_t, uid_t); ++void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++int mm_forward_audit_messages(int); ++void mm_set_monitor_pipe(int); + #endif + + struct Session; + void mm_terminate(void); + int mm_pty_allocate(int *, int *, char *, size_t); + void mm_session_pty_cleanup2(struct Session *); + + /* SSHv1 interfaces */ +diff --git a/openssh-7.2p2/packet.c b/openssh-7.2p2/packet.c +--- a/openssh-7.2p2/packet.c ++++ b/openssh-7.2p2/packet.c +@@ -62,16 +62,17 @@ + #include + + #include + + #include "buffer.h" /* typedefs XXX */ + #include "key.h" /* typedefs XXX */ + + #include "xmalloc.h" ++#include "audit.h" + #include "crc32.h" + #include "deattack.h" + #include "compat.h" + #include "ssh1.h" + #include "ssh2.h" + #include "cipher.h" + #include "sshkey.h" + #include "kex.h" +@@ -449,16 +450,23 @@ ssh_packet_get_connection_in(struct ssh + /* Returns the descriptor used for writing. */ + + int + ssh_packet_get_connection_out(struct ssh *ssh) + { + return ssh->state->connection_out; + } + ++static int ++packet_state_has_keys (const struct session_state *state) ++{ ++ return state != NULL && ++ (state->newkeys[MODE_IN] != NULL || state->newkeys[MODE_OUT] != NULL); ++} ++ + /* + * Returns the IP-address of the remote host as a string. The returned + * string must not be freed. + */ + + const char * + ssh_remote_ipaddr(struct ssh *ssh) + { +@@ -493,23 +501,16 @@ ssh_packet_close(struct ssh *ssh) + { + struct session_state *state = ssh->state; + int r; + u_int mode; + + if (!state->initialized) + return; + state->initialized = 0; +- if (state->connection_in == state->connection_out) { +- shutdown(state->connection_out, SHUT_RDWR); +- close(state->connection_out); +- } else { +- close(state->connection_in); +- close(state->connection_out); +- } + sshbuf_free(state->input); + sshbuf_free(state->output); + sshbuf_free(state->outgoing_packet); + sshbuf_free(state->incoming_packet); + for (mode = 0; mode < MODE_MAX; mode++) + kex_free_newkeys(state->newkeys[mode]); + if (state->compression_buffer) { + sshbuf_free(state->compression_buffer); +@@ -531,22 +532,32 @@ ssh_packet_close(struct ssh *ssh) + (unsigned long long)stream->total_out, + (unsigned long long)stream->total_in, + stream->total_out == 0 ? 0.0 : + (double) stream->total_in / stream->total_out); + if (state->compression_in_failures == 0) + inflateEnd(stream); + } + } +- if ((r = cipher_cleanup(&state->send_context)) != 0) +- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); +- if ((r = cipher_cleanup(&state->receive_context)) != 0) +- error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ if (packet_state_has_keys(state)) { ++ if ((r = cipher_cleanup(&state->send_context)) != 0) ++ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ if ((r = cipher_cleanup(&state->receive_context)) != 0) ++ error("%s: cipher_cleanup failed: %s", __func__, ssh_err(r)); ++ audit_session_key_free(MODE_OUT); ++ } + free(ssh->remote_ipaddr); + ssh->remote_ipaddr = NULL; ++ if (state->connection_in == state->connection_out) { ++ shutdown(state->connection_out, SHUT_RDWR); ++ close(state->connection_out); ++ } else { ++ close(state->connection_in); ++ close(state->connection_out); ++ } + free(ssh->state); + ssh->state = NULL; + } + + /* Sets remote side protocol flags. */ + + void + ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags) +@@ -961,16 +972,17 @@ ssh_set_newkeys(struct ssh *ssh, int mod + } + if (state->newkeys[mode] != NULL) { + debug("set_newkeys: rekeying, input %llu bytes %llu blocks, " + "output %llu bytes %llu blocks", + (unsigned long long)state->p_read.bytes, + (unsigned long long)state->p_read.blocks, + (unsigned long long)state->p_send.bytes, + (unsigned long long)state->p_send.blocks); ++ audit_session_key_free(mode); + if ((r = cipher_cleanup(cc)) != 0) + return r; + enc = &state->newkeys[mode]->enc; + mac = &state->newkeys[mode]->mac; + comp = &state->newkeys[mode]->comp; + mac_clear(mac); + explicit_bzero(enc->iv, enc->iv_len); + explicit_bzero(enc->key, enc->key_len); +@@ -2401,16 +2413,82 @@ ssh_packet_get_input(struct ssh *ssh) + } + + void * + ssh_packet_get_output(struct ssh *ssh) + { + return (void *)ssh->state->output; + } + ++static void ++newkeys_destroy_and_free(struct newkeys *newkeys) ++{ ++ if (newkeys == NULL) ++ return; ++ ++ free(newkeys->enc.name); ++ ++ if (newkeys->mac.enabled) { ++ mac_clear(&newkeys->mac); ++ free(newkeys->mac.name); ++ } ++ ++ free(newkeys->comp.name); ++ ++ newkeys_destroy(newkeys); ++ free(newkeys); ++} ++ ++static void ++packet_destroy_state(struct session_state *state) ++{ ++ if (state == NULL) ++ return; ++ ++ cipher_cleanup(&state->receive_context); ++ cipher_cleanup(&state->send_context); ++ ++ buffer_free(state->input); ++ state->input = NULL; ++ buffer_free(state->output); ++ state->output = NULL; ++ buffer_free(state->outgoing_packet); ++ state->outgoing_packet = NULL; ++ buffer_free(state->incoming_packet); ++ state->incoming_packet = NULL; ++ if( state->compression_buffer ) { ++ buffer_free(state->compression_buffer); ++ state->compression_buffer = NULL; ++ } ++ newkeys_destroy_and_free(state->newkeys[MODE_IN]); ++ state->newkeys[MODE_IN] = NULL; ++ newkeys_destroy_and_free(state->newkeys[MODE_OUT]); ++ state->newkeys[MODE_OUT] = NULL; ++ mac_destroy(state->packet_discard_mac); ++// TAILQ_HEAD(, packet) outgoing; ++// memset(state, 0, sizeof(state)); ++} ++ ++void ++packet_destroy_all(int audit_it, int privsep) ++{ ++ if (audit_it) ++ audit_it = (active_state != NULL && packet_state_has_keys(active_state->state)); ++ if (active_state != NULL) ++ packet_destroy_state(active_state->state); ++ if (audit_it) { ++#ifdef SSH_AUDIT_EVENTS ++ if (privsep) ++ audit_session_key_free(MODE_OUT); ++ else ++ audit_session_key_free_body(MODE_OUT, getpid(), getuid()); ++#endif ++ } ++} ++ + /* Reset after_authentication and reset compression in post-auth privsep */ + static int + ssh_packet_set_postauth(struct ssh *ssh) + { + struct sshcomp *comp; + int r, mode; + + debug("%s: called", __func__); +diff --git a/openssh-7.2p2/packet.h b/openssh-7.2p2/packet.h +--- a/openssh-7.2p2/packet.h ++++ b/openssh-7.2p2/packet.h +@@ -195,9 +195,10 @@ extern struct ssh *active_state; + # undef EC_GROUP + # undef EC_POINT + #elif !defined(OPENSSL_HAS_ECC) + # undef EC_KEY + # undef EC_GROUP + # undef EC_POINT + #endif + ++void packet_destroy_all(int, int); + #endif /* PACKET_H */ +diff --git a/openssh-7.2p2/sandbox-seccomp-filter.c b/openssh-7.2p2/sandbox-seccomp-filter.c +--- a/openssh-7.2p2/sandbox-seccomp-filter.c ++++ b/openssh-7.2p2/sandbox-seccomp-filter.c +@@ -157,16 +157,22 @@ static const struct sock_filter preauth_ + SC_ALLOW(getuid32), + #endif + #ifdef __NR_getrandom + SC_ALLOW(getrandom), + #endif + #ifdef __NR_gettimeofday + SC_ALLOW(gettimeofday), + #endif ++#ifdef SSH_AUDIT_EVENTS ++ SC_ALLOW(getuid), ++#ifdef __NR_getuid32 /* not defined on x86_64 */ ++ SC_ALLOW(getuid32), ++#endif ++#endif + #ifdef __NR_madvise + SC_ALLOW(madvise), + #endif + #ifdef __NR_mmap + SC_ALLOW(mmap), + #endif + #ifdef __NR_mmap2 + SC_ALLOW(mmap2), +diff --git a/openssh-7.2p2/session.c b/openssh-7.2p2/session.c +--- a/openssh-7.2p2/session.c ++++ b/openssh-7.2p2/session.c +@@ -135,17 +135,17 @@ static int session_pty_req(Session *); + + /* import */ + extern ServerOptions options; + extern char *__progname; + extern int log_stderr; + extern int debug_flag; + extern u_int utmp_len; + extern int startup_pipe; +-extern void destroy_sensitive_data(void); ++extern void destroy_sensitive_data(int); + extern Buffer loginmsg; + + /* original command from peer. */ + const char *original_command = NULL; + + /* data */ + static int sessions_first_unused = -1; + static int sessions_nalloc = 0; +@@ -155,16 +155,20 @@ static Session *sessions = NULL; + #define SUBSYSTEM_EXT 1 + #define SUBSYSTEM_INT_SFTP 2 + #define SUBSYSTEM_INT_SFTP_ERROR 3 + + #ifdef HAVE_LOGIN_CAP + login_cap_t *lc; + #endif + ++#ifdef SSH_AUDIT_EVENTS ++int paudit[2]; ++#endif ++ + static int is_child = 0; + static int in_chroot = 0; + + /* Name and directory of socket for authentication agent forwarding. */ + static char *auth_sock_name = NULL; + static char *auth_sock_dir = NULL; + + /* removes the agent forwarding socket */ +@@ -747,16 +751,24 @@ do_exec_pty(Session *s, const char *comm + cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); + #endif + + s->pid = pid; + + /* Parent. Close the slave side of the pseudo tty. */ + close(ttyfd); + ++#if !defined(HAVE_OSF_SIA) && defined(SSH_AUDIT_EVENTS) ++ /* do_login in the child did not affect state in this process, ++ compensate. From an architectural standpoint, this is extremely ++ ugly. */ ++ if (!(options.use_login && command == NULL)) ++ audit_count_session_open(); ++#endif ++ + /* Enter interactive session. */ + s->ptymaster = ptymaster; + packet_set_interactive(1, + options.ip_qos_interactive, options.ip_qos_bulk); + if (compat20) { + session_set_fds(s, ptyfd, fdout, -1, 1, 1); + } else { + server_loop(pid, ptyfd, fdout, -1); +@@ -842,40 +854,60 @@ do_exec(Session *s, const char *command) + tty == NULL ? "" : " on ", + tty == NULL ? "" : tty, + s->pw->pw_name, + get_remote_ipaddr(), + get_remote_port(), + s->self); + + #ifdef SSH_AUDIT_EVENTS ++ if (s->command != NULL || s->command_handle != -1) ++ fatal("do_exec: command already set"); + if (command != NULL) +- PRIVSEP(audit_run_command(command)); ++ s->command = xstrdup(command); + else if (s->ttyfd == -1) { + char *shell = s->pw->pw_shell; + + if (shell[0] == '\0') /* empty shell means /bin/sh */ + shell =_PATH_BSHELL; +- PRIVSEP(audit_run_command(shell)); ++ s->command = xstrdup(shell); + } ++ if (s->command != NULL && s->ptyfd == -1) ++ s->command_handle = PRIVSEP(audit_run_command(s->command)); ++ if (pipe(paudit) < 0) ++ fatal("pipe: %s", strerror(errno)); + #endif + if (s->ttyfd != -1) + ret = do_exec_pty(s, command); + else + ret = do_exec_no_pty(s, command); + + original_command = NULL; + + /* + * Clear loginmsg: it's the child's responsibility to display + * it to the user, otherwise multiple sessions may accumulate + * multiple copies of the login messages. + */ + buffer_clear(&loginmsg); + ++#ifdef SSH_AUDIT_EVENTS ++ close(paudit[1]); ++ if (use_privsep && ret == 0) { ++ /* ++ * Read the audit messages from forked child and send them ++ * back to monitor. We don't want to communicate directly, ++ * because the messages might get mixed up. ++ * Continue after the pipe gets closed (all messages sent). ++ */ ++ ret = mm_forward_audit_messages(paudit[0]); ++ } ++ close(paudit[0]); ++#endif /* SSH_AUDIT_EVENTS */ ++ + return ret; + } + + /* administrative, login(1)-like work */ + void + do_login(Session *s, const char *command) + { + socklen_t fromlen; +@@ -1697,18 +1729,37 @@ do_child(Session *s, const char *command + extern char **environ; + char **env; + int env_size; + char *argv[ARGV_MAX]; + const char *shell, *shell0, *hostname = NULL; + struct passwd *pw = s->pw; + int r = 0; + ++#ifdef SSH_AUDIT_EVENTS ++ int pparent = paudit[1]; ++ close(paudit[0]); ++ /* Hack the monitor pipe to avoid race condition with parent */ ++ if (use_privsep) ++ mm_set_monitor_pipe(pparent); ++#endif ++ + /* remove hostkey from the child's memory */ +- destroy_sensitive_data(); ++ destroy_sensitive_data(use_privsep); ++ /* ++ * We can audit this, because wer hacked the pipe to direct the ++ * messages over postauth child. But this message requires answer ++ * which we can't do using one-way pipe. ++ */ ++ packet_destroy_all(0, 1); ++ ++#ifdef SSH_AUDIT_EVENTS ++ /* Notify parent that we are done */ ++ close(pparent); ++#endif + + /* Force a password change */ + if (s->authctxt->force_pwchange) { + do_setusercontext(pw); + child_close_fds(); + do_pwchange(s); + exit(1); + } +@@ -1925,16 +1976,19 @@ session_unused(int id) + memset(&sessions[id], 0, sizeof(*sessions)); + sessions[id].self = id; + sessions[id].used = 0; + sessions[id].chanid = -1; + sessions[id].ptyfd = -1; + sessions[id].ttyfd = -1; + sessions[id].ptymaster = -1; + sessions[id].x11_chanids = NULL; ++#ifdef SSH_AUDIT_EVENTS ++ sessions[id].command_handle = -1; ++#endif + sessions[id].next_unused = sessions_first_unused; + sessions_first_unused = id; + } + + Session * + session_new(void) + { + Session *s, *tmp; +@@ -2007,16 +2061,29 @@ session_open(Authctxt *authctxt, int cha + if (s->pw == NULL || !authctxt->valid) + fatal("no user for session %d", s->self); + debug("session_open: session %d: link with channel %d", s->self, chanid); + s->chanid = chanid; + return 1; + } + + Session * ++session_by_id(int id) ++{ ++ if (id >= 0 && id < sessions_nalloc) { ++ Session *s = &sessions[id]; ++ if (s->used) ++ return s; ++ } ++ debug("%s: unknown id %d", __func__, id); ++ session_dump(); ++ return NULL; ++} ++ ++Session * + session_by_tty(char *tty) + { + int i; + for (i = 0; i < sessions_nalloc; i++) { + Session *s = &sessions[i]; + if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { + debug("session_by_tty: session %d tty %s", i, tty); + return s; +@@ -2529,16 +2596,42 @@ session_exit_message(Session *s, int sta + * interested in data we write. + * Note that we must not call 'chan_read_failed', since there could + * be some more data waiting in the pipe. + */ + if (c->ostate != CHAN_OUTPUT_CLOSED) + chan_write_failed(c); + } + ++#ifdef SSH_AUDIT_EVENTS ++void ++session_end_command2(Session *s) ++{ ++ if (s->command != NULL) { ++ if (s->command_handle != -1) ++ audit_end_command(s->command_handle, s->command); ++ free(s->command); ++ s->command = NULL; ++ s->command_handle = -1; ++ } ++} ++ ++static void ++session_end_command(Session *s) ++{ ++ if (s->command != NULL) { ++ if (s->command_handle != -1) ++ PRIVSEP(audit_end_command(s->command_handle, s->command)); ++ free(s->command); ++ s->command = NULL; ++ s->command_handle = -1; ++ } ++} ++#endif ++ + void + session_close(Session *s) + { + u_int i; + + verbose("Close session: user %s from %.200s port %d id %d", + s->pw->pw_name, + get_remote_ipaddr(), +@@ -2570,16 +2663,20 @@ session_close(Session *s) + exit(0); + } else if (pid > 0) { + waitpid(pid, NULL, 0); + } + } + + if (s->ttyfd != -1) + session_pty_cleanup(s); ++#ifdef SSH_AUDIT_EVENTS ++ if (s->command) ++ session_end_command(s); ++#endif + free(s->term); + free(s->display); + free(s->x11_chanids); + free(s->auth_display); + free(s->auth_data); + free(s->auth_proto); + free(s->subsys); + if (s->env != NULL) { +@@ -2784,16 +2881,25 @@ session_setup_x11fwd(Session *s) + } + + static void + do_authenticated2(Authctxt *authctxt) + { + server_loop2(authctxt); + } + ++static void ++do_cleanup_one_session(Session *s) ++{ ++ session_pty_cleanup2(s); ++#ifdef SSH_AUDIT_EVENTS ++ session_end_command2(s); ++#endif ++} ++ + void + do_cleanup(Authctxt *authctxt) + { + static int called = 0; + + debug("do_cleanup"); + + /* no cleanup if we're in the child for login shell */ +@@ -2832,10 +2938,10 @@ do_cleanup(Authctxt *authctxt) + /* remove agent socket */ + auth_sock_cleanup_proc(authctxt->pw); + + /* + * Cleanup ptys/utmp only if privsep is disabled, + * or if running in monitor. + */ + if (!use_privsep || mm_is_monitor()) +- session_destroy_all(session_pty_cleanup2); ++ session_destroy_all(do_cleanup_one_session); + } +diff --git a/openssh-7.2p2/session.h b/openssh-7.2p2/session.h +--- a/openssh-7.2p2/session.h ++++ b/openssh-7.2p2/session.h +@@ -56,29 +56,37 @@ struct Session { + int *x11_chanids; + int is_subsystem; + char *subsys; + u_int num_env; + struct { + char *name; + char *val; + } *env; ++ ++ /* exec */ ++#ifdef SSH_AUDIT_EVENTS ++ int command_handle; ++ char *command; ++#endif + }; + + void do_authenticated(Authctxt *); + void do_cleanup(Authctxt *); + + int session_open(Authctxt *, int); + void session_unused(int); + int session_input_channel_req(Channel *, const char *); + void session_close_by_pid(pid_t, int); + void session_close_by_channel(int, void *); + void session_destroy_all(void (*)(Session *)); + void session_pty_cleanup2(Session *); ++void session_end_command2(Session *); + + Session *session_new(void); ++Session *session_by_id(int); + Session *session_by_tty(char *); + void session_close(Session *); + void do_setusercontext(struct passwd *); + void child_set_env(char ***envp, u_int *envsizep, const char *name, + const char *value); + + #endif +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -118,16 +118,17 @@ + #include "channels.h" + #include "session.h" + #include "monitor_mm.h" + #include "monitor.h" + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" ++#include "audit.h" + #include "ssh-sandbox.h" + #include "version.h" + #include "ssherr.h" + + #include "fips.h" + + #ifdef USE_SECURITY_SESSION_API + #include +@@ -263,17 +264,17 @@ Buffer cfg; + + /* message to be displayed after login */ + Buffer loginmsg; + + /* Unprivileged user */ + struct passwd *privsep_pw = NULL; + + /* Prototypes for various functions defined later in this file. */ +-void destroy_sensitive_data(void); ++void destroy_sensitive_data(int); + void demote_sensitive_data(void); + + #ifdef WITH_SSH1 + static void do_ssh1_kex(void); + #endif + static void do_ssh2_kex(void); + + /* +@@ -284,16 +285,25 @@ close_listen_socks(void) + { + int i; + + for (i = 0; i < num_listen_socks; i++) + close(listen_socks[i]); + num_listen_socks = -1; + } + ++/* ++ * Is this process listening for clients (i.e. not specific to any specific ++ * client connection?) ++ */ ++int listening_for_clients(void) ++{ ++ return num_listen_socks > 0; ++} ++ + static void + close_startup_pipes(void) + { + int i; + + if (startup_pipes) + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) +@@ -563,60 +573,105 @@ sshd_exchange_identification(int sock_in + close(sock_out); + logit("Protocol major versions differ for %s: %.200s vs. %.200s", + get_remote_ipaddr(), + server_version_string, client_version_string); + cleanup_exit(255); + } + } + +-/* Destroy the host and server keys. They will no longer be needed. */ ++/* ++ * Destroy the host and server keys. They will no longer be needed. Careful, ++ * this can be called from cleanup_exit() - i.e. from just about anywhere. ++ */ + void +-destroy_sensitive_data(void) ++destroy_sensitive_data(int privsep) + { + int i; ++#ifdef SSH_AUDIT_EVENTS ++ pid_t pid; ++ uid_t uid; + ++ pid = getpid(); ++ uid = getuid(); ++#endif + if (sensitive_data.server_key) { + key_free(sensitive_data.server_key); + sensitive_data.server_key = NULL; + } + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { ++ char *fp; ++ ++ if (key_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = NULL; ++ if (fp != NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ if (privsep) ++ PRIVSEP(audit_destroy_sensitive_data(fp, ++ pid, uid)); ++ else ++ audit_destroy_sensitive_data(fp, ++ pid, uid); ++#endif ++ free(fp); ++ } + } +- if (sensitive_data.host_certificates[i]) { ++ if (sensitive_data.host_certificates ++ && sensitive_data.host_certificates[i]) { + key_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } + } + sensitive_data.ssh1_host_key = NULL; + explicit_bzero(sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); + } + + /* Demote private to public keys for network child */ + void + demote_sensitive_data(void) + { + Key *tmp; + int i; ++#ifdef SSH_AUDIT_EVENTS ++ pid_t pid; ++ uid_t uid; + ++ pid = getpid(); ++ uid = getuid(); ++#endif + if (sensitive_data.server_key) { + tmp = key_demote(sensitive_data.server_key); + key_free(sensitive_data.server_key); + sensitive_data.server_key = tmp; + } + + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { ++ char *fp; ++ ++ if (key_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; + tmp = key_demote(sensitive_data.host_keys[i]); + key_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = tmp; + if (tmp->type == KEY_RSA1) + sensitive_data.ssh1_host_key = tmp; ++ if (fp != NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_destroy_sensitive_data(fp, pid, uid); ++#endif ++ free(fp); ++ } + } + /* Certs do not need demotion */ + } + + /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ + } + + static void +@@ -756,16 +811,22 @@ privsep_postauth(Authctxt *authctxt) + monitor_reinit(pmonitor); + + pmonitor->m_pid = fork(); + if (pmonitor->m_pid == -1) + fatal("fork of unprivileged child failed"); + else if (pmonitor->m_pid != 0) { + verbose("User child is on pid %ld", (long)pmonitor->m_pid); + buffer_clear(&loginmsg); ++ if (*pmonitor->m_pkex != NULL ){ ++ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_OUT]); ++ newkeys_destroy((*pmonitor->m_pkex)->newkeys[MODE_IN]); ++ audit_session_key_free_body(2, getpid(), getuid()); ++ packet_destroy_all(0, 0); ++ } + monitor_child_postauth(pmonitor); + + /* NEVERREACHED */ + exit(0); + } + + /* child */ + +@@ -1283,16 +1344,17 @@ server_accept_loop(int *sock_in, int *so + + /* Wait in select until there is a connection. */ + ret = select(maxfd+1, fdset, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) + error("select: %.100s", strerror(errno)); + if (received_sigterm) { + logit("Received signal %d; terminating.", + (int) received_sigterm); ++ destroy_sensitive_data(0); + close_listen_socks(); + if (options.pid_file != NULL) + unlink(options.pid_file); + exit(received_sigterm == SIGTERM ? 0 : 255); + } + if (key_used && key_do_regen) { + generate_ephemeral_server_key(); + key_used = 0; +@@ -2341,16 +2403,17 @@ main(int ac, char **av) + #endif + } + /* + * If we use privilege separation, the unprivileged child transfers + * the current keystate and exits + */ + if (use_privsep) { + mm_send_keystate(pmonitor); ++ packet_destroy_all(1, 1); + exit(0); + } + + authenticated: + /* + * Cancel the alarm we set to limit the time taken for + * authentication. + */ +@@ -2383,30 +2446,33 @@ main(int ac, char **av) + /* + * In privilege separation, we fork another child and prepare + * file descriptor passing. + */ + if (use_privsep) { + privsep_postauth(authctxt); + /* the monitor process [priv] will not return */ + if (!compat20) +- destroy_sensitive_data(); ++ destroy_sensitive_data(0); + } + + packet_set_timeout(options.client_alive_interval, + options.client_alive_count_max); + + /* Try to send all our hostkeys to the client */ + if (compat20) + notify_hostkeys(active_state); + + /* Start session. */ + do_authenticated(authctxt); + + /* The connection has been terminated. */ ++ packet_destroy_all(1, 1); ++ destroy_sensitive_data(1); ++ + packet_get_bytes(&ibytes, &obytes); + verbose("Transferred: sent %llu, received %llu bytes", + (unsigned long long)obytes, (unsigned long long)ibytes); + + verbose("Closing connection to %.500s port %d", remote_ip, remote_port); + + #ifdef USE_PAM + if (options.use_pam) +@@ -2557,16 +2623,20 @@ do_ssh1_kex(void) + packet_disconnect("Warning: client selects unsupported cipher."); + + /* Get check bytes from the packet. These must match those we + sent earlier with the public key packet. */ + for (i = 0; i < 8; i++) + if (cookie[i] != packet_get_char()) + packet_disconnect("IP Spoofing check bytes do not match."); + ++#ifdef SSH_AUDIT_EVENTS ++ audit_kex(MODE_OUT, cipher_name(cipher_type), "crc", "none", "none"); ++#endif ++ + debug("Encryption type: %.200s", cipher_name(cipher_type)); + + /* Get the encrypted integer. */ + if ((real_key_int = BN_new()) == NULL) + fatal("do_ssh1_kex: BN_new failed"); + packet_get_bignum(real_key_int); + + protocol_flags = packet_get_int(); +@@ -2616,17 +2686,17 @@ do_ssh1_kex(void) + * Xor the first 16 bytes of the session key with the + * session id. + */ + for (i = 0; i < 16; i++) + session_key[i] ^= session_id[i]; + } + + /* Destroy the private and public keys. No longer. */ +- destroy_sensitive_data(); ++ destroy_sensitive_data(1); + + if (use_privsep) + mm_ssh1_session_id(session_id); + + /* Destroy the decrypted integer. It is no longer needed. */ + BN_clear_free(real_key_int); + BN_clear_free(fake_key_int); + +@@ -2790,26 +2860,41 @@ do_ssh2_kex(void) + #endif + debug("KEX done"); + } + + /* server specific fatal cleanup */ + void + cleanup_exit(int i) + { ++ static int in_cleanup = 0; ++ int is_privsep_child; ++ ++ /* cleanup_exit can be called at the very least from the privsep ++ wrappers used for auditing. Make sure we don't recurse ++ indefinitely. */ ++ if (in_cleanup) ++ _exit(i); ++ in_cleanup = 1; ++ + if (the_authctxt) { + do_cleanup(the_authctxt); + if (use_privsep && privsep_is_preauth && + pmonitor != NULL && pmonitor->m_pid > 1) { + debug("Killing privsep child %d", pmonitor->m_pid); + if (kill(pmonitor->m_pid, SIGKILL) != 0 && + errno != ESRCH) + error("%s: kill(%d): %s", __func__, + pmonitor->m_pid, strerror(errno)); + } + } ++ is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; ++ if (sensitive_data.host_keys != NULL) ++ destroy_sensitive_data(is_privsep_child); ++ packet_destroy_all(1, is_privsep_child); + #ifdef SSH_AUDIT_EVENTS + /* done after do_cleanup so it can cancel the PAM auth 'thread' */ +- if (!use_privsep || mm_is_monitor()) ++ if ((the_authctxt == NULL || !the_authctxt->authenticated) && ++ (!use_privsep || mm_is_monitor())) + audit_event(SSH_CONNECTION_ABANDON); + #endif + _exit(i); + } +diff --git a/openssh-7.2p2/sshkey.c b/openssh-7.2p2/sshkey.c +--- a/openssh-7.2p2/sshkey.c ++++ b/openssh-7.2p2/sshkey.c +@@ -299,16 +299,43 @@ sshkey_type_is_valid_ca(int type) + case KEY_ED25519: + return 1; + default: + return 0; + } + } + + int ++sshkey_is_private(const struct sshkey *k) ++{ ++ switch (k->type) { ++#ifdef WITH_OPENSSL ++ case KEY_RSA_CERT: ++ case KEY_RSA1: ++ case KEY_RSA: ++ return k->rsa->d != NULL; ++ case KEY_DSA_CERT: ++ case KEY_DSA: ++ return k->dsa->priv_key != NULL; ++#ifdef OPENSSL_HAS_ECC ++ case KEY_ECDSA_CERT: ++ case KEY_ECDSA: ++ return EC_KEY_get0_private_key(k->ecdsa) != NULL; ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ case KEY_ED25519_CERT: ++ case KEY_ED25519: ++ return (k->ed25519_pk != NULL); ++ default: ++ /* fatal("key_is_private: bad key type %d", k->type); */ ++ return 0; ++ } ++} ++ ++int + sshkey_is_cert(const struct sshkey *k) + { + if (k == NULL) + return 0; + return sshkey_type_is_cert(k->type); + } + + /* Return the cert-less equivalent to a certified key type */ +diff --git a/openssh-7.2p2/sshkey.h b/openssh-7.2p2/sshkey.h +--- a/openssh-7.2p2/sshkey.h ++++ b/openssh-7.2p2/sshkey.h +@@ -128,16 +128,17 @@ const char *sshkey_type(const struct ssh + const char *sshkey_cert_type(const struct sshkey *); + int sshkey_write(const struct sshkey *, FILE *); + int sshkey_read(struct sshkey *, char **); + u_int sshkey_size(const struct sshkey *); + + int sshkey_generate(int type, u_int bits, struct sshkey **keyp); + int sshkey_from_private(const struct sshkey *, struct sshkey **); + int sshkey_type_from_name(const char *); ++int sshkey_is_private(const struct sshkey *); + int sshkey_is_cert(const struct sshkey *); + int sshkey_type_is_cert(int); + int sshkey_type_plain(int); + int sshkey_to_certified(struct sshkey *); + int sshkey_drop_cert(struct sshkey *); + int sshkey_certify(struct sshkey *, struct sshkey *); + int sshkey_cert_copy(const struct sshkey *, struct sshkey *); + int sshkey_cert_check_authority(const struct sshkey *, int, int, diff --git a/openssh-7.2p2-audit_seed_prng.patch b/openssh-7.2p2-audit_seed_prng.patch new file mode 100644 index 0000000..372f43e --- /dev/null +++ b/openssh-7.2p2-audit_seed_prng.patch @@ -0,0 +1,116 @@ +# HG changeset patch +# Parent 3aad88a155050008275527c0624ae6fa05d0cdad +Audit PRNG re-seeding + +diff --git a/openssh-7.2p2/audit-bsm.c b/openssh-7.2p2/audit-bsm.c +--- a/openssh-7.2p2/audit-bsm.c ++++ b/openssh-7.2p2/audit-bsm.c +@@ -504,9 +504,15 @@ audit_destroy_sensitive_data(const char + /* not implemented */ + } + + void + audit_generate_ephemeral_server_key(const char *fp) + { + /* not implemented */ + } ++ ++void ++audit_linux_prng_seed(long bytes, const char *rf) ++{ ++ /* not implemented */ ++} + #endif /* BSM */ +diff --git a/openssh-7.2p2/audit-linux.c b/openssh-7.2p2/audit-linux.c +--- a/openssh-7.2p2/audit-linux.c ++++ b/openssh-7.2p2/audit-linux.c +@@ -402,9 +402,31 @@ audit_generate_ephemeral_server_key(cons + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, + buf, NULL, 0, NULL, 1); + audit_close(audit_fd); + /* do not abort if the error is EPERM and sshd is run as non root user */ + if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) + error("cannot write into audit"); + } ++ ++void ++audit_linux_prng_seed(long bytes, const char *rf) ++{ ++ char buf[AUDIT_LOG_SIZE]; ++ int audit_fd, audit_ok; ++ ++ snprintf(buf, sizeof(buf), "op=prng_seed kind=server bytes=%li source=%s ", bytes, rf); ++ audit_fd = audit_open(); ++ if (audit_fd < 0) { ++ if (errno != EINVAL && errno != EPROTONOSUPPORT && ++ errno != EAFNOSUPPORT) ++ error("cannot open audit"); ++ return; ++ } ++ audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_PARAM_CHANGE_USER, ++ buf, NULL, 0, NULL, 1); ++ audit_close(audit_fd); ++ /* do not abort if the error is EPERM and sshd is run as non root user */ ++ if ((audit_ok < 0) && ((audit_ok != -1) || (getuid() == 0))) ++ error("cannot write into audit"); ++} + #endif /* USE_LINUX_AUDIT */ +diff --git a/openssh-7.2p2/audit.c b/openssh-7.2p2/audit.c +--- a/openssh-7.2p2/audit.c ++++ b/openssh-7.2p2/audit.c +@@ -304,10 +304,16 @@ audit_destroy_sensitive_data(const char + /* + * This will be called on generation of the ephemeral server key + */ + void + audit_generate_ephemeral_server_key(const char *) + { + debug("audit create ephemeral server key euid %d fingerprint %s", geteuid(), fp); + } ++ ++void ++audit_linux_prng_seed(long bytes, const char *rf) ++{ ++ debug("audit PRNG seed euid %d bytes %li source %s", geteuid(), bytes, rf); ++} + # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ + #endif /* SSH_AUDIT_EVENTS */ +diff --git a/openssh-7.2p2/audit.h b/openssh-7.2p2/audit.h +--- a/openssh-7.2p2/audit.h ++++ b/openssh-7.2p2/audit.h +@@ -69,10 +69,11 @@ void audit_key(int, int *, const Key *); + void audit_unsupported(int); + void audit_kex(int, char *, char *, char *, char *); + void audit_unsupported_body(int); + void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); + void audit_session_key_free(int ctos); + void audit_session_key_free_body(int ctos, pid_t, uid_t); + void audit_destroy_sensitive_data(const char *, pid_t, uid_t); + void audit_generate_ephemeral_server_key(const char *); ++void audit_linux_prng_seed(long, const char *); + + #endif /* _SSH_AUDIT_H */ +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -1421,16 +1421,19 @@ server_accept_loop(int *sock_in, int *so + if (maxfd < startup_p[0]) + maxfd = startup_p[0]; + startups++; + break; + } + if(!(--re_seeding_counter)) { + re_seeding_counter = RESEED_AFTER; + linux_seed(); ++#ifdef SSH_AUDIT_EVENTS ++ audit_linux_prng_seed(rand_bytes, rand_file); ++#endif + } + + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* diff --git a/openssh-7.2p2-blocksigalrm.patch b/openssh-7.2p2-blocksigalrm.patch index 16970d5..c2350b1 100644 --- a/openssh-7.2p2-blocksigalrm.patch +++ b/openssh-7.2p2-blocksigalrm.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 5469eb754184144e42c341ccc038309e2880cadc +# Parent 0bfb5dd4b190b546a3e40a59483b2b2884a47c39 block SIGALRM while logging through syslog to prevent deadlocks (through grace_alarm_handler()) diff --git a/openssh-7.2p2-disable_openssl_abi_check.patch b/openssh-7.2p2-disable_openssl_abi_check.patch new file mode 100644 index 0000000..ff077dd --- /dev/null +++ b/openssh-7.2p2-disable_openssl_abi_check.patch @@ -0,0 +1,64 @@ +# HG changeset patch +# Parent 16c4937db837ab7cdbe0422b81de0e7a9a8479cd +disable run-time check for OpenSSL ABI by version number as that is not a +reliable indicator of ABI changes and doesn't make much sense in a +distribution package + +diff --git a/openssh-7.2p2/configure.ac b/openssh-7.2p2/configure.ac +--- a/openssh-7.2p2/configure.ac ++++ b/openssh-7.2p2/configure.ac +@@ -4639,16 +4639,29 @@ AC_ARG_WITH([bsd-auth], + if test "x$withval" != "xno" ; then + AC_DEFINE([BSD_AUTH], [1], + [Define if you have BSD auth support]) + BSD_AUTH_MSG=yes + fi + ] + ) + ++# Whether we are using distribution (Open)SSL, so no runtime checks are necessary ++DISTRO_SSL=no ++AC_ARG_WITH([distro-ssl], ++ [ --with-distro-ssl Disable runtime OpenSSL version checks (good for distributions)], ++ [ ++ if test "x$withval" != "xno" ; then ++ AC_DEFINE([DISTRO_SSL], [1], ++ [Define if you are using distribution SSL library and don;t expect its API/ABI to change]) ++ DISTRO_SSL=yes ++ fi ++ ] ++) ++ + # Where to place sshd.pid + piddir=/var/run + # make sure the directory exists + if test ! -d $piddir ; then + piddir=`eval echo ${sysconfdir}` + case $piddir in + NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; + esac +diff --git a/openssh-7.2p2/entropy.c b/openssh-7.2p2/entropy.c +--- a/openssh-7.2p2/entropy.c ++++ b/openssh-7.2p2/entropy.c +@@ -209,19 +209,21 @@ rexec_recv_rng_seed(Buffer *m) + #endif /* OPENSSL_PRNG_ONLY */ + + void + seed_rng(void) + { + #ifndef OPENSSL_PRNG_ONLY + unsigned char buf[RANDOM_SEED_SIZE]; + #endif ++#ifndef DISTRO_SSL + if (!ssh_compatible_openssl(OPENSSL_VERSION_NUMBER, SSLeay())) + fatal("OpenSSL version mismatch. Built against %lx, you " + "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); ++#endif + + #ifndef OPENSSL_PRNG_ONLY + if (RAND_status() == 1) { + debug3("RNG is ready, skipping seeding"); + return; + } + + if (seed_from_prngd(buf, sizeof(buf)) == -1) diff --git a/openssh-7.2p2-disable_short_DH_parameters.patch b/openssh-7.2p2-disable_short_DH_parameters.patch index dbd4a1f..b08abba 100644 --- a/openssh-7.2p2-disable_short_DH_parameters.patch +++ b/openssh-7.2p2-disable_short_DH_parameters.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent c40dce555117c740f3df867e9fc2b07b64b3ad96 +# Parent 7b5f436e0026923299fdd1994f8da8fd9948be7c Raise minimal size of DH group parameters to 2048 bits like upstream did in 7.2. 1024b values are believed to be in breaking range for state adversaries diff --git a/openssh-7.2p2-dont_use_pthreads_in_PAM.patch b/openssh-7.2p2-dont_use_pthreads_in_PAM.patch index b1027e6..f382f6a 100644 --- a/openssh-7.2p2-dont_use_pthreads_in_PAM.patch +++ b/openssh-7.2p2-dont_use_pthreads_in_PAM.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 2aa634b7522f34ddbd380c96df4e750df0608604 +# Parent e4886597a8984ae1594b6866fe1b232370b23529 # posix threads are generally not supported nor safe # (see upstream log from 2005-05-24) # --used to be called '-pam-fix3' diff --git a/openssh-7.2p2-eal3.patch b/openssh-7.2p2-eal3.patch index 7d8c4e3..201a239 100644 --- a/openssh-7.2p2-eal3.patch +++ b/openssh-7.2p2-eal3.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent bbb49b3f344cf24e9bbd7eb7a7c40fea21be77eb +# Parent f19426f2fa9c634474e635bf33b86acea0518f6d fix paths and references in sshd man pages diff --git a/openssh-7.2p2/sshd.8 b/openssh-7.2p2/sshd.8 diff --git a/openssh-7.2p2-enable_PAM_by_default.patch b/openssh-7.2p2-enable_PAM_by_default.patch index bd3eba5..93bda5c 100644 --- a/openssh-7.2p2-enable_PAM_by_default.patch +++ b/openssh-7.2p2-enable_PAM_by_default.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 477d43e9a3889d36b58ff19cf3cb9583e1abf9ce +# Parent 980f301b2920c09b30577dd722546bca85d25fc1 # force PAM in defaullt install (this was removed from upstream in 3.8p1) # bnc#46749 # --used to be called '-pam-fix2' diff --git a/openssh-7.2p2-fips.patch b/openssh-7.2p2-fips.patch index 8251e5a..8df737a 100644 --- a/openssh-7.2p2-fips.patch +++ b/openssh-7.2p2-fips.patch @@ -1,12 +1,12 @@ # HG changeset patch -# Parent 0dee2a3f80c2db73903388815fb4e311c8588a15 +# Parent 3e1393b771d6430ae09ae30741a3b9b382e3e041 FIPS 140-2 compliance. Perform selftests on start and use only FIPS approved algorithms. diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in --- a/openssh-7.2p2/Makefile.in +++ b/openssh-7.2p2/Makefile.in -@@ -87,17 +87,17 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ +@@ -87,17 +87,18 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ ssh-pkcs11.o smult_curve25519_ref.o \ poly1305.o chacha.o cipher-chachapoly.o \ @@ -16,7 +16,8 @@ diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ - platform-pledge.o -+ platform-pledge.o fips.o ++ platform-pledge.o \ ++ fips.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o mux.o diff --git a/openssh-7.2p2-gssapi_key_exchange.patch b/openssh-7.2p2-gssapi_key_exchange.patch index 481a8de..7bc2da3 100644 --- a/openssh-7.2p2-gssapi_key_exchange.patch +++ b/openssh-7.2p2-gssapi_key_exchange.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent c2049622cf75dbab61a8f49b53a13dc1de6695fd +# Parent 84a6252b7ac18855cf188e5911bdf8a757d4460a GSSAPI Key Exchange implementation diff --git a/openssh-7.2p2/ChangeLog.gssapi b/openssh-7.2p2/ChangeLog.gssapi @@ -136,14 +136,14 @@ diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in - kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o kexgssc.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o kexgsss.o \ - platform-pledge.o fips.o + platform-pledge.o \ + fips.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o mux.o SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ audit.o audit-bsm.o audit-linux.o platform.o \ - sshpty.o sshlogin.o servconf.o serverloop.o \ diff --git a/openssh-7.2p2/auth-krb5.c b/openssh-7.2p2/auth-krb5.c --- a/openssh-7.2p2/auth-krb5.c +++ b/openssh-7.2p2/auth-krb5.c diff --git a/openssh-7.2p2-host_ident.patch b/openssh-7.2p2-host_ident.patch new file mode 100644 index 0000000..5800abd --- /dev/null +++ b/openssh-7.2p2-host_ident.patch @@ -0,0 +1,29 @@ +# HG changeset patch +# Parent 605a6220fcc2c96e9196681fe480fab16b505ee1 +Suggest command line for removal of offending keys from known_hosts file + +diff --git a/openssh-7.2p2/sshconnect.c b/openssh-7.2p2/sshconnect.c +--- a/openssh-7.2p2/sshconnect.c ++++ b/openssh-7.2p2/sshconnect.c +@@ -1086,16 +1086,21 @@ check_host_key(char *hostname, struct so + ip_found->file, ip_found->line); + } + /* The host key has changed. */ + warn_changed_key(host_key); + error("Add correct host key in %.100s to get rid of this message.", + user_hostfiles[0]); + error("Offending %s key in %s:%lu", key_type(host_found->key), + host_found->file, host_found->line); ++ error("You can use following command to remove all keys for this IP:"); ++ if (host_found->file) ++ error("ssh-keygen -R %s -f %s", hostname, host_found->file); ++ else ++ error("ssh-keygen -R %s", hostname); + + /* + * If strict host key checking is in use, the user will have + * to edit the key manually and we can only abort. + */ + if (options.strict_host_key_checking) { + error("%s host key for %.200s has changed and you have " + "requested strict checking.", type, host); diff --git a/openssh-7.2p2-hostname_changes_when_forwarding_X.patch b/openssh-7.2p2-hostname_changes_when_forwarding_X.patch index 493bdfe..4714d67 100644 --- a/openssh-7.2p2-hostname_changes_when_forwarding_X.patch +++ b/openssh-7.2p2-hostname_changes_when_forwarding_X.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent b5245fb016a3b83611d4b4ae0c1fe3423cadd6fe +# Parent f7ba2081f120bd1e44dbe68737c898f078725aab # -- uset do be called '-xauthlocalhostname' handle hostname changes when forwarding X diff --git a/openssh-7.2p2-ignore_PAM_with_UseLogin.patch b/openssh-7.2p2-ignore_PAM_with_UseLogin.patch new file mode 100644 index 0000000..78bd3e8 --- /dev/null +++ b/openssh-7.2p2-ignore_PAM_with_UseLogin.patch @@ -0,0 +1,33 @@ +# HG changeset patch +# Parent cb9be7363a9f32133f0d105d515149dd77cc8cd3 + +Do not import PAM environment variables when using login, since it may have +security implications. + +CVE-2015-8325 +bsc#975865 + +Backport of upstream commit 85bdcd7c92fe7ff133bbc4e10a65c91810f88755 + +diff --git a/openssh-7.2p2/session.c b/openssh-7.2p2/session.c +--- a/openssh-7.2p2/session.c ++++ b/openssh-7.2p2/session.c +@@ -1351,17 +1351,17 @@ do_setup_env(Session *s, const char *she + child_set_env(&env, &envsize, "KRB5CCNAME", + s->authctxt->krb5_ccname); + #endif + #ifdef USE_PAM + /* + * Pull in any environment variables that may have + * been set by PAM. + */ +- if (options.use_pam) { ++ if (options.use_pam && !options.use_login) { + char **p; + + p = fetch_pam_child_environment(); + copy_environment(p, &env, &envsize); + free_pam_environment(p); + + p = fetch_pam_environment(); + copy_environment(p, &env, &envsize); diff --git a/openssh-7.2p2-lastlog.patch b/openssh-7.2p2-lastlog.patch index 45969c5..2accb74 100644 --- a/openssh-7.2p2-lastlog.patch +++ b/openssh-7.2p2-lastlog.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 2ee086fa64dd40d0d50b13fa3a784717bfdd7e4b +# Parent 79c00e0f450c33b3f545ef104112b55186290e2c # set uid for functions that use it to seek in lastlog and wtmp files # bnc#18024 (was suse #3024) diff --git a/openssh-7.2p2-ldap.patch b/openssh-7.2p2-ldap.patch new file mode 100644 index 0000000..db4613a --- /dev/null +++ b/openssh-7.2p2-ldap.patch @@ -0,0 +1,2838 @@ +# HG changeset patch +# Parent b8135c449e59282a8926ff44fcb4670baf8f158e +# Helper app for retrieving keys from a LDAP server +# by Jan F. Chadima +# +# patch for openbsd-compat/base64.* introduces preprocessor macro +# USE_INTERNAL_B64 intended to enforce using ssh supplied functions. +# (The additional -lldap/-llber introduced in the patch cause configure to +# discover the base64 functions in glibc (libresolv) and not to build the +# internal versions. ssh-keyconverter consequently fails to link as it lacks +# the proper flags, and libopenbsd-compat doesn't contain the b64_* functions) + +diff --git a/openssh-7.2p2/HOWTO.ldap-keys b/openssh-7.2p2/HOWTO.ldap-keys +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/HOWTO.ldap-keys +@@ -0,0 +1,108 @@ ++ ++HOW TO START ++ ++1) configure LDAP server ++ * Use LDAP server documentation ++2) add appropriate LDAP schema ++ * For OpenLDAP or SunONE Use attached schema, otherwise you have to create ++ it. ++ * LDAP user entry ++ User entry: ++ - attached to the 'ldapPublicKey' objectclass ++ - attached to the 'posixAccount' objectclass ++ - with a filled 'sshPublicKey' attribute ++3) insert users into LDAP ++ * Use LDAP Tree management tool as useful ++ * Entry in the LDAP server must respect 'posixAccount' and 'ldapPublicKey' ++ which are defined in core.schema and the additionnal lpk.schema. ++ * Example: ++ dn: uid=captain,ou=commanders,dc=enterprise,dc=universe ++ objectclass: top ++ objectclass: person ++ objectclass: organizationalPerson ++ objectclass: posixAccount ++ objectclass: ldapPublicKey ++ description: Jonathan Archer ++ userPassword: Porthos ++ cn: onathan Archer ++ sn: onathan Archer ++ uid: captain ++ uidNumber: 1001 ++ gidNumber: 1001 ++ homeDirectory: /home/captain ++ sshPublicKey: ssh-rss AAAAB3.... =captain@universe ++ sshPublicKey: command="kill -9 1" ssh-rss AAAAM5... ++4) on the ssh side set in sshd_config ++ * Set up the backend ++ AuthorizedKeysCommand "@LIBEXECDIR@/ssh-ldap-wrapper" ++ AuthorizedKeysCommandRunAs ++ * Do not forget to set ++ PubkeyAuthentication yes ++ * Swith off unnecessary auth methods ++5) confugure ldap.conf ++ * Default ldap.conf is placed in /etc/ssh ++ * The configuration style is the same as other ldap based aplications ++6) if necessary edit ssh-ldap-wrapper ++ * There is a possibility to change ldap.conf location ++ * There are some debug options ++ * Example ++ @LIBEXECDIR@/ssh-ldap-wrapper -s -f /etc/ldap.conf -w -d >> /tmp/ldapdebuglog.txt ++ ++HOW TO MIGRATE FROM LPK ++ ++1) goto HOW TO START 4) .... the ldap schema is the same ++ ++2) convert the group requests to the appropriate LDAP requests ++ ++HOW TO SOLVE PROBLEMS ++ ++1) use debug in sshd ++ * /usr/sbin/sshd -d -d -d -d ++2) use debug in ssh-ldap-helper ++ * ssh-ldap-helper -d -d -d -d -s ++3) use tcpdump ... other ldap client etc. ++ ++ADVANTAGES ++ ++1) Blocking an user account can be done directly from LDAP (if sshd is using ++ PubkeyAuthentication + AuthorizedKeysCommand with ldap only). ++ ++DISADVANTAGES ++ ++1) LDAP must be well configured, getting the public key of some user is not ++ a problem, but if anonymous LDAP allows write to users dn, somebody could ++ replace some user's public key by his own and impersonate some of your users ++ in all your server farm -- be VERY CAREFUL. ++2) With incomplete PKI the MITM attack when sshd is requesting the public key, ++ could lead to a compromise of your servers allowing login as the ++ impersonated user. ++3) If LDAP server is down there may be no fallback on passwd auth. ++ ++MISC. ++ ++1) todo ++ * Possibility to reuse the ssh-ldap-helper. ++ * Tune the LDAP part to accept all possible LDAP configurations. ++ ++2) differences from original lpk ++ * No LDAP code in sshd. ++ * Support for various LDAP platforms and configurations. ++ * LDAP is configured in separate ldap.conf file. ++ ++3) docs/link ++ * http://pacsec.jp/core05/psj05-barisani-en.pdf ++ * http://fritz.potsdam.edu/projects/openssh-lpk/ ++ * http://fritz.potsdam.edu/projects/sshgate/ ++ * http://dev.inversepath.com/trac/openssh-lpk ++ * http://lam.sf.net/ ++ ( http://lam.sourceforge.net/documentation/supportedSchemas.htm ) ++ ++4) contributors/ideas/greets ++ - Eric AUGE ++ - Andrea Barisani ++ - Falk Siemonsmeier. ++ - Jacob Rief. ++ - Michael Durchgraf. ++ - frederic peters. ++ - Finlay dobbie. ++ - Stefan Fisher. +diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in +--- a/openssh-7.2p2/Makefile.in ++++ b/openssh-7.2p2/Makefile.in +@@ -21,16 +21,18 @@ top_srcdir=@top_srcdir@ + + DESTDIR= + VPATH=@srcdir@ + SSH_PROGRAM=@bindir@/ssh + ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass + SFTP_SERVER=$(libexecdir)/sftp-server + SSH_KEYSIGN=$(libexecdir)/ssh-keysign + SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper ++SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper ++SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ + STRIP_OPT=@STRIP_OPT@ + TEST_SHELL=@TEST_SHELL@ + + PATHS= -DSSHDIR=\"$(sysconfdir)\" \ + -D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \ + -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \ +@@ -57,17 +59,19 @@ INSTALL=@INSTALL@ + PERL=@PERL@ + SED=@SED@ + ENT=@ENT@ + XAUTH_PATH=@XAUTH_PATH@ + LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ + EXEEXT=@EXEEXT@ + MANFMT=@MANFMT@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ++INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ ++ ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) + + LIBOPENSSH_OBJS=\ + ssh_api.o \ + ssherr.o \ + sshbuf.o \ + sshkey.o \ + sshbuf-getput-basic.o \ + sshbuf-misc.o \ +@@ -109,18 +113,18 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw + monitor_mm.o monitor.o monitor_wrap.o auth-krb5.o \ + auth2-gss.o gss-serv.o gss-serv-krb5.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ + sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ + sandbox-solaris.o + +-MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out +-MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 ++MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out ssh-ldap-helper.8.out ssh-ldap.conf.5.out ++MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 ssh-ldap-helper.8 ssh-ldap.conf.5 + MANTYPE = @MANTYPE@ + + CONFIGFILES=sshd_config.out ssh_config.out moduli.out + CONFIGFILES_IN=sshd_config ssh_config moduli + + PATHSUBS = \ + -e 's|/etc/ssh/ssh_config|$(sysconfdir)/ssh_config|g' \ + -e 's|/etc/ssh/ssh_known_hosts|$(sysconfdir)/ssh_known_hosts|g' \ +@@ -184,16 +188,19 @@ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libss + $(LD) -o $@ ssh-keysign.o readconf.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o + $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ++ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o ++ $(LD) -o $@ ldapconf.o ldapbody.o ldapmisc.o ldap-helper.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ++ + sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o + $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o + $(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) + + # test driver for the loginrec code - not built by default + logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o +@@ -308,32 +315,40 @@ install-files: + $(INSTALL) -m 0755 $(STRIP_OPT) scp$(EXEEXT) $(DESTDIR)$(bindir)/scp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-add$(EXEEXT) $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent$(EXEEXT) $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) ++ if test ! -z "$(INSTALL_SSH_LDAP_HELPER)" ; then \ ++ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \ ++ $(INSTALL) -m 0755 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ ++ fi + $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 + $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 + $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 + $(INSTALL) -m 644 ssh-agent.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 + $(INSTALL) -m 644 ssh-keygen.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 + $(INSTALL) -m 644 ssh-keyscan.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 + $(INSTALL) -m 644 moduli.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/moduli.5 + $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5 + $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5 + $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 ++ if test ! -z "$(INSTALL_SSH_LDAP_HELPER)" ; then \ ++ $(INSTALL) -m 644 ssh-ldap-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-ldap-helper.8 ; \ ++ $(INSTALL) -m 644 ssh-ldap.conf.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh-ldap.conf.5 ; \ ++ fi + + install-sysconf: + if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ + $(srcdir)/mkinstalldirs $(DESTDIR)$(sysconfdir); \ + fi + @if [ ! -f $(DESTDIR)$(sysconfdir)/ssh_config ]; then \ + $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ + else \ +@@ -349,16 +364,23 @@ install-sysconf: + echo "moving $(DESTDIR)$(sysconfdir)/primes to $(DESTDIR)$(sysconfdir)/moduli"; \ + mv "$(DESTDIR)$(sysconfdir)/primes" "$(DESTDIR)$(sysconfdir)/moduli"; \ + else \ + $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/moduli; \ + fi ; \ + else \ + echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ + fi ++ if test ! -z "$(INSTALL_SSH_LDAP_HELPER)" ; then \ ++ if [ ! -f $(DESTDIR)$(sysconfdir)/ldap.conf ]; then \ ++ $(INSTALL) -m 644 ldap.conf $(DESTDIR)$(sysconfdir)/ldap.conf; \ ++ else \ ++ echo "$(DESTDIR)$(sysconfdir)/ldap.conf already exists, install will not overwrite"; \ ++ fi ; \ ++ fi + + host-key: ssh-keygen$(EXEEXT) + @if [ -z "$(DESTDIR)" ] ; then \ + ./ssh-keygen -A; \ + fi + + host-key-force: ssh-keygen$(EXEEXT) ssh$(EXEEXT) + if ./ssh -Q protocol-version | grep '^1$$' >/dev/null; then \ +@@ -389,27 +411,30 @@ uninstall: + -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) + -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) + -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) + -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) + -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) ++ -rm -f $(DESTDIR)$(SSH_LDAP_HELPER)$(EXEEXT) ++ -rm -f $(DESTDIR)$(SSH_LDAP_WRAPPER)$(EXEEXT) + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 + -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 ++ -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-ldap-helper.8 + + regress-prep: + [ -d `pwd`/regress ] || mkdir -p `pwd`/regress + [ -d `pwd`/regress/unittests ] || mkdir -p `pwd`/regress/unittests + [ -d `pwd`/regress/unittests/test_helper ] || \ + mkdir -p `pwd`/regress/unittests/test_helper + [ -d `pwd`/regress/unittests/sshbuf ] || \ + mkdir -p `pwd`/regress/unittests/sshbuf +diff --git a/openssh-7.2p2/configure.ac b/openssh-7.2p2/configure.ac +--- a/openssh-7.2p2/configure.ac ++++ b/openssh-7.2p2/configure.ac +@@ -1620,16 +1620,116 @@ AC_ARG_WITH([audit], + AC_MSG_RESULT([no]) + ;; + *) + AC_MSG_ERROR([Unknown audit module $withval]) + ;; + esac ] + ) + ++# Check whether user wants LDAP support ++LDAP_MSG="no" ++INSTALL_SSH_LDAP_HELPER="" ++AC_ARG_WITH(ldap, ++ [ --with-ldap[[=PATH]] Enable LDAP pubkey support (optionally in PATH)], ++ [ ++ if test "x$withval" != "xno" ; then ++ ++ INSTALL_SSH_LDAP_HELPER="yes" ++ CPPFLAGS="$CPPFLAGS -DLDAP_DEPRECATED" ++ ++ if test "x$withval" != "xyes" ; then ++ CPPFLAGS="$CPPFLAGS -I${withval}/include" ++ LDFLAGS="$LDFLAGS -L${withval}/lib" ++ fi ++ ++ AC_DEFINE([WITH_LDAP_PUBKEY], 1, [Enable LDAP pubkey support]) ++ LDAP_MSG="yes" ++ ++ AC_CHECK_HEADERS(lber.h) ++ AC_CHECK_HEADERS(ldap.h, , AC_MSG_ERROR(could not locate )) ++ AC_CHECK_HEADERS(ldap_ssl.h) ++ ++ AC_ARG_WITH(ldap-lib, ++ [ --with-ldap-lib=type select ldap library [auto|netscape5|netscape4|netscape3|umich|openldap]]) ++ ++ if test -z "$with_ldap_lib"; then ++ with_ldap_lib=auto ++ fi ++ ++ if test -z "$found_ldap_lib" -a \( $with_ldap_lib = auto -o $with_ldap_lib = umich -o $with_ldap_lib = openldap \); then ++ AC_CHECK_LIB(lber, main, LIBS="-llber $LIBS" found_ldap_lib=yes) ++ AC_CHECK_LIB(ldap, main, LIBS="-lldap $LIBS" found_ldap_lib=yes) ++ fi ++ ++ if test -z "$found_ldap_lib" -a \( $with_ldap_lib = auto -o $with_ldap_lib = netscape5 \); then ++ AC_CHECK_LIB(ldap50, main, LIBS="-lldap50 -lssldap50 -lssl3 -lnss3 -lnspr4 -lprldap50 -lplc4 -lplds4 $LIBS" found_ldap_lib=yes) ++ fi ++ ++ if test -z "$found_ldap_lib" -a \( $with_ldap_lib = auto -o $with_ldap_lib = netscape4 \); then ++ AC_CHECK_LIB(ldapssl41, main, LIBS="-lldapssl41 -lplc3 -lplds3 -lnspr3 $LIBS" found_ldap_lib=yes) ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldapssl40, main, LIBS="-lldapssl40 $LIBS" found_ldap_lib=yes) ++ fi ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldap41, main, LIBS="-lldap41 $LIBS" found_ldap_lib=yes) ++ fi ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldap40, main, LIBS="-lldap40 $LIBS" found_ldap_lib=yes) ++ fi ++ fi ++ ++ if test -z "$found_ldap_lib" -a \( $with_ldap_lib = auto -o $with_ldap_lib = netscape3 \); then ++ AC_CHECK_LIB(ldapssl30, main, LIBS="-lldapssl30 $LIBS" found_ldap_lib=yes) ++ fi ++ ++ if test -z "$found_ldap_lib"; then ++ AC_MSG_ERROR(could not locate a valid LDAP library) ++ fi ++ ++ AC_MSG_CHECKING([for working LDAP support]) ++ AC_TRY_COMPILE( ++ [#include ++ #include ], ++ [(void)ldap_init(0, 0);], ++ [AC_MSG_RESULT(yes)], ++ [ ++ AC_MSG_RESULT(no) ++ AC_MSG_ERROR([** Incomplete or missing ldap libraries **]) ++ ]) ++ AC_CHECK_FUNCS( \ ++ ldap_init \ ++ ldap_get_lderrno \ ++ ldap_set_lderrno \ ++ ldap_parse_result \ ++ ldap_memfree \ ++ ldap_controls_free \ ++ ldap_set_option \ ++ ldap_get_option \ ++ ldapssl_init \ ++ ldap_start_tls_s \ ++ ldap_pvt_tls_set_option \ ++ ldap_initialize \ ++ ) ++ AC_CHECK_FUNCS(ldap_set_rebind_proc, ++ AC_MSG_CHECKING([number arguments of ldap_set_rebind_proc]) ++ AC_TRY_COMPILE( ++ [#include ++ #include ], ++ [ldap_set_rebind_proc(0, 0, 0);], ++ [ac_cv_ldap_set_rebind_proc=3], ++ [ac_cv_ldap_set_rebind_proc=2]) ++ AC_MSG_RESULT($ac_cv_ldap_set_rebind_proc) ++ AC_DEFINE(LDAP_SET_REBIND_PROC_ARGS, $ac_cv_ldap_set_rebind_proc, [number arguments of ldap_set_rebind_proc]) ++ ) ++ fi ++ ] ++) ++AC_SUBST(INSTALL_SSH_LDAP_HELPER) ++ + AC_ARG_WITH([pie], + [ --with-pie Build Position Independent Executables if possible], [ + if test "x$withval" = "xno"; then + use_pie=no + fi + if test "x$withval" = "xyes"; then + use_pie=yes + fi +diff --git a/openssh-7.2p2/ldap-helper.c b/openssh-7.2p2/ldap-helper.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldap-helper.c +@@ -0,0 +1,155 @@ ++/* $OpenBSD: ssh-pka-ldap.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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 "ldapincludes.h" ++#include "log.h" ++#include "misc.h" ++#include "xmalloc.h" ++#include "ldapconf.h" ++#include "ldapbody.h" ++#include ++#include ++ ++static int config_debug = 0; ++int config_exclusive_config_file = 0; ++static char *config_file_name = "/etc/ssh/ldap.conf"; ++static char *config_single_user = NULL; ++static int config_verbose = SYSLOG_LEVEL_VERBOSE; ++int config_warning_config_file = 0; ++extern char *__progname; ++ ++static void ++usage(void) ++{ ++ fprintf(stderr, "usage: %s [options]\n", ++ __progname); ++ fprintf(stderr, "Options:\n"); ++ fprintf(stderr, " -d Output the log messages to stderr.\n"); ++ fprintf(stderr, " -e Check the config file for unknown commands.\n"); ++ fprintf(stderr, " -f file Use alternate config file (default is /etc/ssh/ldap.conf).\n"); ++ fprintf(stderr, " -s user Do not demonize, send the user's key to stdout.\n"); ++ fprintf(stderr, " -v Increase verbosity of the debug output (implies -d).\n"); ++ fprintf(stderr, " -w Warn on unknown commands in the config file.\n"); ++ exit(1); ++} ++ ++/* ++ * Main program for the ssh pka ldap agent. ++ */ ++ ++int ++main(int ac, char **av) ++{ ++ int opt; ++ FILE *outfile = NULL; ++ ++ __progname = ssh_get_progname(av[0]); ++ ++ log_init(__progname, SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0); ++ ++ /* ++ * Initialize option structure to indicate that no values have been ++ * set. ++ */ ++ initialize_options(); ++ ++ /* Parse command-line arguments. */ ++ while ((opt = getopt(ac, av, "def:s:vw")) != -1) { ++ switch (opt) { ++ case 'd': ++ config_debug = 1; ++ break; ++ ++ case 'e': ++ config_exclusive_config_file = 1; ++ config_warning_config_file = 1; ++ break; ++ ++ case 'f': ++ config_file_name = optarg; ++ break; ++ ++ case 's': ++ config_single_user = optarg; ++ outfile = fdopen (dup (fileno (stdout)), "w"); ++ break; ++ ++ case 'v': ++ config_debug = 1; ++ if (config_verbose < SYSLOG_LEVEL_DEBUG3) ++ config_verbose++; ++ break; ++ ++ case 'w': ++ config_warning_config_file = 1; ++ break; ++ ++ case '?': ++ default: ++ usage(); ++ break; ++ } ++ } ++ ++ /* Initialize loging */ ++ log_init(__progname, config_verbose, SYSLOG_FACILITY_AUTH, config_debug); ++ ++ if (ac != optind) ++ fatal ("illegal extra parameter %s", av[1]); ++ ++ /* Ensure that fds 0 and 2 are open or directed to /dev/null */ ++ if (config_debug == 0) ++ sanitise_stdfd(); ++ ++ /* Read config file */ ++ read_config_file(config_file_name); ++ fill_default_options(); ++ if (config_verbose == SYSLOG_LEVEL_DEBUG3) { ++ debug3 ("=== Configuration ==="); ++ dump_config(); ++ debug3 ("=== *** ==="); ++ } ++ ++ ldap_checkconfig(); ++ ldap_do_connect(); ++ ++ if (config_single_user) { ++ process_user (config_single_user, outfile); ++ } else { ++ usage(); ++ fatal ("Not yet implemented"); ++/* TODO ++ * open unix socket a run the loop on it ++ */ ++ } ++ ++ ldap_do_close(); ++ return 0; ++} ++ ++/* Ugly hack */ ++void *buffer_get_string(Buffer *b, u_int *l) { return NULL; } ++void buffer_put_string(Buffer *b, const void *f, u_int l) {} ++ +diff --git a/openssh-7.2p2/ldap-helper.h b/openssh-7.2p2/ldap-helper.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldap-helper.h +@@ -0,0 +1,32 @@ ++/* $OpenBSD: ldap-helper.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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. ++ */ ++ ++#ifndef LDAP_HELPER_H ++#define LDAP_HELPER_H ++ ++extern int config_exclusive_config_file; ++extern int config_warning_config_file; ++ ++#endif /* LDAP_HELPER_H */ +diff --git a/openssh-7.2p2/ldap.conf b/openssh-7.2p2/ldap.conf +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldap.conf +@@ -0,0 +1,88 @@ ++# $Id: openssh-5.5p1-ldap.patch,v 1.3 2010/07/07 13:48:36 jfch2222 Exp $ ++# ++# This is the example configuration file for the OpenSSH ++# LDAP backend ++# ++# see ssh-ldap.conf(5) ++# ++ ++# URI with your LDAP server name. This allows to use ++# Unix Domain Sockets to connect to a local LDAP Server. ++#uri ldap://127.0.0.1/ ++#uri ldaps://127.0.0.1/ ++#uri ldapi://%2fvar%2frun%2fldapi_sock/ ++# Note: %2f encodes the '/' used as directory separator ++ ++# Another way to specify your LDAP server is to provide an ++# host name and the port of our LDAP server. Host name ++# must be resolvable without using LDAP. ++# Multiple hosts may be specified, each separated by a ++# space. How long nss_ldap takes to failover depends on ++# whether your LDAP client library supports configurable ++# network or connect timeouts (see bind_timelimit). ++#host 127.0.0.1 ++ ++# The port. ++# Optional: default is 389. ++#port 389 ++ ++# The distinguished name to bind to the server with. ++# Optional: default is to bind anonymously. ++#binddn cn=openssh_keys,dc=example,dc=org ++ ++# The credentials to bind with. ++# Optional: default is no credential. ++#bindpw TopSecret ++ ++# The distinguished name of the search base. ++#base dc=example,dc=org ++ ++# The LDAP version to use (defaults to 3 ++# if supported by client library) ++#ldap_version 3 ++ ++# The search scope. ++#scope sub ++#scope one ++#scope base ++ ++# Search timelimit ++#timelimit 30 ++ ++# Bind/connect timelimit ++#bind_timelimit 30 ++ ++# Reconnect policy: hard (default) will retry connecting to ++# the software with exponential backoff, soft will fail ++# immediately. ++#bind_policy hard ++ ++# SSL setup, may be implied by URI also. ++#ssl no ++#ssl on ++#ssl start_tls ++ ++# OpenLDAP SSL options ++# Require and verify server certificate (yes/no) ++# Default is to use libldap's default behavior, which can be configured in ++# /etc/openldap/ldap.conf using the TLS_REQCERT setting. The default for ++# OpenLDAP 2.0 and earlier is "no", for 2.1 and later is "yes". ++#tls_checkpeer hard ++ ++# CA certificates for server certificate verification ++# At least one of these are required if tls_checkpeer is "yes" ++#tls_cacertfile /etc/ssl/ca.cert ++#tls_cacertdir /etc/pki/tls/certs ++ ++# Seed the PRNG if /dev/urandom is not provided ++#tls_randfile /var/run/egd-pool ++ ++# SSL cipher suite ++# See man ciphers for syntax ++#tls_ciphers TLSv1 ++ ++# Client certificate and key ++# Use these, if your server requires client authentication. ++#tls_cert ++#tls_key ++ +diff --git a/openssh-7.2p2/ldapbody.c b/openssh-7.2p2/ldapbody.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapbody.c +@@ -0,0 +1,494 @@ ++/* $OpenBSD: ldapbody.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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 "ldapincludes.h" ++#include "log.h" ++#include "xmalloc.h" ++#include "ldapconf.h" ++#include "ldapmisc.h" ++#include "ldapbody.h" ++#include ++#include ++ ++#define LDAPSEARCH_FORMAT "(&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%s)%s)" ++#define PUBKEYATTR "sshPublicKey" ++#define LDAP_LOGFILE "%s/ldap.%d" ++ ++static FILE *logfile = NULL; ++static LDAP *ld; ++ ++static char *attrs[] = { ++ PUBKEYATTR, ++ NULL ++}; ++ ++void ++ldap_checkconfig (void) ++{ ++#ifdef HAVE_LDAP_INITIALIZE ++ if (options.host == NULL && options.uri == NULL) ++#else ++ if (options.host == NULL) ++#endif ++ fatal ("missing \"host\" in config file"); ++} ++ ++#if defined(LDAP_API_FEATURE_X_OPENLDAP) && (LDAP_API_VERSION > 2000) ++static int ++_rebind_proc (LDAP * ld, LDAP_CONST char *url, int request, ber_int_t msgid) ++{ ++ struct timeval timeout; ++ int rc; ++#if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE) ++ LDAPMessage *result; ++#endif /* HAVE_LDAP_PARSE_RESULT && HAVE_LDAP_CONTROLS_FREE */ ++ ++ debug2 ("Doing LDAP rebind to %s", options.binddn); ++ if (options.ssl == SSL_START_TLS) { ++ if ((rc = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS) { ++ error ("ldap_starttls_s: %s", ldap_err2string (rc)); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ } ++ ++#if !defined(HAVE_LDAP_PARSE_RESULT) || !defined(HAVE_LDAP_CONTROLS_FREE) ++ return ldap_simple_bind_s (ld, options.binddn, options.bindpw); ++#else ++ if (ldap_simple_bind(ld, options.binddn, options.bindpw) < 0) ++ fatal ("ldap_simple_bind %s", ldap_err2string (ldap_get_lderrno (ld, 0, 0))); ++ ++ timeout.tv_sec = options.bind_timelimit; ++ timeout.tv_usec = 0; ++ result = NULL; ++ if ((rc = ldap_result (ld, msgid, FALSE, &timeout, &result)) < 1) { ++ error ("ldap_result %s", ldap_err2string (ldap_get_lderrno (ld, 0, 0))); ++ ldap_msgfree (result); ++ return LDAP_OPERATIONS_ERROR; ++ } ++ debug3 ("LDAP rebind to %s succesfull", options.binddn); ++ return rc; ++#endif ++} ++#else ++ ++static int ++_rebind_proc (LDAP * ld, char **whop, char **credp, int *methodp, int freeit) ++{ ++ if (freeit) ++ return LDAP_SUCCESS; ++ ++ *whop = strdup (options.binddn); ++ *credp = strdup (options.bindpw); ++ *methodp = LDAP_AUTH_SIMPLE; ++ debug2 ("Doing LDAP rebind for %s", *whop); ++ return LDAP_SUCCESS; ++} ++#endif ++ ++void ++ldap_do_connect(void) ++{ ++ int rc, msgid, ld_errno = 0; ++ struct timeval timeout; ++#if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE) ++ int parserc; ++ LDAPMessage *result; ++ LDAPControl **controls; ++ int reconnect = 0; ++#endif /* HAVE_LDAP_PARSE_RESULT && HAVE_LDAP_CONTROLS_FREE */ ++ ++ debug ("LDAP do connect"); ++ ++retry: ++ if (reconnect) { ++ debug3 ("Reconnecting with ld_errno %d", ld_errno); ++ if (options.bind_policy == 0 || ++ (ld_errno != LDAP_SERVER_DOWN && ld_errno != LDAP_TIMEOUT) || ++ reconnect > 5) ++ fatal ("Cannot connect to LDAP server"); ++ ++ if (reconnect > 1) ++ sleep (reconnect - 1); ++ ++ if (ld != NULL) { ++ ldap_unbind (ld); ++ ld = NULL; ++ } ++ logit("reconnecting to LDAP server..."); ++ } ++ ++ if (ld == NULL) { ++ int rc; ++ struct timeval tv; ++ ++#ifdef HAVE_LDAP_SET_OPTION ++ if (options.debug > 0) { ++#ifdef LBER_OPT_LOG_PRINT_FILE ++ if (options.logdir) { ++ char *logfilename; ++ int logfilenamelen; ++ ++ logfilenamelen = strlen (LDAP_LOGFILE) + strlen ("000000") + strlen (options.logdir); ++ logfilename = xmalloc (logfilenamelen); ++ snprintf (logfilename, logfilenamelen, LDAP_LOGFILE, options.logdir, (int) getpid ()); ++ logfilename[logfilenamelen - 1] = 0; ++ if ((logfile = fopen (logfilename, "a")) == NULL) ++ fatal ("cannot append to %s: %s", logfilename, strerror (errno)); ++ debug3 ("LDAP debug into %s", logfilename); ++ free (logfilename); ++ ber_set_option (NULL, LBER_OPT_LOG_PRINT_FILE, logfile); ++ } ++#endif ++ if (options.debug) { ++#ifdef LBER_OPT_DEBUG_LEVEL ++ ber_set_option (NULL, LBER_OPT_DEBUG_LEVEL, &options.debug); ++#endif /* LBER_OPT_DEBUG_LEVEL */ ++#ifdef LDAP_OPT_DEBUG_LEVEL ++ ldap_set_option (NULL, LDAP_OPT_DEBUG_LEVEL, &options.debug); ++#endif /* LDAP_OPT_DEBUG_LEVEL */ ++ debug3 ("Set LDAP debug to %d", options.debug); ++ } ++ } ++#endif /* HAVE_LDAP_SET_OPTION */ ++ ++ ld = NULL; ++#ifdef HAVE_LDAPSSL_INIT ++ if (options.host != NULL) { ++ if (options.ssl_on == SSL_LDAPS) { ++ if ((rc = ldapssl_client_init (options.sslpath, NULL)) != LDAP_SUCCESS) ++ fatal ("ldapssl_client_init %s", ldap_err2string (rc)); ++ debug3 ("LDAPssl client init"); ++ } ++ ++ if (options.ssl_on != SSL_OFF) { ++ if ((ld = ldapssl_init (options.host, options.port, TRUE)) == NULL) ++ fatal ("ldapssl_init failed"); ++ debug3 ("LDAPssl init"); ++ } ++ } ++#endif /* HAVE_LDAPSSL_INIT */ ++ ++ /* continue with opening */ ++ if (ld == NULL) { ++#if defined (HAVE_LDAP_START_TLS_S) || (defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS)) ++ /* Some global TLS-specific options need to be set before we create our ++ * session context, so we set them here. */ ++ ++#ifdef LDAP_OPT_X_TLS_RANDOM_FILE ++ /* rand file */ ++ if (options.tls_randfile != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_RANDOM_FILE, ++ options.tls_randfile)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_RANDOM_FILE): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS random file %s", options.tls_randfile); ++ } ++#endif /* LDAP_OPT_X_TLS_RANDOM_FILE */ ++ ++ /* ca cert file */ ++ if (options.tls_cacertfile != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTFILE, ++ options.tls_cacertfile)) != LDAP_SUCCESS) ++ error ("ldap_set_option(LDAP_OPT_X_TLS_CACERTFILE): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS CA cert file %s ", options.tls_cacertfile); ++ } ++ ++ /* ca cert directory */ ++ if (options.tls_cacertdir != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CACERTDIR, ++ options.tls_cacertdir)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_CACERTDIR): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS CA cert dir %s ", options.tls_cacertdir); ++ } ++ ++ /* require cert? */ ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, ++ &options.tls_checkpeer)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_REQUIRE_CERT): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS check peer to %d ", options.tls_checkpeer); ++ ++ /* set cipher suite, certificate and private key: */ ++ if (options.tls_ciphers != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CIPHER_SUITE, ++ options.tls_ciphers)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_CIPHER_SUITE): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS ciphers to %s ", options.tls_ciphers); ++ } ++ ++ /* cert file */ ++ if (options.tls_cert != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_CERTFILE, ++ options.tls_cert)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_CERTFILE): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS cert file %s ", options.tls_cert); ++ } ++ ++ /* key file */ ++ if (options.tls_key != NULL) { ++ if ((rc = ldap_set_option (NULL, LDAP_OPT_X_TLS_KEYFILE, ++ options.tls_key)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS_KEYFILE): %s", ++ ldap_err2string (rc)); ++ debug3 ("Set TLS key file %s ", options.tls_key); ++ } ++#endif ++#ifdef HAVE_LDAP_INITIALIZE ++ if (options.uri != NULL) { ++ if ((rc = ldap_initialize (&ld, options.uri)) != LDAP_SUCCESS) ++ fatal ("ldap_initialize %s", ldap_err2string (rc)); ++ debug3 ("LDAP initialize %s", options.uri); ++ } ++ } ++#endif /* HAVE_LDAP_INTITIALIZE */ ++ ++ /* continue with opening */ ++ if ((ld == NULL) && (options.host != NULL)) { ++#ifdef HAVE_LDAP_INIT ++ if ((ld = ldap_init (options.host, options.port)) == NULL) ++ fatal ("ldap_init failed"); ++ debug3 ("LDAP init %s:%d", options.host, options.port); ++#else ++ if ((ld = ldap_open (options.host, options.port)) == NULL) ++ fatal ("ldap_open failed"); ++ debug3 ("LDAP open %s:%d", options.host, options.port); ++#endif /* HAVE_LDAP_INIT */ ++ } ++ ++ if (ld == NULL) ++ fatal ("no way to open ldap"); ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_X_TLS) ++ if (options.ssl == SSL_LDAPS) { ++ if ((rc = ldap_set_option (ld, LDAP_OPT_X_TLS, &options.tls_checkpeer)) != LDAP_SUCCESS) ++ fatal ("ldap_set_option(LDAP_OPT_X_TLS) %s", ldap_err2string (rc)); ++ debug3 ("LDAP set LDAP_OPT_X_TLS_%d", options.tls_checkpeer); ++ } ++#endif /* LDAP_OPT_X_TLS */ ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_PROTOCOL_VERSION) ++ (void) ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, ++ &options.ldap_version); ++#else ++ ld->ld_version = options.ldap_version; ++#endif ++ debug3 ("LDAP set version to %d", options.ldap_version); ++ ++#if LDAP_SET_REBIND_PROC_ARGS == 3 ++ ldap_set_rebind_proc (ld, _rebind_proc, NULL); ++#elif LDAP_SET_REBIND_PROC_ARGS == 2 ++ ldap_set_rebind_proc (ld, _rebind_proc); ++#else ++#warning unknown LDAP_SET_REBIND_PROC_ARGS ++#endif ++ debug3 ("LDAP set rebind proc"); ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_DEREF) ++ (void) ldap_set_option (ld, LDAP_OPT_DEREF, &options.deref); ++#else ++ ld->ld_deref = options.deref; ++#endif ++ debug3 ("LDAP set deref to %d", options.deref); ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_TIMELIMIT) ++ (void) ldap_set_option (ld, LDAP_OPT_TIMELIMIT, ++ &options.timelimit); ++#else ++ ld->ld_timelimit = options.timelimit; ++#endif ++ debug3 ("LDAP set timelimit to %d", options.timelimit); ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_X_OPT_CONNECT_TIMEOUT) ++ /* ++ * This is a new option in the Netscape SDK which sets ++ * the TCP connect timeout. For want of a better value, ++ * we use the bind_timelimit to control this. ++ */ ++ timeout = options.bind_timelimit * 1000; ++ (void) ldap_set_option (ld, LDAP_X_OPT_CONNECT_TIMEOUT, &timeout); ++ debug3 ("LDAP set opt connect timeout to %d", timeout); ++#endif ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_NETWORK_TIMEOUT) ++ tv.tv_sec = options.bind_timelimit; ++ tv.tv_usec = 0; ++ (void) ldap_set_option (ld, LDAP_OPT_NETWORK_TIMEOUT, &tv); ++ debug3 ("LDAP set opt network timeout to %ld.0", tv.tv_sec); ++#endif ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_REFERRALS) ++ (void) ldap_set_option (ld, LDAP_OPT_REFERRALS, ++ options.referrals ? LDAP_OPT_ON : LDAP_OPT_OFF); ++ debug3 ("LDAP set referrals to %d", options.referrals); ++#endif ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_RESTART) ++ (void) ldap_set_option (ld, LDAP_OPT_RESTART, ++ options.restart ? LDAP_OPT_ON : LDAP_OPT_OFF); ++ debug3 ("LDAP set restart to %d", options.restart); ++#endif ++ ++#ifdef HAVE_LDAP_START_TLS_S ++ if (options.ssl == SSL_START_TLS) { ++ int version; ++ ++ if (ldap_get_option (ld, LDAP_OPT_PROTOCOL_VERSION, &version) ++ == LDAP_SUCCESS) { ++ if (version < LDAP_VERSION3) { ++ version = LDAP_VERSION3; ++ (void) ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, ++ &version); ++ debug3 ("LDAP set version to %d", version); ++ } ++ } ++ ++ if ((rc = ldap_start_tls_s (ld, NULL, NULL)) != LDAP_SUCCESS) ++ fatal ("ldap_starttls_s: %s", ldap_err2string (rc)); ++ debug3 ("LDAP start TLS"); ++ } ++#endif /* HAVE_LDAP_START_TLS_S */ ++ } ++ ++ if ((msgid = ldap_simple_bind (ld, options.binddn, ++ options.bindpw)) == -1) { ++ ld_errno = ldap_get_lderrno (ld, 0, 0); ++ ++ error ("ldap_simple_bind %s", ldap_err2string (ld_errno)); ++ reconnect++; ++ goto retry; ++ } ++ debug3 ("LDAP simple bind (%s)", options.binddn); ++ ++ timeout.tv_sec = options.bind_timelimit; ++ timeout.tv_usec = 0; ++ if ((rc = ldap_result (ld, msgid, FALSE, &timeout, &result)) < 1) { ++ ld_errno = ldap_get_lderrno (ld, 0, 0); ++ ++ error ("ldap_result %s", ldap_err2string (ld_errno)); ++ reconnect++; ++ goto retry; ++ } ++ debug3 ("LDAP result in time"); ++ ++#if defined(HAVE_LDAP_PARSE_RESULT) && defined(HAVE_LDAP_CONTROLS_FREE) ++ controls = NULL; ++ if ((parserc = ldap_parse_result (ld, result, &rc, 0, 0, 0, &controls, TRUE)) != LDAP_SUCCESS) ++ fatal ("ldap_parse_result %s", ldap_err2string (parserc)); ++ debug3 ("LDAP parse result OK"); ++ ++ if (controls != NULL) { ++ ldap_controls_free (controls); ++ } ++#else ++ rc = ldap_result2error (session->ld, result, TRUE); ++#endif ++ if (rc != LDAP_SUCCESS) ++ fatal ("error trying to bind as user \"%s\" (%s)", ++ options.binddn, ldap_err2string (rc)); ++ ++ debug2 ("LDAP do connect OK"); ++} ++ ++void ++process_user (const char *user, FILE *output) ++{ ++ LDAPMessage *res, *e; ++ char *buffer; ++ int bufflen, rc, i; ++ struct timeval timeout; ++ ++ debug ("LDAP process user"); ++ ++ /* quick check for attempts to be evil */ ++ if ((strchr(user, '(') != NULL) || (strchr(user, ')') != NULL) || ++ (strchr(user, '*') != NULL) || (strchr(user, '\\') != NULL)) { ++ logit ("illegal user name %s not processed", user); ++ return; ++ } ++ ++ /* build filter for LDAP request */ ++ bufflen = strlen (LDAPSEARCH_FORMAT) + strlen (user); ++ if (options.ssh_filter != NULL) ++ bufflen += strlen (options.ssh_filter); ++ buffer = xmalloc (bufflen); ++ snprintf(buffer, bufflen, LDAPSEARCH_FORMAT, user, (options.ssh_filter != NULL) ? options.ssh_filter : NULL); ++ buffer[bufflen - 1] = 0; ++ ++ debug3 ("LDAP search scope = %d %s", options.scope, buffer); ++ ++ timeout.tv_sec = options.timelimit; ++ timeout.tv_usec = 0; ++ if ((rc = ldap_search_st(ld, options.base, options.scope, buffer, attrs, 0, &timeout, &res)) != LDAP_SUCCESS) { ++ error ("ldap_search_st(): %s", ldap_err2string (rc)); ++ free (buffer); ++ return; ++ } ++ ++ /* free */ ++ free (buffer); ++ ++ for (e = ldap_first_entry(ld, res); e != NULL; e = ldap_next_entry(ld, e)) { ++ int num; ++ struct berval **keys; ++ ++ keys = ldap_get_values_len(ld, e, PUBKEYATTR); ++ num = ldap_count_values_len(keys); ++ for (i = 0 ; i < num ; i++) { ++ char *cp; //, *options = NULL; ++ ++ for (cp = keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++); ++ if (!*cp || *cp == '\n' || *cp == '#') ++ continue; ++ ++ /* We have found the desired key. */ ++ fprintf (output, "%s\n", keys[i]->bv_val); ++ } ++ ++ ldap_value_free_len(keys); ++ } ++ ++ ldap_msgfree(res); ++ debug2 ("LDAP process user finished"); ++} ++ ++void ++ldap_do_close(void) ++{ ++ int rc; ++ ++ debug ("LDAP do close"); ++ if ((rc = ldap_unbind_ext(ld, NULL, NULL)) != LDAP_SUCCESS) ++ fatal ("ldap_unbind_ext: %s", ++ ldap_err2string (rc)); ++ ++ ld = NULL; ++ debug2 ("LDAP do close OK"); ++ return; ++} ++ +diff --git a/openssh-7.2p2/ldapbody.h b/openssh-7.2p2/ldapbody.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapbody.h +@@ -0,0 +1,37 @@ ++/* $OpenBSD: ldapbody.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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. ++ */ ++ ++#ifndef LDAPBODY_H ++#define LDAPBODY_H ++ ++#include ++ ++void ldap_checkconfig(void); ++void ldap_do_connect(void); ++void process_user(const char *, FILE *); ++void ldap_do_close(void); ++ ++#endif /* LDAPBODY_H */ ++ +diff --git a/openssh-7.2p2/ldapconf.c b/openssh-7.2p2/ldapconf.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapconf.c +@@ -0,0 +1,711 @@ ++/* $OpenBSD: ldapconf.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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 "ldapincludes.h" ++#include "ldap-helper.h" ++#include "log.h" ++#include "misc.h" ++#include "xmalloc.h" ++#include "ldapconf.h" ++#include ++#include ++ ++/* Keyword tokens. */ ++ ++typedef enum { ++ lBadOption, ++ lHost, lURI, lBase, lBindDN, lBindPW, lRootBindDN, ++ lScope, lDeref, lPort, lTimeLimit, lBind_TimeLimit, ++ lLdap_Version, lBind_Policy, lSSLPath, lSSL, lReferrals, ++ lRestart, lTLS_CheckPeer, lTLS_CaCertFile, ++ lTLS_CaCertDir, lTLS_Ciphers, lTLS_Cert, lTLS_Key, ++ lTLS_RandFile, lLogDir, lDebug, lSSH_Filter, ++ lDeprecated, lUnsupported ++} OpCodes; ++ ++/* Textual representations of the tokens. */ ++ ++static struct { ++ const char *name; ++ OpCodes opcode; ++} keywords[] = { ++ { "URI", lURI }, ++ { "Base", lBase }, ++ { "BindDN", lBindDN }, ++ { "BindPW", lBindPW }, ++ { "RootBindDN", lRootBindDN }, ++ { "Host", lHost }, ++ { "Port", lPort }, ++ { "Scope", lScope }, ++ { "Deref", lDeref }, ++ { "TimeLimit", lTimeLimit }, ++ { "TimeOut", lTimeLimit }, ++ { "Bind_Timelimit", lBind_TimeLimit }, ++ { "Network_TimeOut", lBind_TimeLimit }, ++/* ++ * Todo ++ * SIZELIMIT ++ */ ++ { "Ldap_Version", lLdap_Version }, ++ { "Version", lLdap_Version }, ++ { "Bind_Policy", lBind_Policy }, ++ { "SSLPath", lSSLPath }, ++ { "SSL", lSSL }, ++ { "Referrals", lReferrals }, ++ { "Restart", lRestart }, ++ { "TLS_CheckPeer", lTLS_CheckPeer }, ++ { "TLS_ReqCert", lTLS_CheckPeer }, ++ { "TLS_CaCertFile", lTLS_CaCertFile }, ++ { "TLS_CaCert", lTLS_CaCertFile }, ++ { "TLS_CaCertDir", lTLS_CaCertDir }, ++ { "TLS_Ciphers", lTLS_Ciphers }, ++ { "TLS_Cipher_Suite", lTLS_Ciphers }, ++ { "TLS_Cert", lTLS_Cert }, ++ { "TLS_Certificate", lTLS_Cert }, ++ { "TLS_Key", lTLS_Key }, ++ { "TLS_RandFile", lTLS_RandFile }, ++/* ++ * Todo ++ * TLS_CRLCHECK ++ * TLS_CRLFILE ++ */ ++ { "LogDir", lLogDir }, ++ { "Debug", lDebug }, ++ { "SSH_Filter", lSSH_Filter }, ++ { NULL, lBadOption } ++}; ++ ++/* Configuration ptions. */ ++ ++Options options; ++ ++/* ++ * Returns the number of the token pointed to by cp or oBadOption. ++ */ ++ ++static OpCodes ++parse_token(const char *cp, const char *filename, int linenum) ++{ ++ u_int i; ++ ++ for (i = 0; keywords[i].name; i++) ++ if (strcasecmp(cp, keywords[i].name) == 0) ++ return keywords[i].opcode; ++ ++ if (config_warning_config_file) ++ logit("%s: line %d: Bad configuration option: %s", ++ filename, linenum, cp); ++ return lBadOption; ++} ++ ++/* ++ * Processes a single option line as used in the configuration files. This ++ * only sets those values that have not already been set. ++ */ ++#define WHITESPACE " \t\r\n" ++ ++static int ++process_config_line(char *line, const char *filename, int linenum) ++{ ++ char *s, **charptr, **xstringptr, *endofnumber, *keyword, *arg; ++ char *rootbinddn = NULL; ++ int opcode, *intptr, value; ++ size_t len; ++ ++ /* Strip trailing whitespace */ ++ for (len = strlen(line) - 1; len > 0; len--) { ++ if (strchr(WHITESPACE, line[len]) == NULL) ++ break; ++ line[len] = '\0'; ++ } ++ ++ s = line; ++ /* Get the keyword. (Each line is supposed to begin with a keyword). */ ++ if ((keyword = strdelim(&s)) == NULL) ++ return 0; ++ /* Ignore leading whitespace. */ ++ if (*keyword == '\0') ++ keyword = strdelim(&s); ++ if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') ++ return 0; ++ ++ opcode = parse_token(keyword, filename, linenum); ++ ++ switch (opcode) { ++ case lBadOption: ++ /* don't panic, but count bad options */ ++ return -1; ++ /* NOTREACHED */ ++ ++ case lHost: ++ xstringptr = &options.host; ++parse_xstring: ++ if (!s || *s == '\0') ++ fatal("%s line %d: missing dn",filename,linenum); ++ if (*xstringptr == NULL) ++ *xstringptr = xstrdup(s); ++ return 0; ++ ++ case lURI: ++ xstringptr = &options.uri; ++ goto parse_xstring; ++ ++ case lBase: ++ xstringptr = &options.base; ++ goto parse_xstring; ++ ++ case lBindDN: ++ xstringptr = &options.binddn; ++ goto parse_xstring; ++ ++ case lBindPW: ++ charptr = &options.bindpw; ++parse_string: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", filename, linenum); ++ if (*charptr == NULL) ++ *charptr = xstrdup(arg); ++ break; ++ ++ case lRootBindDN: ++ xstringptr = &rootbinddn; ++ goto parse_xstring; ++ ++ case lScope: ++ intptr = &options.scope; ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing sub/one/base argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (strcasecmp(arg, "sub") == 0 || ++ strcasecmp(arg, "subtree") == 0) ++ value = LDAP_SCOPE_SUBTREE; ++ else if (strcasecmp(arg, "one") == 0) ++ value = LDAP_SCOPE_ONELEVEL; ++ else if (strcasecmp(arg, "base") == 0) ++ value = LDAP_SCOPE_BASE; ++ else ++ fatal("%.200s line %d: Bad sub/one/base argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lDeref: ++ intptr = &options.scope; ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing never/searching/finding/always argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (!strcasecmp(arg, "never")) ++ value = LDAP_DEREF_NEVER; ++ else if (!strcasecmp(arg, "searching")) ++ value = LDAP_DEREF_SEARCHING; ++ else if (!strcasecmp(arg, "finding")) ++ value = LDAP_DEREF_FINDING; ++ else if (!strcasecmp(arg, "always")) ++ value = LDAP_DEREF_ALWAYS; ++ else ++ fatal("%.200s line %d: Bad never/searching/finding/always argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lPort: ++ intptr = &options.port; ++parse_int: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", filename, linenum); ++ if (arg[0] < '0' || arg[0] > '9') ++ fatal("%.200s line %d: Bad number.", filename, linenum); ++ ++ /* Octal, decimal, or hex format? */ ++ value = strtol(arg, &endofnumber, 0); ++ if (arg == endofnumber) ++ fatal("%.200s line %d: Bad number.", filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lTimeLimit: ++ intptr = &options.timelimit; ++parse_time: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%s line %d: missing time value.", ++ filename, linenum); ++ if ((value = convtime(arg)) == -1) ++ fatal("%s line %d: invalid time value.", ++ filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lBind_TimeLimit: ++ intptr = &options.bind_timelimit; ++ goto parse_time; ++ ++ case lLdap_Version: ++ intptr = &options.ldap_version; ++ goto parse_int; ++ ++ case lBind_Policy: ++ intptr = &options.bind_policy; ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing soft/hard argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (strcasecmp(arg, "hard") == 0 || ++ strcasecmp(arg, "hard_open") == 0 || ++ strcasecmp(arg, "hard_init") == 0) ++ value = 1; ++ else if (strcasecmp(arg, "soft") == 0) ++ value = 0; ++ else ++ fatal("%.200s line %d: Bad soft/hard argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ break; ++ ++ case lSSLPath: ++ charptr = &options.sslpath; ++ goto parse_string; ++ ++ case lSSL: ++ intptr = &options.ssl; ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing yes/no/start_tls argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (strcasecmp(arg, "yes") == 0 || ++ strcasecmp(arg, "true") == 0 || ++ strcasecmp(arg, "on") == 0) ++ value = SSL_LDAPS; ++ else if (strcasecmp(arg, "no") == 0 || ++ strcasecmp(arg, "false") == 0 || ++ strcasecmp(arg, "off") == 0) ++ value = SSL_OFF; ++ else if (!strcasecmp (arg, "start_tls")) ++ value = SSL_START_TLS; ++ else ++ fatal("%.200s line %d: Bad yes/no/start_tls argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lReferrals: ++ intptr = &options.referrals; ++parse_flag: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing yes/no argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (strcasecmp(arg, "yes") == 0 || ++ strcasecmp(arg, "true") == 0 || ++ strcasecmp(arg, "on") == 0) ++ value = 1; ++ else if (strcasecmp(arg, "no") == 0 || ++ strcasecmp(arg, "false") == 0 || ++ strcasecmp(arg, "off") == 0) ++ value = 0; ++ else ++ fatal("%.200s line %d: Bad yes/no argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ *intptr = value; ++ break; ++ ++ case lRestart: ++ intptr = &options.restart; ++ goto parse_flag; ++ ++ case lTLS_CheckPeer: ++ intptr = &options.tls_checkpeer; ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing never/hard/demand/alow/try argument.", ++ filename, linenum); ++ value = 0; /* To avoid compiler warning... */ ++ if (strcasecmp(arg, "never") == 0 || ++ strcasecmp(arg, "no") == 0 || ++ strcasecmp(arg, "false") == 0 || ++ strcasecmp(arg, "off") == 0) ++ value = LDAP_OPT_X_TLS_NEVER; ++ else if (strcasecmp(arg, "hard") == 0 || ++ strcasecmp(arg, "yes") == 0 || ++ strcasecmp(arg, "true") == 0 || ++ strcasecmp(arg, "on") == 0) ++ value = LDAP_OPT_X_TLS_HARD; ++ else if (strcasecmp(arg, "demand") == 0) ++ value = LDAP_OPT_X_TLS_DEMAND; ++ else if (strcasecmp(arg, "allow") == 0) ++ value = LDAP_OPT_X_TLS_ALLOW; ++ else if (strcasecmp(arg, "try") == 0) ++ value = LDAP_OPT_X_TLS_TRY; ++ else ++ fatal("%.200s line %d: Bad never/hard/demand/alow/try argument.", ++ filename, linenum); ++ if (*intptr == -1) ++ break; ++ ++ case lTLS_CaCertFile: ++ charptr = &options.tls_cacertfile; ++ goto parse_string; ++ ++ case lTLS_CaCertDir: ++ charptr = &options.tls_cacertdir; ++ goto parse_string; ++ ++ case lTLS_Ciphers: ++ xstringptr = &options.tls_ciphers; ++ goto parse_xstring; ++ ++ case lTLS_Cert: ++ charptr = &options.tls_cert; ++ goto parse_string; ++ ++ case lTLS_Key: ++ charptr = &options.tls_key; ++ goto parse_string; ++ ++ case lTLS_RandFile: ++ charptr = &options.tls_randfile; ++ goto parse_string; ++ ++ case lLogDir: ++ charptr = &options.logdir; ++ goto parse_string; ++ ++ case lDebug: ++ intptr = &options.debug; ++ goto parse_int; ++ ++ case lSSH_Filter: ++ xstringptr = &options.ssh_filter; ++ goto parse_xstring; ++ ++ case lDeprecated: ++ debug("%s line %d: Deprecated option \"%s\"", ++ filename, linenum, keyword); ++ return 0; ++ ++ case lUnsupported: ++ error("%s line %d: Unsupported option \"%s\"", ++ filename, linenum, keyword); ++ return 0; ++ ++ default: ++ fatal("process_config_line: Unimplemented opcode %d", opcode); ++ } ++ ++ /* Check that there is no garbage at end of line. */ ++ if ((arg = strdelim(&s)) != NULL && *arg != '\0') { ++ fatal("%.200s line %d: garbage at end of line; \"%.200s\".", ++ filename, linenum, arg); ++ } ++ return 0; ++} ++ ++/* ++ * Reads the config file and modifies the options accordingly. Options ++ * should already be initialized before this call. This never returns if ++ * there is an error. If the file does not exist, this returns 0. ++ */ ++ ++void ++read_config_file(const char *filename) ++{ ++ FILE *f; ++ char line[1024]; ++ int active, linenum; ++ int bad_options = 0; ++ struct stat sb; ++ ++ if ((f = fopen(filename, "r")) == NULL) ++ fatal("fopen %s: %s", filename, strerror(errno)); ++ ++ if (fstat(fileno(f), &sb) == -1) ++ fatal("fstat %s: %s", filename, strerror(errno)); ++ if (((sb.st_uid != 0 && sb.st_uid != getuid()) || ++ (sb.st_mode & 022) != 0)) ++ fatal("Bad owner or permissions on %s", filename); ++ ++ debug("Reading configuration data %.200s", filename); ++ ++ /* ++ * Mark that we are now processing the options. This flag is turned ++ * on/off by Host specifications. ++ */ ++ active = 1; ++ linenum = 0; ++ while (fgets(line, sizeof(line), f)) { ++ /* Update line number counter. */ ++ linenum++; ++ if (process_config_line(line, filename, linenum) != 0) ++ bad_options++; ++ } ++ fclose(f); ++ if ((bad_options > 0) && config_exclusive_config_file) ++ fatal("%s: terminating, %d bad configuration options", ++ filename, bad_options); ++} ++ ++/* ++ * Initializes options to special values that indicate that they have not yet ++ * been set. Read_config_file will only set options with this value. Options ++ * are processed in the following order: command line, user config file, ++ * system config file. Last, fill_default_options is called. ++ */ ++ ++void ++initialize_options(void) ++{ ++ memset(&options, 'X', sizeof(options)); ++ options.host = NULL; ++ options.uri = NULL; ++ options.base = NULL; ++ options.binddn = NULL; ++ options.bindpw = NULL; ++ options.scope = -1; ++ options.deref = -1; ++ options.port = -1; ++ options.timelimit = -1; ++ options.bind_timelimit = -1; ++ options.ldap_version = -1; ++ options.bind_policy = -1; ++ options.sslpath = NULL; ++ options.ssl = -1; ++ options.referrals = -1; ++ options.restart = -1; ++ options.tls_checkpeer = -1; ++ options.tls_cacertfile = NULL; ++ options.tls_cacertdir = NULL; ++ options.tls_ciphers = NULL; ++ options.tls_cert = NULL; ++ options.tls_key = NULL; ++ options.tls_randfile = NULL; ++ options.logdir = NULL; ++ options.debug = -1; ++ options.ssh_filter = NULL; ++} ++ ++/* ++ * Called after processing other sources of option data, this fills those ++ * options for which no value has been specified with their default values. ++ */ ++ ++void ++fill_default_options(void) ++{ ++ if (options.uri != NULL) { ++ LDAPURLDesc *ludp; ++ ++ if (ldap_url_parse(options.uri, &ludp) == LDAP_SUCCESS) { ++ if (options.ssl == -1) { ++ if (strcmp(ludp->lud_scheme, "ldap") == 0) ++ options.ssl = 2; ++ if (strcmp(ludp->lud_scheme, "ldapi") == 0) ++ options.ssl = 0; ++ else if (strcmp(ludp->lud_scheme, "ldaps") == 0) ++ options.ssl = 1; ++ } ++ if (options.host == NULL) ++ options.host = xstrdup (ludp->lud_host); ++ if (options.port == -1) ++ options.port = ludp->lud_port; ++ ++ ldap_free_urldesc (ludp); ++ } ++ } ++ if (options.ssl == -1) ++ options.ssl = SSL_START_TLS; ++ if (options.port == -1) ++ options.port = (options.ssl == 0) ? 389 : 636; ++ if (options.uri == NULL) { ++ int len; ++#define MAXURILEN 4096 ++ ++ options.uri = xmalloc (MAXURILEN); ++ len = snprintf(options.uri, MAXURILEN, "ldap%s://%s:%d", ++ (options.ssl == 0) ? "" : "s", options.host, options.port); ++ options.uri[MAXURILEN - 1] = 0; ++ options.uri = xreallocarray(options.uri, len + 1, 1); ++ } ++ if (options.binddn == NULL) ++ options.binddn = ""; ++ if (options.bindpw == NULL) ++ options.bindpw = ""; ++ if (options.scope == -1) ++ options.scope = LDAP_SCOPE_SUBTREE; ++ if (options.deref == -1) ++ options.deref = LDAP_DEREF_NEVER; ++ if (options.timelimit == -1) ++ options.timelimit = 10; ++ if (options.bind_timelimit == -1) ++ options.bind_timelimit = 10; ++ if (options.ldap_version == -1) ++ options.ldap_version = 3; ++ if (options.bind_policy == -1) ++ options.bind_policy = 1; ++ if (options.referrals == -1) ++ options.referrals = 1; ++ if (options.restart == -1) ++ options.restart = 1; ++ if (options.tls_checkpeer == -1) ++ options.tls_checkpeer = LDAP_OPT_X_TLS_HARD; ++ if (options.debug == -1) ++ options.debug = 0; ++ if (options.ssh_filter == NULL) ++ options.ssh_filter = ""; ++} ++ ++static const char * ++lookup_opcode_name(OpCodes code) ++{ ++ u_int i; ++ ++ for (i = 0; keywords[i].name != NULL; i++) ++ if (keywords[i].opcode == code) ++ return(keywords[i].name); ++ return "UNKNOWN"; ++} ++ ++static void ++dump_cfg_string(OpCodes code, const char *val) ++{ ++ if (val == NULL) ++ debug3("%s ", lookup_opcode_name(code)); ++ else ++ debug3("%s %s", lookup_opcode_name(code), val); ++} ++ ++static void ++dump_cfg_int(OpCodes code, int val) ++{ ++ if (val == -1) ++ debug3("%s ", lookup_opcode_name(code)); ++ else ++ debug3("%s %d", lookup_opcode_name(code), val); ++} ++ ++struct names { ++ int value; ++ char *name; ++}; ++ ++static void ++dump_cfg_namedint(OpCodes code, int val, struct names *names) ++{ ++ u_int i; ++ ++ if (val == -1) ++ debug3("%s ", lookup_opcode_name(code)); ++ else { ++ for (i = 0; names[i].value != -1; i++) ++ if (names[i].value == val) { ++ debug3("%s %s", lookup_opcode_name(code), names[i].name); ++ return; ++ } ++ debug3("%s unknown: %d", lookup_opcode_name(code), val); ++ } ++} ++ ++static struct names _yesnotls[] = { ++ { 0, "No" }, ++ { 1, "Yes" }, ++ { 2, "Start_TLS" }, ++ { -1, NULL }}; ++ ++static struct names _scope[] = { ++ { LDAP_SCOPE_BASE, "Base" }, ++ { LDAP_SCOPE_ONELEVEL, "One" }, ++ { LDAP_SCOPE_SUBTREE, "Sub"}, ++ { -1, NULL }}; ++ ++static struct names _deref[] = { ++ { LDAP_DEREF_NEVER, "Never" }, ++ { LDAP_DEREF_SEARCHING, "Searching" }, ++ { LDAP_DEREF_FINDING, "Finding" }, ++ { LDAP_DEREF_ALWAYS, "Always" }, ++ { -1, NULL }}; ++ ++static struct names _yesno[] = { ++ { 0, "No" }, ++ { 1, "Yes" }, ++ { -1, NULL }}; ++ ++static struct names _bindpolicy[] = { ++ { 0, "Soft" }, ++ { 1, "Hard" }, ++ { -1, NULL }}; ++ ++static struct names _checkpeer[] = { ++ { LDAP_OPT_X_TLS_NEVER, "Never" }, ++ { LDAP_OPT_X_TLS_HARD, "Hard" }, ++ { LDAP_OPT_X_TLS_DEMAND, "Demand" }, ++ { LDAP_OPT_X_TLS_ALLOW, "Allow" }, ++ { LDAP_OPT_X_TLS_TRY, "TRY" }, ++ { -1, NULL }}; ++ ++void ++dump_config(void) ++{ ++ dump_cfg_string(lURI, options.uri); ++ dump_cfg_string(lHost, options.host); ++ dump_cfg_int(lPort, options.port); ++ dump_cfg_namedint(lSSL, options.ssl, _yesnotls); ++ dump_cfg_int(lLdap_Version, options.ldap_version); ++ dump_cfg_int(lTimeLimit, options.timelimit); ++ dump_cfg_int(lBind_TimeLimit, options.bind_timelimit); ++ dump_cfg_string(lBase, options.base); ++ dump_cfg_string(lBindDN, options.binddn); ++ dump_cfg_string(lBindPW, options.bindpw); ++ dump_cfg_namedint(lScope, options.scope, _scope); ++ dump_cfg_namedint(lDeref, options.deref, _deref); ++ dump_cfg_namedint(lReferrals, options.referrals, _yesno); ++ dump_cfg_namedint(lRestart, options.restart, _yesno); ++ dump_cfg_namedint(lBind_Policy, options.bind_policy, _bindpolicy); ++ dump_cfg_string(lSSLPath, options.sslpath); ++ dump_cfg_namedint(lTLS_CheckPeer, options.tls_checkpeer, _checkpeer); ++ dump_cfg_string(lTLS_CaCertFile, options.tls_cacertfile); ++ dump_cfg_string(lTLS_CaCertDir, options.tls_cacertdir); ++ dump_cfg_string(lTLS_Ciphers, options.tls_ciphers); ++ dump_cfg_string(lTLS_Cert, options.tls_cert); ++ dump_cfg_string(lTLS_Key, options.tls_key); ++ dump_cfg_string(lTLS_RandFile, options.tls_randfile); ++ dump_cfg_string(lLogDir, options.logdir); ++ dump_cfg_int(lDebug, options.debug); ++ dump_cfg_string(lSSH_Filter, options.ssh_filter); ++} ++ +diff --git a/openssh-7.2p2/ldapconf.h b/openssh-7.2p2/ldapconf.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapconf.h +@@ -0,0 +1,71 @@ ++/* $OpenBSD: ldapconf.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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. ++ */ ++ ++#ifndef LDAPCONF_H ++#define LDAPCONF_H ++ ++#define SSL_OFF 0 ++#define SSL_LDAPS 1 ++#define SSL_START_TLS 2 ++ ++/* Data structure for representing option data. */ ++ ++typedef struct { ++ char *host; ++ char *uri; ++ char *base; ++ char *binddn; ++ char *bindpw; ++ int scope; ++ int deref; ++ int port; ++ int timelimit; ++ int bind_timelimit; ++ int ldap_version; ++ int bind_policy; ++ char *sslpath; ++ int ssl; ++ int referrals; ++ int restart; ++ int tls_checkpeer; ++ char *tls_cacertfile; ++ char *tls_cacertdir; ++ char *tls_ciphers; ++ char *tls_cert; ++ char *tls_key; ++ char *tls_randfile; ++ char *logdir; ++ int debug; ++ char *ssh_filter; ++} Options; ++ ++extern Options options; ++ ++void read_config_file(const char *); ++void initialize_options(void); ++void fill_default_options(void); ++void dump_config(void); ++ ++#endif /* LDAPCONF_H */ +diff --git a/openssh-7.2p2/ldapincludes.h b/openssh-7.2p2/ldapincludes.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapincludes.h +@@ -0,0 +1,41 @@ ++/* $OpenBSD: ldapconf.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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. ++ */ ++ ++#ifndef LDAPINCLUDES_H ++#define LDAPINCLUDES_H ++ ++#include "includes.h" ++ ++#ifdef HAVE_LBER_H ++#include ++#endif ++#ifdef HAVE_LDAP_H ++#include ++#endif ++#ifdef HAVE_LDAP_SSL_H ++#include ++#endif ++ ++#endif /* LDAPINCLUDES_H */ +diff --git a/openssh-7.2p2/ldapmisc.c b/openssh-7.2p2/ldapmisc.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapmisc.c +@@ -0,0 +1,79 @@ ++ ++#include "ldapincludes.h" ++#include "ldapmisc.h" ++ ++#ifndef HAVE_LDAP_GET_LDERRNO ++int ++ldap_get_lderrno (LDAP * ld, char **m, char **s) ++{ ++#ifdef HAVE_LDAP_GET_OPTION ++ int rc; ++#endif ++ int lderrno; ++ ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno)) != LDAP_SUCCESS) ++ return rc; ++#else ++ lderrno = ld->ld_errno; ++#endif ++ ++ if (s != NULL) { ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_STRING) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_ERROR_STRING, s)) != LDAP_SUCCESS) ++ return rc; ++#else ++ *s = ld->ld_error; ++#endif ++ } ++ ++ if (m != NULL) { ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_MATCHED_DN) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_MATCHED_DN, m)) != LDAP_SUCCESS) ++ return rc; ++#else ++ *m = ld->ld_matched; ++#endif ++ } ++ ++ return lderrno; ++} ++#endif ++ ++#ifndef HAVE_LDAP_SET_LDERRNO ++int ++ldap_set_lderrno (LDAP * ld, int lderrno, const char *m, const char *s) ++{ ++#ifdef HAVE_LDAP_SET_OPTION ++ int rc; ++#endif ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_errno = lderrno; ++#endif ++ ++ if (s != NULL) { ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_STRING) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_ERROR_STRING, s)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_error = s; ++#endif ++ } ++ ++ if (m != NULL) { ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_MATCHED_DN) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_MATCHED_DN, m)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_matched = m; ++#endif ++ } ++ ++ return LDAP_SUCCESS; ++} ++#endif ++ +diff --git a/openssh-7.2p2/ldapmisc.h b/openssh-7.2p2/ldapmisc.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ldapmisc.h +@@ -0,0 +1,35 @@ ++/* $OpenBSD: ldapbody.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. 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. ++ */ ++ ++#ifndef LDAPMISC_H ++#define LDAPMISC_H ++ ++#include "ldapincludes.h" ++ ++int ldap_get_lderrno (LDAP *, char **, char **); ++int ldap_set_lderrno (LDAP *, int, const char *, const char *); ++ ++#endif /* LDAPMISC_H */ ++ +diff --git a/openssh-7.2p2/openbsd-compat/base64.c b/openssh-7.2p2/openbsd-compat/base64.c +--- a/openssh-7.2p2/openbsd-compat/base64.c ++++ b/openssh-7.2p2/openbsd-compat/base64.c +@@ -41,17 +41,17 @@ + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + + /* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + + #include "includes.h" + +-#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) ++#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)) || defined(USE_INTERNAL_B64) + + #include + #include + #include + #include + #include + + #include +@@ -124,17 +124,17 @@ static const char Pad64 = '='; + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +-#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) ++#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) || defined(USE_INTERNAL_B64) + int + b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) + { + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + u_int i; + +@@ -180,17 +180,17 @@ b64_ntop(u_char const *src, size_t srcle + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); + } + #endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */ + +-#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) ++#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) || defined(USE_INTERNAL_B64) + + /* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + + int +diff --git a/openssh-7.2p2/openbsd-compat/base64.h b/openssh-7.2p2/openbsd-compat/base64.h +--- a/openssh-7.2p2/openbsd-compat/base64.h ++++ b/openssh-7.2p2/openbsd-compat/base64.h +@@ -42,24 +42,24 @@ + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + + #ifndef _BSD_BASE64_H + #define _BSD_BASE64_H + + #include "includes.h" + +-#ifndef HAVE___B64_NTOP +-# ifndef HAVE_B64_NTOP ++#if !defined(HAVE___B64_NTOP) || defined(USE_INTERNAL_B64) ++# if !defined(HAVE_B64_NTOP) || defined(USE_INTERNAL_B64) + int b64_ntop(u_char const *src, size_t srclength, char *target, + size_t targsize); + # endif /* !HAVE_B64_NTOP */ + # define __b64_ntop(a,b,c,d) b64_ntop(a,b,c,d) + #endif /* HAVE___B64_NTOP */ + +-#ifndef HAVE___B64_PTON +-# ifndef HAVE_B64_PTON ++#if !defined(HAVE___B64_PTON) || defined(USE_INTERNAL_B64) ++# if !defined(HAVE_B64_PTON) || defined(USE_INTERNAL_B64) + int b64_pton(char const *src, u_char *target, size_t targsize); + # endif /* !HAVE_B64_PTON */ + # define __b64_pton(a,b,c) b64_pton(a,b,c) + #endif /* HAVE___B64_PTON */ + + #endif /* _BSD_BASE64_H */ +diff --git a/openssh-7.2p2/openssh-lpk-openldap.schema b/openssh-7.2p2/openssh-lpk-openldap.schema +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/openssh-lpk-openldap.schema +@@ -0,0 +1,21 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# Based on the proposal of : Mark Ruijter ++# ++ ++ ++# octetString SYNTAX ++attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' ++ DESC 'MANDATORY: OpenSSH Public key' ++ EQUALITY octetStringMatch ++ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) ++ ++# printableString SYNTAX yes|no ++objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY ++ DESC 'MANDATORY: OpenSSH LPK objectclass' ++ MUST ( sshPublicKey $ uid ) ++ ) +diff --git a/openssh-7.2p2/openssh-lpk-sun.schema b/openssh-7.2p2/openssh-lpk-sun.schema +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/openssh-lpk-sun.schema +@@ -0,0 +1,23 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# Schema for Sun Directory Server. ++# Based on the original schema, modified by Stefan Fischer. ++# ++ ++dn: cn=schema ++ ++# octetString SYNTAX ++attributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' ++ DESC 'MANDATORY: OpenSSH Public key' ++ EQUALITY octetStringMatch ++ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) ++ ++# printableString SYNTAX yes|no ++objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY ++ DESC 'MANDATORY: OpenSSH LPK objectclass' ++ MUST ( sshPublicKey $ uid ) ++ ) +diff --git a/openssh-7.2p2/ssh-ldap-helper.8 b/openssh-7.2p2/ssh-ldap-helper.8 +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ssh-ldap-helper.8 +@@ -0,0 +1,79 @@ ++.\" $OpenBSD: ssh-ldap-helper.8,v 1.1 2010/02/10 23:20:38 markus Exp $ ++.\" ++.\" Copyright (c) 2010 Jan F. Chadima. All rights reserved. ++.\" ++.\" Permission to use, copy, modify, and distribute this software for any ++.\" purpose with or without fee is hereby granted, provided that the above ++.\" copyright notice and this permission notice appear in all copies. ++.\" ++.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++.\" ++.Dd $Mdocdate: April 29 2010 $ ++.Dt SSH-LDAP-HELPER 8 ++.Os ++.Sh NAME ++.Nm ssh-ldap-helper ++.Nd sshd helper program for ldap support ++.Sh SYNOPSIS ++.Nm ssh-ldap-helper ++.Op Fl devw ++.Op Fl f Ar file ++.Op Fl s Ar user ++.Sh DESCRIPTION ++.Nm ++is used by ++.Xr sshd 1 ++to access keys provided by an LDAP. ++.Nm ++is disabled by default and can only be enabled in the ++sshd configuration file ++.Pa /etc/ssh/sshd_config ++by setting ++.Cm AuthorizedKeysCommand ++to ++.Dq @LIBEXECDIR@/ssh-ldap-wrapper . ++.Pp ++.Nm ++is not intended to be invoked by the user, but from ++.Xr sshd 8 via ++.Xr ssh-ldap-wrapper . ++.Pp ++The options are as follows: ++.Bl -tag -width Ds ++.It Fl d ++Set the debug mode; ++.Nm ++prints all logs to stderr instead of syslog. ++.It Fl e ++Implies \-w; ++.Nm ++halts if it encounters an unknown item in the ldap.conf file. ++.It Fl f ++.Nm ++uses this file as the ldap configuration file instead of /etc/ssh/ldap.conf (default). ++.It Fl s ++.Nm ++prints out the user's keys to stdout and exits. ++.It Fl v ++Implies \-d; ++increases verbosity. ++.It Fl w ++.Nm ++writes warnings about unknown items in the ldap.conf configuration file. ++.El ++.Sh SEE ALSO ++.Xr sshd 8 , ++.Xr sshd_config 5 , ++.Xr ssh-ldap.conf 5 , ++.Sh HISTORY ++.Nm ++first appeared in ++OpenSSH 5.5 + PKA-LDAP . ++.Sh AUTHORS ++.An Jan F. Chadima Aq jchadima@redhat.com +diff --git a/openssh-7.2p2/ssh-ldap-wrapper b/openssh-7.2p2/ssh-ldap-wrapper +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ssh-ldap-wrapper +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++exec @LIBEXECDIR@/ssh-ldap-helper -s "$1" ++ +diff --git a/openssh-7.2p2/ssh-ldap.conf.5 b/openssh-7.2p2/ssh-ldap.conf.5 +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ssh-ldap.conf.5 +@@ -0,0 +1,376 @@ ++.\" $OpenBSD: ssh-ldap.conf.5,v 1.1 2010/02/10 23:20:38 markus Exp $ ++.\" ++.\" Copyright (c) 2010 Jan F. Chadima. All rights reserved. ++.\" ++.\" Permission to use, copy, modify, and distribute this software for any ++.\" purpose with or without fee is hereby granted, provided that the above ++.\" copyright notice and this permission notice appear in all copies. ++.\" ++.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++.\" ++.Dd $Mdocdate: may 12 2010 $ ++.Dt SSH-LDAP.CONF 5 ++.Os ++.Sh NAME ++.Nm ssh-ldap.conf ++.Nd configuration file for ssh-ldap-helper ++.Sh SYNOPSIS ++.Nm /etc/ssh/ldap.conf ++.Sh DESCRIPTION ++.Xr ssh-ldap-helper 8 ++reads configuration data from ++.Pa /etc/ssh/ldap.conf ++(or the file specified with ++.Fl f ++on the command line). ++The file contains keyword-argument pairs, one per line. ++Lines starting with ++.Ql # ++and empty lines are interpreted as comments. ++.Pp ++The value starts with the first non-blank character after ++the keyword's name, and terminates at the end of the line, ++or at the last sequence of blanks before the end of the line. ++Quoting values that contain blanks ++may be incorrect, as the quotes would become part of the value. ++The possible keywords and their meanings are as follows (note that ++keywords are case-insensitive, and arguments, on a case by case basis, may be case-sensitive). ++.Bl -tag -width Ds ++.It Cm URI ++The argument(s) are in the form ++.Pa ldap[si]://[name[:port]] ++and specify the URI(s) of an LDAP server(s) to which the ++.Xr ssh-ldap-helper 8 ++should connect. The URI scheme may be any of ++.Dq ldap , ++.Dq ldaps ++or ++.Dq ldapi , ++which refer to LDAP over TCP, LDAP over SSL (TLS) and LDAP ++over IPC (UNIX domain sockets), respectively. ++Each server's name can be specified as a ++domain-style name or an IP address literal. Optionally, the ++server's name can followed by a ':' and the port number the LDAP ++server is listening on. If no port number is provided, the default ++port for the scheme is used (389 for ldap://, 636 for ldaps://). ++For LDAP over IPC, name is the name of the socket, and no port ++is required, nor allowed; note that directory separators must be ++URL-encoded, like any other characters that are special to URLs; ++A space separated list of URIs may be provided. ++There is no default. ++.It Cm Base ++Specifies the default base Distinguished Name (DN) to use when performing ldap operations. ++The base must be specified as a DN in LDAP format. ++There is no default. ++.It Cm BindDN ++Specifies the default BIND DN to use when connecting to the ldap server. ++The bind DN must be specified as a Distinguished Name in LDAP format. ++There is no default. ++.It Cm BindPW ++Specifies the default password to use when connecting to the ldap server via ++.Cm BindDN . ++There is no default. ++.It Cm RootBindDN ++Intentionaly does nothing. Recognized for compatibility reasons. ++.It Cm Host ++The argument(s) specifies the name(s) of an LDAP server(s) to which the ++.Xr ssh-ldap-helper 8 ++should connect. Each server's name can be specified as a ++domain-style name or an IP address and optionally followed by a ':' and ++the port number the ldap server is listening on. A space-separated ++list of hosts may be provided. ++There is no default. ++.Cm Host ++is deprecated in favor of ++.Cm URI . ++.It Cm Port ++Specifies the default port used when connecting to LDAP servers(s). ++The port may be specified as a number. ++The default port is 389 for ldap:// or 636 for ldaps:// respectively. ++.Cm Port ++is deprecated in favor of ++.Cm URI . ++.It Cm Scope ++Specifies the starting point of an LDAP search and the depth from the base DN to which the search should descend. ++There are three options (values) that can be assigned to the ++.Cm Scope parameter: ++.Dq base , ++.Dq one ++and ++.Dq subtree . ++Alias for the subtree is ++.Dq sub . ++The value ++.Dq base ++is used to indicate searching only the entry at the base DN, resulting in only that entry being returned (keeping in mind that it also has to meet the search filter criteria!). ++The value ++.Dq one ++is used to indicate searching all entries one level under the base DN, but not including the base DN and not including any entries under that one level under the base DN. ++The value ++.Dq subtree ++is used to indicate searching of all entries at all levels under and including the specified base DN. ++The default is ++.Dq subtree . ++.It Cm Deref ++Specifies how alias dereferencing is done when performing a search. There are four ++possible values that can be assigned to the ++.Cm Deref ++parameter: ++.Dq never , ++.Dq searching , ++.Dq finding , ++and ++.Dq always . ++The value ++.Dq never ++means that the aliases are never dereferenced. ++The value ++.Dq searching ++means that the aliases are dereferenced in subordinates of the base object, but ++not in locating the base object of the search. ++The value ++.Dq finding ++means that the aliases are only dereferenced when locating the base object of the search. ++The value ++.Dq always ++means that the aliases are dereferenced both in searching and in locating the base object ++of the search. ++The default is ++.Dq never . ++.It Cm TimeLimit ++Specifies a time limit (in seconds) to use when performing searches. ++The number should be a non-negative integer. A ++.Cm TimeLimit ++of zero (0) specifies that the search time is unlimited. Please note that the server ++may still apply any server-side limit on the duration of a search operation. ++The default value is 10. ++.It Cm TimeOut ++Is an aliast to ++.Cm TimeLimit . ++.It Cm Bind_TimeLimit ++Specifies the timeout (in seconds) after which the poll(2)/select(2) ++following a connect(2) returns in case of no activity. ++The default value is 10. ++.It Cm Network_TimeOut ++Is an alias to ++.Cm Bind_TimeLimit . ++.It Cm Ldap_Version ++Specifies what version of the LDAP protocol should be used. ++The allowed values are 2 or 3. The default is 3. ++.It Cm Version ++Is an alias to ++.Cm Ldap_Version . ++.It Cm Bind_Policy ++Specifies the policy to use for reconnecting to an unavailable LDAP server. There are 2 available values: ++.Dq hard ++and ++.Dq soft. ++.Dq hard has 2 aliases ++.Dq hard_open ++and ++.Dq hard_init . ++The value ++.Dq hard ++means that reconects that the ++.Xr ssh-ldap-helper 8 ++tries to reconnect to the LDAP server 5 times before failure. There is exponential backoff before retrying. ++The value ++.Dq soft ++means that ++.Xr ssh-ldap-helper 8 ++fails immediately when it cannot connect to the LDAP seerver. ++The deault is ++.Dq hard . ++.It Cm SSLPath ++Specifies the path to the X.509 certificate database. ++There is no default. ++.It Cm SSL ++Specifies whether to use SSL/TLS or not. ++There are three allowed values: ++.Dq yes , ++.Dq no ++and ++.Dq start_tls ++Both ++.Dq true ++and ++.Dq on ++are the aliases for ++.Dq yes . ++.Dq false ++and ++.Dq off ++are the aliases for ++.Dq no . ++If ++.Dq start_tls ++is specified then StartTLS is used rather than raw LDAP over SSL. ++The default for ldap:// is ++.Dq start_tls , ++for ldaps:// ++.Dq yes ++and ++.Dq no ++for the ldapi:// . ++In case of host based configuration the default is ++.Dq start_tls . ++.It Cm Referrals ++Specifies if the client should automatically follow referrals returned ++by LDAP servers. ++The value can be or ++.Dq yes ++or ++.Dq no . ++.Dq true ++and ++.Dq on ++are the aliases for ++.Dq yes . ++.Dq false ++and ++.Dq off ++are the aliases for ++.Dq no . ++The default is yes. ++.It Cm Restart ++Specifies whether the LDAP client library should restart the select(2) system call when interrupted. ++The value can be or ++.Dq yes ++or ++.Dq no . ++.Dq true ++and ++.Dq on ++are the aliases for ++.Dq yes . ++.Dq false ++and ++.Dq off ++are the aliases for ++.Dq no . ++The default is yes. ++.It Cm TLS_CheckPeer ++Specifies what checks to perform on server certificates in a TLS session, ++if any. The value ++can be specified as one of the following keywords: ++.Dq never , ++.Dq hard , ++.Dq demand , ++.Dq allow ++and ++.Dq try . ++.Dq true , ++.Dq on ++and ++.Dq yes ++are aliases for ++.Dq hard . ++.Dq false , ++.Dq off ++and ++.Dq no ++are the aliases for ++.Dq never . ++The value ++.Dq never ++means that the client will not request or check any server certificate. ++The value ++.Dq allow ++means that the server certificate is requested. If no certificate is provided, ++the session proceeds normally. If a bad certificate is provided, it will ++be ignored and the session proceeds normally. ++The value ++.Dq try ++means that the server certificate is requested. If no certificate is provided, ++the session proceeds normally. If a bad certificate is provided, ++the session is immediately terminated. ++The value ++.Dq demand ++means that the server certificate is requested. If no ++certificate is provided, or a bad certificate is provided, the session ++is immediately terminated. ++The value ++.Dq hard ++is the same as ++.Dq demand . ++It requires an SSL connection. In the case of the plain conection the ++session is immediately terminated. ++The default is ++.Dq hard . ++.It Cm TLS_ReqCert ++Is an alias for ++.Cm TLS_CheckPeer . ++.It Cm TLS_CACertFile ++Specifies the file that contains certificates for all of the Certificate ++Authorities the client will recognize. ++There is no default. ++.It Cm TLS_CACert ++Is an alias for ++.Cm TLS_CACertFile . ++.It Cm TLS_CACertDIR ++Specifies the path of a directory that contains Certificate Authority ++certificates in separate individual files. The ++.Cm TLS_CACert ++is always used before ++.Cm TLS_CACertDir . ++The specified directory must be managed with the OpenSSL c_rehash utility. ++There is no default. ++.It Cm TLS_Ciphers ++Specifies acceptable cipher suite and preference order. ++The value should be a cipher specification for OpenSSL, ++e.g., ++.Dq HIGH:MEDIUM:+SSLv2 . ++The default is ++.Dq ALL . ++.It Cm TLS_Cipher_Suite ++Is an alias for ++.Cm TLS_Ciphers . ++.It Cm TLS_Cert ++Specifies the file that contains the client certificate. ++There is no default. ++.It Cm TLS_Certificate ++Is an alias for ++.Cm TLS_Cert . ++.It Cm TLS_Key ++Specifies the file that contains the private key that matches the certificate ++stored in the ++.Cm TLS_Cert ++file. Currently, the private key must not be protected with a password, so ++it is of critical importance that the key file is protected carefully. ++There is no default. ++.It Cm TLS_RandFile ++Specifies the file to obtain random bits from when /dev/[u]random is ++not available. Generally set to the name of the EGD/PRNGD socket. ++The environment variable RANDFILE can also be used to specify the filename. ++There is no default. ++.It Cm LogDir ++Specifies the directory used for logging by the LDAP client library. ++There is no default. ++.It Cm Debug ++Specifies the debug level used for logging by the LDAP client library. ++There is no default. ++.It Cm SSH_Filter ++Specifies the user filter applied on the LDAP serch. ++The default is no filter. ++.El ++.Sh FILES ++.Bl -tag -width Ds ++.It Pa /etc/ssh/ldap.conf ++Ldap configuration file for ++.Xr ssh-ldap-helper 8 . ++.El ++.Sh "SEE ALSO" ++.Xr ldap.conf 5 , ++.Xr ssh-ldap-helper 8 ++.Sh HISTORY ++.Nm ++first appeared in ++OpenSSH 5.5 + PKA-LDAP . ++.Sh AUTHORS ++.An Jan F. Chadima Aq jchadima@redhat.com diff --git a/openssh-7.2p2-limit_password_length.patch b/openssh-7.2p2-limit_password_length.patch new file mode 100644 index 0000000..c600b4a --- /dev/null +++ b/openssh-7.2p2-limit_password_length.patch @@ -0,0 +1,52 @@ +# HG changeset patch +# Parent e351203d2784230a3b56b8e3dd6955403ed10ca4 +Limit accepted passwords length to prevent DoS by resource consumption +(via crypt() eating CPU cycles). + +CVE-2016-6515 +bsc#992533 + +upstream commit: fcd135c9df440bcd2d5870405ad3311743d78d97 + +diff --git a/openssh-7.2p2/auth-passwd.c b/openssh-7.2p2/auth-passwd.c +--- a/openssh-7.2p2/auth-passwd.c ++++ b/openssh-7.2p2/auth-passwd.c +@@ -61,16 +61,18 @@ extern ServerOptions options; + #ifdef HAVE_LOGIN_CAP + extern login_cap_t *lc; + #endif + + + #define DAY (24L * 60 * 60) /* 1 day in seconds */ + #define TWO_WEEKS (2L * 7 * DAY) /* 2 weeks in seconds */ + ++#define MAX_PASSWORD_LEN 1024 ++ + void + disable_forwarding(void) + { + no_port_forwarding_flag = 1; + no_agent_forwarding_flag = 1; + no_x11_forwarding_flag = 1; + } + +@@ -82,16 +84,19 @@ int + auth_password(Authctxt *authctxt, const char *password) + { + struct passwd * pw = authctxt->pw; + int result, ok = authctxt->valid; + #if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE) + static int expire_checked = 0; + #endif + ++ if (strlen(password) > MAX_PASSWORD_LEN) ++ return 0; ++ + #ifndef HAVE_CYGWIN + if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) + ok = 0; + #endif + if (*password == '\0' && options.permit_empty_passwd == 0) + return 0; + + #ifdef KRB5 diff --git a/openssh-7.2p2-login_options.patch b/openssh-7.2p2-login_options.patch new file mode 100644 index 0000000..23abf8c --- /dev/null +++ b/openssh-7.2p2-login_options.patch @@ -0,0 +1,32 @@ +# HG changeset patch +# Parent 295ae9c5f5da12d273f3b91e90145b449984a7dc +# HG changeset patch +# Parent b262fd34c8ecd55e93d457b3ca5593abce716856 +# login-pam cannot handle the option terminator "--" as login from util-linux +# (this is correct behaviour considering its man-page), hence use option which +# selects the compile-time branch in the code which doesn't use the terminator +# +# bnc#833605 + +diff --git a/openssh-7.2p2/configure.ac b/openssh-7.2p2/configure.ac +--- a/openssh-7.2p2/configure.ac ++++ b/openssh-7.2p2/configure.ac +@@ -770,16 +770,18 @@ main() { if (NSVersionOfRunTimeLibrary(" + AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) + AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) + ;; + *-*-linux*) + no_dev_ptmx=1 + use_pie=auto + check_for_libcrypt_later=1 + check_for_openpty_ctty_bug=1 ++ AC_DEFINE([LOGIN_NO_ENDOPT], [1], ++ [Define if your login program cannot handle end of options ("--")]) + AC_DEFINE([PAM_TTY_KLUDGE], [1], + [Work around problematic Linux PAM modules handling of PAM_TTY]) + AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"], + [String used in /etc/passwd to denote locked account]) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) + AC_DEFINE([LINK_OPNOTSUPP_ERRNO], [EPERM], + [Define to whatever link() returns for "not supported" + if it doesn't return EOPNOTSUPP.]) diff --git a/openssh-7.2p2-no_fork-no_pid_file.patch b/openssh-7.2p2-no_fork-no_pid_file.patch new file mode 100644 index 0000000..8ae51d6 --- /dev/null +++ b/openssh-7.2p2-no_fork-no_pid_file.patch @@ -0,0 +1,26 @@ +# HG changeset patch +# Parent 7ce81a30bb196401c63782b646d8a6d511ddec4b +Do not write a PID file when not daemonizing (e.g. when running from systemd) + +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -2107,17 +2107,17 @@ main(int ac, char **av) + signal(SIGCHLD, main_sigchld_handler); + signal(SIGTERM, sigterm_handler); + signal(SIGQUIT, sigterm_handler); + + /* + * Write out the pid file after the sigterm handler + * is setup and the listen sockets are bound + */ +- if (options.pid_file != NULL && !debug_flag) { ++ if (!no_daemon_flag && options.pid_file != NULL && !debug_flag) { + FILE *f = fopen(options.pid_file, "w"); + + if (f == NULL) { + error("Couldn't create pid file \"%s\": %s", + options.pid_file, strerror(errno)); + } else { + fprintf(f, "%ld\n", (long) getpid()); + fclose(f); diff --git a/openssh-7.2p2-pam_check_locks.patch b/openssh-7.2p2-pam_check_locks.patch index d99fd58..ac8a767 100644 --- a/openssh-7.2p2-pam_check_locks.patch +++ b/openssh-7.2p2-pam_check_locks.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 5b217a9abc32fa963a125ae29c766c015db53bde +# Parent ac7f843cd7ebec413691d51823cdc67b611abdff new option UsePAMCheckLocks to enforce checking for locked accounts while UsePAM is used diff --git a/openssh-7.2p2-prevent_timing_user_enumeration.patch b/openssh-7.2p2-prevent_timing_user_enumeration.patch new file mode 100644 index 0000000..857749f --- /dev/null +++ b/openssh-7.2p2-prevent_timing_user_enumeration.patch @@ -0,0 +1,264 @@ +# HG changeset patch +# Parent 323ac0fc20b1d5e9bf7037e020adfd760dd2d5f2 +Prevent user enumeration through password processing timing +CVE-2016-6210 +bsc#989363 + +non-PAM part: +upstream commit: 9286875a73b2de7736b5e50692739d314cd8d9dc + +PAM part: +upstream commit: 283b97ff33ea2c641161950849931bd578de6946 + +diff --git a/openssh-7.2p2/auth-pam.c b/openssh-7.2p2/auth-pam.c +--- a/openssh-7.2p2/auth-pam.c ++++ b/openssh-7.2p2/auth-pam.c +@@ -227,17 +227,16 @@ static pam_handle_t *sshpam_handle = NUL + static int sshpam_err = 0; + static int sshpam_authenticated = 0; + static int sshpam_session_open = 0; + static int sshpam_cred_established = 0; + static int sshpam_account_status = -1; + static char **sshpam_env = NULL; + static Authctxt *sshpam_authctxt = NULL; + static const char *sshpam_password = NULL; +-static char badpw[] = "\b\n\r\177INCORRECT"; + + /* Some PAM implementations don't implement this */ + #ifndef HAVE_PAM_GETENVLIST + static char ** + pam_getenvlist(pam_handle_t *pamh) + { + /* + * XXX - If necessary, we can still support envrionment passing +@@ -807,22 +806,45 @@ sshpam_query(void *ctx, char **name, cha + free(msg); + ctxt->pam_done = -1; + return (-1); + } + } + return (-1); + } + ++/* ++ * Returns a junk password of identical length to that the user supplied. ++ * Used to mitigate timing attacks against crypt(3)/PAM stacks that ++ * vary processing time in proportion to password length. ++ */ ++static char * ++fake_password(const char *wire_password) ++{ ++ const char junk[] = "\b\n\r\177INCORRECT"; ++ char *ret = NULL; ++ size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; ++ ++ if (l >= INT_MAX) ++ fatal("%s: password length too long: %zu", __func__, l); ++ ++ ret = xmalloc(l + 1); ++ for (i = 0; i < l; i++) ++ ret[i] = junk[i % (sizeof(junk) - 1)]; ++ ret[i] = '\0'; ++ return ret; ++} ++ + /* XXX - see also comment in auth-chall.c:verify_response */ + static int + sshpam_respond(void *ctx, u_int num, char **resp) + { + Buffer buffer; + struct pam_ctxt *ctxt = ctx; ++ char *fake; + + debug2("PAM: %s entering, %u responses", __func__, num); + switch (ctxt->pam_done) { + case 1: + sshpam_authenticated = 1; + return (0); + case 0: + break; +@@ -833,18 +855,21 @@ sshpam_respond(void *ctx, u_int num, cha + error("PAM: expected one response, got %u", num); + return (-1); + } + buffer_init(&buffer); + if (sshpam_authctxt->valid && + (sshpam_authctxt->pw->pw_uid != 0 || + options.permit_root_login == PERMIT_YES)) + buffer_put_cstring(&buffer, *resp); +- else +- buffer_put_cstring(&buffer, badpw); ++ else { ++ fake = fake_password(*resp); ++ buffer_put_cstring(&buffer, fake); ++ free(fake); ++ } + if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { + buffer_free(&buffer); + return (-1); + } + buffer_free(&buffer); + return (1); + } + +@@ -1178,41 +1203,43 @@ static struct pam_conv passwd_conv = { s + /* + * Attempt password authentication via PAM + */ + int + sshpam_auth_passwd(Authctxt *authctxt, const char *password) + { + int flags = (options.permit_empty_passwd == 0 ? + PAM_DISALLOW_NULL_AUTHTOK : 0); ++ char *fake = NULL; + + if (!options.use_pam || sshpam_handle == NULL) + fatal("PAM: %s called when PAM disabled or failed to " + "initialise.", __func__); + + sshpam_password = password; + sshpam_authctxt = authctxt; + + /* + * If the user logging in is invalid, or is root but is not permitted + * by PermitRootLogin, use an invalid password to prevent leaking + * information via timing (eg if the PAM config has a delay on fail). + */ + if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && + options.permit_root_login != PERMIT_YES)) +- sshpam_password = badpw; ++ sshpam_password = fake = fake_password(password); + + sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, + (const void *)&passwd_conv); + if (sshpam_err != PAM_SUCCESS) + fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, + pam_strerror(sshpam_handle, sshpam_err)); + + sshpam_err = pam_authenticate(sshpam_handle, flags); + sshpam_password = NULL; ++ free(fake); + if (sshpam_err == PAM_SUCCESS && authctxt->valid) { + debug("PAM: password authentication accepted for %.100s", + authctxt->user); + return 1; + } else { + debug("PAM: password authentication failed for %.100s: %s", + authctxt->valid ? authctxt->user : "an illegal user", + pam_strerror(sshpam_handle, sshpam_err)); +diff --git a/openssh-7.2p2/auth-passwd.c b/openssh-7.2p2/auth-passwd.c +--- a/openssh-7.2p2/auth-passwd.c ++++ b/openssh-7.2p2/auth-passwd.c +@@ -188,28 +188,32 @@ sys_auth_passwd(Authctxt *authctxt, cons + return (auth_close(as)); + } + } + #elif !defined(CUSTOM_SYS_AUTH_PASSWD) + int + sys_auth_passwd(Authctxt *authctxt, const char *password) + { + struct passwd *pw = authctxt->pw; +- char *encrypted_password; ++ char *encrypted_password, *salt = NULL; + + /* Just use the supplied fake password if authctxt is invalid */ + char *pw_password = authctxt->valid ? shadow_pw(pw) : pw->pw_passwd; + + /* Check for users with no password. */ + if (strcmp(pw_password, "") == 0 && strcmp(password, "") == 0) + return (1); + +- /* Encrypt the candidate password using the proper salt. */ +- encrypted_password = xcrypt(password, +- (pw_password[0] && pw_password[1]) ? pw_password : "xx"); ++ /* ++ * Encrypt the candidate password using the proper salt, or pass a ++ * NULL and let xcrypt pick one. ++ */ ++ if (authctxt->valid && pw_password[0] && pw_password[1]) ++ salt = pw_password; ++ encrypted_password = xcrypt(password, salt); + + /* + * Authentication is accepted if the encrypted passwords + * are identical. + */ + return encrypted_password != NULL && + strcmp(encrypted_password, pw_password) == 0; + } +diff --git a/openssh-7.2p2/openbsd-compat/xcrypt.c b/openssh-7.2p2/openbsd-compat/xcrypt.c +--- a/openssh-7.2p2/openbsd-compat/xcrypt.c ++++ b/openssh-7.2p2/openbsd-compat/xcrypt.c +@@ -20,16 +20,17 @@ + * 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 "includes.h" + + #include ++#include + #include + #include + + # if defined(HAVE_CRYPT_H) && !defined(HAVE_SECUREWARE) + # include + # endif + + # ifdef __hpux +@@ -57,21 +58,54 @@ + # include "md5crypt.h" + # endif + + # if defined(WITH_OPENSSL) && !defined(HAVE_CRYPT) && defined(HAVE_DES_CRYPT) + # include + # define crypt DES_crypt + # endif + ++/* ++ * Pick an appropriate password encryption type and salt for the running ++ * system. ++ */ ++static const char * ++pick_salt(void) ++{ ++ struct passwd *pw; ++ char *passwd, *p; ++ size_t typelen; ++ static char salt[32]; ++ ++ if (salt[0] != '\0') ++ return salt; ++ strlcpy(salt, "xx", sizeof(salt)); ++ if ((pw = getpwuid(0)) == NULL) ++ return salt; ++ passwd = shadow_pw(pw); ++ if (passwd[0] != '$' || (p = strrchr(passwd + 1, '$')) == NULL) ++ return salt; /* no $, DES */ ++ typelen = p - passwd + 1; ++ strlcpy(salt, passwd, MIN(typelen, sizeof(salt))); ++ explicit_bzero(passwd, strlen(passwd)); ++ return salt; ++} ++ + char * + xcrypt(const char *password, const char *salt) + { + char *crypted; + ++ /* ++ * If we don't have a salt we are encrypting a fake password for ++ * for timing purposes. Pick an appropriate salt. ++ */ ++ if (salt == NULL) ++ salt = pick_salt(); ++ + # ifdef HAVE_MD5_PASSWORDS + if (is_md5_salt(salt)) + crypted = md5_crypt(password, salt); + else + crypted = crypt(password, salt); + # elif defined(__hpux) && !defined(HAVE_SECUREWARE) + if (iscomsec()) + crypted = bigcrypt(password, salt); diff --git a/openssh-7.2p2-pts_names_formatting.patch b/openssh-7.2p2-pts_names_formatting.patch index 72420cb..3973e17 100644 --- a/openssh-7.2p2-pts_names_formatting.patch +++ b/openssh-7.2p2-pts_names_formatting.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 870f97b01b9ed00bac9ff0b8014a998434a6161b +# Parent 787bc0aab11e5a7b6510c8dbf771958743ca25b0 # use same lines naming as utempter (prevents problems with using different # formats in ?tmp? files) # --used to be called '-pts' diff --git a/openssh-7.2p2-remove_xauth_cookies_on_exit.patch b/openssh-7.2p2-remove_xauth_cookies_on_exit.patch index 838d356..47a3fdd 100644 --- a/openssh-7.2p2-remove_xauth_cookies_on_exit.patch +++ b/openssh-7.2p2-remove_xauth_cookies_on_exit.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 07998e381c9867b8b6f7b9205261811934bef40f +# Parent 18c2690afd988b9cb0fd0fa927d02cf5336dce9c # --used to be called '-xauth' try to remove xauth cookies on logout diff --git a/openssh-7.2p2-seccomp_getuid.patch b/openssh-7.2p2-seccomp_getuid.patch index 45c7b13..05acb22 100644 --- a/openssh-7.2p2-seccomp_getuid.patch +++ b/openssh-7.2p2-seccomp_getuid.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 3582dd949a01d8eca2816986ca4bc0c87c96bed3 +# Parent c66097e5e31cd607bf2206b2da95730cce518b7a add 'getuid' syscall to list of allowed ones to prevent the sanboxed thread from being killed by the seccomp filter diff --git a/openssh-7.2p2-seccomp_stat.patch b/openssh-7.2p2-seccomp_stat.patch index d0d637c..2926ff2 100644 --- a/openssh-7.2p2-seccomp_stat.patch +++ b/openssh-7.2p2-seccomp_stat.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent d3afe6b01f8769713bde6c175e29a50412799e27 +# Parent def949a57b8101691c79ecce6366cc7ae1685b07 Allow the stat() syscall for OpenSSL re-seed patch (which causes OpenSSL use stat() on some file) diff --git a/openssh-7.2p2-seed-prng.patch b/openssh-7.2p2-seed-prng.patch index 3c30b92..8f9f690 100644 --- a/openssh-7.2p2-seed-prng.patch +++ b/openssh-7.2p2-seed-prng.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 36ab4b78afea8cea4e3bed1291a49ba05cbb9115 +# Parent 6ece65e11f754d75dd33d72b6f8e487a9d047f2e # extended support for (re-)seeding the OpenSSL PRNG from /dev/random # bnc#703221, FATE#312172 diff --git a/openssh-7.2p2-send_locale.patch b/openssh-7.2p2-send_locale.patch index 025aac1..ac6aa63 100644 --- a/openssh-7.2p2-send_locale.patch +++ b/openssh-7.2p2-send_locale.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent 505927e61d1a7848f0003adb3619cc726b8e5d15 +# Parent dfcac093fca4d826a806b9d1c0bdc26e7ae8ee8e send locales in default configuration bnc#65747 diff --git a/openssh-7.2p2-sftp_force_permissions.patch b/openssh-7.2p2-sftp_force_permissions.patch new file mode 100644 index 0000000..c419220 --- /dev/null +++ b/openssh-7.2p2-sftp_force_permissions.patch @@ -0,0 +1,157 @@ +# HG changeset patch +# Parent efa850d8312ceef224dbec0f2ae1002201afabd9 +additional option for sftp-server to force file mode for new files +FATE#312774 +http://lists.mindrot.org/pipermail/openssh-unix-dev/2010-November/029044.html +http://marc.info/?l=openssh-unix-dev&m=128896838930893 + +diff --git a/openssh-7.2p2/sftp-server.8 b/openssh-7.2p2/sftp-server.8 +--- a/openssh-7.2p2/sftp-server.8 ++++ b/openssh-7.2p2/sftp-server.8 +@@ -33,16 +33,17 @@ + .Bk -words + .Op Fl ehR + .Op Fl d Ar start_directory + .Op Fl f Ar log_facility + .Op Fl l Ar log_level + .Op Fl P Ar blacklisted_requests + .Op Fl p Ar whitelisted_requests + .Op Fl u Ar umask ++.Op Fl m Ar force_file_permissions + .Ek + .Nm + .Fl Q Ar protocol_feature + .Sh DESCRIPTION + .Nm + is a program that speaks the server side of SFTP protocol + to stdout and expects client requests from stdin. + .Nm +@@ -133,16 +134,20 @@ Places this instance of + into a read-only mode. + Attempts to open files for writing, as well as other operations that change + the state of the filesystem, will be denied. + .It Fl u Ar umask + Sets an explicit + .Xr umask 2 + to be applied to newly-created files and directories, instead of the + user's default mask. ++.It Fl m Ar force_file_permissions ++Sets explicit file permissions to be applied to newly-created files instead ++of the default or client requested mode. Numeric values include: ++777, 755, 750, 666, 644, 640, etc. Option -u is ineffective if -m is set. + .El + .Pp + On some systems, + .Nm + must be able to access + .Pa /dev/log + for logging to work, and use of + .Nm +diff --git a/openssh-7.2p2/sftp-server.c b/openssh-7.2p2/sftp-server.c +--- a/openssh-7.2p2/sftp-server.c ++++ b/openssh-7.2p2/sftp-server.c +@@ -73,16 +73,20 @@ static u_int version; + static int init_done; + + /* Disable writes */ + static int readonly; + + /* Requests that are allowed/denied */ + static char *request_whitelist, *request_blacklist; + ++/* Force file permissions */ ++int permforce = 0; ++long permforcemode; ++ + /* portable attributes, etc. */ + typedef struct Stat Stat; + + struct Stat { + char *name; + char *long_name; + Attrib attrib; + }; +@@ -687,16 +691,20 @@ process_open(u_int32_t id) + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ + (r = decode_attrib(iqueue, &a)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + debug3("request %u: open flags %d", id, pflags); + flags = flags_from_portable(pflags); + mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; ++ if (permforce == 1) { ++ mode = permforcemode; ++ (void)umask(0); /* so umask does not interfere */ ++ } + logit("open \"%s\" flags %s mode 0%o", + name, string_from_portable(pflags), mode); + if (readonly && + ((flags & O_ACCMODE) == O_WRONLY || + (flags & O_ACCMODE) == O_RDWR)) { + verbose("Refusing open request in read-only mode"); + status = SSH2_FX_PERMISSION_DENIED; + } else { +@@ -1489,17 +1497,18 @@ sftp_server_cleanup_exit(int i) + static void + sftp_server_usage(void) + { + extern char *__progname; + + fprintf(stderr, + "usage: %s [-ehR] [-d start_directory] [-f log_facility] " + "[-l log_level]\n\t[-P blacklisted_requests] " +- "[-p whitelisted_requests] [-u umask]\n" ++ "[-p whitelisted_requests] [-u umask]\n\t" ++ "[-m force_file_permissions]\n" + " %s -Q protocol_feature\n", + __progname, __progname); + exit(1); + } + + int + sftp_server_main(int argc, char **argv, struct passwd *user_pw) + { +@@ -1515,17 +1524,17 @@ sftp_server_main(int argc, char **argv, + + ssh_malloc_init(); /* must be called before any mallocs */ + __progname = ssh_get_progname(argv[0]); + log_init(__progname, log_level, log_facility, log_stderr); + + pw = pwcopy(user_pw); + + while (!skipargs && (ch = getopt(argc, argv, +- "d:f:l:P:p:Q:u:cehR")) != -1) { ++ "d:f:l:P:p:Q:u:m:cehR")) != -1) { + switch (ch) { + case 'Q': + if (strcasecmp(optarg, "requests") != 0) { + fprintf(stderr, "Invalid query type\n"); + exit(1); + } + for (i = 0; handlers[i].handler != NULL; i++) + printf("%s\n", handlers[i].name); +@@ -1575,16 +1584,23 @@ sftp_server_main(int argc, char **argv, + case 'u': + errno = 0; + mask = strtol(optarg, &cp, 8); + if (mask < 0 || mask > 0777 || *cp != '\0' || + cp == optarg || (mask == 0 && errno != 0)) + fatal("Invalid umask \"%s\"", optarg); + (void)umask((mode_t)mask); + break; ++ case 'm': ++ permforce = 1; ++ permforcemode = strtol(optarg, &cp, 8); ++ if (permforcemode < 0 || permforcemode > 0777 || *cp != '\0' || ++ cp == optarg || (permforcemode == 0 && errno != 0)) ++ fatal("Invalid umask \"%s\"", optarg); ++ break; + case 'h': + default: + sftp_server_usage(); + } + } + + log_init(__progname, log_level, log_facility, log_stderr); + diff --git a/openssh-7.2p2-sftp_homechroot.patch b/openssh-7.2p2-sftp_homechroot.patch new file mode 100644 index 0000000..8c59d75 --- /dev/null +++ b/openssh-7.2p2-sftp_homechroot.patch @@ -0,0 +1,366 @@ +# HG changeset patch +# Parent 9b1033f35a6cb173fbc13416065ed40c4b14e656 +run sftp sessions inside a chroot + +diff --git a/openssh-7.2p2/session.c b/openssh-7.2p2/session.c +--- a/openssh-7.2p2/session.c ++++ b/openssh-7.2p2/session.c +@@ -123,16 +123,18 @@ int do_exec(Session *, const char *); + void do_login(Session *, const char *); + #ifdef LOGIN_NEEDS_UTMPX + static void do_pre_login(Session *s); + #endif + void do_child(Session *, const char *); + void do_motd(void); + int check_quietlogin(Session *, const char *); + ++int chroot_no_tree = 0; ++ + static void do_authenticated1(Authctxt *); + static void do_authenticated2(Authctxt *); + + static int session_pty_req(Session *); + + /* import */ + extern ServerOptions options; + extern char *__progname; +@@ -838,16 +840,21 @@ do_exec(Session *s, const char *command) + "subsystem '%.900s'", s->subsys); + } else if (command == NULL) { + snprintf(session_type, sizeof(session_type), "shell"); + } else { + /* NB. we don't log unforced commands to preserve privacy */ + snprintf(session_type, sizeof(session_type), "command"); + } + ++ if ((s->is_subsystem != SUBSYSTEM_INT_SFTP) && chroot_no_tree) { ++ logit("You aren't welcomed, go away!"); ++ exit (1); ++ } ++ + if (s->ttyfd != -1) { + tty = s->tty; + if (strncmp(tty, "/dev/", 5) == 0) + tty += 5; + } + + verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", + session_type, +@@ -1492,58 +1499,123 @@ do_nologin(struct passwd *pw) + while (fgets(buf, sizeof(buf), f)) + fputs(buf, stderr); + fclose(f); + } + exit(254); + } + + /* ++ * Test if filesystem is mounted nosuid and nodev ++ */ ++ ++static void ++test_nosuid (char * path, dev_t fs) ++{ ++ FILE *f; ++ struct stat st; ++ char buf[4096], *s, *on, *mountpoint, *opt; ++ int nodev, nosuid; ++ ++ if (!(f = popen ("/bin/mount", "r"))) ++ fatal ("%s: popen(\"/bin/mount\", \"r\"): %s", ++ __func__, strerror (errno)); ++ for (;;) { ++ s = fgets (buf, sizeof (buf), f); ++ if (ferror (f)) ++ fatal ("%s: read from popen: %s", __func__, ++ strerror (errno)); ++ if (!s) { ++ pclose (f); ++ fatal ("cannot find filesystem with the chroot directory"); ++ } ++ (void) strtok (buf, " "); ++ on = strtok (NULL, " "); ++ if (strcmp (on, "on")) { ++ pclose (f); ++ fatal ("bad format of mount output"); ++ } ++ mountpoint = strtok (NULL, " "); ++ if (memcmp (path, mountpoint, strlen (mountpoint))) ++ continue; ++ if (stat(mountpoint, &st) != 0) { ++ pclose (f); ++ fatal("%s: stat(\"%s\"): %s", __func__, ++ mountpoint, strerror(errno)); ++ } ++ if (fs != st.st_dev) ++ continue; ++ nodev = nosuid = 0; ++ for (opt = strtok (NULL, "("); opt; opt = strtok (NULL, " ,)")) { ++ if (!strcmp (opt, "nodev")) ++ nodev = 1; ++ else if (!strcmp (opt, "nosuid")) ++ nosuid = 1; ++ else if (!strcmp (opt, "noexec")) ++ nosuid = 1; ++ if (nodev && nosuid) { ++ pclose (f); ++ return; ++ } ++ } ++ fatal ("chroot into directory without nodev and either noexec or nosuid"); ++ } ++} ++ ++/* + * Chroot into a directory after checking it for safety: all path components + * must be root-owned directories with strict permissions. + */ + static void + safely_chroot(const char *path, uid_t uid) + { + const char *cp; + char component[PATH_MAX]; + struct stat st; ++ int last; + + if (*path != '/') + fatal("chroot path does not begin at root"); + if (strlen(path) >= sizeof(component)) + fatal("chroot path too long"); + + /* + * Descend the path, checking that each component is a + * root-owned directory with strict permissions. + */ + for (cp = path; cp != NULL;) { +- if ((cp = strchr(cp, '/')) == NULL) ++ if (last = ((cp = strchr(cp, '/')) == NULL)) + strlcpy(component, path, sizeof(component)); + else { + cp++; + memcpy(component, path, cp - path); + component[cp - path] = '\0'; + } + + debug3("%s: checking '%s'", __func__, component); + + if (stat(component, &st) != 0) + fatal("%s: stat(\"%s\"): %s", __func__, + component, strerror(errno)); +- if (st.st_uid != 0 || (st.st_mode & 022) != 0) ++ if ((st.st_uid != 0 || (st.st_mode & 022) != 0) && !(last && st.st_uid == uid)) + fatal("bad ownership or modes for chroot " + "directory %s\"%s\"", + cp == NULL ? "" : "component ", component); + if (!S_ISDIR(st.st_mode)) + fatal("chroot path %s\"%s\" is not a directory", + cp == NULL ? "" : "component ", component); + + } ++ setenv ("TZ", "/etc/localtime", 0); ++ tzset(); ++ ++ if (st.st_uid) { ++ test_nosuid(path, st.st_dev); ++ ++chroot_no_tree; ++ } + + if (chdir(path) == -1) + fatal("Unable to chdir to chroot path \"%s\": " + "%s", path, strerror(errno)); + if (chroot(path) == -1) + fatal("chroot(\"%s\"): %s", path, strerror(errno)); + if (chdir("/") == -1) + fatal("%s: chdir(/) after chroot: %s", +diff --git a/openssh-7.2p2/sftp-chrootenv.h b/openssh-7.2p2/sftp-chrootenv.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/sftp-chrootenv.h +@@ -0,0 +1,30 @@ ++/* ++ * Copyright (c) 2009 Jan F Chadima. 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. ++ */ ++#ifndef CHROOTENV_H ++#define CHROOTENV_H ++ ++extern int chroot_no_tree; ++ ++#endif ++ +diff --git a/openssh-7.2p2/sftp-common.c b/openssh-7.2p2/sftp-common.c +--- a/openssh-7.2p2/sftp-common.c ++++ b/openssh-7.2p2/sftp-common.c +@@ -43,16 +43,17 @@ + + #include "xmalloc.h" + #include "ssherr.h" + #include "sshbuf.h" + #include "log.h" + + #include "sftp.h" + #include "sftp-common.h" ++#include "sftp-chrootenv.h" + + /* Clear contents of attributes structure */ + void + attrib_clear(Attrib *a) + { + a->flags = 0; + a->size = 0; + a->uid = 0; +@@ -216,23 +217,23 @@ ls_file(const char *name, const struct s + int ulen, glen, sz = 0; + struct tm *ltime = localtime(&st->st_mtime); + char *user, *group; + char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + char sbuf[FMT_SCALED_STRSIZE]; + time_t now; + + strmode(st->st_mode, mode); +- if (!remote) { ++ if (!remote && !chroot_no_tree) { + user = user_from_uid(st->st_uid, 0); + } else { + snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid); + user = ubuf; + } +- if (!remote) { ++ if (!remote && !chroot_no_tree) { + group = group_from_gid(st->st_gid, 0); + } else { + snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid); + group = gbuf; + } + if (ltime != NULL) { + now = time(NULL); + if (now - (365*24*60*60)/2 < st->st_mtime && +diff --git a/openssh-7.2p2/sftp-server-main.c b/openssh-7.2p2/sftp-server-main.c +--- a/openssh-7.2p2/sftp-server-main.c ++++ b/openssh-7.2p2/sftp-server-main.c +@@ -17,22 +17,25 @@ + + #include "includes.h" + + #include + #include + #include + #include + #include ++//#include + + #include "log.h" + #include "sftp.h" + #include "misc.h" + #include "xmalloc.h" + ++int chroot_no_tree = 0; ++ + void + cleanup_exit(int i) + { + sftp_server_cleanup_exit(i); + } + + int + main(int argc, char **argv) +diff --git a/openssh-7.2p2/sftp.c b/openssh-7.2p2/sftp.c +--- a/openssh-7.2p2/sftp.c ++++ b/openssh-7.2p2/sftp.c +@@ -112,16 +112,18 @@ struct complete_ctx { + char **remote_pathp; + }; + + int remote_glob(struct sftp_conn *, const char *, int, + int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ + + extern char *__progname; + ++int chroot_no_tree = 0; ++ + /* Separators for interactive commands */ + #define WHITESPACE " \t\r\n" + + /* ls flags */ + #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ + #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ + #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ + #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ +diff --git a/openssh-7.2p2/sshd_config.0 b/openssh-7.2p2/sshd_config.0 +--- a/openssh-7.2p2/sshd_config.0 ++++ b/openssh-7.2p2/sshd_config.0 +@@ -251,16 +251,24 @@ DESCRIPTION + directory on some operating systems (see sftp-server(8) for + details). + + For safety, it is very important that the directory hierarchy be + prevented from modification by other processes on the system + (especially those outside the jail). Misconfiguration can lead + to unsafe environments which sshd(8) cannot detect. + ++ In the special case when only sftp is used, not ssh nor scp, it ++ is possible to use ChrootDirectory %h or ChrootDirectory ++ /some/path/%u. The file system containing this directory must be ++ mounted with options nodev and either nosuid or noexec. The owner ++ of the directory should be the user. The ownership of the other ++ components of the path must fulfill the usual conditions. No adi- ++ tional files are required to be present in the directory. ++ + The default is M-bM-^@M-^\noneM-bM-^@M-^], indicating not to chroot(2). + + Ciphers + Specifies the ciphers allowed. Multiple ciphers must be comma- + separated. If the specified value begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, + then the specified ciphers will be appended to the default set + instead of replacing them. + +diff --git a/openssh-7.2p2/sshd_config.5 b/openssh-7.2p2/sshd_config.5 +--- a/openssh-7.2p2/sshd_config.5 ++++ b/openssh-7.2p2/sshd_config.5 +@@ -424,16 +424,27 @@ for details). + .Pp + For safety, it is very important that the directory hierarchy be + prevented from modification by other processes on the system (especially + those outside the jail). + Misconfiguration can lead to unsafe environments which + .Xr sshd 8 + cannot detect. + .Pp ++In the special case when only sftp is used, not ssh nor scp, ++it is possible to use ++.Cm ChrootDirectory ++%h or ++.Cm ChrootDirectory ++/some/path/%u. The file system containing this directory must be ++mounted with options nodev and either nosuid or noexec. The owner of the ++directory should be the user. The ownership of the other components of the path ++must fulfill the usual conditions. No aditional files are required to be present ++in the directory. ++.Pp + The default is + .Dq none , + indicating not to + .Xr chroot 2 . + .It Cm Ciphers + Specifies the ciphers allowed. + Multiple ciphers must be comma-separated. + If the specified value begins with a diff --git a/openssh.changes b/openssh.changes index a4e526e..75dcc5a 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,43 @@ +------------------------------------------------------------------- +Thu Sep 29 23:27:49 UTC 2016 - pcerny@suse.com + +- remaining patches that were still missing + since the update to 7.2p2 (FATE#319675): +- allow X forwarding over IPv4 when IPv6 sockets is not available + [openssh-7.2p2-X_forward_with_disabled_ipv6.patch] +- do not write PID file when not daemonizing + [openssh-7.2p2-no_fork-no_pid_file.patch] +- use correct options when invoking login + [openssh-7.2p2-login_options.patch] +- helper application for retrieving users' public keys from + an LDAP server + [openssh-7.2p2-ldap.patch] +- allow forcing permissions over sftp + [openssh-7.2p2-sftp_force_permissions.patch] +- do not perform run-time checks for OpenSSL API/ABI change + [openssh-7.2p2-disable_openssl_abi_check.patch] +- suggest commands for cleaning known hosts file + [openssh-7.2p2-host_ident.patch] +- sftp home chroot patch + [openssh-7.2p2-sftp_homechroot.patch] +- ssh sessions auditing + [openssh-7.2p2-audit.patch] +- enable seccomp sandbox on additional architectures + [openssh-7.2p2-additional_seccomp_archs.patch] +- fix forwarding with IPv6 addresses in DISPLAY (bnc#847710) + [openssh-7.2p2-IPv6_X_forwarding.patch] +- ignore PAM environment when using login + (bsc#975865, CVE-2015-8325) + [openssh-7.2p2-ignore_PAM_with_UseLogin.patch] +- limit accepted password length (prevents possible DoS) + (bsc#992533, CVE-2016-6515) + [openssh-7.2p2-limit_password_length.patch] +- Prevent user enumeration through the timing of password + processing (bsc#989363, CVE-2016-6210) + [openssh-7.2p2-prevent_timing_user_enumeration.patch] +- Add auditing for PRNG re-seeding + [openssh-7.2p2-audit_seed_prng.patch] + ------------------------------------------------------------------- Fri Sep 16 12:45:11 UTC 2016 - pcerny@suse.com diff --git a/openssh.spec b/openssh.spec index 4bcc000..8a0d1f6 100644 --- a/openssh.spec +++ b/openssh.spec @@ -53,11 +53,9 @@ %endif %define sandbox_seccomp 0 -%ifarch %ix86 x86_64 %if 0%{?suse_version} > 1220 %define sandbox_seccomp 1 %endif -%endif %define _fwdir %{_sysconfdir}/sysconfig/SuSEfirewall2.d %define _fwdefdir %{_fwdir}/services @@ -88,7 +86,10 @@ BuildRequires: pkgconfig(systemd) %{?systemd_requires} %endif BuildRequires: tcpd-devel -PreReq: pwdutils %{insserv_prereq} %{fillup_prereq} coreutils +PreReq: pwdutils %{fillup_prereq} coreutils +%if ! %{uses_systemd} +PreReq: %{insserv_prereq} +%endif Version: 7.2p2 Release: 0 Summary: Secure Shell Client and Server (Remote Login Program) @@ -128,6 +129,21 @@ Patch15: openssh-7.2p2-seccomp_stat.patch Patch16: openssh-7.2p2-fips.patch Patch17: openssh-7.2p2-seed-prng.patch Patch18: openssh-7.2p2-gssapi_key_exchange.patch +Patch19: openssh-7.2p2-audit.patch +Patch20: openssh-7.2p2-audit_seed_prng.patch +Patch21: openssh-7.2p2-login_options.patch +Patch22: openssh-7.2p2-disable_openssl_abi_check.patch +Patch23: openssh-7.2p2-no_fork-no_pid_file.patch +Patch24: openssh-7.2p2-host_ident.patch +Patch25: openssh-7.2p2-sftp_homechroot.patch +Patch26: openssh-7.2p2-sftp_force_permissions.patch +Patch27: openssh-7.2p2-X_forward_with_disabled_ipv6.patch +Patch28: openssh-7.2p2-ldap.patch +Patch29: openssh-7.2p2-additional_seccomp_archs.patch +Patch30: openssh-7.2p2-IPv6_X_forwarding.patch +Patch31: openssh-7.2p2-ignore_PAM_with_UseLogin.patch +Patch32: openssh-7.2p2-prevent_timing_user_enumeration.patch +Patch33: openssh-7.2p2-limit_password_length.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build Conflicts: nonfreessh Recommends: audit @@ -198,14 +214,29 @@ FIPS140 CAVS tests related parts of the OpenSSH package %patch16 -p2 %patch17 -p2 %patch18 -p2 +%patch19 -p2 +%patch20 -p2 +%patch21 -p2 +%patch22 -p2 +%patch23 -p2 +%patch24 -p2 +%patch25 -p2 +%patch26 -p2 +%patch27 -p2 +%patch28 -p2 +%patch29 -p2 +%patch30 -p2 +%patch31 -p2 +%patch32 -p2 +%patch33 -p2 cp %{SOURCE3} %{SOURCE4} %{SOURCE11} . %build -#### set libexec dir in the LDAP patch -###sed -i.libexec 's,@LIBEXECDIR@,%{_libexecdir}/ssh,' \ -### $( grep -Rl @LIBEXECDIR@ \ -### $( grep "^+++" %{PATCH40} | sed -r 's@^.+/([^/\t ]+).*$@\1@' ) -### ) +# set libexec dir in the LDAP patch +sed -i.libexec 's,@LIBEXECDIR@,%{_libexecdir}/ssh,' \ + $( grep -Rl @LIBEXECDIR@ \ + $( grep "^+++" %{PATCH28} | sed -r 's@^.+/([^/\t ]+).*$@\1@' ) + ) autoreconf -fiv %ifarch s390 s390x %sparc @@ -271,7 +302,7 @@ install -d -m 755 %{buildroot}%{_initddir} %if %{uses_systemd} install -m 0755 %{SOURCE1} . install -D -m 0644 %{SOURCE10} %{buildroot}%{_unitdir}/sshd.service -ln -s /usr/sbin/service %{buildroot}%{_sbindir}/rcsshd +ln -s /sbin/service %{buildroot}%{_sbindir}/rcsshd %else install -D -m 0755 %{SOURCE1} %{buildroot}%{_initddir}/sshd install -m 0644 %{SOURCE10} . @@ -376,7 +407,7 @@ rpm -q openssh-fips >& /dev/null && DISABLE_RESTART_ON_UPDATE=yes %attr(0755,root,root) %{_bindir}/* %attr(0755,root,root) %{_sbindir}/* %attr(0755,root,root) %dir %{_libexecdir}/ssh -###%exclude %{_libexecdir}/ssh/ssh-ldap* +%exclude %{_libexecdir}/ssh/ssh-ldap* %attr(0755,root,root) %{_libexecdir}/ssh/* %attr(0444,root,root) %doc %{_mandir}/man1/* %attr(0444,root,root) %doc %{_mandir}/man5/* @@ -395,10 +426,10 @@ rpm -q openssh-fips >& /dev/null && DISABLE_RESTART_ON_UPDATE=yes %files helpers %defattr(-,root,root) %attr(0755,root,root) %dir %{_sysconfdir}/ssh -###%verify(not mode) %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ldap.conf +%verify(not mode) %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ldap.conf %attr(0755,root,root) %dir %{_libexecdir}/ssh -###%attr(0755,root,root) %{_libexecdir}/ssh/ssh-ldap* -###%doc HOWTO.ldap-keys openssh-lpk-openldap.schema openssh-lpk-sun.schema +%attr(0755,root,root) %{_libexecdir}/ssh/ssh-ldap* +%doc HOWTO.ldap-keys openssh-lpk-openldap.schema openssh-lpk-sun.schema %files fips %defattr(-,root,root)