diff --git a/README.SUSE b/README.SUSE index ff52c59..d52608c 100644 --- a/README.SUSE +++ b/README.SUSE @@ -1,11 +1,9 @@ -This is OpenSSH version 7.2p2 for SLE12 - There are following changes in default settings of ssh client and server: * Accepting and sending of locale environment variables in protocol 2 is enabled. -* PAM authentication is enabled. +* PAM authentication is enabled and mostly even required, do not turn it off. * root authentiation with password is enabled by default (PermitRootLogin yes). NOTE: this has security implications and is only done in order to not change @@ -26,4 +24,3 @@ There are following changes in default settings of ssh client and server: ssh_config and sshd_config manual pages. For more information on differences in SUSE OpenSSH package see README.FIPS - diff --git a/README.kerberos b/README.kerberos index d01ccd3..284a9fe 100644 --- a/README.kerberos +++ b/README.kerberos @@ -3,9 +3,6 @@ for Kerberos-related names. These DNS lookups were problematic for dialup users because they would lead to excessive delays if DNS was not reachable. -In order to disable these lookups, I had to change the default -configuration, disabling GSSAPI authentication. - If you do use Kerberos, please make sure you edit the server and client configuration files as follows: @@ -19,4 +16,3 @@ client configuration files as follows: ... lots of other options ... GSSAPIAuthentication yes GSSAPIDelegateCredentials yes - diff --git a/cavs_driver-ssh.pl b/cavs_driver-ssh.pl index 4868415..df1411e 100644 --- a/cavs_driver-ssh.pl +++ b/cavs_driver-ssh.pl @@ -1,4 +1,4 @@ -#!/usr/bin/env perl +#!/usr/bin/perl # # CAVS test driver for OpenSSH # diff --git a/openssh-7.7p1-IPv6_X_forwarding.patch b/openssh-7.7p1-IPv6_X_forwarding.patch new file mode 100644 index 0000000..3a3b2d9 --- /dev/null +++ b/openssh-7.7p1-IPv6_X_forwarding.patch @@ -0,0 +1,54 @@ +# HG changeset patch +# Parent 8df645ca39d64de025d8838c5713812e72308c92 +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.7p1/channels.c b/openssh-7.7p1/channels.c +--- openssh-7.7p1/channels.c ++++ openssh-7.7p1/channels.c +@@ -4590,33 +4590,42 @@ x11_connect_display(struct ssh *ssh) + 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 numeric 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 = ssh->chanctxt->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.7p1-SUSE_patches.tar.gz b/openssh-7.7p1-SUSE_patches.tar.gz deleted file mode 100644 index 8a1dc3e..0000000 --- a/openssh-7.7p1-SUSE_patches.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:87754e4234f7ed87e145cc61ea4c1e71121dd0ff10e28e86336f95033b8f7300 -size 147974 diff --git a/openssh-7.7p1-X11_trusted_forwarding.patch b/openssh-7.7p1-X11_trusted_forwarding.patch new file mode 100644 index 0000000..b48d480 --- /dev/null +++ b/openssh-7.7p1-X11_trusted_forwarding.patch @@ -0,0 +1,47 @@ +# HG changeset patch +# Parent d25c96855fd67e997e25ec1198d953af33eb289c +# enable trusted X11 forwarding by default in both sshd and sshsystem-wide +# configuration +# bnc#50836 (was suse #35836) +Enable Trusted X11 forwarding by default, since the security benefits of +having it disabled are negligible these days with XI2 being widely used. + +Index: openssh-7.8p1/ssh_config +=================================================================== +--- openssh-7.8p1.orig/ssh_config ++++ openssh-7.8p1/ssh_config +@@ -17,9 +17,20 @@ + # list of available options, their meanings and defaults, please see the + # ssh_config(5) man page. + +-# Host * ++Host * + # ForwardAgent no + # ForwardX11 no ++ ++# If you do not trust your remote host (or its administrator), you ++# should not forward X11 connections to your local X11-display for ++# security reasons: Someone stealing the authentification data on the ++# remote side (the "spoofed" X-server by the remote sshd) can read your ++# keystrokes as you type, just like any other X11 client could do. ++# Set this to "no" here for global effect or in your own ~/.ssh/config ++# file if you want to have the remote X11 authentification data to ++# expire after twenty minutes after remote login. ++ ForwardX11Trusted yes ++ + # PasswordAuthentication yes + # HostbasedAuthentication no + # GSSAPIAuthentication no +Index: openssh-7.8p1/sshd_config +=================================================================== +--- openssh-7.8p1.orig/sshd_config ++++ openssh-7.8p1/sshd_config +@@ -84,7 +84,7 @@ AuthorizedKeysFile .ssh/authorized_keys + #AllowAgentForwarding yes + #AllowTcpForwarding yes + #GatewayPorts no +-#X11Forwarding no ++X11Forwarding yes + #X11DisplayOffset 10 + #X11UseLocalhost yes + #PermitTTY yes diff --git a/openssh-7.7p1-X_forward_with_disabled_ipv6.patch b/openssh-7.7p1-X_forward_with_disabled_ipv6.patch new file mode 100644 index 0000000..5b4983a --- /dev/null +++ b/openssh-7.7p1-X_forward_with_disabled_ipv6.patch @@ -0,0 +1,32 @@ +# HG changeset patch +# Parent 44592f09f090e74432f608084069d30d808fda69 +Do not throw away already open sockets for X11 forwarding if another socket +family is not available for bind() + +diff --git a/openssh-7.7p1/channels.c b/openssh-7.7p1/channels.c +--- openssh-7.7p1/channels.c ++++ openssh-7.7p1/channels.c +@@ -4421,16 +4421,23 @@ x11_create_display_inet(struct ssh *ssh, + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); + if (x11_use_localhost) + set_reuseaddr(sock); + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug2("%s: bind port %d: %.100s", __func__, + port, strerror(errno)); + close(sock); ++ /* do not remove successfully opened sockets if ++ * the request failed because the protocol ++ * IPv4/6 is not available (e.g. IPv6 may be ++ * disabled while being supported) ++ */ ++ if (EADDRNOTAVAIL == errno) ++ continue; + for (n = 0; n < num_socks; n++) + close(socks[n]); + num_socks = 0; + break; + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; diff --git a/openssh-7.7p1-allow_root_password_login.patch b/openssh-7.7p1-allow_root_password_login.patch new file mode 100644 index 0000000..13b0ae7 --- /dev/null +++ b/openssh-7.7p1-allow_root_password_login.patch @@ -0,0 +1,95 @@ +# HG changeset patch +# Parent 3bf0158be93bd08d60a30a320650ea7f9844ef50 +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 +versions shipped with SLE. + +diff --git a/openssh-7.7p1/servconf.c b/openssh-7.7p1/servconf.c +--- openssh-7.7p1/servconf.c ++++ openssh-7.7p1/servconf.c +@@ -265,17 +265,17 @@ fill_default_server_options(ServerOption + options->address_family = AF_UNSPEC; + if (options->listen_addrs == NULL) + add_listen_addr(options, NULL, NULL, 0); + if (options->pid_file == NULL) + options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); + if (options->login_grace_time == -1) + options->login_grace_time = 120; + if (options->permit_root_login == PERMIT_NOT_SET) +- options->permit_root_login = PERMIT_NO_PASSWD; ++ options->permit_root_login = PERMIT_YES; + if (options->ignore_rhosts == -1) + options->ignore_rhosts = 1; + if (options->ignore_user_known_hosts == -1) + options->ignore_user_known_hosts = 0; + if (options->print_motd == -1) + options->print_motd = 1; + if (options->print_lastlog == -1) + options->print_lastlog = 1; +diff --git a/openssh-7.7p1/sshd_config b/openssh-7.7p1/sshd_config +--- openssh-7.7p1/sshd_config ++++ openssh-7.7p1/sshd_config +@@ -24,17 +24,17 @@ + + # Logging + #SyslogFacility AUTH + #LogLevel INFO + + # Authentication: + + #LoginGraceTime 2m +-#PermitRootLogin prohibit-password ++#PermitRootLogin yes + #StrictModes yes + #MaxAuthTries 6 + #MaxSessions 10 + + #PubkeyAuthentication yes + + # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 + # but this is overridden so installations will only check .ssh/authorized_keys +diff --git a/openssh-7.7p1/sshd_config.0 b/openssh-7.7p1/sshd_config.0 +--- openssh-7.7p1/sshd_config.0 ++++ openssh-7.7p1/sshd_config.0 +@@ -709,17 +709,17 @@ DESCRIPTION + none can be used to prohibit all forwarding requests. The + wildcard M-bM-^@M-^X*M-bM-^@M-^Y can be used for host or port to allow all hosts or + ports, respectively. By default all port forwarding requests are + permitted. + + PermitRootLogin + Specifies whether root can log in using ssh(1). The argument + must be yes, prohibit-password, forced-commands-only, or no. The +- default is prohibit-password. ++ default is yes. + + If this option is set to prohibit-password (or its deprecated + alias, without-password), password and keyboard-interactive + authentication are disabled for root. + + If this option is set to forced-commands-only, root login with + public key authentication will be allowed, but only if the + command option has been specified (which may be useful for taking +diff --git a/openssh-7.7p1/sshd_config.5 b/openssh-7.7p1/sshd_config.5 +--- openssh-7.7p1/sshd_config.5 ++++ openssh-7.7p1/sshd_config.5 +@@ -1220,17 +1220,17 @@ Specifies whether root can log in using + .Xr ssh 1 . + The argument must be + .Cm yes , + .Cm prohibit-password , + .Cm forced-commands-only , + or + .Cm no . + The default is +-.Cm prohibit-password . ++.Cm yes . + .Pp + If this option is set to + .Cm prohibit-password + (or its deprecated alias, + .Cm without-password ) , + password and keyboard-interactive authentication are disabled for root. + .Pp + If this option is set to diff --git a/openssh-7.7p1-audit.patch b/openssh-7.7p1-audit.patch new file mode 100644 index 0000000..7acc3eb --- /dev/null +++ b/openssh-7.7p1-audit.patch @@ -0,0 +1,2264 @@ +# HG changeset patch +# Parent 8f6ff259bbb7a7f173004e8cb11a16e7a9a29c7f +Extended auditing through the Linux Auditing subsystem +RH patch from git://pkgs.fedoraproject.org/openssh.git + +Index: openssh-7.8p1/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/Makefile.in ++++ openssh-7.8p1/Makefile.in +@@ -110,6 +110,8 @@ LIBSSH_OBJS += fips.o + + LIBSSH_OBJS += kexgssc.o kexgsss.o + ++LIBSSH_OBJS += auditstub.o ++ + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o + +Index: openssh-7.8p1/audit-bsm.c +=================================================================== +--- openssh-7.8p1.orig/audit-bsm.c ++++ openssh-7.8p1/audit-bsm.c +@@ -372,10 +372,23 @@ audit_connection_from(const char *host, + #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 +@@ -390,6 +403,12 @@ audit_session_close(struct logininfo *li + /* not implemented */ + } + ++int ++audit_keyusage(int host_user, char *fp, int rv) ++{ ++ /* not implemented */ ++} ++ + void + audit_event(ssh_audit_event_t event) + { +@@ -451,4 +470,34 @@ audit_event(ssh_audit_event_t event) + 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 */ ++} + #endif /* BSM */ +Index: openssh-7.8p1/audit-linux.c +=================================================================== +--- openssh-7.8p1.orig/audit-linux.c ++++ openssh-7.8p1/audit-linux.c +@@ -33,27 +33,40 @@ + + #include "log.h" + #include "audit.h" ++#include "sshkey.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" ++#include "channels.h" ++#include "session.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 *ip, const char *ttyn, int success, int event) + { + int audit_fd, rc, saved_errno; + + if ((audit_fd = audit_open()) < 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); ++ username == NULL ? uid : -1, NULL, ip, ttyn, success); + saved_errno = errno; + close(audit_fd); + +@@ -65,9 +78,96 @@ linux_audit_record_event(int uid, const + 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 *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, NULL, 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, 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 grantors=auth-key", host_user ? "pubkey" : "hostbased"); ++ rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, ++ buf, audit_username(), -1, NULL, ssh_remote_ipaddr(active_state), NULL, rv); ++ if ((rc < 0) && ((rc != -1) || (getuid() == 0))) ++ goto out; ++ snprintf(buf, sizeof(buf), "op=negotiate kind=auth-key fp=%s", fp); ++ rc = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, buf, NULL, ++ ssh_remote_ipaddr(active_state), 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 +@@ -76,24 +176,55 @@ audit_connection_from(const char *host, + /* not implemented */ + } + +-void ++int + audit_run_command(const char *command) + { +- /* not implemented */ ++ if (!user_login_count++) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, ++ ssh_remote_ipaddr(active_state), ++ "ssh", 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, ++ ssh_remote_ipaddr(active_state), ++ "ssh", 1, AUDIT_USER_START); ++ return 0; ++} ++ ++void ++audit_end_command(int handle, const char *command) ++{ ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, ++ ssh_remote_ipaddr(active_state), ++ "ssh", 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, ++ ssh_remote_ipaddr(active_state), ++ "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, ++ li->line, 1, AUDIT_USER_LOGIN); ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ li->line, 1, AUDIT_USER_START); + } + + void + audit_session_close(struct logininfo *li) + { +- /* not implemented */ ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ li->line, 1, AUDIT_USER_END); ++ if (user_login_count && !--user_login_count) ++ linux_audit_user_logxxx(li->uid, NULL, li->hostname, ++ li->line, 1, AUDIT_USER_LOGOUT); + } + + void +@@ -102,25 +233,155 @@ audit_event(ssh_audit_event_t event) + struct ssh *ssh = active_state; /* XXX */ + + 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(), ++ ssh_remote_ipaddr(ssh), "ssh", 0, event); ++ linux_audit_user_logxxx(-1, audit_username(), ++ ssh_remote_ipaddr(ssh), "ssh", 0, AUDIT_USER_LOGIN); + break; +- case SSH_AUTH_FAIL_NONE: + case SSH_AUTH_FAIL_PASSWD: ++ if (options.use_pam) ++ break; ++ case SSH_LOGIN_EXCEED_MAXTRIES: + 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(), ++ ssh_remote_ipaddr(ssh), "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, ++ ssh_remote_ipaddr(ssh), ++ "ssh", 1, AUDIT_USER_END); ++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, ++ ssh_remote_ipaddr(ssh), ++ "ssh", 1, AUDIT_USER_LOGOUT); ++ } ++ break; ++ ++ case SSH_CONNECTION_ABANDON: + case SSH_INVALID_USER: +- linux_audit_record_event(-1, audit_username(), NULL, +- ssh_remote_ipaddr(ssh), "sshd", 0); ++ linux_audit_user_logxxx(-1, audit_username(), ++ ssh_remote_ipaddr(ssh), "ssh", 0, AUDIT_USER_LOGIN); + break; + default: + debug("%s: unhandled event %d", __func__, event); + break; + } + } ++ ++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], ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), ++ ssh_local_port(active_state)); ++ 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, ssh_remote_ipaddr(active_state), 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, ++ ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), ssh_local_port(active_state)); ++ 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, ssh_remote_ipaddr(active_state), 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, ++ ssh_remote_port(active_state), ++ (s = get_local_ipaddr(packet_get_connection_in())), ++ ssh_local_port(active_state)); ++ 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, ssh_remote_ipaddr(active_state), 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 : ssh_remote_ipaddr(active_state), ++ 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 */ +Index: openssh-7.8p1/audit.c +=================================================================== +--- openssh-7.8p1.orig/audit.c ++++ openssh-7.8p1/audit.c +@@ -34,13 +34,19 @@ + #include "log.h" + #include "hostfile.h" + #include "auth.h" +- ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "xmalloc.h" ++#include "misc.h" ++#include "servconf.h" ++#include "ssherr.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 +@@ -69,13 +75,10 @@ audit_classify_auth(const char *method) + 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); + } + +@@ -109,6 +112,35 @@ audit_event_lookup(ssh_audit_event_t ev) + return(event_lookup[i].name); + } + ++void ++audit_key(int host_user, int *rv, const struct sshkey *key) ++{ ++ char *fp; ++ ++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX); ++ if (audit_keyusage(host_user, fp, (*rv == 0)) == 0) ++ *rv = -SSH_ERR_INTERNAL_ERROR; ++ 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. +@@ -138,6 +170,17 @@ audit_event(ssh_audit_event_t 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. + * +@@ -172,13 +215,82 @@ audit_session_close(struct logininfo *li + /* + * 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, char *fp, int rv) ++{ ++ debug("audit %s key usage euid %d user %s fingerprint %s, result %d", ++ host_user ? "pubkey" : "hostbased", geteuid(), audit_username(), ++ 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); + } + # endif /* !defined CUSTOM_SSH_AUDIT_EVENTS */ + #endif /* SSH_AUDIT_EVENTS */ +Index: openssh-7.8p1/audit.h +=================================================================== +--- openssh-7.8p1.orig/audit.h ++++ openssh-7.8p1/audit.h +@@ -26,6 +26,7 @@ + # define _SSH_AUDIT_H + + #include "loginrec.h" ++#include "sshkey.h" + + enum ssh_audit_event_type { + SSH_LOGIN_EXCEED_MAXTRIES, +@@ -43,13 +44,32 @@ enum ssh_audit_event_type { + 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, char *, int); ++void audit_key(int, int *, const struct sshkey *); ++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); + + #endif /* _SSH_AUDIT_H */ +Index: openssh-7.8p1/auditstub.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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) ++{ ++} +Index: openssh-7.8p1/auth.c +=================================================================== +--- openssh-7.8p1.orig/auth.c ++++ openssh-7.8p1/auth.c +@@ -362,7 +362,7 @@ auth_log(Authctxt *authctxt, int authent + # endif + #endif + #ifdef SSH_AUDIT_EVENTS +- if (authenticated == 0 && !authctxt->postponed) ++ if (authenticated == 0 && !authctxt->postponed && !partial) + audit_event(audit_classify_auth(method)); + #endif + } +@@ -601,9 +601,6 @@ getpwnamallow(const char *user) + record_failed_login(user, + auth_get_canonical_hostname(ssh, options.use_dns), "ssh"); + #endif +-#ifdef SSH_AUDIT_EVENTS +- audit_event(SSH_INVALID_USER); +-#endif /* SSH_AUDIT_EVENTS */ + return (NULL); + } + if (!allowed_user(pw)) +Index: openssh-7.8p1/auth.h +=================================================================== +--- openssh-7.8p1.orig/auth.h ++++ openssh-7.8p1/auth.h +@@ -193,6 +193,8 @@ struct passwd * getpwnamallow(const char + + char *expand_authorized_keys(const char *, struct passwd *pw); + char *authorized_principals_file(struct passwd *); ++int user_key_verify(const struct sshkey *, const u_char *, size_t, ++ const u_char *, size_t, const char *, u_int); + + FILE *auth_openkeyfile(const char *, struct passwd *, int); + FILE *auth_openprincipals(const char *, struct passwd *, int); +@@ -212,6 +214,8 @@ struct sshkey *get_hostkey_private_by_ty + int get_hostkey_index(struct sshkey *, int, struct ssh *); + int sshd_hostkey_sign(struct sshkey *, struct sshkey *, u_char **, + size_t *, const u_char *, size_t, const char *, u_int); ++int hostbased_key_verify(const struct sshkey *, const u_char *, size_t, ++ const u_char *, size_t, const char *, u_int); + + /* Key / cert options linkage to auth layer */ + const struct sshauthopt *auth_options(struct ssh *); +Index: openssh-7.8p1/auth2-hostbased.c +=================================================================== +--- openssh-7.8p1.orig/auth2-hostbased.c ++++ openssh-7.8p1/auth2-hostbased.c +@@ -141,7 +141,7 @@ userauth_hostbased(struct ssh *ssh) + /* test for allowed key and correct signature */ + authenticated = 0; + if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && +- PRIVSEP(sshkey_verify(key, sig, slen, ++ PRIVSEP(hostbased_key_verify(key, sig, slen, + sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat)) == 0) + authenticated = 1; + +@@ -158,6 +158,19 @@ done: + return authenticated; + } + ++int ++hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, ++ const u_char *data, size_t datalen, const char *alg, u_int compat) ++{ ++ int rv; ++ ++ rv = sshkey_verify(key, sig, slen, data, datalen, alg, compat); ++#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, +Index: openssh-7.8p1/auth2-pubkey.c +=================================================================== +--- openssh-7.8p1.orig/auth2-pubkey.c ++++ openssh-7.8p1/auth2-pubkey.c +@@ -187,7 +187,7 @@ userauth_pubkey(struct ssh *ssh) + /* test for correct signature */ + authenticated = 0; + if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && +- PRIVSEP(sshkey_verify(key, sig, slen, ++ PRIVSEP(user_key_verify(key, sig, slen, + sshbuf_ptr(b), sshbuf_len(b), + (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, + ssh->compat)) == 0) { +@@ -246,6 +246,19 @@ done: + return authenticated; + } + ++int ++user_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, ++ const u_char *data, size_t datalen, const char *alg, u_int compat) ++{ ++ int rv; ++ ++ rv = sshkey_verify(key, sig, slen, data, datalen, alg, compat); ++#ifdef SSH_AUDIT_EVENTS ++ audit_key(1, &rv, key); ++#endif ++ return rv; ++} ++ + static int + match_principals_option(const char *principal_list, struct sshkey_cert *cert) + { +@@ -767,7 +780,7 @@ user_cert_trusted_ca(struct ssh *ssh, st + found_principal = 1; + /* If principals file or command is specified, then require a match */ + use_authorized_principals = principals_file != NULL || +- options.authorized_principals_command != NULL; ++ options.authorized_principals_command != NULL; + if (!found_principal && use_authorized_principals) { + reason = "Certificate does not contain an authorized principal"; + goto fail_reason; +Index: openssh-7.8p1/auth2.c +=================================================================== +--- openssh-7.8p1.orig/auth2.c ++++ openssh-7.8p1/auth2.c +@@ -284,9 +284,6 @@ input_userauth_request(int type, u_int32 + } else { + /* Invalid user, fake password information */ + authctxt->pw = fakepw(); +-#ifdef SSH_AUDIT_EVENTS +- PRIVSEP(audit_event(SSH_INVALID_USER)); +-#endif + } + #ifdef USE_PAM + if (options.use_pam) +Index: openssh-7.8p1/cipher.c +=================================================================== +--- openssh-7.8p1.orig/cipher.c ++++ openssh-7.8p1/cipher.c +@@ -54,25 +54,6 @@ + #include "fips.h" + #include "log.h" + +-struct sshcipher { +- char *name; +- u_int block_size; +- u_int key_len; +- u_int iv_len; /* defaults to block_size */ +- u_int auth_len; +- u_int flags; +-#define CFLAG_CBC (1<<0) +-#define CFLAG_CHACHAPOLY (1<<1) +-#define CFLAG_AESCTR (1<<2) +-#define CFLAG_NONE (1<<3) +-#define CFLAG_INTERNAL CFLAG_NONE /* Don't use "none" for packets */ +-#ifdef WITH_OPENSSL +- const EVP_CIPHER *(*evptype)(void); +-#else +- void *ignored; +-#endif +-}; +- + static const struct sshcipher ciphers_all[] = { + #ifdef WITH_OPENSSL + #ifndef OPENSSL_NO_DES +@@ -447,7 +428,7 @@ cipher_get_length(struct sshcipher_ctx * + void + cipher_free(struct sshcipher_ctx *cc) + { +- if (cc == NULL) ++ if (cc == NULL || cc->cipher == NULL) + return; + if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) + explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); +Index: openssh-7.8p1/cipher.h +=================================================================== +--- openssh-7.8p1.orig/cipher.h ++++ openssh-7.8p1/cipher.h +@@ -45,7 +45,25 @@ + #define CIPHER_ENCRYPT 1 + #define CIPHER_DECRYPT 0 + +-struct sshcipher; ++struct sshcipher { ++ char *name; ++ u_int block_size; ++ u_int key_len; ++ u_int iv_len; /* defaults to block_size */ ++ u_int auth_len; ++ u_int flags; ++#define CFLAG_CBC (1<<0) ++#define CFLAG_CHACHAPOLY (1<<1) ++#define CFLAG_AESCTR (1<<2) ++#define CFLAG_NONE (1<<3) ++#define CFLAG_INTERNAL CFLAG_NONE /* Don't use "none" for packets */ ++#ifdef WITH_OPENSSL ++ const EVP_CIPHER *(*evptype)(void); ++#else ++ void *ignored; ++#endif ++}; ++ + struct sshcipher_ctx { + int plaintext; + int encrypt; +Index: openssh-7.8p1/kex.c +=================================================================== +--- openssh-7.8p1.orig/kex.c ++++ openssh-7.8p1/kex.c +@@ -53,6 +53,7 @@ + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" ++#include "audit.h" + + #include "fips.h" + +@@ -765,8 +766,12 @@ choose_enc(struct sshenc *enc, char *cli + { + 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) { + free(name); + return SSH_ERR_INTERNAL_ERROR; +@@ -786,8 +791,12 @@ choose_mac(struct ssh *ssh, struct sshma + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) +- return SSH_ERR_NO_MAC_ALG_MATCH; ++ 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) { + free(name); + return SSH_ERR_INTERNAL_ERROR; +@@ -803,8 +812,12 @@ choose_comp(struct sshcomp *comp, char * + { + 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) { +@@ -974,6 +987,10 @@ kex_choose_conf(struct ssh *ssh) + dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); + dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); + dh_need = MAXIMUM(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; +@@ -1106,3 +1123,33 @@ dump_digest(char *msg, u_char *digest, i + 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)); ++} +Index: openssh-7.8p1/kex.h +=================================================================== +--- openssh-7.8p1.orig/kex.h ++++ openssh-7.8p1/kex.h +@@ -213,6 +213,8 @@ int kexgss_client(struct ssh *); + int kexgss_server(struct ssh *); + #endif + ++void newkeys_destroy(struct newkeys *newkeys); ++ + int kex_dh_hash(int, 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 *); +Index: openssh-7.8p1/mac.c +=================================================================== +--- openssh-7.8p1.orig/mac.c ++++ openssh-7.8p1/mac.c +@@ -280,6 +280,20 @@ mac_clear(struct sshmac *mac) + 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 +Index: openssh-7.8p1/mac.h +=================================================================== +--- openssh-7.8p1.orig/mac.h ++++ openssh-7.8p1/mac.h +@@ -49,5 +49,6 @@ int mac_compute(struct sshmac *, u_int3 + int mac_check(struct sshmac *, u_int32_t, const u_char *, size_t, + const u_char *, size_t); + void mac_clear(struct sshmac *); ++void mac_destroy(struct sshmac *); + + #endif /* SSHMAC_H */ +Index: openssh-7.8p1/monitor.c +=================================================================== +--- openssh-7.8p1.orig/monitor.c ++++ openssh-7.8p1/monitor.c +@@ -91,6 +91,7 @@ + #include "compat.h" + #include "ssh2.h" + #include "authfd.h" ++#include "audit.h" + #include "match.h" + #include "ssherr.h" + +@@ -105,6 +106,8 @@ extern u_char session_id[]; + extern struct sshbuf *loginmsg; + extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ + ++extern void destroy_sensitive_data(int); ++ + /* State exported from the child */ + static struct sshbuf *child_state; + +@@ -150,6 +153,11 @@ int mm_answer_gss_updatecreds(int, struc + #ifdef SSH_AUDIT_EVENTS + int mm_answer_audit_event(int, struct sshbuf *); + int mm_answer_audit_command(int, struct sshbuf *); ++int mm_answer_audit_end_command(int, struct sshbuf *); ++int mm_answer_audit_unsupported_body(int, struct sshbuf *); ++int mm_answer_audit_kex_body(int, struct sshbuf *); ++int mm_answer_audit_session_key_free_body(int, struct sshbuf *); ++int mm_answer_audit_server_key_free(int, struct sshbuf *); + #endif + + static int monitor_read_log(struct monitor *); +@@ -203,6 +211,11 @@ struct mon_table mon_dispatch_proto20[] + #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}, +@@ -231,6 +244,11 @@ struct mon_table mon_dispatch_postauth20 + #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 + #ifdef GSSAPI + {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, +@@ -1375,6 +1393,7 @@ mm_answer_keyverify(int sock, struct ssh + char *sigalg; + size_t signaturelen, datalen, bloblen; + int r, ret, valid_data = 0, encoded_ret; ++ int type = 0; + + if ((r = sshbuf_get_string(m, &blob, &bloblen)) != 0 || + (r = sshbuf_get_string(m, &signature, &signaturelen)) != 0 || +@@ -1385,6 +1404,8 @@ mm_answer_keyverify(int sock, struct ssh + 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__); + + /* Empty signature algorithm means NULL. */ + if (*sigalg == '\0') { +@@ -1399,22 +1420,25 @@ mm_answer_keyverify(int sock, struct ssh + switch (key_blobtype) { + case MM_USERKEY: + valid_data = monitor_valid_userblob(data, datalen); ++ ret = user_key_verify(key, signature, signaturelen, data, ++ datalen, sigalg, active_state->compat); + auth_method = "publickey"; + break; + case MM_HOSTKEY: + valid_data = monitor_valid_hostbasedblob(data, datalen, + hostbased_cuser, hostbased_chost); ++ ret = hostbased_key_verify(key, signature, signaturelen, data, ++ datalen, sigalg, active_state->compat); + auth_method = "hostbased"; + break; + default: + valid_data = 0; ++ ret = 0; + break; + } + if (!valid_data) + fatal("%s: bad signature data blob", __func__); + +- ret = sshkey_verify(key, signature, signaturelen, data, datalen, +- sigalg, active_state->compat); + debug3("%s: %s %p signature %s", __func__, auth_method, key, + (ret == 0) ? "verified" : "unverified"); + auth2_record_key(authctxt, ret == 0, key); +@@ -1474,6 +1498,12 @@ mm_session_close(Session *s) + 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); + } + +@@ -1582,6 +1612,8 @@ mm_answer_term(int sock, struct sshbuf * + sshpam_cleanup(); + #endif + ++ destroy_sensitive_data(0); ++ + while (waitpid(pmonitor->m_pid, &status, 0) == -1) + if (errno != EINTR) + exit(1); +@@ -1628,14 +1660,50 @@ mm_answer_audit_command(int socket, stru + { + char *cmd; + int r; ++ Session *s; + + debug3("%s entering", __func__); + if ((r = sshbuf_get_cstring(m, &cmd, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ + /* sanity check command, if so how? */ +- audit_run_command(cmd); +- free(cmd); +- return (0); ++ s = session_new(); ++ if (s == NULL) ++ fatal("%s: error allocating a session", __func__); ++ s->command = cmd; ++#ifdef SSH_AUDIT_EVENTS ++ s->command_handle = audit_run_command(cmd); ++#endif ++ ++ sshbuf_reset(m); ++ sshbuf_put_u32(m, s->self); ++ ++ mm_request_send(socket, MONITOR_ANS_AUDIT_COMMAND, m); ++ ++ return (0); ++} ++ ++int ++mm_answer_audit_end_command(int socket, struct sshbuf *m) ++{ ++ int handle, r; ++ size_t len; ++ u_char *cmd = NULL; ++ Session *s; ++ ++ debug3("%s entering", __func__); ++ if ((r = sshbuf_get_u32(m, &handle)) != 0 || ++ (r = sshbuf_get_string(m, &cmd, &len)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ 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 */ + +@@ -1697,6 +1765,7 @@ monitor_apply_keystate(struct monitor *p + void + mm_get_keystate(struct monitor *pmonitor) + { ++ struct sshbuf *m; + debug3("%s: Waiting for new keys", __func__); + + if ((child_state = sshbuf_new()) == NULL) +@@ -1704,6 +1773,19 @@ mm_get_keystate(struct monitor *pmonitor + mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, + child_state); + debug3("%s: GOT new keys", __func__); ++ ++#ifdef SSH_AUDIT_EVENTS ++ m = sshbuf_new(); ++ 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); ++ sshbuf_free(m); ++#endif ++ ++ /* Drain any buffered messages from the child */ ++ while (pmonitor->m_log_recvfd >= 0 && monitor_read_log(pmonitor) == 0) ++ ; ++ + } + + +@@ -1902,19 +1984,19 @@ mm_answer_gss_sign(int socket, struct ss + int r; + + if (!options.gss_authentication && !options.gss_keyex) +- fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); + + if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (data.length != 20) +- fatal("%s: data length incorrect: %d", __func__, +- (int) data.length); ++ fatal("%s: data length incorrect: %d", __func__, ++ (int) data.length); + + /* Save the session ID on the first time around */ + if (session_id2_len == 0) { +- session_id2_len = data.length; +- session_id2 = xmalloc(session_id2_len); +- memcpy(session_id2, data.value, session_id2_len); ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); + } + major = ssh_gssapi_sign(gsscontext, &data, &hash); + +@@ -1962,3 +2044,102 @@ mm_answer_gss_updatecreds(int socket, st + } + + #endif /* GSSAPI */ ++ ++#ifdef SSH_AUDIT_EVENTS ++int ++mm_answer_audit_unsupported_body(int sock, struct sshbuf *m) ++{ ++ int what, r; ++ ++ if ((r = sshbuf_get_u32(m, &what)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ audit_unsupported_body(what); ++ ++ sshbuf_reset(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_UNSUPPORTED, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_kex_body(int sock, struct sshbuf *m) ++{ ++ int ctos, r; ++ char *cipher, *mac, *compress, *pfs; ++ u_int64_t tmp; ++ pid_t pid; ++ uid_t uid; ++ ++ if ((r = sshbuf_get_u32(m, &ctos)) != 0 || ++ (r = sshbuf_get_cstring(m, &cipher, NULL)) != 0 || ++ (r = sshbuf_get_cstring(m, &mac, NULL)) != 0 || ++ (r = sshbuf_get_cstring(m, &compress, NULL)) != 0 || ++ (r = sshbuf_get_cstring(m, &pfs, NULL)) != 0 || ++ (r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ pid = (pid_t) tmp; ++ if ((r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ uid = (pid_t) tmp; ++ ++ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); ++ ++ free(cipher); ++ free(mac); ++ free(compress); ++ free(pfs); ++ sshbuf_reset(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_KEX, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_session_key_free_body(int sock, struct sshbuf *m) ++{ ++ int ctos, r; ++ u_int64_t tmp; ++ pid_t pid; ++ uid_t uid; ++ ++ if ((r = sshbuf_get_u32(m, &ctos)) != 0 || ++ (r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ pid = (pid_t) tmp; ++ if ((r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ uid = (uid_t) tmp; ++ ++ audit_session_key_free_body(ctos, pid, uid); ++ sshbuf_reset(m); ++ ++ mm_request_send(sock, MONITOR_ANS_AUDIT_SESSION_KEY_FREE, m); ++ return 0; ++} ++ ++int ++mm_answer_audit_server_key_free(int sock, struct sshbuf *m) ++{ ++ size_t len, r; ++ char *fp; ++ u_int64_t tmp; ++ pid_t pid; ++ uid_t uid; ++ ++ if ((r = sshbuf_get_cstring(m, &fp, &len)) != 0 || ++ (r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ pid = (pid_t) tmp; ++ if ((r = sshbuf_get_u64(m, &tmp)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ uid = (uid_t) tmp; ++ ++ audit_destroy_sensitive_data(fp, pid, uid); ++ ++ free(fp); ++ sshbuf_reset(m); ++ ++ return 0; ++} ++#endif /* SSH_AUDIT_EVENTS */ +Index: openssh-7.8p1/monitor.h +=================================================================== +--- openssh-7.8p1.orig/monitor.h ++++ openssh-7.8p1/monitor.h +@@ -61,7 +61,13 @@ enum monitor_reqtype { + 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, +Index: openssh-7.8p1/monitor_wrap.c +=================================================================== +--- openssh-7.8p1.orig/monitor_wrap.c ++++ openssh-7.8p1/monitor_wrap.c +@@ -497,7 +497,7 @@ mm_key_allowed(enum mm_keytype type, con + */ + + int +-mm_sshkey_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++mm_sshkey_verify(enum mm_keytype type, const struct sshkey *key, const u_char *sig, size_t siglen, + const u_char *data, size_t datalen, const char *sigalg, u_int compat) + { + struct sshbuf *m; +@@ -506,10 +506,10 @@ mm_sshkey_verify(const struct sshkey *ke + + debug3("%s entering", __func__); + +- + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); +- if ((r = sshkey_puts(key, m)) != 0 || ++ if ((r = sshbuf_put_u32(m, type)) != 0 || ++ (r = sshkey_puts(key, m)) != 0 || + (r = sshbuf_put_string(m, sig, siglen)) != 0 || + (r = sshbuf_put_string(m, data, datalen)) != 0 || + (r = sshbuf_put_cstring(m, sigalg == NULL ? "" : sigalg)) != 0) +@@ -531,6 +531,22 @@ mm_sshkey_verify(const struct sshkey *ke + return 0; + } + ++int ++mm_hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++ const u_char *data, size_t datalen, const char *alg, u_int compat) ++{ ++ return mm_sshkey_verify(MM_HOSTKEY, key, sig, siglen, data, datalen, ++ alg, compat); ++} ++ ++int ++mm_user_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++ const u_char *data, size_t datalen, const char *alg, u_int compat) ++{ ++ return mm_sshkey_verify(MM_USERKEY, key, sig, siglen, data, datalen, ++ alg, compat); ++} ++ + void + mm_send_keystate(struct monitor *monitor) + { +@@ -885,11 +901,12 @@ mm_audit_event(ssh_audit_event_t event) + sshbuf_free(m); + } + +-void ++int + mm_audit_run_command(const char *command) + { + struct sshbuf *m; + int r; ++ int handle; + + debug3("%s entering command %s", __func__, command); + +@@ -899,7 +916,31 @@ mm_audit_run_command(const char *command + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_COMMAND, m); +- sshbuf_free(m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_COMMAND, m); ++ ++ if ((r = sshbuf_get_u32(m, &handle)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ sshbuf_free(m); ++ ++ return (handle); ++} ++ ++void ++mm_audit_end_command(int handle, const char *command) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ debug3("%s entering command %s", __func__, command); ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, handle)) != 0 || ++ (r = sshbuf_put_cstring(m, command)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, m); ++ sshbuf_free(m); + } + #endif /* SSH_AUDIT_EVENTS */ + +@@ -1052,3 +1093,84 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc + return (ok); + } + #endif /* GSSAPI */ ++ ++#ifdef SSH_AUDIT_EVENTS ++void ++mm_audit_unsupported_body(int what) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, what)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_UNSUPPORTED, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_UNSUPPORTED, ++ m); ++ ++ sshbuf_free(m); ++} ++ ++void ++mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, ++ uid_t uid) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, ctos)) != 0 || ++ (r = sshbuf_put_cstring(m, cipher)) != 0 || ++ (r = sshbuf_put_cstring(m, (mac ? mac : ""))) != 0 || ++ (r = sshbuf_put_cstring(m, compress)) != 0 || ++ (r = sshbuf_put_cstring(m, fps)) != 0 || ++ (r = sshbuf_put_u64(m, pid)) != 0 || ++ (r = sshbuf_put_u64(m, uid)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_KEX, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_AUDIT_KEX, ++ m); ++ ++ sshbuf_free(m); ++} ++ ++void ++mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, ctos)) != 0 || ++ (r = sshbuf_put_u64(m, pid)) != 0 || ++ (r = sshbuf_put_u64(m, uid)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ 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); ++ sshbuf_free(m); ++} ++ ++void ++mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_cstring(m, fp)) != 0 || ++ (r = sshbuf_put_u64(m, pid)) != 0 || ++ (r = sshbuf_put_u64(m, uid)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, m); ++ sshbuf_free(m); ++} ++#endif /* SSH_AUDIT_EVENTS */ +Index: openssh-7.8p1/monitor_wrap.h +=================================================================== +--- openssh-7.8p1.orig/monitor_wrap.h ++++ openssh-7.8p1/monitor_wrap.h +@@ -53,7 +53,9 @@ int mm_user_key_allowed(struct ssh *, st + struct sshauthopt **); + int mm_hostbased_key_allowed(struct passwd *, const char *, + const char *, struct sshkey *); +-int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, ++int mm_hostbased_key_verify(const struct sshkey *, const u_char *, size_t, ++ const u_char *, size_t, const char *, u_int); ++int mm_user_key_verify(const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int); + + #ifdef GSSAPI +@@ -78,7 +80,12 @@ void mm_sshpam_free_ctx(void *); + #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); + #endif + + struct Session; +Index: openssh-7.8p1/packet.c +=================================================================== +--- openssh-7.8p1.orig/packet.c ++++ openssh-7.8p1/packet.c +@@ -76,6 +76,7 @@ + #include + + #include "xmalloc.h" ++#include "audit.h" + #include "crc32.h" + #include "compat.h" + #include "ssh2.h" +@@ -506,6 +507,13 @@ ssh_packet_get_connection_out(struct 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. +@@ -583,22 +591,19 @@ ssh_packet_close_internal(struct ssh *ss + { + struct session_state *state = ssh->state; + u_int mode; ++ u_int had_keys = packet_state_has_keys(state); + + if (!state->initialized) + return; + state->initialized = 0; +- if (do_close) { +- if (state->connection_in == state->connection_out) { +- close(state->connection_out); +- } else { +- close(state->connection_in); +- close(state->connection_out); +- } +- } + sshbuf_free(state->input); ++ state->input = NULL; + sshbuf_free(state->output); ++ state->output = NULL; + sshbuf_free(state->outgoing_packet); ++ state->outgoing_packet = NULL; + sshbuf_free(state->incoming_packet); ++ state->incoming_packet = NULL; + for (mode = 0; mode < MODE_MAX; mode++) { + kex_free_newkeys(state->newkeys[mode]); /* current keys */ + state->newkeys[mode] = NULL; +@@ -632,8 +637,18 @@ ssh_packet_close_internal(struct ssh *ss + } + cipher_free(state->send_context); + cipher_free(state->receive_context); ++ if (had_keys && state->server_side) { ++ /* Assuming this is called only from privsep child */ ++ audit_session_key_free(MODE_MAX); ++ } + state->send_context = state->receive_context = NULL; + if (do_close) { ++ if (state->connection_in == state->connection_out) { ++ close(state->connection_out); ++ } else { ++ close(state->connection_in); ++ close(state->connection_out); ++ } + free(ssh->local_ipaddr); + ssh->local_ipaddr = NULL; + free(ssh->remote_ipaddr); +@@ -858,6 +873,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod + (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); + cipher_free(*ccp); + *ccp = NULL; + kex_free_newkeys(state->newkeys[mode]); +@@ -952,7 +968,7 @@ ssh_packet_need_rekeying(struct ssh *ssh + return 1; + + /* +- * Always rekey when MAX_PACKETS sent in either direction ++ * Always rekey when MAX_PACKETS sent in either direction + * As per RFC4344 section 3.1 we do this after 2^31 packets. + */ + if (state->p_send.packets > MAX_PACKETS || +@@ -2143,6 +2159,72 @@ 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_free(state->receive_context); ++ cipher_free(state->send_context); ++ ++ sshbuf_free(state->input); ++ state->input = NULL; ++ sshbuf_free(state->output); ++ state->output = NULL; ++ sshbuf_free(state->outgoing_packet); ++ state->outgoing_packet = NULL; ++ sshbuf_free(state->incoming_packet); ++ state->incoming_packet = NULL; ++ if (state->compression_buffer) { ++ sshbuf_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_MAX); ++ else ++ audit_session_key_free_body(MODE_MAX, getpid(), getuid()); ++#endif ++ } ++} ++ + /* Reset after_authentication and reset compression in post-auth privsep */ + static int + ssh_packet_set_postauth(struct ssh *ssh) +Index: openssh-7.8p1/packet.h +=================================================================== +--- openssh-7.8p1.orig/packet.h ++++ openssh-7.8p1/packet.h +@@ -219,4 +219,5 @@ extern struct ssh *active_state; + # undef EC_POINT + #endif + ++void packet_destroy_all(int, int); + #endif /* PACKET_H */ +Index: openssh-7.8p1/session.c +=================================================================== +--- openssh-7.8p1.orig/session.c ++++ openssh-7.8p1/session.c +@@ -139,7 +139,7 @@ extern char *__progname; + 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 struct sshbuf *loginmsg; + extern struct sshauthopt *auth_opts; + char *tun_fwd_ifnames; /* serverloop.c */ +@@ -648,6 +648,14 @@ do_exec_pty(struct ssh *ssh, Session *s, + /* 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 (command != NULL) ++ audit_count_session_open(); ++#endif ++ + /* Enter interactive session. */ + s->ptymaster = ptymaster; + packet_set_interactive(1, +@@ -739,15 +747,19 @@ do_exec(struct ssh *ssh, Session *s, con + 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)); + #endif + if (s->ttyfd != -1) + ret = do_exec_pty(ssh, s, command); +@@ -1551,8 +1563,11 @@ do_child(struct ssh *ssh, Session *s, co + int r = 0; + + /* remove hostkey from the child's memory */ +- destroy_sensitive_data(); ++ destroy_sensitive_data(1); + packet_clear_keys(); ++ /* Don't audit this - both us and the parent would be talking to the ++ monitor over a single socket, with no synchronization. */ ++ packet_destroy_all(0, 1); + + /* Force a password change */ + if (s->authctxt->force_pwchange) { +@@ -1759,6 +1774,9 @@ session_unused(int id) + 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; + } +@@ -1841,6 +1859,19 @@ session_open(Authctxt *authctxt, int cha + } + + 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; +@@ -2352,6 +2383,32 @@ session_exit_message(struct ssh *ssh, Se + chan_write_failed(ssh, 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(struct ssh *ssh, Session *s) + { +@@ -2393,6 +2450,10 @@ session_close(struct ssh *ssh, Session * + + 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); +@@ -2600,6 +2661,15 @@ do_authenticated2(struct ssh *ssh, Authc + server_loop2(ssh, 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(struct ssh *ssh, Authctxt *authctxt) + { +@@ -2657,7 +2727,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au + * or if running in monitor. + */ + if (!use_privsep || mm_is_monitor()) +- session_destroy_all(ssh, session_pty_cleanup2); ++ session_destroy_all(ssh, do_cleanup_one_session); + } + + /* Return a name for the remote host that fits inside utmp_size */ +Index: openssh-7.8p1/session.h +=================================================================== +--- openssh-7.8p1.orig/session.h ++++ openssh-7.8p1/session.h +@@ -60,6 +60,12 @@ struct Session { + char *name; + char *val; + } *env; ++ ++ /* exec */ ++#ifdef SSH_AUDIT_EVENTS ++ int command_handle; ++ char *command; ++#endif + }; + + void do_authenticated(struct ssh *, Authctxt *); +@@ -72,8 +78,10 @@ void session_close_by_pid(struct ssh *s + void session_close_by_channel(struct ssh *, int, void *); + void session_destroy_all(struct ssh *, 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(struct ssh *, Session *); + void do_setusercontext(struct passwd *); +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -124,6 +124,7 @@ + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" ++#include "audit.h" + #include "ssh-sandbox.h" + #include "auth-options.h" + #include "version.h" +@@ -265,7 +266,7 @@ struct sshbuf *loginmsg; + 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); + static void do_ssh2_kex(void); + +@@ -282,6 +283,15 @@ close_listen_socks(void) + 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) + { +@@ -488,18 +498,46 @@ sshd_exchange_identification(struct ssh + } + } + +-/* 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) + { + u_int i; ++#ifdef SSH_AUDIT_EVENTS ++ pid_t pid; ++ uid_t uid; ++ ++ pid = getpid(); ++ uid = getuid(); ++#endif + + for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { +- sshkey_free(sensitive_data.host_keys[i]); ++ char *fp; ++ ++ if (sshkey_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; ++ sshkey_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]) { + sshkey_free(sensitive_data.host_certificates[i]); + sensitive_data.host_certificates[i] = NULL; + } +@@ -513,9 +551,22 @@ demote_sensitive_data(void) + struct sshkey *tmp; + u_int i; + int r; ++#ifdef SSH_AUDIT_EVENTS ++ pid_t pid; ++ uid_t uid; + +- for (i = 0; i < options.num_host_key_files; i++) { ++ pid = getpid(); ++ uid = getuid(); ++#endif ++ ++ for (i = 0; i < options.num_host_key_files; i++) { + if (sensitive_data.host_keys[i]) { ++ char *fp; ++ ++ if (sshkey_is_private(sensitive_data.host_keys[i])) ++ fp = sshkey_fingerprint(sensitive_data.host_keys[i], options.fingerprint_hash, SSH_FP_HEX); ++ else ++ fp = NULL; + if ((r = sshkey_demote(sensitive_data.host_keys[i], + &tmp)) != 0) + fatal("could not demote host %s key: %s", +@@ -523,6 +574,12 @@ demote_sensitive_data(void) + ssh_err(r)); + sshkey_free(sensitive_data.host_keys[i]); + sensitive_data.host_keys[i] = tmp; ++ if (fp != NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_destroy_sensitive_data(fp, pid, uid); ++#endif ++ free(fp); ++ } + } + /* Certs do not need demotion */ + } +@@ -601,7 +658,7 @@ privsep_preauth(Authctxt *authctxt) + + if (use_privsep == PRIVSEP_ON) + box = ssh_sandbox_init(pmonitor); +- pid = fork(); ++ pmonitor->m_pid = pid = fork(); + if (pid == -1) { + fatal("fork of unprivileged child failed"); + } else if (pid != 0) { +@@ -1198,6 +1255,7 @@ server_accept_loop(int *sock_in, int *so + 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); +@@ -2362,6 +2420,9 @@ main(int ac, char **av) + do_authenticated(ssh, 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); +@@ -2532,6 +2593,15 @@ void + cleanup_exit(int i) + { + struct ssh *ssh = active_state; /* XXX */ ++ 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(ssh, the_authctxt); +@@ -2544,9 +2614,14 @@ cleanup_exit(int i) + 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); +Index: openssh-7.8p1/sshkey.c +=================================================================== +--- openssh-7.8p1.orig/sshkey.c ++++ openssh-7.8p1/sshkey.c +@@ -326,6 +326,32 @@ sshkey_type_is_valid_ca(int type) + } + + int ++sshkey_is_private(const struct sshkey *k) ++{ ++ switch (k->type) { ++#ifdef WITH_OPENSSL ++ case KEY_RSA_CERT: ++ 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) +Index: openssh-7.8p1/sshkey.h +=================================================================== +--- openssh-7.8p1.orig/sshkey.h ++++ openssh-7.8p1/sshkey.h +@@ -148,6 +148,7 @@ 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); diff --git a/openssh-7.7p1-blocksigalrm.patch b/openssh-7.7p1-blocksigalrm.patch new file mode 100644 index 0000000..a2a5716 --- /dev/null +++ b/openssh-7.7p1-blocksigalrm.patch @@ -0,0 +1,75 @@ +# HG changeset patch +# Parent 2e66b48b2212113d9897a58aaada67557b7c4f35 +block SIGALRM while logging through syslog to prevent deadlocks +(through grace_alarm_handler()) + +bnc#57354 + +diff --git a/openssh-7.7p1/log.c b/openssh-7.7p1/log.c +--- openssh-7.7p1/log.c ++++ openssh-7.7p1/log.c +@@ -46,16 +46,17 @@ + #include + #include + #include + #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) + # include + #endif + + #include "log.h" ++#include + + static LogLevel log_level = SYSLOG_LEVEL_INFO; + static int log_on_stderr = 1; + static int log_stderr_fd = STDERR_FILENO; + static int log_facility = LOG_AUTH; + static char *argv0; + static log_handler_fn *log_handler; + static void *log_handler_ctx; +@@ -396,16 +397,17 @@ do_log(LogLevel level, const char *fmt, + { + #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + struct syslog_data sdata = SYSLOG_DATA_INIT; + #endif + char msgbuf[MSGBUFSIZ]; + char fmtbuf[MSGBUFSIZ]; + char *txt = NULL; + int pri = LOG_INFO; ++ sigset_t nset, oset; + int saved_errno = errno; + log_handler_fn *tmp_handler; + + if (level > log_level) + return; + + switch (level) { + case SYSLOG_LEVEL_FATAL: +@@ -455,20 +457,28 @@ do_log(LogLevel level, const char *fmt, + log_handler = NULL; + tmp_handler(level, fmtbuf, log_handler_ctx); + log_handler = tmp_handler; + } else if (log_on_stderr) { + snprintf(msgbuf, sizeof msgbuf, "%.*s\r\n", + (int)sizeof msgbuf - 3, fmtbuf); + (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); + } else { ++ /* Prevent a race between the grace_alarm which writes a ++ * log message and terminates and main sshd code that leads ++ * to deadlock as syslog is not async safe. ++ */ ++ sigemptyset(&nset); ++ sigaddset(&nset, SIGALRM); ++ sigprocmask(SIG_BLOCK, &nset, &oset); + #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); + syslog_r(pri, &sdata, "%.500s", fmtbuf); + closelog_r(&sdata); + #else + openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); + syslog(pri, "%.500s", fmtbuf); + closelog(); + #endif ++ sigprocmask(SIG_SETMASK, &oset, NULL); + } + errno = saved_errno; + } diff --git a/openssh-7.7p1-cavstest-ctr.patch b/openssh-7.7p1-cavstest-ctr.patch new file mode 100644 index 0000000..88bb869 --- /dev/null +++ b/openssh-7.7p1-cavstest-ctr.patch @@ -0,0 +1,361 @@ +# HG changeset patch +# Parent cc1022edba2c5eeb0facba08468f65afc2466b63 +CAVS test for OpenSSH's own CTR encryption mode implementation + +diff --git a/openssh-7.7p1/Makefile.in b/openssh-7.7p1/Makefile.in +--- openssh-7.7p1/Makefile.in ++++ openssh-7.7p1/Makefile.in +@@ -19,16 +19,17 @@ 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 ++CAVSTEST_CTR=$(libexecdir)/cavstest-ctr + 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,16 +58,18 @@ ENT=@ENT@ + XAUTH_PATH=@XAUTH_PATH@ + LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ + EXEEXT=@EXEEXT@ + MANFMT=@MANFMT@ + MKDIR_P=@MKDIR_P@ + + 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) + ++TARGETS += cavstest-ctr$(EXEEXT) ++ + XMSS_OBJS=\ + ssh-xmss.o \ + sshkey-xmss.o \ + xmss_commons.o \ + xmss_fast.o \ + xmss_hash.o \ + xmss_hash_address.o \ + xmss_wots.o +@@ -199,16 +202,20 @@ ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libss + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(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) + ++# FIPS tests ++cavstest-ctr$(EXEEXT): $(LIBCOMPAT) libssh.a cavstest-ctr.o ++ $(LD) -o $@ cavstest-ctr.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) ++ + # test driver for the loginrec code - not built by default + logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o + $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) + + $(MANPAGES): $(MANPAGES_IN) + if test "$(MANTYPE)" = "cat"; then \ + manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \ + else \ +@@ -339,16 +346,17 @@ install-files: + $(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) + $(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 0755 $(STRIP_OPT) cavstest-ctr$(EXEEXT) $(DESTDIR)$(libexecdir)/cavstest-ctr$(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 +diff --git a/openssh-7.7p1/cavstest-ctr.c b/openssh-7.7p1/cavstest-ctr.c +new file mode 100644 +--- /dev/null ++++ openssh-7.7p1/cavstest-ctr.c +@@ -0,0 +1,214 @@ ++/* ++ * ++ * invocation (all of the following are equal): ++ * ./ctr-cavstest --algo aes128-ctr --key 987212980144b6a632e864031f52dacc --mode encrypt --data a6deca405eef2e8e4609abf3c3ccf4a6 ++ * ./ctr-cavstest --algo aes128-ctr --key 987212980144b6a632e864031f52dacc --mode encrypt --data a6deca405eef2e8e4609abf3c3ccf4a6 --iv 00000000000000000000000000000000 ++ * echo -n a6deca405eef2e8e4609abf3c3ccf4a6 | ./ctr-cavstest --algo aes128-ctr --key 987212980144b6a632e864031f52dacc --mode encrypt ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xmalloc.h" ++#include "log.h" ++#include "cipher.h" ++ ++/* compatibility with old or broken OpenSSL versions */ ++#include "openbsd-compat/openssl-compat.h" ++ ++void ++usage(void) ++{ ++ fprintf(stderr, "Usage: ctr-cavstest --algo \n" ++ " --key --mode \n" ++ " [--iv ] --data \n\n" ++ "Hexadecimal output is printed to stdout.\n" ++ "Hexadecimal input data can be alternatively read from stdin.\n"); ++ exit(1); ++} ++ ++void * ++fromhex(char *hex, size_t * len) ++{ ++ unsigned char *bin; ++ char *p; ++ size_t n = 0; ++ int shift = 4; ++ unsigned char out = 0; ++ unsigned char *optr; ++ ++ bin = xmalloc(strlen(hex) / 2); ++ optr = bin; ++ ++ for (p = hex; *p != '\0'; ++p) { ++ unsigned char c; ++ ++ c = *p; ++ if (isspace(c)) ++ continue; ++ ++ if (c >= '0' && c <= '9') { ++ c = c - '0'; ++ } else if (c >= 'A' && c <= 'F') { ++ c = c - 'A' + 10; ++ } else if (c >= 'a' && c <= 'f') { ++ c = c - 'a' + 10; ++ } else { ++ /* truncate on nonhex cipher */ ++ break; ++ } ++ ++ out |= c << shift; ++ shift = (shift + 4) % 8; ++ ++ if (shift) { ++ *(optr++) = out; ++ out = 0; ++ ++n; ++ } ++ } ++ ++ *len = n; ++ return bin; ++} ++ ++#define READ_CHUNK 4096 ++#define MAX_READ_SIZE 1024*1024*100 ++char * ++read_stdin(void) ++{ ++ char *buf; ++ size_t n, total = 0; ++ ++ buf = xmalloc(READ_CHUNK); ++ ++ do { ++ n = fread(buf + total, 1, READ_CHUNK, stdin); ++ if (n < READ_CHUNK) /* terminate on short read */ ++ break; ++ ++ total += n; ++ buf = xreallocarray(buf, total + READ_CHUNK, 1); ++ } while (total < MAX_READ_SIZE); ++ return buf; ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ ++ struct sshcipher *c; ++ struct sshcipher_ctx cc; ++ struct sshcipher_ctx *ccp; ++ char *algo = "aes128-ctr"; ++ char *hexkey = NULL; ++ char *hexiv = "00000000000000000000000000000000"; ++ char *hexdata = NULL; ++ char *p; ++ int i; ++ int encrypt = 1; ++ void *key; ++ size_t keylen; ++ void *iv; ++ size_t ivlen; ++ void *data; ++ size_t datalen; ++ void *outdata; ++ ++ for (i = 1; i < argc; ++i) { ++ if (strcmp(argv[i], "--algo") == 0) { ++ algo = argv[++i]; ++ } else if (strcmp(argv[i], "--key") == 0) { ++ hexkey = argv[++i]; ++ } else if (strcmp(argv[i], "--mode") == 0) { ++ ++i; ++ if (argv[i] == NULL) { ++ usage(); ++ } ++ if (strncmp(argv[i], "enc", 3) == 0) { ++ encrypt = 1; ++ } else if (strncmp(argv[i], "dec", 3) == 0) { ++ encrypt = 0; ++ } else { ++ usage(); ++ } ++ } else if (strcmp(argv[i], "--iv") == 0) { ++ hexiv = argv[++i]; ++ } else if (strcmp(argv[i], "--data") == 0) { ++ hexdata = argv[++i]; ++ } ++ } ++ ++ if (hexkey == NULL || algo == NULL) { ++ usage(); ++ } ++ ++ SSLeay_add_all_algorithms(); ++ ++ c = cipher_by_name(algo); ++ if (c == NULL) { ++ fprintf(stderr, "Error: unknown algorithm\n"); ++ return 2; ++ } ++ ++ if (hexdata == NULL) { ++ hexdata = read_stdin(); ++ } else { ++ hexdata = xstrdup(hexdata); ++ } ++ ++ key = fromhex(hexkey, &keylen); ++ ++ if (keylen != 16 && keylen != 24 && keylen == 32) { ++ fprintf(stderr, "Error: unsupported key length\n"); ++ return 2; ++ } ++ ++ iv = fromhex(hexiv, &ivlen); ++ ++ if (ivlen != 16) { ++ fprintf(stderr, "Error: unsupported iv length\n"); ++ return 2; ++ } ++ ++ data = fromhex(hexdata, &datalen); ++ ++ if (data == NULL || datalen == 0) { ++ fprintf(stderr, "Error: no data to encrypt/decrypt\n"); ++ return 2; ++ } ++ ++ ccp = &cc; ++ cipher_init(&ccp, c, key, keylen, iv, ivlen, encrypt); ++ ++ free(key); ++ free(iv); ++ ++ outdata = malloc(datalen); ++ if (outdata == NULL) { ++ fprintf(stderr, "Error: memory allocation failure\n"); ++ return 2; ++ } ++ ++ cipher_crypt(&cc, 0, outdata, data, datalen, 0, 0); ++ ++ free(data); ++ ++ cipher_free(&cc); ++ ++ for (p = outdata; datalen > 0; ++p, --datalen) { ++ printf("%02X", (unsigned char) *p); ++ } ++ ++ free(outdata); ++ ++ printf("\n"); ++ return 0; ++} +diff --git a/openssh-7.7p1/cipher.c b/openssh-7.7p1/cipher.c +--- openssh-7.7p1/cipher.c ++++ openssh-7.7p1/cipher.c +@@ -49,25 +49,16 @@ + #include "ssherr.h" + #include "digest.h" + + #include "openbsd-compat/openssl-compat.h" + + #include "fips.h" + #include "log.h" + +-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; +-}; +- + struct sshcipher { + char *name; + u_int block_size; + u_int key_len; + u_int iv_len; /* defaults to block_size */ + u_int auth_len; + u_int flags; + #define CFLAG_CBC (1<<0) +diff --git a/openssh-7.7p1/cipher.h b/openssh-7.7p1/cipher.h +--- openssh-7.7p1/cipher.h ++++ openssh-7.7p1/cipher.h +@@ -41,17 +41,25 @@ + #include + #include "cipher-chachapoly.h" + #include "cipher-aesctr.h" + + #define CIPHER_ENCRYPT 1 + #define CIPHER_DECRYPT 0 + + struct sshcipher; +-struct sshcipher_ctx; ++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; ++}; ++ + + const struct sshcipher *cipher_by_name(const char *); + const char *cipher_warning_message(const struct sshcipher_ctx *); + int ciphers_valid(const char *); + char *cipher_alg_list(char, int); + int cipher_init(struct sshcipher_ctx **, const struct sshcipher *, + const u_char *, u_int, const u_char *, u_int, int); + int cipher_crypt(struct sshcipher_ctx *, u_int, u_char *, const u_char *, diff --git a/openssh-7.7p1-cavstest-kdf.patch b/openssh-7.7p1-cavstest-kdf.patch new file mode 100644 index 0000000..ec7117d --- /dev/null +++ b/openssh-7.7p1-cavstest-kdf.patch @@ -0,0 +1,474 @@ +# HG changeset patch +# Parent 1e1d5a2ab8bddfc800f570755f9ea1addcc878c1 +CAVS test for KDF implementation in OpenSSH + +diff --git a/openssh-7.7p1/Makefile.in b/openssh-7.7p1/Makefile.in +--- openssh-7.7p1/Makefile.in ++++ openssh-7.7p1/Makefile.in +@@ -20,16 +20,17 @@ 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 + CAVSTEST_CTR=$(libexecdir)/cavstest-ctr ++CAVSTEST_KDF=$(libexecdir)/cavstest-kdf + 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)\" \ +@@ -58,17 +59,17 @@ ENT=@ENT@ + XAUTH_PATH=@XAUTH_PATH@ + LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ + EXEEXT=@EXEEXT@ + MANFMT=@MANFMT@ + MKDIR_P=@MKDIR_P@ + + 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) + +-TARGETS += cavstest-ctr$(EXEEXT) ++TARGETS += cavstest-ctr$(EXEEXT) cavstest-kdf$(EXEEXT) + + XMSS_OBJS=\ + ssh-xmss.o \ + sshkey-xmss.o \ + xmss_commons.o \ + xmss_fast.o \ + xmss_hash.o \ + xmss_hash_address.o \ +@@ -206,16 +207,19 @@ sftp-server$(EXEEXT): $(LIBCOMPAT) libss + + 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) + + # FIPS tests + cavstest-ctr$(EXEEXT): $(LIBCOMPAT) libssh.a cavstest-ctr.o + $(LD) -o $@ cavstest-ctr.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + ++cavstest-kdf$(EXEEXT): $(LIBCOMPAT) libssh.a cavstest-kdf.o ++ $(LD) -o $@ cavstest-kdf.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) ++ + # test driver for the loginrec code - not built by default + logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o + $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) + + $(MANPAGES): $(MANPAGES_IN) + if test "$(MANTYPE)" = "cat"; then \ + manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \ + else \ +@@ -347,16 +351,17 @@ install-files: + $(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) + $(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 0755 $(STRIP_OPT) cavstest-ctr$(EXEEXT) $(DESTDIR)$(libexecdir)/cavstest-ctr$(EXEEXT) ++ $(INSTALL) -m 0755 $(STRIP_OPT) cavstest-kdf$(EXEEXT) $(DESTDIR)$(libexecdir)/cavstest-kdf$(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 +diff --git a/openssh-7.7p1/cavstest-kdf.c b/openssh-7.7p1/cavstest-kdf.c +new file mode 100644 +--- /dev/null ++++ openssh-7.7p1/cavstest-kdf.c +@@ -0,0 +1,387 @@ ++/* ++ * Copyright (C) 2015, Stephan Mueller ++ * ++ * 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, and the entire permission notice in its entirety, ++ * including the disclaimer of warranties. ++ * 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. ++ * 3. The name of the author may not be used to endorse or promote ++ * products derived from this software without specific prior ++ * written permission. ++ * ++ * ALTERNATIVELY, this product may be distributed under the terms of ++ * the GNU General Public License, in which case the provisions of the GPL2 ++ * are required INSTEAD OF the above restrictions. (This clause is ++ * necessary due to a potential bad interaction between the GPL and ++ * the restrictions contained in a BSD-style copyright.) ++ * ++ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED ++ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF ++ * WHICH ARE HEREBY 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 NOT ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "packet.h" ++ ++static int bin_char(unsigned char hex) ++{ ++ if (48 <= hex && 57 >= hex) ++ return (hex - 48); ++ if (65 <= hex && 70 >= hex) ++ return (hex - 55); ++ if (97 <= hex && 102 >= hex) ++ return (hex - 87); ++ return 0; ++} ++ ++/* ++ * Convert hex representation into binary string ++ * @hex input buffer with hex representation ++ * @hexlen length of hex ++ * @bin output buffer with binary data ++ * @binlen length of already allocated bin buffer (should be at least ++ * half of hexlen -- if not, only a fraction of hexlen is converted) ++ */ ++static void hex2bin(const char *hex, size_t hexlen, ++ unsigned char *bin, size_t binlen) ++{ ++ size_t i = 0; ++ size_t chars = (binlen > (hexlen / 2)) ? (hexlen / 2) : binlen; ++ ++ for (i = 0; i < chars; i++) { ++ bin[i] = bin_char(hex[(i*2)]) << 4; ++ bin[i] |= bin_char(hex[((i*2)+1)]); ++ } ++} ++ ++/* ++ * Allocate sufficient space for binary representation of hex ++ * and convert hex into bin ++ * ++ * Caller must free bin ++ * @hex input buffer with hex representation ++ * @hexlen length of hex ++ * @bin return value holding the pointer to the newly allocated buffer ++ * @binlen return value holding the allocated size of bin ++ * ++ * return: 0 on success, !0 otherwise ++ */ ++static int hex2bin_alloc(const char *hex, size_t hexlen, ++ unsigned char **bin, size_t *binlen) ++{ ++ unsigned char *out = NULL; ++ size_t outlen = 0; ++ ++ if (!hexlen) ++ return -EINVAL; ++ ++ outlen = (hexlen + 1) / 2; ++ ++ out = calloc(1, outlen); ++ if (!out) ++ return -errno; ++ ++ hex2bin(hex, hexlen, out, outlen); ++ *bin = out; ++ *binlen = outlen; ++ return 0; ++} ++ ++static char hex_char_map_l[] = { '0', '1', '2', '3', '4', '5', '6', '7', ++ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; ++static char hex_char_map_u[] = { '0', '1', '2', '3', '4', '5', '6', '7', ++ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; ++static char hex_char(unsigned int bin, int u) ++{ ++ if (bin < sizeof(hex_char_map_l)) ++ return (u) ? hex_char_map_u[bin] : hex_char_map_l[bin]; ++ return 'X'; ++} ++ ++/* ++ * Convert binary string into hex representation ++ * @bin input buffer with binary data ++ * @binlen length of bin ++ * @hex output buffer to store hex data ++ * @hexlen length of already allocated hex buffer (should be at least ++ * twice binlen -- if not, only a fraction of binlen is converted) ++ * @u case of hex characters (0=>lower case, 1=>upper case) ++ */ ++static void bin2hex(const unsigned char *bin, size_t binlen, ++ char *hex, size_t hexlen, int u) ++{ ++ size_t i = 0; ++ size_t chars = (binlen > (hexlen / 2)) ? (hexlen / 2) : binlen; ++ ++ for (i = 0; i < chars; i++) { ++ hex[(i*2)] = hex_char((bin[i] >> 4), u); ++ hex[((i*2)+1)] = hex_char((bin[i] & 0x0f), u); ++ } ++} ++ ++struct kdf_cavs { ++ unsigned char *K; ++ size_t Klen; ++ unsigned char *H; ++ size_t Hlen; ++ unsigned char *session_id; ++ size_t session_id_len; ++ ++ unsigned int iv_len; ++ unsigned int ek_len; ++ unsigned int ik_len; ++}; ++ ++static int sshkdf_cavs(struct kdf_cavs *test) ++{ ++ int ret = 0; ++ struct kex kex; ++ BIGNUM *Kbn = NULL; ++ int mode = 0; ++ struct newkeys *keys_client; ++ struct newkeys *keys_server; ++ struct ssh *ssh = NULL; ++ ++#define HEXOUTLEN 500 ++ char hex[HEXOUTLEN]; ++ ++ memset(&kex, 0, sizeof(struct kex)); ++ ++ Kbn = BN_new(); ++ BN_bin2bn(test->K, test->Klen, Kbn); ++ if (!Kbn) { ++ printf("cannot convert K into BIGNUM\n"); ++ ret = 1; ++ goto out; ++ } ++ ++ kex.session_id = test->session_id; ++ kex.session_id_len = test->session_id_len; ++ ++ /* setup kex */ ++ ++ /* select the right hash based on struct ssh_digest digests */ ++ switch (test->ik_len) { ++ case 20: ++ kex.hash_alg = 2; ++ break; ++ case 32: ++ kex.hash_alg = 3; ++ break; ++ case 48: ++ kex.hash_alg = 4; ++ break; ++ case 64: ++ kex.hash_alg = 5; ++ break; ++ default: ++ printf("Wrong hash type %u\n", test->ik_len); ++ ret = 1; ++ goto out; ++ } ++ ++ /* implement choose_enc */ ++ for (mode = 0; mode < 2; mode++) { ++ kex.newkeys[mode] = calloc(1, sizeof(struct newkeys)); ++ if (!kex.newkeys[mode]) { ++ printf("allocation of newkeys failed\n"); ++ ret = 1; ++ goto out; ++ } ++ kex.newkeys[mode]->enc.iv_len = test->iv_len; ++ kex.newkeys[mode]->enc.key_len = test->ek_len; ++ kex.newkeys[mode]->enc.block_size = (test->iv_len == 64) ? 8 : 16; ++ kex.newkeys[mode]->mac.key_len = test->ik_len; ++ } ++ ++ /* implement kex_choose_conf */ ++ kex.we_need = kex.newkeys[0]->enc.key_len; ++ if (kex.we_need < kex.newkeys[0]->enc.block_size) ++ kex.we_need = kex.newkeys[0]->enc.block_size; ++ if (kex.we_need < kex.newkeys[0]->enc.iv_len) ++ kex.we_need = kex.newkeys[0]->enc.iv_len; ++ if (kex.we_need < kex.newkeys[0]->mac.key_len) ++ kex.we_need = kex.newkeys[0]->mac.key_len; ++ ++ /* MODE_OUT (1) -> server to client ++ * MODE_IN (0) -> client to server */ ++ kex.server = 1; ++ ++ /* do it */ ++ if ((ssh = ssh_packet_set_connection(NULL, -1, -1)) == NULL){ ++ printf("Allocation error\n"); ++ goto out; ++ } ++ ssh->kex = &kex; ++ kex_derive_keys_bn(ssh, test->H, test->Hlen, Kbn); ++ ++ keys_client = kex.newkeys[0]; ++ keys_server = kex.newkeys[1]; ++ ++ /* get data */ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_client->enc.iv, (size_t)keys_client->enc.iv_len, ++ hex, HEXOUTLEN, 0); ++ printf("Initial IV (client to server) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_server->enc.iv, (size_t)keys_server->enc.iv_len, ++ hex, HEXOUTLEN, 0); ++ printf("Initial IV (server to client) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_client->enc.key, (size_t)keys_client->enc.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Encryption key (client to server) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_server->enc.key, (size_t)keys_server->enc.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Encryption key (server to client) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_client->mac.key, (size_t)keys_client->mac.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Integrity key (client to server) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(keys_server->mac.key, (size_t)keys_server->mac.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Integrity key (server to client) = %s\n", hex); ++ ++ free(keys_client); ++ free(keys_server); ++ ++out: ++ if (Kbn) ++ BN_free(Kbn); ++ if (ssh) ++ ssh_packet_close(ssh); ++ if (kex.newkeys[0]) ++ free(kex.newkeys[0]); ++ if (kex.newkeys[1]) ++ free(kex.newkeys[1]); ++ return ret; ++} ++ ++static void usage(void) ++{ ++ fprintf(stderr, "\nOpenSSH KDF CAVS Test\n\n"); ++ fprintf(stderr, "Usage:\n"); ++ fprintf(stderr, "\t-K\tShared secret string\n"); ++ fprintf(stderr, "\t-H\tHash string\n"); ++ fprintf(stderr, "\t-s\tSession ID string\n"); ++ fprintf(stderr, "\t-i\tIV length to be generated\n"); ++ fprintf(stderr, "\t-e\tEncryption key length to be generated\n"); ++ fprintf(stderr, "\t-m\tMAC key length to be generated\n"); ++} ++ ++/* ++ * Test command example: ++ * ./ssh-cavs -K 0055d50f2d163cc07cd8a93cc7c3430c30ce786b572c01ad29fec7597000cf8618d664e2ec3dcbc8bb7a1a7eb7ef67f61cdaf291625da879186ac0a5cb27af571b59612d6a6e0627344d846271959fda61c78354aa498773d59762f8ca2d0215ec590d8633de921f920d41e47b3de6ab9a3d0869e1c826d0e4adebf8e3fb646a15dea20a410b44e969f4b791ed6a67f13f1b74234004d5fa5e87eff7abc32d49bbdf44d7b0107e8f10609233b7e2b7eff74a4daf25641de7553975dac6ac1e5117df6f6dbaa1c263d23a6c3e5a3d7d49ae8a828c1e333ac3f85fbbf57b5c1a45be45e43a7be1a4707eac779b8285522d1f531fe23f890fd38a004339932b93eda4 -H d3ab91a850febb417a25d892ec48ed5952c7a5de -s d3ab91a850febb417a25d892ec48ed5952c7a5de -i 8 -e 24 -m 20 ++ * ++ * Expected result for example: ++ * Initial IV (client to server) = 4bb320d1679dfd3a ++ * Encryption key (client to server) = 13048cc600b9d3cf9095aa6cf8e2ff9cf1c54ca0520c89ed ++ * Integrity key (client to server) = ecef63a092b0dcc585bdc757e01b2740af57d640 ++ * Initial IV (server to client) = 43dea6fdf263a308 ++ * Encryption key (server to client) = 1e483c5134e901aa11fc4e0a524e7ec7b75556148a222bb0 ++ * Integrity key (server to client) = 7424b05f3c44a72b4ebd281fb71f9cbe7b64d479 ++ */ ++int main(int argc, char *argv[]) ++{ ++ struct kdf_cavs test; ++ int ret = 1; ++ int opt = 0; ++ ++ memset(&test, 0, sizeof(struct kdf_cavs)); ++ while((opt = getopt(argc, argv, "K:H:s:i:e:m:")) != -1) ++ { ++ size_t len = 0; ++ switch(opt) ++ { ++ /* ++ * CAVS K is MPINT ++ * we want a hex (i.e. the caller must ensure the ++ * following transformations already happened): ++ * 1. cut off first four bytes ++ * 2. if most significant bit of value is ++ * 1, prepend 0 byte ++ */ ++ case 'K': ++ len = strlen(optarg); ++ ret = hex2bin_alloc(optarg, len, ++ &test.K, &test.Klen); ++ if (ret) ++ goto out; ++ break; ++ case 'H': ++ len = strlen(optarg); ++ ret = hex2bin_alloc(optarg, len, ++ &test.H, &test.Hlen); ++ if (ret) ++ goto out; ++ break; ++ case 's': ++ len = strlen(optarg); ++ ret = hex2bin_alloc(optarg, len, ++ &test.session_id, ++ &test.session_id_len); ++ if (ret) ++ goto out; ++ break; ++ case 'i': ++ test.iv_len = strtoul(optarg, NULL, 10); ++ break; ++ case 'e': ++ test.ek_len = strtoul(optarg, NULL, 10); ++ break; ++ case 'm': ++ test.ik_len = strtoul(optarg, NULL, 10); ++ break; ++ default: ++ usage(); ++ goto out; ++ } ++ } ++ ++ ret = sshkdf_cavs(&test); ++ ++out: ++ if (test.session_id) ++ free(test.session_id); ++ if (test.K) ++ free(test.K); ++ if (test.H) ++ free(test.H); ++ return ret; ++ ++} diff --git a/openssh-7.7p1-disable_openssl_abi_check.patch b/openssh-7.7p1-disable_openssl_abi_check.patch new file mode 100644 index 0000000..3d032ef --- /dev/null +++ b/openssh-7.7p1-disable_openssl_abi_check.patch @@ -0,0 +1,64 @@ +# HG changeset patch +# Parent b13da8c3e99081cb92ab226d2c512241a82cd0d5 +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.7p1/configure.ac b/openssh-7.7p1/configure.ac +--- openssh-7.7p1/configure.ac ++++ openssh-7.7p1/configure.ac +@@ -4895,16 +4895,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.7p1/entropy.c b/openssh-7.7p1/entropy.c +--- openssh-7.7p1/entropy.c ++++ openssh-7.7p1/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.7p1-disable_short_DH_parameters.patch b/openssh-7.7p1-disable_short_DH_parameters.patch new file mode 100644 index 0000000..8347915 --- /dev/null +++ b/openssh-7.7p1-disable_short_DH_parameters.patch @@ -0,0 +1,425 @@ +# HG changeset patch +# Parent 681914438b1a02c1940d19204138e9b8eacfda7b + +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 +and the default moduli shipped with openssh have been around long enough to +make it more likely for them to be broken. + +Also provide an option that allows the client to accept shorter (RFC4419 +compliant) parameters. + +CVE-2015-4000 (LOGJAM) +bsc#932483 + +Index: openssh-7.8p1/dh.c +=================================================================== +--- openssh-7.8p1.orig/dh.c ++++ openssh-7.8p1/dh.c +@@ -43,6 +43,8 @@ + #include "misc.h" + #include "ssherr.h" + ++int dh_grp_min = DH_GRP_MIN; ++ + static int + parse_prime(int linenum, char *line, struct dhgroup *dhg) + { +Index: openssh-7.8p1/dh.h +=================================================================== +--- openssh-7.8p1.orig/dh.h ++++ openssh-7.8p1/dh.h +@@ -50,6 +50,7 @@ u_int dh_estimate(int); + * Max value from RFC4419. + * Miniumum increased in light of DH precomputation attacks. + */ ++#define DH_GRP_MIN_RFC 1024 + #define DH_GRP_MIN 2048 + #define DH_GRP_MAX 8192 + +Index: openssh-7.8p1/kexgexc.c +=================================================================== +--- openssh-7.8p1.orig/kexgexc.c ++++ openssh-7.8p1/kexgexc.c +@@ -51,6 +51,9 @@ + #include "sshbuf.h" + #include "misc.h" + ++/* import from dh.c */ ++extern int dh_grp_min; ++ + static int input_kex_dh_gex_group(int, u_int32_t, struct ssh *); + static int input_kex_dh_gex_reply(int, u_int32_t, struct ssh *); + +@@ -63,7 +66,7 @@ kexgex_client(struct ssh *ssh) + + nbits = dh_estimate(kex->dh_need * 8); + +- kex->min = DH_GRP_MIN; ++ kex->min = dh_grp_min; + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (datafellows & SSH_BUG_DHGEX_LARGE) +@@ -108,6 +111,12 @@ input_kex_dh_gex_group(int type, u_int32 + goto out; + if ((bits = BN_num_bits(p)) < 0 || + (u_int)bits < kex->min || (u_int)bits > kex->max) { ++ if ((u_int)bits < kex->min && (u_int)bits >= DH_GRP_MIN_RFC) ++ logit("DH parameter offered by the server (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted the minimum " ++ "via the KexDHMin option.", ++ bits); + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } +Index: openssh-7.8p1/kexgexs.c +=================================================================== +--- openssh-7.8p1.orig/kexgexs.c ++++ openssh-7.8p1/kexgexs.c +@@ -54,6 +54,9 @@ + #include "sshbuf.h" + #include "misc.h" + ++/* import from dh.c */ ++extern int dh_grp_min; ++ + static int input_kex_dh_gex_request(int, u_int32_t, struct ssh *); + static int input_kex_dh_gex_init(int, u_int32_t, struct ssh *); + +@@ -82,13 +85,19 @@ input_kex_dh_gex_request(int type, u_int + kex->nbits = nbits; + kex->min = min; + kex->max = max; +- min = MAXIMUM(DH_GRP_MIN, min); ++ min = MAXIMUM(dh_grp_min, min); + max = MINIMUM(DH_GRP_MAX, max); +- nbits = MAXIMUM(DH_GRP_MIN, nbits); ++ nbits = MAXIMUM(dh_grp_min, nbits); + nbits = MINIMUM(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits || kex->max < DH_GRP_MIN) { ++ if (kex->nbits < kex->min && kex->nbits >= DH_GRP_MIN_RFC) ++ logit("DH parameter requested by the client (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted minimum " ++ "via the KexDHMin option.", ++ kex->nbits); + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } +Index: openssh-7.8p1/readconf.c +=================================================================== +--- openssh-7.8p1.orig/readconf.c ++++ openssh-7.8p1/readconf.c +@@ -67,6 +67,7 @@ + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" ++#include "dh.h" + + /* Format of the configuration file: + +@@ -167,7 +168,7 @@ typedef enum { + oTunnel, oTunnelDevice, + oLocalCommand, oPermitLocalCommand, oRemoteCommand, + oVisualHostKey, +- oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, ++ oKexAlgorithms, oKexDHMin, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, + oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, + oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, + oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, +@@ -291,6 +292,7 @@ static struct { + { "remotecommand", oRemoteCommand }, + { "visualhostkey", oVisualHostKey }, + { "kexalgorithms", oKexAlgorithms }, ++ { "kexdhmin", oKexDHMin }, + { "ipqos", oIPQoS }, + { "requesttty", oRequestTTY }, + { "proxyusefdpass", oProxyUseFdpass }, +@@ -312,6 +314,9 @@ static struct { + { NULL, oBadOption } + }; + ++/* import from dh.c */ ++extern int dh_grp_min; ++ + /* + * Adds a local TCP/IP port forward to options. Never returns if there is an + * error. +@@ -1206,6 +1211,10 @@ parse_int: + options->kex_algorithms = xstrdup(arg); + break; + ++ case oKexDHMin: ++ intptr = &options->kex_dhmin; ++ goto parse_int; ++ + case oHostKeyAlgorithms: + charptr = &options->hostkeyalgorithms; + parse_keytypes: +@@ -1835,6 +1844,7 @@ initialize_options(Options * options) + options->ciphers = NULL; + options->macs = NULL; + options->kex_algorithms = NULL; ++ options->kex_dhmin = -1; + options->hostkeyalgorithms = NULL; + options->num_identity_files = 0; + options->num_certificate_files = 0; +@@ -1988,6 +1998,13 @@ fill_default_options(Options * options) + options->connection_attempts = 1; + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; ++ if (options->kex_dhmin == -1) ++ options->kex_dhmin = DH_GRP_MIN; ++ else { ++ options->kex_dhmin = MAXIMUM(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MINIMUM(options->kex_dhmin, DH_GRP_MAX); ++ } ++ dh_grp_min = options->kex_dhmin; + /* options->hostkeyalgorithms, default set in myproposals.h */ + if (options->add_keys_to_agent == -1) + options->add_keys_to_agent = 0; +Index: openssh-7.8p1/readconf.h +=================================================================== +--- openssh-7.8p1.orig/readconf.h ++++ openssh-7.8p1/readconf.h +@@ -67,6 +67,7 @@ typedef struct { + char *macs; /* SSH2 macs in order of preference. */ + char *hostkeyalgorithms; /* SSH2 server key types in order of preference. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ ++ int kex_dhmin; /* minimum bit length of the DH group parameter */ + char *hostname; /* Real host to connect. */ + char *host_key_alias; /* hostname alias for .ssh/known_hosts */ + char *proxy_command; /* Proxy command for connecting the host. */ +Index: openssh-7.8p1/servconf.c +=================================================================== +--- openssh-7.8p1.orig/servconf.c ++++ openssh-7.8p1/servconf.c +@@ -64,6 +64,10 @@ + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "dh.h" ++ ++/* import from dh.c */ ++extern int dh_grp_min; + + static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +@@ -146,6 +150,7 @@ initialize_server_options(ServerOptions + options->ciphers = NULL; + options->macs = NULL; + options->kex_algorithms = NULL; ++ options->kex_dhmin = -1; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; +@@ -263,6 +268,14 @@ fill_default_server_options(ServerOption + if (options->use_pam_check_locks == -1) + options->use_pam_check_locks = 0; + ++ if (options->kex_dhmin == -1) ++ options->kex_dhmin = DH_GRP_MIN; ++ else { ++ options->kex_dhmin = MAXIMUM(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MINIMUM(options->kex_dhmin, DH_GRP_MAX); ++ } ++ dh_grp_min = options->kex_dhmin; ++ + /* Standard Options */ + if (options->num_host_key_files == 0) { + /* fill default hostkeys for protocols */ +@@ -490,7 +503,7 @@ typedef enum { + sHostCertificate, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, +- sKexAlgorithms, sIPQoS, sVersionAddendum, ++ sKexAlgorithms, sKexDHMin, sIPQoS, sVersionAddendum, + sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, + sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, + sStreamLocalBindMask, sStreamLocalBindUnlink, +@@ -631,6 +644,7 @@ static struct { + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, + { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, ++ { "kexdhmin", sKexDHMin }, + { "ipqos", sIPQoS, SSHCFG_ALL }, + { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, + { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, +@@ -1726,6 +1740,10 @@ process_server_config_line(ServerOptions + options->kex_algorithms = xstrdup(arg); + break; + ++ case sKexDHMin: ++ intptr = &options->kex_dhmin; ++ goto parse_int; ++ + case sSubsystem: + if (options->num_subsystems >= MAX_SUBSYSTEMS) { + fatal("%s line %d: too many subsystems defined.", +@@ -2540,6 +2558,7 @@ dump_config(ServerOptions *o) + dump_cfg_int(sClientAliveInterval, o->client_alive_interval); + dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); + dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask); ++ dump_cfg_int(sKexDHMin, o->kex_dhmin); + + /* formatted integer arguments */ + dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); +Index: openssh-7.8p1/servconf.h +=================================================================== +--- openssh-7.8p1.orig/servconf.h ++++ openssh-7.8p1/servconf.h +@@ -103,6 +103,7 @@ typedef struct { + char *ciphers; /* Supported SSH2 ciphers. */ + char *macs; /* Supported SSH2 macs. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ ++ int kex_dhmin; /* minimum bit length of the DH group parameter */ + struct ForwardOptions fwd_opts; /* forwarding options */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for system logging. */ +Index: openssh-7.8p1/ssh_config +=================================================================== +--- openssh-7.8p1.orig/ssh_config ++++ openssh-7.8p1/ssh_config +@@ -17,6 +17,11 @@ + # list of available options, their meanings and defaults, please see the + # ssh_config(5) man page. + ++# Minimum accepted size of the DH parameter p. By default this is set to 1024 ++# to maintain compatibility with RFC4419, but should be set higher. ++# Upstream default is identical to setting this to 2048. ++#KexDHMin 1024 ++ + Host * + # ForwardAgent no + # ForwardX11 no +Index: openssh-7.8p1/ssh_config.0 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.0 ++++ openssh-7.8p1/ssh_config.0 +@@ -595,6 +595,23 @@ DESCRIPTION + The list of available key exchange algorithms may also be + obtained using "ssh -Q kex". + ++ KexDHMin ++ Specifies the minimum accepted bit length of the DH group ++ parameter p. ++ ++ As per RFC4419, this is 1024 bits, however this has increasingly ++ been seen as insecure, which prompted the change to 2048 bits. ++ Setting this option allows the client to accept parameters shorter ++ than the current minimum, down to the RFC specified 1024 bits. ++ Using this option may be needed when connecting to servers that ++ only know short DH group parameters. ++ ++ Note, that while by default this option is set to 1024 to maintain ++ maximum backward compatibility, using it can severly impact ++ security and thus should be viewed as a temporary fix of last ++ resort and all efforts should be made to fix the (broken) ++ counterparty. ++ + LocalCommand + Specifies a command to execute on the local machine after + successfully connecting to the server. The command string +Index: openssh-7.8p1/ssh_config.5 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.5 ++++ openssh-7.8p1/ssh_config.5 +@@ -1025,6 +1025,22 @@ diffie-hellman-group14-sha1 + .Pp + The list of available key exchange algorithms may also be obtained using + .Qq ssh -Q kex . ++.It Cm KexDHMin ++Specifies the minimum accepted bit length of the DH group ++parameter p. ++.Pp ++As per RFC4419, this is 1024 bits, however this has increasingly ++been seen as insecure, which prompted the change to 2048 bits. ++Setting this option allows the client to accept parameters shorter ++than the current minimum, down to the RFC specified 1024 bits. ++Using this option may be needed when connecting to servers that ++only know short DH group parameters. ++.Pp ++Note, that while by default this option is set to 1024 to maintain ++maximum backward compatibility, using it can severly impact ++security and thus should be viewed as a temporary fix of last ++resort and all efforts should be made to fix the (broken) ++counterparty. + .It Cm LocalCommand + Specifies a command to execute on the local machine after successfully + connecting to the server. +Index: openssh-7.8p1/sshd_config +=================================================================== +--- openssh-7.8p1.orig/sshd_config ++++ openssh-7.8p1/sshd_config +@@ -19,6 +19,13 @@ + #HostKey /etc/ssh/ssh_host_ecdsa_key + #HostKey /etc/ssh/ssh_host_ed25519_key + ++# Minimum accepted size of the DH parameter p. The default, which replicates ++# upstream behaviour, is 2048. To maintain compatibility with RFC4419 it should ++# be set to 1024. ++# You will also need to add a HostKey directive to load a DSA key (typically ++# located in /etc/ssh/ssh_host_dsa_key), which is not loaded by default. ++#KexDHMin 2048 ++ + # Ciphers and keying + #RekeyLimit default none + +Index: openssh-7.8p1/sshd_config.0 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.0 ++++ openssh-7.8p1/sshd_config.0 +@@ -545,6 +545,23 @@ DESCRIPTION + The list of available key exchange algorithms may also be + obtained using "ssh -Q kex". + ++ KexDHMin ++ Specifies the minimum accepted bit length of the DH group ++ parameter p. ++ ++ As per RFC4419, this is 1024 bits, however this has increasingly ++ been seen as insecure, which prompted the change to 2048 bits. ++ Setting this option allows the server to accept parameters shorter ++ than the current minimum, down to the RFC specified 1024 bits. ++ Using this option may be needed when some of the connectiong ++ clients only know short DH group parameters. ++ ++ Note, that while by default this option is set to 1024 to maintain ++ maximum backward compatibility, using it can severly impact ++ security and thus should be viewed as a temporary fix of last ++ resort and all efforts should be made to fix the (broken) ++ counterparty. ++ + ListenAddress + Specifies the local addresses sshd(8) should listen on. The + following forms may be used: +Index: openssh-7.8p1/sshd_config.5 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.5 ++++ openssh-7.8p1/sshd_config.5 +@@ -912,6 +912,22 @@ diffie-hellman-group14-sha256,diffie-hel + .Pp + The list of available key exchange algorithms may also be obtained using + .Qq ssh -Q kex . ++.It Cm KexDHMin ++Specifies the minimum accepted bit length of the DH group ++parameter p. ++.Pp ++As per RFC4419, this is 1024 bits, however this has increasingly ++been seen as insecure, which prompted the change to 2048 bits. ++Setting this option allows the server to accept parameters shorter ++than the current minimum, down to the RFC specified 1024 bits. ++Using this option may be needed when some of the connectiong ++clients only know short DH group parameters. ++.Pp ++Note, that while by default this option is set to 1024 to maintain ++maximum backward compatibility, using it can severly impact ++security and thus should be viewed as a temporary fix of last ++resort and all efforts should be made to fix the (broken) ++counterparty. + .It Cm ListenAddress + Specifies the local addresses + .Xr sshd 8 diff --git a/openssh-7.7p1-eal3.patch b/openssh-7.7p1-eal3.patch new file mode 100644 index 0000000..8faf689 --- /dev/null +++ b/openssh-7.7p1-eal3.patch @@ -0,0 +1,27 @@ +# HG changeset patch +# Parent 8fd4e445c3a5c823568661a4f71b064cbcb217a9 +fix paths and references in sshd man pages + +diff --git a/openssh-7.7p1/Makefile.in b/openssh-7.7p1/Makefile.in +--- openssh-7.7p1/Makefile.in ++++ openssh-7.7p1/Makefile.in +@@ -127,17 +127,18 @@ 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' \ + -e 's|/etc/ssh/sshd_config|$(sysconfdir)/sshd_config|g' \ + -e 's|/usr/libexec|$(libexecdir)|g' \ +- -e 's|/etc/shosts.equiv|$(sysconfdir)/shosts.equiv|g' \ ++ -e 's|login\.conf|login.defs|g' \ ++ -e 's|/etc/shosts.equiv|$(sysconfdir)/ssh/shosts.equiv|g' \ + -e 's|/etc/ssh/ssh_host_key|$(sysconfdir)/ssh_host_key|g' \ + -e 's|/etc/ssh/ssh_host_ecdsa_key|$(sysconfdir)/ssh_host_ecdsa_key|g' \ + -e 's|/etc/ssh/ssh_host_dsa_key|$(sysconfdir)/ssh_host_dsa_key|g' \ + -e 's|/etc/ssh/ssh_host_rsa_key|$(sysconfdir)/ssh_host_rsa_key|g' \ + -e 's|/etc/ssh/ssh_host_ed25519_key|$(sysconfdir)/ssh_host_ed25519_key|g' \ + -e 's|/var/run/sshd.pid|$(piddir)/sshd.pid|g' \ + -e 's|/etc/moduli|$(sysconfdir)/moduli|g' \ + -e 's|/etc/ssh/moduli|$(sysconfdir)/moduli|g' \ diff --git a/openssh-7.7p1-enable_PAM_by_default.patch b/openssh-7.7p1-enable_PAM_by_default.patch new file mode 100644 index 0000000..61576cd --- /dev/null +++ b/openssh-7.7p1-enable_PAM_by_default.patch @@ -0,0 +1,28 @@ +# HG changeset patch +# Parent 5c1e122e31b601de64d81085294216af33f31aed +# 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.7p1/sshd_config b/openssh-7.7p1/sshd_config +--- openssh-7.7p1/sshd_config ++++ openssh-7.7p1/sshd_config +@@ -74,17 +74,17 @@ AuthorizedKeysFile .ssh/authorized_keys + # and session processing. If this is enabled, PAM authentication will + # be allowed through the ChallengeResponseAuthentication and + # PasswordAuthentication. Depending on your PAM configuration, + # PAM authentication via ChallengeResponseAuthentication may bypass + # the setting of "PermitRootLogin without-password". + # If you just want the PAM account and session checks to run without + # PAM authentication, then enable this but set PasswordAuthentication + # and ChallengeResponseAuthentication to 'no'. +-#UsePAM no ++UsePAM yes + + #AllowAgentForwarding yes + #AllowTcpForwarding yes + #GatewayPorts no + X11Forwarding yes + #X11DisplayOffset 10 + #X11UseLocalhost yes + #PermitTTY yes diff --git a/openssh-7.7p1-fips.patch b/openssh-7.7p1-fips.patch new file mode 100644 index 0000000..9de361f --- /dev/null +++ b/openssh-7.7p1-fips.patch @@ -0,0 +1,1010 @@ +# HG changeset patch +# Parent 92d953171b34f6fba18d24085eeeaa24b1d2d5b5 +FIPS 140-2 compliance. Perform selftests on start and use only FIPS approved +algorithms. + +Index: openssh-7.8p1/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/Makefile.in ++++ openssh-7.8p1/Makefile.in +@@ -102,6 +102,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ + platform-pledge.o platform-tracing.o platform-misc.o + ++LIBSSH_OBJS += fips.o ++ + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o + +Index: openssh-7.8p1/cipher-ctr.c +=================================================================== +--- openssh-7.8p1.orig/cipher-ctr.c ++++ openssh-7.8p1/cipher-ctr.c +@@ -27,6 +27,8 @@ + #include "xmalloc.h" + #include "log.h" + ++#include "fips.h" ++ + /* compatibility with old or broken OpenSSL versions */ + #include "openbsd-compat/openssl-compat.h" + +@@ -139,6 +141,8 @@ evp_aes_128_ctr(void) + #ifndef SSH_OLD_EVP + aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | + EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; ++ if (fips_mode()) ++ aes_ctr.flags |= EVP_CIPH_FLAG_FIPS; + #endif + return (&aes_ctr); + } +Index: openssh-7.8p1/cipher.c +=================================================================== +--- openssh-7.8p1.orig/cipher.c ++++ openssh-7.8p1/cipher.c +@@ -51,6 +51,8 @@ + + #include "openbsd-compat/openssl-compat.h" + ++#include "fips.h" ++#include "log.h" + + struct sshcipher_ctx { + int plaintext; +@@ -80,7 +82,7 @@ struct sshcipher { + #endif + }; + +-static const struct sshcipher ciphers[] = { ++static const struct sshcipher ciphers_all[] = { + #ifdef WITH_OPENSSL + #ifndef OPENSSL_NO_DES + { "3des-cbc", 8, 24, 0, 0, CFLAG_CBC, EVP_des_ede3_cbc }, +@@ -111,8 +113,52 @@ static const struct sshcipher ciphers[] + { NULL, 0, 0, 0, 0, 0, NULL } + }; + ++static const struct sshcipher ciphers_fips140_2[] = { ++#ifdef WITH_OPENSSL ++ { "aes128-cbc", 16, 16, 0, 0, CFLAG_CBC, EVP_aes_128_cbc }, ++ { "aes192-cbc", 16, 24, 0, 0, CFLAG_CBC, EVP_aes_192_cbc }, ++ { "aes256-cbc", 16, 32, 0, 0, CFLAG_CBC, EVP_aes_256_cbc }, ++ { "rijndael-cbc@lysator.liu.se", ++ 16, 32, 0, 0, CFLAG_CBC, EVP_aes_256_cbc }, ++ { "aes128-ctr", 16, 16, 0, 0, 0, EVP_aes_128_ctr }, ++ { "aes192-ctr", 16, 24, 0, 0, 0, EVP_aes_192_ctr }, ++ { "aes256-ctr", 16, 32, 0, 0, 0, EVP_aes_256_ctr }, ++# ifdef OPENSSL_HAVE_EVPGCM ++ { "aes128-gcm@openssh.com", ++ 16, 16, 12, 16, 0, EVP_aes_128_gcm }, ++ { "aes256-gcm@openssh.com", ++ 16, 32, 12, 16, 0, EVP_aes_256_gcm }, ++# endif /* OPENSSL_HAVE_EVPGCM */ ++#else ++ { "aes128-ctr", 16, 16, 0, 0, CFLAG_AESCTR, NULL }, ++ { "aes192-ctr", 16, 24, 0, 0, CFLAG_AESCTR, NULL }, ++ { "aes256-ctr", 16, 32, 0, 0, CFLAG_AESCTR, NULL }, ++#endif ++ { "none", 8, 0, 0, 0, CFLAG_NONE, NULL }, ++ ++ { NULL, 0, 0, 0, 0, 0, NULL } ++}; ++ + /*--*/ + ++/* Returns array of ciphers available depending on selected FIPS mode */ ++static const struct sshcipher * ++fips_select_ciphers(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return ciphers_all; ++ case 1: ++ return ciphers_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ return NULL; ++ } ++} ++ + /* Returns a comma-separated list of supported ciphers. */ + char * + cipher_alg_list(char sep, int auth_only) +@@ -121,7 +167,7 @@ cipher_alg_list(char sep, int auth_only) + size_t nlen, rlen = 0; + const struct sshcipher *c; + +- for (c = ciphers; c->name != NULL; c++) { ++ for (c = fips_select_ciphers(); c->name != NULL; c++) { + if ((c->flags & CFLAG_INTERNAL) != 0) + continue; + if (auth_only && c->auth_len == 0) +@@ -193,7 +239,7 @@ const struct sshcipher * + cipher_by_name(const char *name) + { + const struct sshcipher *c; +- for (c = ciphers; c->name != NULL; c++) ++ for (c = fips_select_ciphers(); c->name != NULL; c++) + if (strcmp(c->name, name) == 0) + return c; + return NULL; +Index: openssh-7.8p1/dh.h +=================================================================== +--- openssh-7.8p1.orig/dh.h ++++ openssh-7.8p1/dh.h +@@ -52,6 +52,7 @@ u_int dh_estimate(int); + */ + #define DH_GRP_MIN_RFC 1024 + #define DH_GRP_MIN 2048 ++#define DH_GRP_MIN_FIPS 2048 + #define DH_GRP_MAX 8192 + + /* +Index: openssh-7.8p1/fips.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/fips.c +@@ -0,0 +1,237 @@ ++/* ++ * Copyright (c) 2012 Petr Cerny. 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 "includes.h" ++ ++#include "fips.h" ++ ++#include "cipher.h" ++#include "dh.h" ++#include "digest.h" ++#include "kex.h" ++#include "sshkey.h" ++#include "mac.h" ++#include "log.h" ++#include "xmalloc.h" ++ ++#include ++#include ++ ++/* import from dh.c */ ++extern int dh_grp_min; ++ ++static int fips_state = -1; ++ ++static int ++fips_check_required_env(void) ++{ ++ int fips_required = 0; ++ char *env = getenv(SSH_FORCE_FIPS_ENV); ++ ++ if (env) { ++ errno = 0; ++ fips_required = strtol(env, NULL, 10); ++ if (errno) { ++ debug("bogus value in the %s environment variable, ignoring\n" ++ , SSH_FORCE_FIPS_ENV); ++ fips_required = 0; ++ } else ++ fips_required = 1; ++ } ++ return fips_required; ++} ++ ++int ++fips_mode(void) ++{ ++ if (-1 == fips_state) { ++ fips_state = FIPS_mode(); ++ if (fips_state) ++ debug("FIPS mode initialized"); ++ else { ++ if (fips_check_required_env()) { ++ debug("FIPS mode requested through the environment variable '%s'" ++ , SSH_FORCE_FIPS_ENV); ++ if (!FIPS_mode_set(1)) ++ fatal("Unable to enter FIPS mode as requested through the environment variable '%s'" ++ , SSH_FORCE_FIPS_ENV); ++ fips_state = 1; ++ } ++ } ++ } ++ return fips_state; ++} ++ ++int ++fips_correct_dgst(int digest) ++{ ++ int fips; ++ int rv = -1; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ rv = digest; ++ break; ++ case 1: ++ switch (digest) { ++ case SSH_DIGEST_MD5: ++ case SSH_DIGEST_SHA1: ++ debug("MD5/RIPEMD160 digests not allowed in FIPS 140-2 mode" ++ "using SHA-256 instead."); ++ rv = SSH_DIGEST_SHA256; ++ break; ++ default: ++ rv = digest; ++ break; ++ } ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ ++ return rv; ++} ++ ++/* ++ * filter out FIPS disallowed algorithms ++ * *crypto MUST be free()-able - it is assigned newly allocated memory and ++ * the previous one is freed ++ * ++ * returns zero if all algorithms were rejected, non-zero otherwise ++ */ ++int ++fips_filter_crypto(char **crypto, fips_filters filter) ++{ ++ char *token, *tmp, *tmp_sav, *new; ++ int plus = 0; ++ int valid; ++ int comma = 0; ++ int empty = 1; ++ size_t len; ++ ++ tmp = tmp_sav = xstrdup(*crypto); ++ ++ len = strlen(tmp) + 1; ++ new = xcalloc(1, len); ++ ++ if ('+' == *tmp) { ++ plus = 1; ++ tmp++; ++ } ++ ++ while ((token = strsep(&tmp, ",")) != NULL) { ++ switch(filter) { ++ case FIPS_FILTER_CIPHERS: ++ valid = ciphers_valid(token); ++ if (!valid) ++ debug("Cipher '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ case FIPS_FILTER_MACS: ++ valid = mac_valid(token); ++ if (!valid) ++ debug("MAC '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ case FIPS_FILTER_KEX_ALGS: ++ valid = kex_names_valid(token); ++ if (!valid) ++ debug("KEX '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS filter '%i' requested at %s:%u", ++ filter, __FILE__, __LINE__); ++ } ++ ++ if (valid) { ++ empty = 0; ++ if (plus) { ++ strlcat(new, "+", len); ++ plus = 0; ++ } ++ if (comma) ++ strlcat(new, ",", len); ++ else ++ comma = 1; ++ strlcat(new, token, len); ++ } ++ } ++ ++ /* free tmp and re-allocate shorter buffer for result if necessary */ ++ free(tmp_sav); ++ free(*crypto); ++ *crypto = new; ++ ++ return (!empty); ++} ++ ++int ++fips_dgst_min(void) ++{ ++ int fips; ++ int dgst; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ dgst = SSH_DIGEST_MD5; ++ break; ++ case 1: ++ dgst = SSH_DIGEST_SHA256; ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ return dgst; ++} ++ ++int ++fips_dh_grp_min(void) ++{ ++ int fips; ++ int dh; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ dh = dh_grp_min; ++ break; ++ case 1: ++ dh = DH_GRP_MIN_FIPS; ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ return dh; ++} ++ +Index: openssh-7.8p1/fips.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/fips.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2012 Petr Cerny. 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 FIPS_H ++#define FIPS_H ++ ++#include "sshkey.h" ++ ++#define SSH_FORCE_FIPS_ENV "SSH_FORCE_FIPS" ++ ++typedef enum { ++ FIPS_FILTER_CIPHERS, ++ FIPS_FILTER_MACS, ++ FIPS_FILTER_KEX_ALGS ++} fips_filters; ++ ++int fips_mode(void); ++int fips_correct_dgst(int); ++int fips_dgst_min(void); ++int fips_dh_grp_min(void); ++enum fp_type fips_correct_fp_type(enum fp_type); ++int fips_filter_crypto(char **, fips_filters); ++ ++#endif ++ +Index: openssh-7.8p1/hmac.c +=================================================================== +--- openssh-7.8p1.orig/hmac.c ++++ openssh-7.8p1/hmac.c +@@ -144,7 +144,7 @@ hmac_test(void *key, size_t klen, void * + size_t i; + u_char digest[16]; + +- if ((ctx = ssh_hmac_start(SSH_DIGEST_MD5)) == NULL) ++ if ((ctx = ssh_hmac_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL) + printf("ssh_hmac_start failed"); + if (ssh_hmac_init(ctx, key, klen) < 0 || + ssh_hmac_update(ctx, m, mlen) < 0 || +Index: openssh-7.8p1/kex.c +=================================================================== +--- openssh-7.8p1.orig/kex.c ++++ openssh-7.8p1/kex.c +@@ -54,6 +54,8 @@ + #include "sshbuf.h" + #include "digest.h" + ++#include "fips.h" ++ + /* prototype */ + static int kex_choose_conf(struct ssh *); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); +@@ -77,7 +79,7 @@ struct kexalg { + int ec_nid; + int hash_alg; + }; +-static const struct kexalg kexalgs[] = { ++static const struct kexalg kexalgs_all[] = { + #ifdef WITH_OPENSSL + { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, +@@ -106,6 +108,47 @@ static const struct kexalg kexalgs[] = { + { NULL, -1, -1, -1}, + }; + ++static const struct kexalg kexalgs_fips140_2[] = { ++#ifdef WITH_OPENSSL ++ { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, ++ { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, ++ { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 }, ++ { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++#ifdef HAVE_EVP_SHA256 ++ { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, ++#endif /* HAVE_EVP_SHA256 */ ++#ifdef OPENSSL_HAS_ECC ++ { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, ++ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, ++ { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, ++ SSH_DIGEST_SHA384 }, ++# ifdef OPENSSL_HAS_NISTP521 ++ { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, ++ SSH_DIGEST_SHA512 }, ++# endif /* OPENSSL_HAS_NISTP521 */ ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ { NULL, -1, -1, -1}, ++}; ++ ++/* Returns array of macs available depending on selected FIPS mode */ ++static const struct kexalg * ++fips_select_kexalgs(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return kexalgs_all; ++ case 1: ++ return kexalgs_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++} ++ + char * + kex_alg_list(char sep) + { +@@ -113,7 +156,7 @@ kex_alg_list(char sep) + size_t nlen, rlen = 0; + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); +@@ -133,7 +176,7 @@ kex_alg_by_name(const char *name) + { + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; + } +@@ -153,7 +196,10 @@ kex_names_valid(const char *names) + for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + if (kex_alg_by_name(p) == NULL) { ++ /* do not complain here - MACs and ciphers checks ++ * are silent here + error("Unsupported KEX algorithm \"%.100s\"", p); ++ */ + free(s); + return 0; + } +Index: openssh-7.8p1/kexgexc.c +=================================================================== +--- openssh-7.8p1.orig/kexgexc.c ++++ openssh-7.8p1/kexgexc.c +@@ -51,8 +51,7 @@ + #include "sshbuf.h" + #include "misc.h" + +-/* import from dh.c */ +-extern int dh_grp_min; ++#include "fips.h" + + static int input_kex_dh_gex_group(int, u_int32_t, struct ssh *); + static int input_kex_dh_gex_reply(int, u_int32_t, struct ssh *); +@@ -66,7 +65,7 @@ kexgex_client(struct ssh *ssh) + + nbits = dh_estimate(kex->dh_need * 8); + +- kex->min = dh_grp_min; ++ kex->min = fips_dh_grp_min(); + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (datafellows & SSH_BUG_DHGEX_LARGE) +Index: openssh-7.8p1/kexgexs.c +=================================================================== +--- openssh-7.8p1.orig/kexgexs.c ++++ openssh-7.8p1/kexgexs.c +@@ -54,8 +54,7 @@ + #include "sshbuf.h" + #include "misc.h" + +-/* import from dh.c */ +-extern int dh_grp_min; ++#include "fips.h" + + static int input_kex_dh_gex_request(int, u_int32_t, struct ssh *); + static int input_kex_dh_gex_init(int, u_int32_t, struct ssh *); +@@ -85,9 +84,9 @@ input_kex_dh_gex_request(int type, u_int + kex->nbits = nbits; + kex->min = min; + kex->max = max; +- min = MAXIMUM(dh_grp_min, min); ++ min = MAXIMUM(fips_dh_grp_min(), min); + max = MINIMUM(DH_GRP_MAX, max); +- nbits = MAXIMUM(dh_grp_min, nbits); ++ nbits = MAXIMUM(fips_dh_grp_min(), nbits); + nbits = MINIMUM(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || +Index: openssh-7.8p1/mac.c +=================================================================== +--- openssh-7.8p1.orig/mac.c ++++ openssh-7.8p1/mac.c +@@ -40,6 +40,9 @@ + + #include "openbsd-compat/openssl-compat.h" + ++#include "fips.h" ++#include "log.h" ++ + #define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ + #define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ + #define SSH_UMAC128 3 +@@ -54,7 +57,7 @@ struct macalg { + int etm; /* Encrypt-then-MAC */ + }; + +-static const struct macalg macs[] = { ++static const struct macalg macs_all[] = { + /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ + { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, + { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, +@@ -82,6 +85,41 @@ static const struct macalg macs[] = { + { NULL, 0, 0, 0, 0, 0, 0 } + }; + ++static const struct macalg macs_fips140_2[] = { ++ /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ ++ { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, ++#ifdef HAVE_EVP_SHA256 ++ { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, ++ { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, ++#endif ++ ++ /* Encrypt-then-MAC variants */ ++ { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, ++#ifdef HAVE_EVP_SHA256 ++ { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, ++ { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, ++#endif ++ ++ { NULL, 0, 0, 0, 0, 0, 0 } ++}; ++ ++/* Returns array of macs available depending on selected FIPS mode */ ++static const struct macalg * ++fips_select_macs(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return macs_all; ++ case 1: ++ return macs_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++} ++ + /* Returns a list of supported MACs separated by the specified char. */ + char * + mac_alg_list(char sep) +@@ -90,7 +128,7 @@ mac_alg_list(char sep) + size_t nlen, rlen = 0; + const struct macalg *m; + +- for (m = macs; m->name != NULL; m++) { ++ for (m = fips_select_macs(); m->name != NULL; m++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(m->name); +@@ -129,7 +167,7 @@ mac_setup(struct sshmac *mac, char *name + { + const struct macalg *m; + +- for (m = macs; m->name != NULL; m++) { ++ for (m = fips_select_macs(); m->name != NULL; m++) { + if (strcmp(name, m->name) != 0) + continue; + if (mac != NULL) +Index: openssh-7.8p1/myproposal.h +=================================================================== +--- openssh-7.8p1.orig/myproposal.h ++++ openssh-7.8p1/myproposal.h +@@ -141,6 +141,8 @@ + + #else /* WITH_OPENSSL */ + ++#error "OpenSSL support is needed for FIPS mode to compile" ++ + #define KEX_SERVER_KEX \ + "curve25519-sha256," \ + "curve25519-sha256@libssh.org" +Index: openssh-7.8p1/readconf.c +=================================================================== +--- openssh-7.8p1.orig/readconf.c ++++ openssh-7.8p1/readconf.c +@@ -68,6 +68,7 @@ + #include "myproposal.h" + #include "digest.h" + #include "dh.h" ++#include "fips.h" + + /* Format of the configuration file: + +@@ -1800,6 +1801,23 @@ option_clear_or_none(const char *o) + return o == NULL || strcasecmp(o, "none") == 0; + } + ++/* remove algorithms not approved for use in FIPS mode, when running in FIPS ++ * mode ++ */ ++void ++filter_fips_algorithms(Options *o) ++{ ++ if (fips_mode()) { ++ if (!fips_filter_crypto(&o->ciphers, FIPS_FILTER_CIPHERS)) ++ fatal("None of selected ciphers can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->macs, FIPS_FILTER_MACS)) ++ fatal("None of selected MAC algorithms can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->kex_algorithms, FIPS_FILTER_KEX_ALGS)) ++ fatal("None of selected KEX algorithms can be used in FIPS mode"); ++ } ++ return; ++} ++ + /* + * 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 +@@ -1999,9 +2017,9 @@ fill_default_options(Options * options) + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; + if (options->kex_dhmin == -1) +- options->kex_dhmin = DH_GRP_MIN; ++ options->kex_dhmin = fips_dh_grp_min(); + else { +- options->kex_dhmin = MAXIMUM(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MAXIMUM(options->kex_dhmin, fips_dh_grp_min()); + options->kex_dhmin = MINIMUM(options->kex_dhmin, DH_GRP_MAX); + } + dh_grp_min = options->kex_dhmin; +@@ -2086,6 +2104,8 @@ fill_default_options(Options * options) + options->canonicalize_hostname = SSH_CANONICALISE_NO; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; ++ options->fingerprint_hash = ++ fips_correct_dgst(options->fingerprint_hash); + if (options->update_hostkeys == -1) + options->update_hostkeys = 0; + +@@ -2110,6 +2130,7 @@ fill_default_options(Options * options) + free(all_mac); + free(all_kex); + free(all_key); ++ filter_fips_algorithms(options); + + #define CLEAR_ON_NONE(v) \ + do { \ +Index: openssh-7.8p1/readconf.h +=================================================================== +--- openssh-7.8p1.orig/readconf.h ++++ openssh-7.8p1/readconf.h +@@ -197,6 +197,7 @@ typedef struct { + #define SSH_STRICT_HOSTKEY_YES 2 + #define SSH_STRICT_HOSTKEY_ASK 3 + ++void filter_fips_algorithms(Options *o); + void initialize_options(Options *); + void fill_default_options(Options *); + void fill_default_options_for_canonicalization(Options *); +Index: openssh-7.8p1/servconf.c +=================================================================== +--- openssh-7.8p1.orig/servconf.c ++++ openssh-7.8p1/servconf.c +@@ -65,6 +65,7 @@ + #include "myproposal.h" + #include "digest.h" + #include "dh.h" ++#include "fips.h" + + /* import from dh.c */ + extern int dh_grp_min; +@@ -194,6 +195,23 @@ option_clear_or_none(const char *o) + return o == NULL || strcasecmp(o, "none") == 0; + } + ++/* remove algorithms not approved for use in FIPS mode, when running in FIPS ++ * mode ++ */ ++static void ++filter_fips_algorithms_s(ServerOptions *o) ++{ ++ if (fips_mode()) { ++ if (!fips_filter_crypto(&o->ciphers, FIPS_FILTER_CIPHERS)) ++ fatal("None of selected ciphers can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->macs, FIPS_FILTER_MACS)) ++ fatal("None of selected MAC algorithms can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->kex_algorithms, FIPS_FILTER_KEX_ALGS)) ++ fatal("None of selected KEX algorithms can be used in FIPS mode"); ++ } ++ return; ++} ++ + static void + assemble_algorithms(ServerOptions *o) + { +@@ -220,6 +238,8 @@ assemble_algorithms(ServerOptions *o) + free(all_mac); + free(all_kex); + free(all_key); ++ ++ filter_fips_algorithms_s(o); + } + + static void +@@ -269,9 +289,9 @@ fill_default_server_options(ServerOption + options->use_pam_check_locks = 0; + + if (options->kex_dhmin == -1) +- options->kex_dhmin = DH_GRP_MIN; ++ options->kex_dhmin = fips_dh_grp_min(); + else { +- options->kex_dhmin = MAXIMUM(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MAXIMUM(options->kex_dhmin, fips_dh_grp_min()); + options->kex_dhmin = MINIMUM(options->kex_dhmin, DH_GRP_MAX); + } + dh_grp_min = options->kex_dhmin; +@@ -419,6 +439,8 @@ fill_default_server_options(ServerOption + options->fwd_opts.streamlocal_bind_unlink = 0; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; ++ options->fingerprint_hash = ++ fips_correct_dgst(options->fingerprint_hash); + if (options->disable_forwarding == -1) + options->disable_forwarding = 0; + if (options->expose_userauth_info == -1) +Index: openssh-7.8p1/ssh-keygen.c +=================================================================== +--- openssh-7.8p1.orig/ssh-keygen.c ++++ openssh-7.8p1/ssh-keygen.c +@@ -61,6 +61,8 @@ + #include "utf8.h" + #include "authfd.h" + ++#include "fips.h" ++ + #ifdef WITH_OPENSSL + # define DEFAULT_KEY_TYPE_NAME "rsa" + #else +@@ -965,11 +967,13 @@ do_fingerprint(struct passwd *pw) + static void + do_gen_all_hostkeys(struct passwd *pw) + { +- struct { ++ struct Key_types { + char *key_type; + char *key_type_display; + char *path; +- } key_types[] = { ++ }; ++ ++ struct Key_types key_types_all[] = { + #ifdef WITH_OPENSSL + { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, + { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, +@@ -984,6 +988,17 @@ do_gen_all_hostkeys(struct passwd *pw) + { NULL, NULL, NULL } + }; + ++ struct Key_types key_types_fips140_2[] = { ++#ifdef WITH_OPENSSL ++ { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, ++#ifdef OPENSSL_HAS_ECC ++ { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ { NULL, NULL, NULL } ++ }; ++ ++ struct Key_types *key_types; + int first = 0; + struct stat st; + struct sshkey *private, *public; +@@ -991,6 +1006,12 @@ do_gen_all_hostkeys(struct passwd *pw) + int i, type, fd, r; + FILE *f; + ++ if (fips_mode()) { ++ key_types = key_types_fips140_2; ++ } else { ++ key_types = key_types_all; ++ } ++ + for (i = 0; key_types[i].key_type; i++) { + public = private = NULL; + prv_tmp = pub_tmp = prv_file = pub_file = NULL; +@@ -2727,6 +2748,15 @@ main(int argc, char **argv) + key_type_name = DEFAULT_KEY_TYPE_NAME; + + type = sshkey_type_from_name(key_type_name); ++ ++ /* protocol v1 is not allowed in FIPS mode, DSA is not acceptable because ++ * it has to be 1024 bit due to RFC 4253 using SHA-1 which implies 1024 bit ++ * keys due to FIPS-186 specification for DSS */ ++ if (fips_mode() && ++ (type == KEY_DSA || type == KEY_ED25519 || ++ type == KEY_DSA_CERT || type == KEY_ED25519_CERT)) ++ fatal("Key type %s not alowed in FIPS mode", key_type_name); ++ + type_bits_valid(type, key_type_name, &bits); + + if (!quiet) +Index: openssh-7.8p1/ssh_config.0 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.0 ++++ openssh-7.8p1/ssh_config.0 +@@ -343,6 +343,9 @@ DESCRIPTION + Specifies the hash algorithm used when displaying key + fingerprints. Valid options are: md5 and sha256 (the default). + ++ In the FIPS mode the minimum of SHA-1 is enforced (which means ++ sha256). ++ + ForwardAgent + Specifies whether the connection to the authentication agent (if + any) will be forwarded to the remote machine. The argument must +@@ -612,6 +615,9 @@ DESCRIPTION + resort and all efforts should be made to fix the (broken) + counterparty. + ++ In the FIPS mode the FIPS standard takes precedence over RFC and ++ forces the minimum to a higher value, currently 2048 bits. ++ + LocalCommand + Specifies a command to execute on the local machine after + successfully connecting to the server. The command string +Index: openssh-7.8p1/ssh_config.5 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.5 ++++ openssh-7.8p1/ssh_config.5 +@@ -628,6 +628,8 @@ Valid options are: + and + .Cm sha256 + (the default). ++.Pp ++In the FIPS mode the minimum of SHA-1 is enforced (which means sha256). + .It Cm ForwardAgent + Specifies whether the connection to the authentication agent (if any) + will be forwarded to the remote machine. +@@ -1041,6 +1043,9 @@ maximum backward compatibility, using it + security and thus should be viewed as a temporary fix of last + resort and all efforts should be made to fix the (broken) + counterparty. ++.Pp ++In the FIPS mode the FIPS standard takes precedence over RFC and ++forces the minimum to a higher value, currently 2048 bits. + .It Cm LocalCommand + Specifies a command to execute on the local machine after successfully + connecting to the server. +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -123,6 +123,8 @@ + #include "version.h" + #include "ssherr.h" + ++#include "fips.h" ++ + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +Index: openssh-7.8p1/sshd_config.0 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.0 ++++ openssh-7.8p1/sshd_config.0 +@@ -338,6 +338,9 @@ DESCRIPTION + Specifies the hash algorithm used when logging key fingerprints. + Valid options are: md5 and sha256. The default is sha256. + ++ In the FIPS mode the minimum of SHA-1 is enforced (which means ++ sha256). ++ + ForceCommand + Forces the execution of the command specified by ForceCommand, + ignoring any command supplied by the client and ~/.ssh/rc if +@@ -562,6 +565,9 @@ DESCRIPTION + resort and all efforts should be made to fix the (broken) + counterparty. + ++ In the FIPS mode the FIPS standard takes precedence over RFC and ++ forces the minimum to a higher value, currently 2048 bits. ++ + ListenAddress + Specifies the local addresses sshd(8) should listen on. The + following forms may be used: +Index: openssh-7.8p1/sshd_config.5 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.5 ++++ openssh-7.8p1/sshd_config.5 +@@ -592,6 +592,8 @@ and + .Cm sha256 . + The default is + .Cm sha256 . ++.Pp ++In the FIPS mode the minimum of SHA-1 is enforced (which means sha256). + .It Cm ForceCommand + Forces the execution of the command specified by + .Cm ForceCommand , diff --git a/openssh-7.7p1-fips_checks.patch b/openssh-7.7p1-fips_checks.patch new file mode 100644 index 0000000..9a13bd0 --- /dev/null +++ b/openssh-7.7p1-fips_checks.patch @@ -0,0 +1,479 @@ +# HG changeset patch +# Parent e9b69da9a0f8dca923f8fc2836b38fe6590c791a +# +# Simple implementation of FIPS 140-2 selfchecks. Use OpenSSL to generate and +# verify checksums of binaries. Any hash iused in OpenSSH can be used (MD5 would +# obviously be a poor choice, since OpenSSL would barf and abort immediately in +# FIPS mode). SHA-2 seems to be a reasonable choice. +# +# The logic of the checks is as follows: decide whether FIPS mode is mandated +# (either by checking /proc/sys/crypto/fips_enabled or envoroinment variable +# SSH_FORCE_FIPS. In FIPS mode, checksums are required to match (inability to +# retrieve pre-calculated hash is a fatal error). In non-FIPS mode the checks +# still must be performed, unless the hashes are not installed. Thus if the hash +# file is not found (or the hash matches), proceed in non-FIPS mode and abort +# otherwise. + +Index: openssh-7.8p1/fips-check.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/fips-check.c +@@ -0,0 +1,34 @@ ++#include "includes.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "digest.h" ++#include "fips.h" ++ ++#include ++ ++#define PROC_NAME_LEN 64 ++ ++static const char *argv0; ++ ++void ++print_help_exit(int ev) ++{ ++ fprintf(stderr, "%s <-c|-w> \n", argv0); ++ fprintf(stderr, " -c verify hash of 'file' against hash in 'checksum_file'\n"); ++ fprintf(stderr, " -w write hash of 'file' into 'checksum_file'\n"); ++ exit(ev); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ fips_ssh_init(); ++ return 0; ++} +Index: openssh-7.8p1/fips.c +=================================================================== +--- openssh-7.8p1.orig/fips.c ++++ openssh-7.8p1/fips.c +@@ -35,33 +35,296 @@ + #include "log.h" + #include "xmalloc.h" + ++#include ++#include + #include ++#include ++#include ++#include ++#include + #include ++#include ++#include + + /* import from dh.c */ + extern int dh_grp_min; + + static int fips_state = -1; + ++/* calculates HMAC of contents of a file given by filename using the hash ++ * algorithm specified by FIPS_HMAC_EVP in fips.h and placing the result into ++ * newly allacated memory - remember to free it when not needed anymore */ + static int +-fips_check_required_env(void) ++hmac_file(const char *filename, u_char **hmac_out) ++{ ++ int check = -1; ++ int fd; ++ struct stat fs; ++ void *hmap; ++ unsigned char *hmac; ++ unsigned char *hmac_rv = NULL; ++ ++ hmac = xmalloc(FIPS_HMAC_LEN); ++ ++ fd = open(filename, O_RDONLY); ++ if (-1 == fd) ++ goto bail_out; ++ ++ if (-1 == fstat(fd, &fs)) ++ goto bail_out; ++ ++ hmap = mmap(NULL, fs.st_size, PROT_READ, MAP_SHARED, fd, 0); ++ ++ if ((void *)(-1) != hmap) { ++ hmac_rv = HMAC(FIPS_HMAC_EVP(), FIPS_HMAC_KEY ++ , strlen(FIPS_HMAC_KEY), hmap, fs.st_size, hmac, NULL); ++ check = CHECK_OK; ++ munmap(hmap, fs.st_size); ++ } ++ close(fd); ++ ++bail_out: ++ if (hmac_rv) { ++ check = CHECK_OK; ++ *hmac_out = hmac; ++ } else { ++ check = CHECK_FAIL; ++ *hmac_out = NULL; ++ free(hmac); ++ } ++ return check; ++} ++ ++/* find pathname of binary of process with PID pid. exe is buffer expected to ++ * be capable of holding at least max_pathlen characters ++ */ ++static int ++get_executable_path(pid_t pid, char *exe, int max_pathlen) ++{ ++ char exe_sl[PROC_EXE_PATH_LEN]; ++ int n; ++ int rv = -1; ++ ++ n = snprintf(exe_sl, sizeof(exe_sl), "/proc/%u/exe", pid); ++ if ((n <= 10) || (n >= max_pathlen)) { ++ fatal("error compiling filename of link to executable"); ++ } ++ ++ exe[0] = 0; ++ n = readlink(exe_sl, exe, max_pathlen); ++ /* the file doesn't need to exist - procfs might not be mounted in ++ * chroot */ ++ if (n == -1) { ++ rv = CHECK_MISSING; ++ } else { ++ if (n < max_pathlen) { ++ exe[n] = 0; ++ rv = CHECK_OK; ++ } else { ++ rv = CHECK_FAIL; ++ } ++ } ++ return rv; ++} ++ ++/* Read HMAC from file chk, allocating enough memory to hold the HMAC and ++ * return it in *hmac. ++ * Remember to free() it when it's not needed anymore. ++ */ ++static int ++read_hmac(const char *chk, u_char **hmac) ++{ ++ int check = -1; ++ int fdh, n; ++ u_char *hmac_in; ++ ++ *hmac = NULL; ++ ++ fdh = open(chk, O_RDONLY); ++ if (-1 == fdh) { ++ switch (errno) { ++ case ENOENT: ++ check = CHECK_MISSING; ++ debug("fips: checksum file %s is missing\n", chk); ++ break; ++ default: ++ check = CHECK_FAIL; ++ debug("fips: ckecksum file %s not accessible\n", chk); ++ break; ++ ++ } ++ goto bail_out; ++ } ++ ++ hmac_in = xmalloc(FIPS_HMAC_LEN); ++ ++ n = read(fdh, (void *)hmac_in, FIPS_HMAC_LEN); ++ if (FIPS_HMAC_LEN != n) { ++ debug("fips: unable to read whole checksum from checksum file\n"); ++ free (hmac_in); ++ check = CHECK_FAIL; ++ } else { ++ check = CHECK_OK; ++ *hmac = hmac_in; ++ } ++bail_out: ++ return check; ++} ++ ++static int ++fips_hmac_self(void) ++{ ++ int check = -1; ++ u_char *hmac = NULL, *hmac_chk = NULL; ++ char *exe, *chk; ++ ++ exe = xmalloc(PATH_MAX); ++ chk = xmalloc(PATH_MAX); ++ ++ /* we will need to add the suffix and the null terminator */ ++ check = get_executable_path(getpid(), exe ++ , PATH_MAX - strlen(CHECKSUM_SUFFIX) - 1); ++ if (CHECK_OK != check) ++ goto cleanup; ++ ++ strncpy(chk, exe, PATH_MAX); ++ strlcat(chk, CHECKSUM_SUFFIX, PATH_MAX); ++ ++ check = read_hmac(chk, &hmac_chk); ++ if (CHECK_OK != check) ++ goto cleanup; ++ ++ check = hmac_file(exe, &hmac); ++ if (CHECK_OK != check) ++ goto cleanup; ++ ++ check = memcmp(hmac, hmac_chk, FIPS_HMAC_LEN); ++ if (0 == check) { ++ check = CHECK_OK; ++ debug("fips: checksum matches\n"); ++ } else { ++ check = CHECK_FAIL; ++ debug("fips: checksum mismatch!\n"); ++ } ++ ++cleanup: ++ free(hmac); ++ free(hmac_chk); ++ free(chk); ++ free(exe); ++ ++ return check; ++} ++ ++static int ++fips_check_required_proc(void) + { + int fips_required = 0; +- char *env = getenv(SSH_FORCE_FIPS_ENV); ++ int fips_fd; ++ char fips_sys = 0; + +- if (env) { +- errno = 0; +- fips_required = strtol(env, NULL, 10); +- if (errno) { +- debug("bogus value in the %s environment variable, ignoring\n" +- , SSH_FORCE_FIPS_ENV); +- fips_required = 0; +- } else +- fips_required = 1; ++ struct stat dummy; ++ if (-1 == stat(FIPS_PROC_PATH, &dummy)) { ++ switch (errno) { ++ case ENOENT: ++ case ENOTDIR: ++ break; ++ default: ++ fatal("Check for system-wide FIPS mode is required and %s cannot" ++ " be accessed for reason other than non-existence - aborting" ++ , FIPS_PROC_PATH); ++ break; ++ } ++ } else { ++ if (-1 == (fips_fd = open(FIPS_PROC_PATH, O_RDONLY))) ++ fatal("Check for system-wide FIPS mode is required and %s cannot" ++ " be opened for reading - aborting" ++ , FIPS_PROC_PATH); ++ if (1 > read(fips_fd, &fips_sys, 1)) ++ fatal("Check for system-wide FIPS mode is required and %s doesn't" ++ " return at least one character - aborting" ++ , FIPS_PROC_PATH); ++ close(fips_sys); ++ switch (fips_sys) { ++ case '0': ++ case '1': ++ fips_required = fips_sys - '0'; ++ break; ++ default: ++ fatal("Bogus character %c found in %s - aborting" ++ , fips_sys, FIPS_PROC_PATH); ++ } + } + return fips_required; + } + ++static int ++fips_check_required_env(void) ++{ ++ return (NULL != getenv(SSH_FORCE_FIPS_ENV)); ++} ++ ++static int ++fips_required(void) ++{ ++ int fips_requests = 0; ++ fips_requests += fips_check_required_proc(); ++ fips_requests += fips_check_required_env(); ++ return fips_requests; ++} ++ ++/* check whether FIPS mode is required and perform selfchecksum/selftest */ ++void ++fips_ssh_init(void) ++{ ++ int checksum; ++ ++ checksum = fips_hmac_self(); ++ ++ if (fips_required()) { ++ switch (checksum) { ++ case CHECK_OK: ++ debug("fips: mandatory checksum ok"); ++ break; ++ case CHECK_FAIL: ++ fatal("fips: mandatory checksum failed - aborting"); ++ break; ++ case CHECK_MISSING: ++ fatal("fips: mandatory checksum data missing - aborting"); ++ break; ++ default: ++ fatal("Fatal error: internal error at %s:%u" ++ , __FILE__, __LINE__); ++ break; ++ } ++ fips_state = FIPS_mode_set(1); ++ if (1 != fips_state) { ++ ERR_load_crypto_strings(); ++ u_long err = ERR_get_error(); ++ error("fips: OpenSSL error %lx: %s" ++ , err, ERR_error_string(err, NULL)); ++ fatal("fips: unable to set OpenSSL into FIPS mode - aborting"); ++ } ++ } else { ++ switch (checksum) { ++ case CHECK_OK: ++ debug("fips: checksum ok"); ++ break; ++ case CHECK_FAIL: ++ fatal("fips: checksum failed - aborting"); ++ break; ++ case CHECK_MISSING: ++ debug("fips: checksum data missing, but not required - continuing non-FIPS"); ++ break; ++ default: ++ fatal("Fatal error: internal error at %s:%u", ++ __FILE__, __LINE__); ++ break; ++ } ++ } ++ return; ++} ++ + int + fips_mode(void) + { +@@ -234,4 +497,3 @@ fips_dh_grp_min(void) + } + return dh; + } +- +Index: openssh-7.8p1/fips.h +=================================================================== +--- openssh-7.8p1.orig/fips.h ++++ openssh-7.8p1/fips.h +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2012 Petr Cerny. All rights reserved. ++ * Copyright (c) 2012-2014 Petr Cerny. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -27,6 +27,15 @@ + #include "sshkey.h" + + #define SSH_FORCE_FIPS_ENV "SSH_FORCE_FIPS" ++#define FIPS_PROC_PATH "/proc/sys/crypto/fips_enabled" ++ ++#define PROC_EXE_PATH_LEN 64 ++#define CHECKSUM_SUFFIX ".hmac" ++#define FIPS_HMAC_KEY "HMAC_KEY:OpenSSH-FIPS@SLE" ++#define FIPS_HMAC_EVP EVP_sha256 ++#define FIPS_HMAC_LEN 32 ++ ++void fips_ssh_init(void); + + typedef enum { + FIPS_FILTER_CIPHERS, +@@ -34,6 +43,12 @@ typedef enum { + FIPS_FILTER_KEX_ALGS + } fips_filters; + ++typedef enum { ++ CHECK_OK = 0, ++ CHECK_FAIL, ++ CHECK_MISSING ++} fips_checksum_status; ++ + int fips_mode(void); + int fips_correct_dgst(int); + int fips_dgst_min(void); +@@ -42,4 +57,3 @@ enum fp_type fips_correct_fp_type(enum + int fips_filter_crypto(char **, fips_filters); + + #endif +- +Index: openssh-7.8p1/sftp-server.c +=================================================================== +--- openssh-7.8p1.orig/sftp-server.c ++++ openssh-7.8p1/sftp-server.c +@@ -51,6 +51,8 @@ + #include "sftp.h" + #include "sftp-common.h" + ++#include "fips.h" ++ + /* Our verbosity */ + static LogLevel log_level = SYSLOG_LEVEL_ERROR; + +@@ -1509,6 +1511,9 @@ sftp_server_main(int argc, char **argv, + extern char *optarg; + extern char *__progname; + ++ /* initialize fips */ ++ fips_ssh_init(); ++ + ssh_malloc_init(); /* must be called before any mallocs */ + __progname = ssh_get_progname(argv[0]); + log_init(__progname, log_level, log_facility, log_stderr); +Index: openssh-7.8p1/ssh.c +=================================================================== +--- openssh-7.8p1.orig/ssh.c ++++ openssh-7.8p1/ssh.c +@@ -113,6 +113,8 @@ + #include "ssh-pkcs11.h" + #endif + ++#include "fips.h" ++ + extern char *__progname; + + /* Saves a copy of argv for setproctitle emulation */ +@@ -593,6 +595,10 @@ main(int ac, char **av) + struct ssh_digest_ctx *md; + u_char conn_hash[SSH_DIGEST_MAX_LENGTH]; + ++ /* initialize fips - can go before ssh_malloc_init(), since that is a ++ * OpenBSD-only thing (as of OpenSSH 7.6p1) */ ++ fips_ssh_init(); ++ + ssh_malloc_init(); /* must be called before any mallocs */ + /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ + sanitise_stdfd(); +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -1486,6 +1486,10 @@ main(int ac, char **av) + Authctxt *authctxt; + struct connection_info *connection_info = NULL; + ++ /* initialize fips - can go before ssh_malloc_init(), since that is a ++ * OpenBSD-only thing (as of OpenSSH 7.6p1) */ ++ fips_ssh_init(); ++ + ssh_malloc_init(); /* must be called before any mallocs */ + + #ifdef HAVE_SECUREWARE diff --git a/openssh-7.7p1-gssapi_key_exchange.patch b/openssh-7.7p1-gssapi_key_exchange.patch new file mode 100644 index 0000000..eb36435 --- /dev/null +++ b/openssh-7.7p1-gssapi_key_exchange.patch @@ -0,0 +1,3319 @@ +# HG changeset patch +# Parent 6a2300496d25e85647e718287d4d9f37170f492a + +Index: openssh-7.8p1/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/Makefile.in ++++ openssh-7.8p1/Makefile.in +@@ -108,6 +108,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + + LIBSSH_OBJS += fips.o + ++LIBSSH_OBJS += kexgssc.o kexgsss.o ++ + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o + +Index: openssh-7.8p1/auth-krb5.c +=================================================================== +--- openssh-7.8p1.orig/auth-krb5.c ++++ openssh-7.8p1/auth-krb5.c +@@ -182,8 +182,13 @@ auth_krb5_password(Authctxt *authctxt, c + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); ++#ifdef USE_CCAPI ++ snprintf(authctxt->krb5_ccname, len, "API:%s", ++ authctxt->krb5_ticket_file); ++#else + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -243,12 +248,18 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_c + int tmpfd, ret, oerrno; + char ccname[40]; + mode_t old_umask; ++#ifdef USE_CCAPI ++ char cctemplate[] = "API:krb5cc_%d"; ++#else ++ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; ++#endif + + ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ cctemplate, geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + ++#ifndef USE_CCAPI + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; +@@ -265,6 +276,7 @@ ssh_krb5_cc_gen(krb5_context ctx, krb5_c + return oerrno; + } + close(tmpfd); ++#endif + + return (krb5_cc_resolve(ctx, ccname, ccache)); + } +Index: openssh-7.8p1/auth.c +=================================================================== +--- openssh-7.8p1.orig/auth.c ++++ openssh-7.8p1/auth.c +@@ -395,6 +395,7 @@ auth_root_allowed(struct ssh *ssh, const + case PERMIT_NO_PASSWD: + if (strcmp(method, "publickey") == 0 || + strcmp(method, "hostbased") == 0 || ++ strcmp(method, "gssapi-keyex") == 0 || + strcmp(method, "gssapi-with-mic") == 0) + return 1; + break; +Index: openssh-7.8p1/auth2-gss.c +=================================================================== +--- openssh-7.8p1.orig/auth2-gss.c ++++ openssh-7.8p1/auth2-gss.c +@@ -31,6 +31,7 @@ + #include + + #include ++#include + + #include "xmalloc.h" + #include "sshkey.h" +@@ -55,6 +56,44 @@ static int input_gssapi_exchange_complet + static int input_gssapi_errtok(int, u_int32_t, struct ssh *); + + /* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(struct ssh *ssh) ++{ ++ Authctxt *authctxt = ssh->authctxt; ++ int authenticated = 0; ++ struct sshbuf *b = NULL; ++ gss_buffer_desc mic, gssbuf; ++ u_int len; ++ ++ mic.value = packet_get_string(&len); ++ mic.length = len; ++ ++ packet_check_eom(); ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = sshbuf_mutable_ptr(b); ++ gssbuf.length = sshbuf_len(b); ++ ++ /* gss_kex_context is NULL with privsep, so we can't check it here */ ++ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, ++ &gssbuf, &mic)))) ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); ++ ++ sshbuf_free(b); ++ free(mic.value); ++ ++ return (authenticated); ++} ++ ++/* + * We only support those mechanisms that we know about (ie ones that we know + * how to check local user kuserok and the like) + */ +@@ -260,7 +299,8 @@ input_gssapi_exchange_complete(int type, + if ((r = sshpkt_get_end(ssh)) != 0) + fatal("%s: %s", __func__, ssh_err(r)); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) +@@ -306,7 +346,8 @@ input_gssapi_mic(int type, u_int32_t ple + gssbuf.length = sshbuf_len(b); + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = ++ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); + else + logit("GSSAPI MIC check failed"); + +@@ -326,6 +367,12 @@ input_gssapi_mic(int type, u_int32_t ple + return 0; + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, +Index: openssh-7.8p1/auth2.c +=================================================================== +--- openssh-7.8p1.orig/auth2.c ++++ openssh-7.8p1/auth2.c +@@ -74,6 +74,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -81,6 +82,7 @@ Authmethod *authmethods[] = { + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, +Index: openssh-7.8p1/clientloop.c +=================================================================== +--- openssh-7.8p1.orig/clientloop.c ++++ openssh-7.8p1/clientloop.c +@@ -112,6 +112,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1357,9 +1361,18 @@ client_loop(struct ssh *ssh, int have_pt + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(ssh)) ++ if (!ssh_packet_is_rekeying(ssh)) { + channel_after_select(ssh, readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated((Gssctxt *)NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(readset); + +Index: openssh-7.8p1/configure.ac +=================================================================== +--- openssh-7.8p1.orig/configure.ac ++++ openssh-7.8p1/configure.ac +@@ -673,6 +673,30 @@ main() { if (NSVersionOfRunTimeLibrary(" + [Use tunnel device compatibility to OpenBSD]) + AC_DEFINE([SSH_TUN_PREPEND_AF], [1], + [Prepend the address family to IP tunnel traffic]) ++ AC_MSG_CHECKING(if we have the Security Authorization Session API) ++ AC_TRY_COMPILE([#include ], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE(USE_SECURITY_SESSION_API, 1, ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes)], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT(no)]) ++ AC_MSG_CHECKING(if we have an in-memory credentials cache) ++ AC_TRY_COMPILE( ++ [#include ], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE(USE_CCAPI, 1, ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT(yes) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) ++ fi], ++ [AC_MSG_RESULT(no)] ++ ) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) +@@ -1853,9 +1877,9 @@ AC_RUN_IFELSE( + ) + + AC_LINK_IFELSE( +- [AC_LANG_PROGRAM( +- [[ #include ]], +- [[ return (isblank('a')); ]])], ++ [AC_LANG_PROGRAM( ++ [[ #include ]], ++ [[ return (isblank('a')); ]])], + [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) + ]) + +@@ -2161,7 +2185,7 @@ int snprintf(char *a, size_t b, const ch + ]])], + [AC_MSG_RESULT([yes]) + AC_DEFINE([SNPRINTF_CONST], [const], +- [Define as const if snprintf() can declare const char *fmt])], ++ [Define as const if snprintf() can declare const char *fmt])], + [AC_MSG_RESULT([no]) + AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) + +@@ -2176,7 +2200,7 @@ if test "x$ac_cv_func_getpeereid" != "xy + AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) + ], [AC_MSG_RESULT([no]) + NO_PEERCHECK=1 +- ]) ++ ]) + fi + + dnl see whether mkstemp() requires XXXXXX +@@ -2600,12 +2624,12 @@ if test "x$openssl" = "xyes" ; then + case "$ssl_library_ver" in + 10000*|0*) + AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) +- ;; ++ ;; + 100*) ;; # 1.0.x + 200*) ;; # LibreSSL +- *) ++ *) + AC_MSG_ERROR([OpenSSL >= 1.1.0 is not yet supported (have "$ssl_library_ver")]) +- ;; ++ ;; + esac + AC_MSG_RESULT([$ssl_library_ver]) + ], +@@ -4473,7 +4497,7 @@ AC_ARG_WITH([maildir], + if test "X$withval" != X && test "x$withval" != xno && \ + test "x${withval}" != xyes; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], +- [Set this to your mail directory if you do not have _PATH_MAILDIR]) ++ [Set this to your mail directory if you do not have _PATH_MAILDIR]) + fi + ],[ + if test "X$maildir" != "X"; then +Index: openssh-7.8p1/gss-genr.c +=================================================================== +--- openssh-7.8p1.orig/gss-genr.c ++++ openssh-7.8p1/gss-genr.c +@@ -41,12 +41,169 @@ + #include "sshbuf.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "sshkey.h" ++#include "kex.h" ++#include + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok() { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client, ++ const char *kex) { ++ gss_OID_set gss_supported; ++ OM_uint32 min_status; ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) ++ return NULL; ++ ++ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, ++ host, client)); ++} ++ ++char * ++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, ++ const char *host, const char *client) { ++ struct sshbuf *buf; ++ size_t i; ++ int oidpos, enclen, r; ++ char *mechs, *encoded; ++ u_char digest[EVP_MAX_MD_SIZE]; ++ char deroid[2]; ++ const EVP_MD *evp_md = EVP_md5(); ++ EVP_MD_CTX md; ++ ++ if (gss_enc2oid != NULL) { ++ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) ++ free(gss_enc2oid[i].encoded); ++ free(gss_enc2oid); ++ } ++ ++ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * ++ (gss_supported->count + 1)); ++ ++ if ((buf = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ oidpos = 0; ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ deroid[0] = SSH_GSS_OIDTYPE; ++ deroid[1] = gss_supported->elements[i].length; ++ ++ EVP_DigestInit(&md, evp_md); ++ EVP_DigestUpdate(&md, deroid, 2); ++ EVP_DigestUpdate(&md, ++ gss_supported->elements[i].elements, ++ gss_supported->elements[i].length); ++ EVP_DigestFinal(&md, digest, NULL); ++ ++ encoded = xmalloc(EVP_MD_size(evp_md) * 2); ++ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), ++ encoded, EVP_MD_size(evp_md) * 2); ++ ++ cp = strncpy(s, kex, strlen(kex)); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (sshbuf_len(buf) != 0) ++ if ((r = sshbuf_put_u8(buf, ',')) !=0) ++ fatal("%s: buffer error: %s", ++ __func__, ssh_err(r)); ++ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0) ++ fatal("%s: buffer error: %s", ++ __func__, ssh_err(r)); ++ } ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ free(s); ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ if ((r = sshbuf_put_u8(buf, '\0')) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mechs = xmalloc(sshbuf_len(buf)); ++ sshbuf_get(buf, mechs, sshbuf_len(buf)); ++ sshbuf_free(buf); ++ ++ if (strlen(mechs) == 0) { ++ free(mechs); ++ mechs = NULL; ++ } ++ ++ return (mechs); ++} ++ ++gss_OID ++ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { ++ int i = 0; ++ ++ switch (kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; ++ break; ++ case KEX_GSS_GEX_SHA1: ++ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; ++ break; ++ default: ++ return GSS_C_NO_OID; ++ } ++ ++ while (gss_enc2oid[i].encoded != NULL && ++ strcmp(name, gss_enc2oid[i].encoded) != 0) ++ i++; ++ ++ if (gss_enc2oid[i].oid != NULL && ctx != NULL) ++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); ++ ++ return gss_enc2oid[i].oid; ++} ++ + /* sshbuf_get for gss_buffer_desc */ + int + ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) +@@ -218,7 +375,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de + } + + ctx->major = gss_init_sec_context(&ctx->minor, +- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, ++ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, + GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, + 0, NULL, recv_tok, NULL, send_tok, flags, NULL); + +@@ -248,8 +405,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con + } + + OM_uint32 ++ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) ++{ ++ gss_buffer_desc gssbuf; ++ gss_name_t gssname; ++ OM_uint32 status; ++ gss_OID_set oidset; ++ ++ gssbuf.value = (void *) name; ++ gssbuf.length = strlen(gssbuf.value); ++ ++ gss_create_empty_oid_set(&status, &oidset); ++ gss_add_oid_set_member(&status, ctx->oid, &oidset); ++ ++ ctx->major = gss_import_name(&ctx->minor, &gssbuf, ++ GSS_C_NT_USER_NAME, &gssname); ++ ++ if (!ctx->major) ++ ctx->major = gss_acquire_cred(&ctx->minor, ++ gssname, 0, oidset, GSS_C_INITIATE, ++ &ctx->client_creds, NULL, NULL); ++ ++ gss_release_name(&status, &gssname); ++ gss_release_oid_set(&status, &oidset); ++ ++ if (ctx->major) ++ ssh_gssapi_error(ctx); ++ ++ return(ctx->major); ++} ++ ++OM_uint32 + ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) + { ++ if (ctx == NULL) ++ return -1; ++ + if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, + GSS_C_QOP_DEFAULT, buffer, hash))) + ssh_gssapi_error(ctx); +@@ -257,6 +448,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer + return (ctx->major); + } + ++/* Priviledged when used by server */ ++OM_uint32 ++ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++{ ++ if (ctx == NULL) ++ return -1; ++ ++ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, ++ gssbuf, gssmic, NULL); ++ ++ return (ctx->major); ++} ++ + void + ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, + const char *context) +@@ -273,22 +477,31 @@ ssh_gssapi_buildmic(struct sshbuf *b, co + } + + int +-ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) ++ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, ++ const char *client) + { + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; ++ Gssctxt *intctx = NULL; ++ ++ if (ctx == NULL) ++ ctx = &intctx; + + /* RFC 4462 says we MUST NOT do SPNEGO */ +- if (oid->length == spnego_oid.length && ++ if (oid->length == spnego_oid.length && + (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) + return 0; /* false */ + + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { +- major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, ++ major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); + gss_release_buffer(&minor, &token); + if ((*ctx)->context != GSS_C_NO_CONTEXT) +@@ -296,10 +509,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx + GSS_C_NO_BUFFER); + } + +- if (GSS_ERROR(major)) ++ if (GSS_ERROR(major) || intctx != NULL) + ssh_gssapi_delete_ctx(ctx); + + return (!GSS_ERROR(major)); + } + ++int ++ssh_gssapi_credentials_updated(Gssctxt *ctxt) { ++ static gss_name_t saved_name = GSS_C_NO_NAME; ++ static OM_uint32 saved_lifetime = 0; ++ static gss_OID saved_mech = GSS_C_NO_OID; ++ static gss_name_t name; ++ static OM_uint32 last_call = 0; ++ OM_uint32 lifetime, now, major, minor; ++ int equal; ++ ++ now = time(NULL); ++ ++ if (ctxt) { ++ debug("Rekey has happened - updating saved versions"); ++ ++ if (saved_name != GSS_C_NO_NAME) ++ gss_release_name(&minor, &saved_name); ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &saved_name, &saved_lifetime, NULL, NULL); ++ ++ if (!GSS_ERROR(major)) { ++ saved_mech = ctxt->oid; ++ saved_lifetime+= now; ++ } else { ++ /* Handle the error */ ++ } ++ return 0; ++ } ++ ++ if (now - last_call < 10) ++ return 0; ++ ++ last_call = now; ++ ++ if (saved_mech == GSS_C_NO_OID) ++ return 0; ++ ++ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, ++ &name, &lifetime, NULL, NULL); ++ if (major == GSS_S_CREDENTIALS_EXPIRED) ++ return 0; ++ else if (GSS_ERROR(major)) ++ return 0; ++ ++ major = gss_compare_name(&minor, saved_name, name, &equal); ++ gss_release_name(&minor, &name); ++ if (GSS_ERROR(major)) ++ return 0; ++ ++ if (equal && (saved_lifetime < lifetime + now - 10)) ++ return 1; ++ ++ return 0; ++} ++ + #endif /* GSSAPI */ +Index: openssh-7.8p1/gss-serv-krb5.c +=================================================================== +--- openssh-7.8p1.orig/gss-serv-krb5.c ++++ openssh-7.8p1/gss-serv-krb5.c +@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- int len; ++ const char *new_ccname, *new_cctype; + const char *errmsg; + + if (client->creds == NULL) { +@@ -180,11 +180,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); ++ new_cctype = krb5_cc_get_type(krb_context, ccache); ++ new_ccname = krb5_cc_get_name(krb_context, ccache); ++ + client->store.envvar = "KRB5CCNAME"; +- len = strlen(client->store.filename) + 6; +- client->store.envval = xmalloc(len); +- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); ++#ifdef USE_CCAPI ++ xasprintf(&client->store.envval, "API:%s", new_ccname); ++ client->store.filename = NULL; ++#else ++ if (new_ccname[0] == ':') ++ new_ccname++; ++ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); ++ if (strcmp(new_cctype, "DIR") == 0) { ++ char *p; ++ p = strrchr(client->store.envval, '/'); ++ if (p) ++ *p = '\0'; ++ } ++ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0)) ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -193,9 +208,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + + krb5_cc_close(krb_context, ccache); + ++ client->store.data = krb_context; ++ + return; + } + ++int ++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, ++ ssh_gssapi_client *client) ++{ ++ krb5_ccache ccache = NULL; ++ krb5_principal principal = NULL; ++ char *name = NULL; ++ krb5_error_code problem; ++ OM_uint32 maj_status, min_status; ++ ++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { ++ logit("krb5_cc_resolve(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ return 0; ++ } ++ ++ /* Find out who the principal in this cache is */ ++ if ((problem = krb5_cc_get_principal(krb_context, ccache, ++ &principal))) { ++ logit("krb5_cc_get_principal(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { ++ logit("krb5_unparse_name(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ ++ if (strcmp(name,client->exportedname.value)!=0) { ++ debug("Name in local credentials cache differs. Not storing"); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ krb5_free_unparsed_name(krb_context, name); ++ return 0; ++ } ++ krb5_free_unparsed_name(krb_context, name); ++ ++ /* Name matches, so lets get on with it! */ ++ ++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { ++ logit("krb5_cc_initialize(): %.100s", ++ krb5_get_err_text(krb_context, problem)); ++ krb5_free_principal(krb_context, principal); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ krb5_free_principal(krb_context, principal); ++ ++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, ++ ccache))) { ++ logit("gss_krb5_copy_ccache() failed. Sorry!"); ++ krb5_cc_close(krb_context, ccache); ++ return 0; ++ } ++ ++ return 1; ++} ++ + ssh_gssapi_mech gssapi_kerberos_mech = { + "toWM5Slw5Ew8Mqkay+al2g==", + "Kerberos", +@@ -203,7 +285,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ +Index: openssh-7.8p1/gss-serv.c +=================================================================== +--- openssh-7.8p1.orig/gss-serv.c ++++ openssh-7.8p1/gss-serv.c +@@ -44,17 +44,19 @@ + #include "session.h" + #include "misc.h" + #include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" + + extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; ++ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL}, 0, 0}; + + ssh_gssapi_mech gssapi_null_mech = +- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; ++ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; + + #ifdef KRB5 + extern ssh_gssapi_mech gssapi_kerberos_mech; +@@ -141,6 +143,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss + } + + /* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms() { ++ if (supported_oids == NULL) ++ ssh_gssapi_prepare_supported_oids(); ++ return (ssh_gssapi_kex_mechs(supported_oids, ++ &ssh_gssapi_server_check_mech, NULL, NULL)); ++} ++ ++/* Unprivileged */ ++int ++ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, ++ const char *dummy) { ++ Gssctxt *ctx = NULL; ++ int res; ++ ++ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); ++ ssh_gssapi_delete_ctx(&ctx); ++ ++ return (res); ++} ++ ++/* Unprivileged */ + void + ssh_gssapi_supported_oids(gss_OID_set *oidset) + { +@@ -150,7 +174,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o + gss_OID_set supported; + + gss_create_empty_oid_set(&min_status, oidset); +- gss_indicate_mechs(&min_status, &supported); ++ ++ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) ++ return; + + while (supported_mechs[i]->name != NULL) { + if (GSS_ERROR(gss_test_oid_set_member(&min_status, +@@ -276,8 +302,48 @@ OM_uint32 + ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + { + int i = 0; ++ int equal = 0; ++ gss_name_t new_name = GSS_C_NO_NAME; ++ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; ++ ++ if (options.gss_store_rekey && client->used && ctx->client_creds) { ++ if (client->mech->oid.length != ctx->oid->length || ++ (memcmp(client->mech->oid.elements, ++ ctx->oid->elements, ctx->oid->length) !=0)) { ++ debug("Rekeyed credentials have different mechanism"); ++ return GSS_S_COMPLETE; ++ } + +- gss_buffer_desc ename; ++ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &new_name, ++ NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ ctx->major = gss_compare_name(&ctx->minor, client->name, ++ new_name, &equal); ++ ++ if (GSS_ERROR(ctx->major)) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ ++ if (!equal) { ++ debug("Rekeyed credentials have different name"); ++ return GSS_S_COMPLETE; ++ } ++ ++ debug("Marking rekeyed credentials for export"); ++ ++ gss_release_name(&ctx->minor, &client->name); ++ gss_release_cred(&ctx->minor, &client->creds); ++ client->name = new_name; ++ client->creds = ctx->client_creds; ++ ctx->client_creds = GSS_C_NO_CREDENTIAL; ++ client->updated = 1; ++ return GSS_S_COMPLETE; ++ } + + client->mech = NULL; + +@@ -292,6 +358,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + if (client->mech == NULL) + return GSS_S_FAILURE; + ++ if (ctx->client_creds && ++ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ++ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { ++ ssh_gssapi_error(ctx); ++ return (ctx->major); ++ } ++ + if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, + &client->displayname, NULL))) { + ssh_gssapi_error(ctx); +@@ -309,6 +382,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + return (ctx->major); + } + ++ gss_release_buffer(&ctx->minor, &ename); ++ + /* We can't copy this structure, so we just move the pointer to it */ + client->creds = ctx->client_creds; + ctx->client_creds = GSS_C_NO_CREDENTIAL; +@@ -319,11 +394,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + void + ssh_gssapi_cleanup_creds(void) + { +- if (gssapi_client.store.filename != NULL) { +- /* Unlink probably isn't sufficient */ +- debug("removing gssapi cred file\"%s\"", +- gssapi_client.store.filename); +- unlink(gssapi_client.store.filename); ++ krb5_ccache ccache = NULL; ++ krb5_error_code problem; ++ ++ if (gssapi_client.store.data != NULL) { ++ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { ++ debug("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { ++ debug("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else { ++ krb5_free_context(gssapi_client.store.data); ++ gssapi_client.store.data = NULL; ++ } + } + } + +@@ -356,7 +440,7 @@ ssh_gssapi_do_child(char ***envp, u_int + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw) + { + OM_uint32 lmin; + +@@ -366,9 +450,11 @@ ssh_gssapi_userok(char *user) + return 0; + } + if (gssapi_client.mech && gssapi_client.mech->userok) +- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) ++ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { ++ gssapi_client.used = 1; ++ gssapi_client.store.owner = pw; + return 1; +- else { ++ } else { + /* Destroy delegated credentials if userok fails */ + gss_release_buffer(&lmin, &gssapi_client.displayname); + gss_release_buffer(&lmin, &gssapi_client.exportedname); +@@ -382,14 +468,90 @@ ssh_gssapi_userok(char *user) + return (0); + } + +-/* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* These bits are only used for rekeying. The unpriviledged child is running ++ * as the user, the monitor is root. ++ * ++ * In the child, we want to : ++ * *) Ask the monitor to store our credentials into the store we specify ++ * *) If it succeeds, maybe do a PAM update ++ */ ++ ++/* Stuff for PAM */ ++ ++#ifdef USE_PAM ++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, ++ struct pam_response **resp, void *data) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++void ++ssh_gssapi_rekey_creds() { ++ int ok; ++ int ret; ++#ifdef USE_PAM ++ pam_handle_t *pamh = NULL; ++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; ++ char *envstr; ++#endif ++ ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL && ++ gssapi_client.store.envvar == NULL) ++ return; ++ ++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ++ if (!ok) ++ return; ++ ++ debug("Rekeyed credentials stored successfully"); ++ ++ /* Actually managing to play with the ssh pam stack from here will ++ * be next to impossible. In any case, we may want different options ++ * for rekeying. So, use our own :) ++ */ ++#ifdef USE_PAM ++ if (!use_privsep) { ++ debug("Not even going to try and do PAM with privsep disabled"); ++ return; ++ } ++ ++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, ++ &pamconv, &pamh); ++ if (ret) ++ return; ++ ++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, ++ gssapi_client.store.envval); ++ ++ ret = pam_putenv(pamh, envstr); ++ if (!ret) ++ pam_setcred(pamh, PAM_REINITIALIZE_CRED); ++ pam_end(pamh, PAM_SUCCESS); ++#endif ++} ++ ++int ++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { ++ int ok = 0; ++ ++ /* Check we've got credentials to store */ ++ if (!gssapi_client.updated) ++ return 0; ++ ++ gssapi_client.updated = 0; ++ ++ temporarily_use_uid(gssapi_client.store.owner); ++ if (gssapi_client.mech && gssapi_client.mech->updatecreds) ++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); ++ else ++ debug("No update function for this mechanism"); ++ ++ restore_uid(); ++ ++ return ok; + } + + /* Privileged */ +Index: openssh-7.8p1/kex.c +=================================================================== +--- openssh-7.8p1.orig/kex.c ++++ openssh-7.8p1/kex.c +@@ -56,6 +56,10 @@ + + #include "fips.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* prototype */ + static int kex_choose_conf(struct ssh *); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); +@@ -105,6 +109,11 @@ static const struct kexalg kexalgs_all[] + { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++#endif + { NULL, -1, -1, -1}, + }; + +@@ -129,6 +138,10 @@ static const struct kexalg kexalgs_fips1 + # endif /* OPENSSL_HAS_NISTP521 */ + #endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++#endif + { NULL, -1, -1, -1}, + }; + +@@ -179,6 +192,12 @@ kex_alg_by_name(const char *name) + for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; ++#ifdef GSSAPI ++ if (strncmp(name, "gss-", 4) == 0) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } ++#endif + } + return NULL; + } +Index: openssh-7.8p1/kex.h +=================================================================== +--- openssh-7.8p1.orig/kex.h ++++ openssh-7.8p1/kex.h +@@ -100,6 +100,11 @@ enum kex_exchange { + KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, + KEX_C25519_SHA256, ++#ifdef GSSAPI ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, ++#endif + KEX_MAX + }; + +@@ -148,6 +153,12 @@ struct kex { + u_int flags; + int hash_alg; + int ec_nid; ++#ifdef GSSAPI ++ int gss_deleg_creds; ++ int gss_trust_dns; ++ char *gss_host; ++ char *gss_client; ++#endif + char *client_version_string; + char *server_version_string; + char *failed_choice; +@@ -197,6 +208,10 @@ int kexecdh_client(struct ssh *); + 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 + + int kex_dh_hash(int, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, +Index: openssh-7.8p1/kexgssc.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/kexgssc.c +@@ -0,0 +1,348 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. 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 "includes.h" ++ ++#ifdef GSSAPI ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++ ++#include "ssh-gss.h" ++ ++#include "fips.h" ++ ++int ++kexgss_client(struct ssh *ssh) ++{ ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; ++ Gssctxt *ctxt; ++ OM_uint32 maj_status, min_status, ret_flags; ++ u_int klen, kout, slen = 0, strlen; ++ DH *dh; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ /* TODO ++ char *lang; ++ */ ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = fips_dh_grp_min(), max = DH_GRP_MAX; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (ssh->kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(ssh->kex->we_need * 8); ++ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); ++ packet_put_int(min); ++ packet_put_int(nbits); ++ packet_put_int(max); ++ ++ packet_send(); ++ ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); ++ ++ if ((p = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (BN_num_bits(p) < min || BN_num_bits(p) > max) ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, BN_num_bits(p), max); ++ ++ dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, ++ &ret_flags); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length != 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ fatal("gss_init_context failed"); ++ } ++ ++ /* If we've got an old receive buffer get rid of it */ ++ if (token_ptr != GSS_C_NO_BUFFER) ++ free(recv_tok.value); ++ ++ if (maj_status == GSS_S_COMPLETE) { ++ /* If mutual state flag is not true, kex fails */ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual authentication failed"); ++ ++ /* If integ avail flag is not true kex fails */ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity check failed"); ++ } ++ ++ /* ++ * If we have data to send, then the last message that we ++ * received cannot have been a 'complete'. ++ */ ++ if (send_tok.length != 0) { ++ if (first) { ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ packet_put_bignum2(dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ /* TODO ++ lang = packet_get_string(NULL); ++ */ ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, dh); ++ if ((int)kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_client: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexdh_client: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), ++ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ++ ssh->kex->server_version_string, ++ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), ++ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ min, nbits, max, ++ dh->p, dh->g, ++ dh->pub_key, ++ dh_server_pub, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ packet_disconnect("Hash's MIC didn't verify"); ++ ++ free(msg_tok.value); ++ ++ DH_free(dh); ++ if (serverhostkey) ++ free(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* save session id */ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ if (ssh->kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* TODO: check kex_derive_keys_bn return value */ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ return kex_send_newkeys(ssh); ++} ++ ++#endif /* GSSAPI */ +Index: openssh-7.8p1/kexgsss.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/kexgsss.c +@@ -0,0 +1,307 @@ ++/* ++ * Copyright (c) 2001-2009 Simon Wilkinson. 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 "includes.h" ++ ++#ifdef GSSAPI ++ ++#include ++ ++#include ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "digest.h" ++ ++#include "fips.h" ++ ++extern ServerOptions options; ++ ++int ++kexgss_server(struct ssh *ssh) ++{ ++ OM_uint32 maj_status, min_status; ++ ++ /* ++ * Some GSSAPI implementations use the input value of ret_flags (an ++ * output variable) as a means of triggering mechanism specific ++ * features. Initializing it to zero avoids inadvertently ++ * activating this non-standard behaviour. ++ */ ++ ++ OM_uint32 ret_flags = 0; ++ gss_buffer_desc gssbuf, recv_tok, msg_tok; ++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; ++ Gssctxt *ctxt = NULL; ++ u_int slen, klen, kout; ++ u_char *kbuf; ++ DH *dh; ++ int min = -1, max = -1, nbits = -1; ++ int cmin = -1, cmax = -1; /* client proposal */ ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ ++ /* Initialise GSSAPI */ ++ ++ /* If we're rekeying, privsep means that some of the private structures ++ * in the GSSAPI code are no longer available. This kludges them back ++ * into life ++ */ ++ if (!ssh_gssapi_oid_table_ok()) ++ if ((mechs = ssh_gssapi_server_mechanisms())) ++ free(mechs); ++ ++ debug2("%s: Identifying %s", __func__, ssh->kex->name); ++ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ /* store client proposal to provide valid signature */ ++ cmin = packet_get_int(); ++ nbits = packet_get_int(); ++ cmax = packet_get_int(); ++ min = MAX(fips_dh_grp_min(), cmin); ++ max = MIN(DH_GRP_MAX, cmax); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) { ++ if (nbits < min && nbits >= DH_GRP_MIN_RFC) ++ logit("DH parameter requested by the client (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted minimum " ++ "via the KexDHMin option.", ++ nbits); ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ } ++ dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(dh->p); ++ packet_put_bignum2(dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ dh_gen_key(dh, ssh->kex->we_need * 8); ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (dh_client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ if ((dh_client_pub = BN_new()) == NULL) ++ fatal("dh_client_pub == NULL"); ++ ++ packet_get_bignum2(dh_client_pub); ++ ++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ ++ break; ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ break; ++ default: ++ packet_disconnect( ++ "Protocol error: didn't expect packet type %d", ++ type); ++ } ++ ++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, ++ &send_tok, &ret_flags)); ++ ++ free(recv_tok.value); ++ ++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) ++ fatal("Zero length token output when incomplete"); ++ ++ if (dh_client_pub == NULL) ++ fatal("No client public key"); ++ ++ if (maj_status & GSS_S_CONTINUE_NEEDED) { ++ debug("Sending GSSAPI_CONTINUE"); ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ if (GSS_ERROR(maj_status)) { ++ if (send_tok.length > 0) { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, send_tok.length); ++ packet_send(); ++ } ++ fatal("accept_ctx died"); ++ } ++ ++ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) ++ fatal("Mutual Authentication flag wasn't set"); ++ ++ if (!(ret_flags & GSS_C_INTEG_FLAG)) ++ fatal("Integrity flag wasn't set"); ++ ++ if (!dh_pub_is_valid(dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, dh); ++ if ((int)kout < 0) ++ fatal("DH_compute_key: failed"); ++ ++ shared_secret = BN_new(); ++ if (shared_secret == NULL) ++ fatal("kexgss_server: BN_new failed"); ++ ++ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) ++ fatal("kexgss_server: BN_bin2bn failed"); ++ ++ memset(kbuf, 0, klen); ++ free(kbuf); ++ ++ hashlen = sizeof(hash); ++ switch (ssh->kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), ++ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, dh->pub_key, shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ ssh->kex->hash_alg, ++ ssh->kex->client_version_string, ssh->kex->server_version_string, ++ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), ++ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), ++ NULL, 0, ++ cmin, nbits, cmax, ++ dh->p, dh->g, ++ dh_client_pub, ++ dh->pub_key, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); ++ } ++ ++ BN_clear_free(dh_client_pub); ++ ++ if (ssh->kex->session_id == NULL) { ++ ssh->kex->session_id_len = hashlen; ++ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); ++ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); ++ } ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) ++ fatal("Couldn't get MIC"); ++ ++ packet_start(SSH2_MSG_KEXGSS_COMPLETE); ++ packet_put_bignum2(dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ DH_free(dh); ++ ++ /* TODO: check kex_derive_keys_bn return value */ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ kex_send_newkeys(ssh); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++ return 0; ++} ++#endif /* GSSAPI */ +Index: openssh-7.8p1/monitor.c +=================================================================== +--- openssh-7.8p1.orig/monitor.c ++++ openssh-7.8p1/monitor.c +@@ -143,6 +143,8 @@ int mm_answer_gss_setup_ctx(int, struct + int mm_answer_gss_accept_ctx(int, struct sshbuf *); + int mm_answer_gss_userok(int, struct sshbuf *); + int mm_answer_gss_checkmic(int, struct sshbuf *); ++int mm_answer_gss_sign(int, struct sshbuf *); ++int mm_answer_gss_updatecreds(int, struct sshbuf *); + #endif + + #ifdef SSH_AUDIT_EVENTS +@@ -213,6 +215,7 @@ struct mon_table mon_dispatch_proto20[] + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + {0, 0, NULL} + }; +@@ -229,6 +232,12 @@ struct mon_table mon_dispatch_postauth20 + {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, + {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, + #endif ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, ++#endif + {0, 0, NULL} + }; + +@@ -287,7 +296,10 @@ monitor_child_preauth(Authctxt *_authctx + /* Permit requests for moduli and signatures */ + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); +- ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + /* The first few requests do not require asynchronous access */ + while (!authenticated) { + partial = 0; +@@ -399,6 +411,10 @@ monitor_child_postauth(struct monitor *p + monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); ++#ifdef GSSAPI ++ /* and for the GSSAPI key exchange */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); ++#endif + + if (auth_opts->permit_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); +@@ -605,7 +621,7 @@ mm_answer_moduli(int sock, struct sshbuf + int + mm_answer_sign(int sock, struct sshbuf *m) + { +- struct ssh *ssh = active_state; /* XXX */ ++ struct ssh *ssh = active_state; /* XXX */ + extern int auth_sock; /* XXX move to state struct? */ + struct sshkey *key; + struct sshbuf *sigbuf = NULL; +@@ -1643,7 +1659,7 @@ monitor_apply_keystate(struct monitor *p + + debug3("%s: packet_set_state", __func__); + if ((r = ssh_packet_set_state(ssh, child_state)) != 0) +- fatal("%s: packet_set_state: %s", __func__, ssh_err(r)); ++ fatal("%s: packet_set_state: %s", __func__, ssh_err(r)); + sshbuf_free(child_state); + child_state = NULL; + +@@ -1662,6 +1678,13 @@ monitor_apply_keystate(struct monitor *p + # endif + #endif /* WITH_OPENSSL */ + kex->kex[KEX_C25519_SHA256] = kexc25519_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->load_host_public_key=&get_hostkey_public_by_type; + kex->load_host_private_key=&get_hostkey_private_by_type; + kex->host_key_index=&get_hostkey_index; +@@ -1752,8 +1775,8 @@ mm_answer_gss_setup_ctx(int sock, struct + u_char *p; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI authentication not enabled", __func__); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1785,7 +1808,7 @@ mm_answer_gss_accept_ctx(int sock, struc + OM_uint32 flags = 0; /* GSI needs this */ + int r; + +- if (!options.gss_authentication) ++ if (!options.gss_authentication && !options.gss_keyex) + fatal("%s: GSSAPI authentication not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) +@@ -1806,6 +1829,7 @@ mm_answer_gss_accept_ctx(int sock, struc + monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); + } + return (0); + } +@@ -1817,7 +1841,7 @@ mm_answer_gss_checkmic(int sock, struct + OM_uint32 ret; + int r; + +- if (!options.gss_authentication) ++ if (!options.gss_authentication && !options.gss_keyex) + fatal("%s: GSSAPI authentication not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || +@@ -1847,10 +1871,11 @@ mm_answer_gss_userok(int sock, struct ss + int r, authenticated; + const char *displayname; + +- if (!options.gss_authentication) ++ if (!options.gss_authentication && !options.gss_keyex) + fatal("%s: GSSAPI authentication not enabled", __func__); + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) +@@ -1867,5 +1892,73 @@ mm_answer_gss_userok(int sock, struct ss + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } +-#endif /* GSSAPI */ + ++int ++mm_answer_gss_sign(int socket, struct sshbuf *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ int r; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ ++ if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ if (data.length != 20) ++ fatal("%s: data length incorrect: %d", __func__, ++ (int) data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ free(data.value); ++ ++ sshbuf_reset(m); ++ if ((r = sshbuf_put_u32(m, major)) != 0 || ++ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ /* And credential updating, for when rekeying */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); ++ ++ return (0); ++} ++ ++int ++mm_answer_gss_updatecreds(int socket, struct sshbuf *m) { ++ ssh_gssapi_ccache store; ++ int ok, r; ++ ++ if ((r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 || ++ (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ free(store.envvar); ++ free(store.envval); ++ ++ sshbuf_reset(m); ++ if ((r = sshbuf_put_u32(m, ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ ++#endif /* GSSAPI */ +Index: openssh-7.8p1/monitor.h +=================================================================== +--- openssh-7.8p1.orig/monitor.h ++++ openssh-7.8p1/monitor.h +@@ -63,6 +63,9 @@ enum monitor_reqtype { + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, + MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, + ++ MONITOR_REQ_GSSSIGN = 201, MONITOR_ANS_GSSSIGN = 202, ++ MONITOR_REQ_GSSUPCREDS = 203, MONITOR_ANS_GSSUPCREDS = 204, ++ + }; + + struct monitor { +Index: openssh-7.8p1/monitor_wrap.c +=================================================================== +--- openssh-7.8p1.orig/monitor_wrap.c ++++ openssh-7.8p1/monitor_wrap.c +@@ -984,7 +984,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw) + { + struct sshbuf *m; + int r, authenticated = 0; +@@ -1003,4 +1003,52 @@ mm_ssh_gssapi_userok(char *user) + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ struct sshbuf *m; ++ OM_uint32 major; ++ int r; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); ++ ++ if ((r = sshbuf_get_u32(m, &major)) != 0 || ++ (r = sshbuf_get_string(m, (u_char **)&hash->value, &hash->length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return(major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ struct sshbuf *m; ++ int ok, r; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ if ((r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 || ++ (r = sshbuf_put_cstring(m, store->envval ? store->envval : "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); ++ ++ if ((r = sshbuf_get_u32(m, &ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (ok); ++} + #endif /* GSSAPI */ +Index: openssh-7.8p1/monitor_wrap.h +=================================================================== +--- openssh-7.8p1.orig/monitor_wrap.h ++++ openssh-7.8p1/monitor_wrap.h +@@ -60,8 +60,10 @@ int mm_sshkey_verify(const struct sshkey + 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 *); +-int mm_ssh_gssapi_userok(char *user); ++int mm_ssh_gssapi_userok(char *user, struct passwd *); + OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); ++OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); ++int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); + #endif + + #ifdef USE_PAM +Index: openssh-7.8p1/readconf.c +=================================================================== +--- openssh-7.8p1.orig/readconf.c ++++ openssh-7.8p1/readconf.c +@@ -163,6 +163,8 @@ typedef enum { + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -203,10 +205,20 @@ static struct { + /* Sometimes-unsupported options */ + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, + # else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapiserveridentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, +@@ -979,10 +991,30 @@ parse_time: + intptr = &options->gss_authentication; + goto parse_flag; + ++ case oGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case oGssDelegateCreds: + intptr = &options->gss_deleg_creds; + goto parse_flag; + ++ case oGssTrustDns: ++ intptr = &options->gss_trust_dns; ++ goto parse_flag; ++ ++ case oGssClientIdentity: ++ charptr = &options->gss_client_identity; ++ goto parse_string; ++ ++ case oGssServerIdentity: ++ charptr = &options->gss_server_identity; ++ goto parse_string; ++ ++ case oGssRenewalRekey: ++ intptr = &options->gss_renewal_rekey; ++ goto parse_flag; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1844,7 +1876,12 @@ initialize_options(Options * options) + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -1990,8 +2027,14 @@ fill_default_options(Options * options) + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +Index: openssh-7.8p1/readconf.h +=================================================================== +--- openssh-7.8p1.orig/readconf.h ++++ openssh-7.8p1/readconf.h +@@ -40,7 +40,12 @@ typedef struct { + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ ++ char *gss_server_identity; /* GSSAPI target principal */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +Index: openssh-7.8p1/regress/cert-hostkey.sh +=================================================================== +--- openssh-7.8p1.orig/regress/cert-hostkey.sh ++++ openssh-7.8p1/regress/cert-hostkey.sh +@@ -66,7 +66,7 @@ touch $OBJ/host_revoked_plain + touch $OBJ/host_revoked_cert + cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca + +-PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` ++PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` + + if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then + PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" +Index: openssh-7.8p1/regress/cert-userkey.sh +=================================================================== +--- openssh-7.8p1.orig/regress/cert-userkey.sh ++++ openssh-7.8p1/regress/cert-userkey.sh +@@ -7,7 +7,7 @@ rm -f $OBJ/authorized_keys_$USER $OBJ/us + cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak + cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak + +-PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` ++PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` + EXTRA_TYPES="" + + if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then +Index: openssh-7.8p1/regress/kextype.sh +=================================================================== +--- openssh-7.8p1.orig/regress/kextype.sh ++++ openssh-7.8p1/regress/kextype.sh +@@ -14,6 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/ssh + + tries="1 2 3 4" + for k in `${SSH} -Q kex`; do ++ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then ++ continue ++ fi + verbose "kex $k" + for i in $tries; do + ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true +Index: openssh-7.8p1/regress/rekey.sh +=================================================================== +--- openssh-7.8p1.orig/regress/rekey.sh ++++ openssh-7.8p1/regress/rekey.sh +@@ -38,6 +38,9 @@ increase_datafile_size 300 + + opts="" + for i in `${SSH} -Q kex`; do ++ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then ++ continue ++ fi + opts="$opts KexAlgorithms=$i" + done + for i in `${SSH} -Q cipher`; do +@@ -56,6 +59,9 @@ done + if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then + for c in `${SSH} -Q cipher-auth`; do + for kex in `${SSH} -Q kex`; do ++ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then ++ continue ++ fi + verbose "client rekey $c $kex" + ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c + done +Index: openssh-7.8p1/servconf.c +=================================================================== +--- openssh-7.8p1.orig/servconf.c ++++ openssh-7.8p1/servconf.c +@@ -130,8 +130,10 @@ initialize_server_options(ServerOptions + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; + options->gss_authentication=-1; ++ options->gss_keyex = -1; + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; ++ options->gss_store_rekey = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -369,10 +371,14 @@ fill_default_server_options(ServerOption + options->kerberos_get_afs_token = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_cleanup_creds == -1) + options->gss_cleanup_creds = 1; + if (options->gss_strict_acceptor == -1) + options->gss_strict_acceptor = 1; ++ if (options->gss_store_rekey == -1) ++ options->gss_store_rekey = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -519,6 +525,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -596,11 +603,17 @@ static struct { + { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, + { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, + { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, + { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, + { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1503,6 +1516,10 @@ process_server_config_line(ServerOptions + intptr = &options->gss_authentication; + goto parse_flag; + ++ case sGssKeyEx: ++ intptr = &options->gss_keyex; ++ goto parse_flag; ++ + case sGssCleanupCreds: + intptr = &options->gss_cleanup_creds; + goto parse_flag; +@@ -1511,6 +1528,10 @@ process_server_config_line(ServerOptions + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2304,6 +2325,10 @@ copy_set_server_options(ServerOptions *d + + M_CP_INTOPT(password_authentication); + M_CP_INTOPT(gss_authentication); ++ M_CP_INTOPT(gss_keyex); ++ M_CP_INTOPT(gss_cleanup_creds); ++ M_CP_INTOPT(gss_strict_acceptor); ++ M_CP_INTOPT(gss_store_rekey); + M_CP_INTOPT(pubkey_authentication); + M_CP_INTOPT(kerberos_authentication); + M_CP_INTOPT(hostbased_authentication); +@@ -2600,7 +2625,10 @@ dump_config(ServerOptions *o) + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +Index: openssh-7.8p1/servconf.h +=================================================================== +--- openssh-7.8p1.orig/servconf.h ++++ openssh-7.8p1/servconf.h +@@ -16,6 +16,8 @@ + #ifndef SERVCONF_H + #define SERVCONF_H + ++#include "misc.h" ++ + #define MAX_PORTS 256 /* Max # ports. */ + + #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ +@@ -125,8 +127,10 @@ typedef struct { + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ + int gss_authentication; /* If true, permit GSSAPI authentication */ ++ int gss_keyex; /* If true, permit GSSAPI key exchange */ + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ ++ int gss_store_rekey; + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +Index: openssh-7.8p1/ssh-gss.h +=================================================================== +--- openssh-7.8p1.orig/ssh-gss.h ++++ openssh-7.8p1/ssh-gss.h +@@ -61,10 +61,22 @@ + + #define SSH_GSS_OIDTYPE 0x06 + ++#define SSH2_MSG_KEXGSS_INIT 30 ++#define SSH2_MSG_KEXGSS_CONTINUE 31 ++#define SSH2_MSG_KEXGSS_COMPLETE 32 ++#define SSH2_MSG_KEXGSS_HOSTKEY 33 ++#define SSH2_MSG_KEXGSS_ERROR 34 ++#define SSH2_MSG_KEXGSS_GROUPREQ 40 ++#define SSH2_MSG_KEXGSS_GROUP 41 ++#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" ++#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" ++#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++ + typedef struct { + char *filename; + char *envvar; + char *envval; ++ struct passwd *owner; + void *data; + } ssh_gssapi_ccache; + +@@ -72,8 +84,11 @@ typedef struct { + gss_buffer_desc displayname; + gss_buffer_desc exportedname; + gss_cred_id_t creds; ++ gss_name_t name; + struct ssh_gssapi_mech_struct *mech; + ssh_gssapi_ccache store; ++ int used; ++ int updated; + } ssh_gssapi_client; + + typedef struct ssh_gssapi_mech_struct { +@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); + void (*storecreds) (ssh_gssapi_client *); ++ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + + typedef struct { +@@ -94,10 +110,11 @@ typedef struct { + gss_OID oid; /* client */ + gss_cred_id_t creds; /* server */ + gss_name_t client; /* server */ +- gss_cred_id_t client_creds; /* server */ ++ gss_cred_id_t client_creds; /* both */ + } Gssctxt; + + extern ssh_gssapi_mech *supported_mechs[]; ++extern Gssctxt *gss_kex_context; + + int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); + void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); +@@ -123,17 +140,31 @@ void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(struct sshbuf *, const char *, + const char *, const char *); +-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); ++int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); ++OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); ++int ssh_gssapi_credentials_updated(Gssctxt *); + + /* In the server */ ++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, const char *); ++char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *); ++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, ++ const char *); ++gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); ++int ssh_gssapi_server_check_mech(Gssctxt **, gss_OID, const char *, const char *); + OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +-int ssh_gssapi_userok(char *name); ++int ssh_gssapi_userok(char *name, struct passwd *); + OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_do_child(char ***, u_int *); + void ssh_gssapi_cleanup_creds(void); + void ssh_gssapi_storecreds(void); + const char *ssh_gssapi_displayname(void); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(); ++ ++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); ++ ++void ssh_gssapi_rekey_creds(void); + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +Index: openssh-7.8p1/ssh_config +=================================================================== +--- openssh-7.8p1.orig/ssh_config ++++ openssh-7.8p1/ssh_config +@@ -45,6 +45,8 @@ Host * + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +Index: openssh-7.8p1/ssh_config.0 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.0 ++++ openssh-7.8p1/ssh_config.0 +@@ -410,9 +410,40 @@ DESCRIPTION + Specifies whether user authentication based on GSSAPI is allowed. + The default is no. + ++ GSSAPIKeyExchange ++ Specifies whether key exchange based on GSSAPI may be used. When ++ using GSSAPI key exchange the server need not have a host key. ++ The default is no. ++ Note that this option applies to protocol version 2 only. ++ ++ GSSAPIClientIdentity ++ If set, specifies the GSSAPI client identity that ssh should use ++ when connecting to the server. The default is unset, which means ++ that the default identity will be used. ++ ++ GSSAPIServerIdentity ++ If set, specifies the GSSAPI server identity that ssh should expect ++ when connecting to the server. The default is unset, which means ++ that the expected GSSAPI server identity will be determined from ++ the target hostname. ++ + GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. The default is no. + ++ GSSAPIRenewalForcesRekey ++ If set to yes then renewal of the client's GSSAPI credentials will ++ force the rekeying of the ssh connection. With a compatible server, ++ this can delegate the renewed credentials to a session on the ++ server. The default is no. ++ ++ GSSAPITrustDns ++ Set to yes to indicate that the DNS is trusted to securely ++ canonicalize the name of the host being connected to. If no, the ++ hostname entered on the command line will be passed untouched to ++ the GSSAPI library. The default is no. ++ This option only applies to protocol version 2 connections using ++ GSSAPI. ++ + HashKnownHosts + Indicates that ssh(1) should hash host names and addresses when + they are added to ~/.ssh/known_hosts. These hashed names may be +Index: openssh-7.8p1/ssh_config.5 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.5 ++++ openssh-7.8p1/ssh_config.5 +@@ -720,10 +720,40 @@ The default is + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Cm no . ++.It Cm GSSAPIClientIdentity ++If set, specifies the GSSAPI client identity that ssh should use when ++connecting to the server. The default is unset, which means that the default ++identity will be used. + .It Cm GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + The default is + .Cm no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI may be used. When using ++GSSAPI key exchange the server need not have a host key. ++The default is ++.Dq no . ++.It Cm GSSAPIRenewalForcesRekey ++If set to ++.Dq yes ++then renewal of the client's GSSAPI credentials will force the rekeying of the ++ssh connection. With a compatible server, this can delegate the renewed ++credentials to a session on the server. ++The default is ++.Dq no . ++.It Cm GSSAPIServerIdentity ++If set, specifies the GSSAPI server identity that ssh should expect when ++connecting to the server. The default is unset, which means that the ++expected GSSAPI server identity will be determined from the target ++hostname. ++.It Cm GSSAPITrustDns ++Set to ++.Dq yes to indicate that the DNS is trusted to securely canonicalize ++the name of the host being connected to. If ++.Dq no, the hostname entered on the ++command line will be passed untouched to the GSSAPI library. ++The default is ++.Dq no . + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +Index: openssh-7.8p1/sshconnect2.c +=================================================================== +--- openssh-7.8p1.orig/sshconnect2.c ++++ openssh-7.8p1/sshconnect2.c +@@ -82,6 +82,124 @@ extern char *client_version_string; + extern char *server_version_string; + extern Options options; + ++/* XXX from auth.h -- refactoring move these useful functions away of client context*/ ++ ++/* ++ * Returns the remote DNS hostname as a string. The returned string must not ++ * be freed. NB. this will usually trigger a DNS query the first time it is ++ * called. ++ * This function does additional checks on the hostname to mitigate some ++ * attacks on legacy rhosts-style authentication. ++ * XXX is RhostsRSAAuthentication vulnerable to these? ++ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) ++ */ ++ ++static char * ++remote_hostname(struct ssh *ssh) ++{ ++ struct sockaddr_storage from; ++ socklen_t fromlen; ++ struct addrinfo hints, *ai, *aitop; ++ char name[NI_MAXHOST], ntop2[NI_MAXHOST]; ++ const char *ntop = ssh_remote_ipaddr(ssh); ++ ++ /* Get IP address of client. */ ++ fromlen = sizeof(from); ++ memset(&from, 0, sizeof(from)); ++ if (getpeername(ssh_packet_get_connection_in(ssh), ++ (struct sockaddr *)&from, &fromlen) < 0) { ++ debug("getpeername failed: %.100s", strerror(errno)); ++ return strdup(ntop); ++ } ++ ++ ipv64_normalise_mapped(&from, &fromlen); ++ if (from.ss_family == AF_INET6) ++ fromlen = sizeof(struct sockaddr_in6); ++ ++ debug3("Trying to reverse map address %.100s.", ntop); ++ /* Map the IP address to a host name. */ ++ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), ++ NULL, 0, NI_NAMEREQD) != 0) { ++ /* Host name not found. Use ip address. */ ++ return strdup(ntop); ++ } ++ ++ /* ++ * if reverse lookup result looks like a numeric hostname, ++ * someone is trying to trick us by PTR record like following: ++ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { ++ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", ++ name, ntop); ++ freeaddrinfo(ai); ++ return strdup(ntop); ++ } ++ ++ /* Names are stored in lowercase. */ ++ lowercase(name); ++ ++ /* ++ * Map it back to an IP address and check that the given ++ * address actually is an address of this host. This is ++ * necessary because anyone with access to a name server can ++ * define arbitrary names for an IP address. Mapping from ++ * name to IP address can be trusted better (but can still be ++ * fooled if the intruder has access to the name server of ++ * the domain). ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = from.ss_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { ++ logit("reverse mapping checking getaddrinfo for %.700s " ++ "[%s] failed.", name, ntop); ++ return strdup(ntop); ++ } ++ /* Look for the address from the list of addresses. */ ++ for (ai = aitop; ai; ai = ai->ai_next) { ++ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, ++ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && ++ (strcmp(ntop, ntop2) == 0)) ++ break; ++ } ++ freeaddrinfo(aitop); ++ /* If we reached the end of the list, the address was not there. */ ++ if (ai == NULL) { ++ /* Address not found for the host name. */ ++ logit("Address %.100s maps to %.600s, but this does not " ++ "map back to the address.", ntop, name); ++ return strdup(ntop); ++ } ++ return strdup(name); ++} ++ ++/* ++ * Return the canonical name of the host in the other side of the current ++ * connection. The host name is cached, so it is efficient to call this ++ * several times. ++ */ ++ ++const char * ++get_canonical_hostname(struct ssh *ssh, int use_dns) ++{ ++ static char *dnsname; ++ ++ if (!use_dns) ++ return ssh_remote_ipaddr(ssh); ++ else if (dnsname != NULL) ++ return dnsname; ++ else { ++ dnsname = remote_hostname(ssh); ++ return dnsname; ++ } ++} ++ ++ ++ + /* + * SSH2 key exchange + */ +@@ -162,9 +280,37 @@ ssh_kex2(char *host, struct sockaddr *ho + struct kex *kex; + int r; + ++#ifdef GSSAPI ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + ++#ifdef GSSAPI ++ /* TODO: should we use myproposal[PROPOSAL_KEX_ALGS] ++ * instead of options.kex_algorithms? */ ++ if (options.gss_keyex) { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = options.kex_algorithms; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(active_state, 1); ++ else ++ gss_host = host; ++ ++ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity, ++ options.kex_algorithms); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&options.kex_algorithms, ++ "%s,%s", gss, orig); ++ } ++ } ++#endif ++ + if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) + fatal("%s: kex_names_cat", __func__); + myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); +@@ -194,6 +340,17 @@ ssh_kex2(char *host, struct sockaddr *ho + order_hostkeyalgs(host, hostaddr, port)); + } + ++#ifdef GSSAPI ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ if (options.gss_keyex && gss) { ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ free(gss); ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits(options.rekey_limit, + options.rekey_interval); +@@ -215,10 +372,30 @@ ssh_kex2(char *host, struct sockaddr *ho + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ } ++#endif + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_client = options.gss_client_identity; ++ if (options.gss_server_identity) { ++ kex->gss_host = options.gss_server_identity; ++ } else { ++ kex->gss_host = gss_host; ++ } ++ } ++#endif ++ + ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); + + /* remove ext-info from the KEX proposals for rekeying */ +@@ -314,6 +491,7 @@ int input_gssapi_token(int type, u_int32 + int input_gssapi_hash(int type, u_int32_t, struct ssh *); + int input_gssapi_error(int, u_int32_t, struct ssh *); + int input_gssapi_errtok(int, u_int32_t, struct ssh *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); +@@ -330,6 +508,11 @@ static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + NULL, +@@ -657,19 +840,31 @@ userauth_gssapi(Authctxt *authctxt) + static u_int mech = 0; + OM_uint32 min; + int r, ok = 0; ++ const char *gss_host; ++ ++if (options.gss_server_identity) ++ gss_host = options.gss_server_identity; ++ else if (options.gss_trust_dns) ++ gss_host = get_canonical_hostname(active_state, 1); ++ else ++ gss_host = authctxt->host; + + /* Try one GSSAPI method at a time, rather than sending them all at + * once. */ + + if (gss_supported == NULL) +- gss_indicate_mechs(&min, &gss_supported); ++ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { ++ gss_supported = NULL; ++ return 0; ++ } + + /* Check to see if the mechanism is usable before we offer it */ + while (mech < gss_supported->count && !ok) { + /* My DER encoding requires length<128 */ + if (gss_supported->elements[mech].length < 128 && + ssh_gssapi_check_mechanism(&gssctxt, +- &gss_supported->elements[mech], authctxt->host)) { ++ &gss_supported->elements[mech], gss_host, ++ options.gss_client_identity)) { + ok = 1; /* Mechanism works */ + } else { + mech++; +@@ -906,6 +1101,51 @@ input_gssapi_error(int type, u_int32_t p + free(lang); + return r; + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ struct sshbuf *b = NULL; ++ gss_buffer_desc gssbuf; ++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; ++ OM_uint32 ms; ++ ++ static int attempt = 0; ++ if (attempt++ >= 1) ++ return (0); ++ ++ if (gss_kex_context == NULL) { ++ debug("No valid Key exchange context"); ++ return (0); ++ } ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = sshbuf_mutable_ptr(b); ++ gssbuf.length = sshbuf_len(b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ sshbuf_free(b); ++ return (0); ++ } ++ ++ packet_start(SSH2_MSG_USERAUTH_REQUEST); ++ packet_put_cstring(authctxt->server_user); ++ packet_put_cstring(authctxt->service); ++ packet_put_cstring(authctxt->method->name); ++ packet_put_string(mic.value, mic.length); ++ packet_send(); ++ ++ sshbuf_free(b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int +@@ -1443,8 +1683,8 @@ key_type_allowed_by_config(struct sshkey + + /* + * try keys in the following order: +- * 1. certificates listed in the config file +- * 2. other input certificates ++ * 1. certificates listed in the config file ++ * 2. other input certificates + * 3. agent keys that are found in the config file + * 4. other agent keys + * 5. keys that are only listed in the config file +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -131,6 +131,10 @@ + + #include "fips.h" + ++#ifdef USE_SECURITY_SESSION_API ++#include ++#endif ++ + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) +@@ -555,7 +559,7 @@ privsep_preauth_child(void) + + #ifdef GSSAPI + /* Cache supported mechanism OIDs for later use */ +- if (options.gss_authentication) ++ if (options.gss_authentication || options.gss_keyex) + ssh_gssapi_prepare_supported_oids(); + #endif + +@@ -898,8 +902,9 @@ notify_hostkeys(struct ssh *ssh) + } + debug3("%s: sent %u hostkeys", __func__, nkeys); + if (nkeys == 0) +- fatal("%s: no hostkeys", __func__); +- packet_send(); ++ debug3("%s: no hostkeys", __func__); ++ else ++ packet_send(); + sshbuf_free(buf); + } + +@@ -1838,7 +1843,12 @@ main(int ac, char **av) + free(fp); + } + accumulate_host_timing_secret(cfg, NULL); ++#ifndef GSSAPI + if (!sensitive_data.have_ssh2_key) { ++#else ++ /* The GSSAPI key exchange can run without a host key */ ++ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { ++#endif + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } +@@ -2016,6 +2026,60 @@ main(int ac, char **av) + /* This is the child processing a new connection. */ + setproctitle("%s", "[accepted]"); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. We don't +@@ -2137,6 +2201,60 @@ main(int ac, char **av) + rdomain == NULL ? "" : "\""); + free(laddr); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is +@@ -2320,6 +2438,48 @@ do_ssh2_kex(void) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#ifdef GSSAPI ++ { ++ char *orig; ++ char *gss = NULL; ++ char *newstr = NULL; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ /* ++ * If we don't have a host key, then there's no point advertising ++ * the other key exchange algorithms ++ */ ++ ++ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) ++ orig = NULL; ++ ++ if (options.gss_keyex) ++ gss = ssh_gssapi_server_mechanisms(); ++ else ++ gss = NULL; ++ ++ if (gss && orig) ++ xasprintf(&newstr, "%s,%s", gss, orig); ++ else if (gss) ++ newstr = gss; ++ else if (orig) ++ newstr = orig; ++ ++ /* ++ * If we've got GSSAPI mechanisms, then we've got the 'null' host ++ * key alg, but we can't tell people about it unless its the only ++ * host key algorithm we support ++ */ ++ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) ++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; ++ ++ if (newstr) ++ myproposal[PROPOSAL_KEX_ALGS] = newstr; ++ else ++ fatal("No supported key exchange algorithms"); ++ } ++#endif ++ + /* start key exchange */ + if ((r = kex_setup(active_state, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); +@@ -2337,6 +2497,13 @@ do_ssh2_kex(void) + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_server; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ } ++#endif + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; +Index: openssh-7.8p1/sshd_config +=================================================================== +--- openssh-7.8p1.orig/sshd_config ++++ openssh-7.8p1/sshd_config +@@ -76,6 +76,8 @@ AuthorizedKeysFile .ssh/authorized_keys + # GSSAPI options + #GSSAPIAuthentication no + #GSSAPICleanupCredentials yes ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +Index: openssh-7.8p1/sshd_config.5 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.5 ++++ openssh-7.8p1/sshd_config.5 +@@ -644,6 +644,11 @@ Specifies whether to automatically destr + on logout. + The default is + .Cm yes . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Dq no . + .It Cm GSSAPIStrictAcceptorCheck + Determines whether to be strict about the identity of the GSSAPI acceptor + a client authenticates against. +@@ -658,6 +663,11 @@ machine's default store. + This facility is provided to assist with operation on multi homed machines. + The default is + .Cm yes . ++.It Cm GSSAPIStoreCredentialsOnRekey ++Controls whether the user's GSSAPI credentials should be updated following a ++successful connection rekeying. This option can be used to accepted renewed ++or updated credentials from a compatible client. The default is ++.Dq no . + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. +@@ -1632,16 +1642,16 @@ as a non-root user. + The default is + .Cm no . + .It Cm UsePAMCheckLocks +-When set to ++When set to + .Dq yes + , the checks whether the account has been locked with + .Pa passwd -l +-are performed even when PAM authentication is enabled via ++are performed even when PAM authentication is enabled via + .Cm UsePAM . + This is to ensure that it is not possible to log in with e.g. a + public key (in such a case PAM is used only to set up the session and some PAM + modules will not check whether the account is locked in this scenario). The +-default is ++default is + .Dq no . + .It Cm VersionAddendum + Optionally specifies additional text to append to the SSH protocol banner +Index: openssh-7.8p1/sshkey.c +=================================================================== +--- openssh-7.8p1.orig/sshkey.c ++++ openssh-7.8p1/sshkey.c +@@ -140,6 +140,7 @@ static const struct keytype keytypes[] = + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", NULL, KEY_NULL, 0, 0, 1 }, + { NULL, NULL, NULL, -1, -1, 0, 0 } + }; + +Index: openssh-7.8p1/sshkey.h +=================================================================== +--- openssh-7.8p1.orig/sshkey.h ++++ openssh-7.8p1/sshkey.h +@@ -63,6 +63,7 @@ enum sshkey_types { + KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + +Index: openssh-7.8p1/sshd_config.0 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.0 ++++ openssh-7.8p1/sshd_config.0 +@@ -370,6 +370,12 @@ DESCRIPTION + Specifies whether user authentication based on GSSAPI is allowed. + The default is no. + ++ GSSAPIKeyExchange ++ Specifies whether key exchange based on GSSAPI is allowed. GSSAPI ++ key exchange doesn't rely on ssh keys to verify host identity. The ++ default is no. ++ Note that this option applies to protocol version 2 only. ++ + GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials + cache on logout. The default is yes. +@@ -383,6 +388,12 @@ DESCRIPTION + facility is provided to assist with operation on multi homed + machines. The default is yes. + ++ GSSAPIStoreCredentialsOnRekey ++ Controls whether the user's GSSAPI credentials should be updated ++ following a successful connection rekeying. This option can be used ++ to accepted renewed or updated credentials from a compatible ++ client. The default is no. ++ + HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased + authentication as a list of comma-separated patterns. diff --git a/openssh-7.7p1-host_ident.patch b/openssh-7.7p1-host_ident.patch new file mode 100644 index 0000000..e164b01 --- /dev/null +++ b/openssh-7.7p1-host_ident.patch @@ -0,0 +1,29 @@ +# HG changeset patch +# Parent 0f731d0b541b8a919d24ac91098f560a49712822 +Suggest command line for removal of offending keys from known_hosts file + +diff --git a/openssh-7.7p1/sshconnect.c b/openssh-7.7p1/sshconnect.c +--- openssh-7.7p1/sshconnect.c ++++ openssh-7.7p1/sshconnect.c +@@ -1147,16 +1147,21 @@ check_host_key(char *hostname, struct so + } + /* 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", + sshkey_type(host_found->key), + host_found->file, host_found->line); ++ error("You can use following command to remove the offending key:"); ++ if (host_found->file) ++ error("ssh-keygen -R %s -f %s", host, host_found->file); ++ else ++ error("ssh-keygen -R %s", host); + + /* + * 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 != + SSH_STRICT_HOSTKEY_OFF) { + error("%s host key for %.200s has changed and you have " diff --git a/openssh-7.7p1-hostname_changes_when_forwarding_X.patch b/openssh-7.7p1-hostname_changes_when_forwarding_X.patch new file mode 100644 index 0000000..c7daaf1 --- /dev/null +++ b/openssh-7.7p1-hostname_changes_when_forwarding_X.patch @@ -0,0 +1,85 @@ +# HG changeset patch +# Parent 5e19a205fa03584bb0d829ecbba7495ce1899b65 +# -- uset do be called '-xauthlocalhostname' +handle hostname changes when forwarding X + +bnc#98627 + +Index: openssh-7.8p1/session.c +=================================================================== +--- openssh-7.8p1.orig/session.c ++++ openssh-7.8p1/session.c +@@ -1009,7 +1009,7 @@ copy_environment(char **source, char *** + } + + static char ** +-do_setup_env(struct ssh *ssh, Session *s, const char *shell) ++do_setup_env(struct ssh *ssh, Session *s, const char *shell, int *env_size) + { + char buf[256]; + size_t n; +@@ -1213,6 +1213,8 @@ do_setup_env(struct ssh *ssh, Session *s + for (i = 0; env[i]; i++) + fprintf(stderr, " %.200s\n", env[i]); + } ++ ++ *env_size = envsize; + return env; + } + +@@ -1221,7 +1223,7 @@ do_setup_env(struct ssh *ssh, Session *s + * first in this order). + */ + static void +-do_rc_files(struct ssh *ssh, Session *s, const char *shell) ++do_rc_files(struct ssh *ssh, Session *s, const char *shell, char **env, int *env_size) + { + FILE *f = NULL; + char cmd[1024]; +@@ -1276,12 +1278,20 @@ do_rc_files(struct ssh *ssh, Session *s, + options.xauth_location); + f = popen(cmd, "w"); + if (f) { ++ char hostname[MAXHOSTNAMELEN]; ++ + fprintf(f, "remove %s\n", + s->auth_display); + fprintf(f, "add %s %s %s\n", + s->auth_display, s->auth_proto, + s->auth_data); + pclose(f); ++ if (gethostname(hostname,sizeof(hostname)) >= 0) ++ child_set_env(&env,env_size,"XAUTHLOCALHOSTNAME", ++ hostname); ++ else ++ debug("Cannot set up XAUTHLOCALHOSTNAME %s\n", ++ strerror(errno)); + } else { + fprintf(stderr, "Could not run %s\n", + cmd); +@@ -1534,6 +1544,7 @@ do_child(struct ssh *ssh, Session *s, co + { + extern char **environ; + char **env; ++ int env_size; + char *argv[ARGV_MAX]; + const char *shell, *shell0; + struct passwd *pw = s->pw; +@@ -1591,7 +1602,7 @@ do_child(struct ssh *ssh, Session *s, co + * Make sure $SHELL points to the shell from the password file, + * even if shell is overridden from login.conf + */ +- env = do_setup_env(ssh, s, shell); ++ env = do_setup_env(ssh, s, shell, &env_size); + + #ifdef HAVE_LOGIN_CAP + shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); +@@ -1655,7 +1666,7 @@ do_child(struct ssh *ssh, Session *s, co + + closefrom(STDERR_FILENO + 1); + +- do_rc_files(ssh, s, shell); ++ do_rc_files(ssh, s, shell, env, &env_size); + + /* restore SIGPIPE for child */ + signal(SIGPIPE, SIG_DFL); diff --git a/openssh-7.7p1-ldap.patch b/openssh-7.7p1-ldap.patch new file mode 100644 index 0000000..5d6aa88 --- /dev/null +++ b/openssh-7.7p1-ldap.patch @@ -0,0 +1,2710 @@ +# HG changeset patch +# Parent 860ce7cab52a11c12df044de6ffdcffa58c13f23 +# 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) + +Index: openssh-7.8p1/HOWTO.ldap-keys +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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. +Index: openssh-7.8p1/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/Makefile.in ++++ openssh-7.8p1/Makefile.in +@@ -24,6 +24,8 @@ ASKPASS_PROGRAM=$(libexecdir)/ssh-askpas + 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 + CAVSTEST_CTR=$(libexecdir)/cavstest-ctr + CAVSTEST_KDF=$(libexecdir)/cavstest-kdf + PRIVSEP_PATH=@PRIVSEP_PATH@ +@@ -66,6 +68,9 @@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-a + + TARGETS += cavstest-ctr$(EXEEXT) cavstest-kdf$(EXEEXT) + ++INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ ++TARGETS += ssh-ldap-helper$(EXEEXT) ++ + XMSS_OBJS=\ + ssh-xmss.o \ + sshkey-xmss.o \ +@@ -132,8 +137,8 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw + sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ + sandbox-solaris.o uidswap.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 +@@ -208,6 +213,9 @@ ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) + 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) + +@@ -363,6 +371,10 @@ install-files: + $(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 0755 $(STRIP_OPT) cavstest-ctr$(EXEEXT) $(DESTDIR)$(libexecdir)/cavstest-ctr$(EXEEXT) +@@ -381,6 +393,10 @@ install-files: + $(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: + $(MKDIR_P) $(DESTDIR)$(sysconfdir) +@@ -404,6 +420,13 @@ install-sysconf: + 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 \ +@@ -441,6 +464,8 @@ uninstall: + -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 +@@ -452,6 +477,7 @@ uninstall: + -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: + $(MKDIR_P) `pwd`/regress/unittests/test_helper +Index: openssh-7.8p1/configure.ac +=================================================================== +--- openssh-7.8p1.orig/configure.ac ++++ openssh-7.8p1/configure.ac +@@ -1680,6 +1680,106 @@ AC_ARG_WITH([audit], + 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 +Index: openssh-7.8p1/ldap-helper.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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(struct sshbuf *b, u_int *l) { return NULL; } ++void buffer_put_string(struct sshbuf *b, const void *f, u_int l) {} ++ +Index: openssh-7.8p1/ldap-helper.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 */ +Index: openssh-7.8p1/ldap.conf +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 ++ +Index: openssh-7.8p1/ldapbody.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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; ++} ++ +Index: openssh-7.8p1/ldapbody.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 */ ++ +Index: openssh-7.8p1/ldapconf.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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); ++} ++ +Index: openssh-7.8p1/ldapconf.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 */ +Index: openssh-7.8p1/ldapincludes.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 */ +Index: openssh-7.8p1/ldapmisc.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 ++ +Index: openssh-7.8p1/ldapmisc.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 */ ++ +Index: openssh-7.8p1/openbsd-compat/base64.c +=================================================================== +--- openssh-7.8p1.orig/openbsd-compat/base64.c ++++ openssh-7.8p1/openbsd-compat/base64.c +@@ -46,7 +46,7 @@ + + #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 +@@ -129,7 +129,7 @@ static const char Pad64 = '='; + 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) + { +@@ -185,7 +185,7 @@ b64_ntop(u_char const *src, size_t srcle + } + #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) +Index: openssh-7.8p1/openbsd-compat/base64.h +=================================================================== +--- openssh-7.8p1.orig/openbsd-compat/base64.h ++++ openssh-7.8p1/openbsd-compat/base64.h +@@ -45,16 +45,16 @@ + + #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) +Index: openssh-7.8p1/openssh-lpk-openldap.schema +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 ) ++ ) +Index: openssh-7.8p1/openssh-lpk-sun.schema +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 ) ++ ) +Index: openssh-7.8p1/ssh-ldap-helper.8 +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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 +Index: openssh-7.8p1/ssh-ldap-wrapper +=================================================================== +--- /dev/null ++++ openssh-7.8p1/ssh-ldap-wrapper +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++exec @LIBEXECDIR@/ssh-ldap-helper -s "$1" ++ +Index: openssh-7.8p1/ssh-ldap.conf.5 +=================================================================== +--- /dev/null ++++ openssh-7.8p1/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.7p1-no_fork-no_pid_file.patch b/openssh-7.7p1-no_fork-no_pid_file.patch new file mode 100644 index 0000000..75d275c --- /dev/null +++ b/openssh-7.7p1-no_fork-no_pid_file.patch @@ -0,0 +1,26 @@ +# HG changeset patch +# Parent bad0c8b3b8d72abb6960ed85b57ee42352371738 +Do not write a PID file when not daemonizing (e.g. when running from systemd) + +diff --git a/openssh-7.7p1/sshd.c b/openssh-7.7p1/sshd.c +--- openssh-7.7p1/sshd.c ++++ openssh-7.7p1/sshd.c +@@ -1996,17 +1996,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.7p1-openssl_1.1.0.patch b/openssh-7.7p1-openssl_1.1.0.patch new file mode 100644 index 0000000..4cedd9a --- /dev/null +++ b/openssh-7.7p1-openssl_1.1.0.patch @@ -0,0 +1,3102 @@ +# HG changeset patch +# Parent 6e5e3cb13cb379ca302c54a1f21156364781f8b6 +OpenSSL 1.1.0 API shims and crutches +modified from RH patch + +Index: openssh-7.8p1/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/Makefile.in ++++ openssh-7.8p1/Makefile.in +@@ -112,6 +112,8 @@ LIBSSH_OBJS += kexgssc.o kexgsss.o + + LIBSSH_OBJS += auditstub.o + ++LIBSSH_OBJS += libcrypto-compat.o ++ + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o + +Index: openssh-7.8p1/auth-pam.c +=================================================================== +--- openssh-7.8p1.orig/auth-pam.c ++++ openssh-7.8p1/auth-pam.c +@@ -128,6 +128,10 @@ extern u_int utmp_len; + typedef pthread_t sp_pthread_t; + #else + typedef pid_t sp_pthread_t; ++# define pthread_create(a, b, c, d) _ssh_compat_pthread_create(a, b, c, d) ++# define pthread_exit(a) _ssh_compat_pthread_exit(a) ++# define pthread_cancel(a) _ssh_compat_pthread_cancel(a) ++# define pthread_join(a, b) _ssh_compat_pthread_join(a, b) + #endif + + struct pam_ctxt { +Index: openssh-7.8p1/cavstest-ctr.c +=================================================================== +--- openssh-7.8p1.orig/cavstest-ctr.c ++++ openssh-7.8p1/cavstest-ctr.c +@@ -150,7 +150,7 @@ main(int argc, char *argv[]) + usage(); + } + +- SSLeay_add_all_algorithms(); ++ OpenSSL_add_all_algorithms(); + + c = cipher_by_name(algo); + if (c == NULL) { +Index: openssh-7.8p1/cipher.c +=================================================================== +--- openssh-7.8p1.orig/cipher.c ++++ openssh-7.8p1/cipher.c +@@ -299,7 +299,7 @@ cipher_init(struct sshcipher_ctx **ccp, + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if (EVP_CipherInit(cc->evp, type, NULL, (u_char *)iv, ++ if (EVP_CipherInit(cc->evp, type, (u_char *)key, (u_char *)iv, + (do_encrypt == CIPHER_ENCRYPT)) == 0) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; +@@ -317,10 +317,6 @@ cipher_init(struct sshcipher_ctx **ccp, + goto out; + } + } +- if (EVP_CipherInit(cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { +- ret = SSH_ERR_LIBCRYPTO_ERROR; +- goto out; +- } + ret = 0; + #endif /* WITH_OPENSSL */ + out: +@@ -503,7 +499,7 @@ cipher_get_keyiv(struct sshcipher_ctx *c + len, iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else +- memcpy(iv, cc->evp->iv, len); ++ memcpy(iv, EVP_CIPHER_CTX_iv(cc->evp), len); + #endif + return 0; + } +@@ -537,14 +533,14 @@ cipher_set_keyiv(struct sshcipher_ctx *c + EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) + return SSH_ERR_LIBCRYPTO_ERROR; + } else +- memcpy(cc->evp->iv, iv, evplen); ++ memcpy(EVP_CIPHER_CTX_iv_noconst(cc->evp), iv, evplen); + #endif + return 0; + } + + #ifdef WITH_OPENSSL +-#define EVP_X_STATE(evp) (evp)->cipher_data +-#define EVP_X_STATE_LEN(evp) (evp)->cipher->ctx_size ++#define EVP_X_STATE(evp) EVP_CIPHER_CTX_get_cipher_data(evp) ++#define EVP_X_STATE_LEN(evp) EVP_CIPHER_impl_ctx_size(EVP_CIPHER_CTX_cipher(evp)) + #endif + + int +Index: openssh-7.8p1/configure.ac +=================================================================== +--- openssh-7.8p1.orig/configure.ac ++++ openssh-7.8p1/configure.ac +@@ -2626,6 +2626,7 @@ if test "x$openssl" = "xyes" ; then + AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) + ;; + 100*) ;; # 1.0.x ++ 101*) ;; # 1.1.x is supported by this patch too + 200*) ;; # LibreSSL + *) + AC_MSG_ERROR([OpenSSL >= 1.1.0 is not yet supported (have "$ssl_library_ver")]) +Index: openssh-7.8p1/dh.c +=================================================================== +--- openssh-7.8p1.orig/dh.c ++++ openssh-7.8p1/dh.c +@@ -218,14 +218,15 @@ choose_dh(int min, int wantbits, int max + /* diffie-hellman-groupN-sha1 */ + + int +-dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) ++dh_pub_is_valid(const DH *dh, const BIGNUM *dh_pub) + { + int i; + int n = BN_num_bits(dh_pub); + int bits_set = 0; + BIGNUM *tmp; ++ const BIGNUM *p; + +- if (dh_pub->neg) { ++ if (BN_is_negative(dh_pub)) { + logit("invalid public DH value: negative"); + return 0; + } +@@ -238,7 +239,8 @@ dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) + error("%s: BN_new failed", __func__); + return 0; + } +- if (!BN_sub(tmp, dh->p, BN_value_one()) || ++ DH_get0_pqg(dh, &p, NULL, NULL); ++ if (!BN_sub(tmp, p, BN_value_one()) || + BN_cmp(dh_pub, tmp) != -1) { /* pub_exp > p-2 */ + BN_clear_free(tmp); + logit("invalid public DH value: >= p-1"); +@@ -249,14 +251,14 @@ dh_pub_is_valid(DH *dh, BIGNUM *dh_pub) + for (i = 0; i <= n; i++) + if (BN_is_bit_set(dh_pub, i)) + bits_set++; +- debug2("bits set: %d/%d", bits_set, BN_num_bits(dh->p)); ++ debug2("bits set: %d/%d", bits_set, BN_num_bits(p)); + + /* + * if g==2 and bits_set==1 then computing log_g(dh_pub) is trivial + */ + if (bits_set < 4) { + logit("invalid public DH value (%d/%d)", +- bits_set, BN_num_bits(dh->p)); ++ bits_set, BN_num_bits(p)); + return 0; + } + return 1; +@@ -266,9 +268,11 @@ int + dh_gen_key(DH *dh, int need) + { + int pbits; ++ const BIGNUM *p, *pub_key; + +- if (need < 0 || dh->p == NULL || +- (pbits = BN_num_bits(dh->p)) <= 0 || ++ DH_get0_pqg(dh, &p, NULL, NULL); ++ if (need < 0 || p == NULL || ++ (pbits = BN_num_bits(p)) <= 0 || + need > INT_MAX / 2 || 2 * need > pbits) + return SSH_ERR_INVALID_ARGUMENT; + if (need < 256) +@@ -277,11 +281,11 @@ dh_gen_key(DH *dh, int need) + * Pollard Rho, Big step/Little Step attacks are O(sqrt(n)), + * so double requested need here. + */ +- dh->length = MINIMUM(need * 2, pbits - 1); +- if (DH_generate_key(dh) == 0 || +- !dh_pub_is_valid(dh, dh->pub_key)) { +- BN_clear_free(dh->priv_key); +- dh->priv_key = NULL; ++ DH_set_length(dh, MINIMUM(need * 2, pbits - 1)); ++ if (DH_generate_key(dh) == 0) ++ return SSH_ERR_LIBCRYPTO_ERROR; ++ DH_get0_key(dh, &pub_key, NULL); ++ if (!dh_pub_is_valid(dh, pub_key)) { + return SSH_ERR_LIBCRYPTO_ERROR; + } + return 0; +@@ -291,15 +295,22 @@ DH * + dh_new_group_asc(const char *gen, const char *modulus) + { + DH *dh; ++ BIGNUM *p = NULL, *g = NULL; + +- if ((dh = DH_new()) == NULL) +- return NULL; +- if (BN_hex2bn(&dh->p, modulus) == 0 || +- BN_hex2bn(&dh->g, gen) == 0) { +- DH_free(dh); +- return NULL; +- } ++ if ((dh = DH_new()) == NULL || ++ (p = BN_new()) == NULL || ++ (g = BN_new()) == NULL) ++ goto err; ++ if (BN_hex2bn(&p, modulus) == 0 || ++ BN_hex2bn(&g, gen) == 0 || ++ DH_set0_pqg(dh, p, NULL, g) == 0) ++ goto err; + return (dh); ++err: ++ DH_free(dh); ++ BN_free(p); ++ BN_free(g); ++ return NULL; + } + + /* +@@ -314,8 +325,7 @@ dh_new_group(BIGNUM *gen, BIGNUM *modulu + + if ((dh = DH_new()) == NULL) + return NULL; +- dh->p = modulus; +- dh->g = gen; ++ DH_set0_pqg(dh, modulus, NULL, gen); + + return (dh); + } +Index: openssh-7.8p1/dh.h +=================================================================== +--- openssh-7.8p1.orig/dh.h ++++ openssh-7.8p1/dh.h +@@ -42,7 +42,7 @@ DH *dh_new_group18(void); + DH *dh_new_group_fallback(int); + + int dh_gen_key(DH *, int); +-int dh_pub_is_valid(DH *, BIGNUM *); ++int dh_pub_is_valid(const DH *, const BIGNUM *); + + u_int dh_estimate(int); + +Index: openssh-7.8p1/digest-openssl.c +=================================================================== +--- openssh-7.8p1.orig/digest-openssl.c ++++ openssh-7.8p1/digest-openssl.c +@@ -43,7 +43,7 @@ + + struct ssh_digest_ctx { + int alg; +- EVP_MD_CTX mdctx; ++ EVP_MD_CTX *mdctx; + }; + + struct ssh_digest { +@@ -106,7 +106,7 @@ ssh_digest_bytes(int alg) + size_t + ssh_digest_blocksize(struct ssh_digest_ctx *ctx) + { +- return EVP_MD_CTX_block_size(&ctx->mdctx); ++ return EVP_MD_CTX_block_size(ctx->mdctx); + } + + struct ssh_digest_ctx * +@@ -118,8 +118,10 @@ ssh_digest_start(int alg) + if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) + return NULL; + ret->alg = alg; +- EVP_MD_CTX_init(&ret->mdctx); +- if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) { ++ ret->mdctx = EVP_MD_CTX_new(); ++ if (ret->mdctx == NULL || ++ EVP_DigestInit_ex(ret->mdctx, digest->mdfunc(), NULL) != 1) { ++ EVP_MD_CTX_free(ret->mdctx); + free(ret); + return NULL; + } +@@ -132,7 +134,7 @@ ssh_digest_copy_state(struct ssh_digest_ + if (from->alg != to->alg) + return SSH_ERR_INVALID_ARGUMENT; + /* we have bcopy-style order while openssl has memcpy-style */ +- if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) ++ if (!EVP_MD_CTX_copy_ex(to->mdctx, from->mdctx)) + return SSH_ERR_LIBCRYPTO_ERROR; + return 0; + } +@@ -140,7 +142,7 @@ ssh_digest_copy_state(struct ssh_digest_ + int + ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) + { +- if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) ++ if (EVP_DigestUpdate(ctx->mdctx, m, mlen) != 1) + return SSH_ERR_LIBCRYPTO_ERROR; + return 0; + } +@@ -161,7 +163,7 @@ ssh_digest_final(struct ssh_digest_ctx * + return SSH_ERR_INVALID_ARGUMENT; + if (dlen < digest->digest_len) /* No truncation allowed */ + return SSH_ERR_INVALID_ARGUMENT; +- if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) ++ if (EVP_DigestFinal_ex(ctx->mdctx, d, &l) != 1) + return SSH_ERR_LIBCRYPTO_ERROR; + if (l != digest->digest_len) /* sanity */ + return SSH_ERR_INTERNAL_ERROR; +@@ -172,7 +174,7 @@ void + ssh_digest_free(struct ssh_digest_ctx *ctx) + { + if (ctx != NULL) { +- EVP_MD_CTX_cleanup(&ctx->mdctx); ++ EVP_MD_CTX_free(ctx->mdctx); + explicit_bzero(ctx, sizeof(*ctx)); + free(ctx); + } +Index: openssh-7.8p1/gss-genr.c +=================================================================== +--- openssh-7.8p1.orig/gss-genr.c ++++ openssh-7.8p1/gss-genr.c +@@ -87,12 +87,12 @@ ssh_gssapi_client_mechanisms(const char + return NULL; + + return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, +- host, client)); ++ host, client, kex)); + } + + char * + ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, +- const char *host, const char *client) { ++ const char *host, const char *client, const char *kex) { + struct sshbuf *buf; + size_t i; + int oidpos, enclen, r; +@@ -100,7 +100,8 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + u_char digest[EVP_MAX_MD_SIZE]; + char deroid[2]; + const EVP_MD *evp_md = EVP_md5(); +- EVP_MD_CTX md; ++ EVP_MD_CTX *md; ++ char *s, *cp, *p; + + if (gss_enc2oid != NULL) { + for (i = 0; gss_enc2oid[i].encoded != NULL; i++) +@@ -114,7 +115,9 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + if ((buf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + ++ md = EVP_MD_CTX_new(); + oidpos = 0; ++ s = cp = xstrdup(kex); + for (i = 0; i < gss_supported->count; i++) { + if (gss_supported->elements[i].length < 128 && + (*check)(NULL, &(gss_supported->elements[i]), host, client)) { +@@ -122,26 +125,27 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + deroid[0] = SSH_GSS_OIDTYPE; + deroid[1] = gss_supported->elements[i].length; + +- EVP_DigestInit(&md, evp_md); +- EVP_DigestUpdate(&md, deroid, 2); +- EVP_DigestUpdate(&md, ++ EVP_MD_CTX_reset(md); ++ EVP_DigestInit(md, evp_md); ++ EVP_DigestUpdate(md, deroid, 2); ++ EVP_DigestUpdate(md, + gss_supported->elements[i].elements, + gss_supported->elements[i].length); +- EVP_DigestFinal(&md, digest, NULL); ++ EVP_DigestFinal(md, digest, NULL); + + encoded = xmalloc(EVP_MD_size(evp_md) * 2); + enclen = __b64_ntop(digest, EVP_MD_size(evp_md), + encoded, EVP_MD_size(evp_md) * 2); + + cp = strncpy(s, kex, strlen(kex)); +- for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; + (p = strsep(&cp, ","))) { + if (sshbuf_len(buf) != 0) + if ((r = sshbuf_put_u8(buf, ',')) !=0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || +- (r = sshbuf_put(buf, encoded, enclen)) != 0) ++ (r = sshbuf_put(buf, encoded, enclen)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + } +@@ -151,7 +155,8 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + oidpos++; + } + } +- free(s); ++ free(s); ++ EVP_MD_CTX_free(md); + gss_enc2oid[oidpos].oid = NULL; + gss_enc2oid[oidpos].encoded = NULL; + +Index: openssh-7.8p1/includes.h +=================================================================== +--- openssh-7.8p1.orig/includes.h ++++ openssh-7.8p1/includes.h +@@ -166,6 +166,7 @@ + + #ifdef WITH_OPENSSL + #include /* For OPENSSL_VERSION_NUMBER */ ++#include "libcrypto-compat.h" + #endif + + #include "defines.h" +Index: openssh-7.8p1/kexdhc.c +=================================================================== +--- openssh-7.8p1.orig/kexdhc.c ++++ openssh-7.8p1/kexdhc.c +@@ -56,6 +56,7 @@ kexdh_client(struct ssh *ssh) + { + struct kex *kex = ssh->kex; + int r; ++ const BIGNUM *pub_key; + + /* generate and send 'e', client DH public key */ + switch (kex->kex_type) { +@@ -81,21 +82,27 @@ kexdh_client(struct ssh *ssh) + goto out; + } + debug("sending SSH2_MSG_KEXDH_INIT"); +- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || +- (r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_INIT)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + #ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "pub= "); +- BN_print_fp(stderr, kex->dh->pub_key); ++ BN_print_fp(stderr, pub_key); + fprintf(stderr, "\n"); + #endif + debug("expecting SSH2_MSG_KEXDH_REPLY"); + ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_REPLY, &input_kex_dh); + r = 0; + out: ++ if (r != 0) { ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ } + return r; + } + +@@ -109,6 +116,7 @@ input_kex_dh(int type, u_int32_t seq, st + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t klen = 0, slen, sbloblen, hashlen; + int kout, r; ++ const BIGNUM *pub_key; + + if (kex->verify_host_key == NULL) { + r = SSH_ERR_INVALID_ARGUMENT; +@@ -168,6 +176,7 @@ input_kex_dh(int type, u_int32_t seq, st + #endif + + /* calc and verify H */ ++ DH_get0_key(kex->dh, &pub_key, NULL); + hashlen = sizeof(hash); + if ((r = kex_dh_hash( + kex->hash_alg, +@@ -176,7 +185,7 @@ input_kex_dh(int type, u_int32_t seq, st + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + server_host_key_blob, sbloblen, +- kex->dh->pub_key, ++ pub_key, + dh_server_pub, + shared_secret, + hash, &hashlen)) != 0) +Index: openssh-7.8p1/kexdhs.c +=================================================================== +--- openssh-7.8p1.orig/kexdhs.c ++++ openssh-7.8p1/kexdhs.c +@@ -87,6 +87,10 @@ kexdh_server(struct ssh *ssh) + ssh_dispatch_set(ssh, SSH2_MSG_KEXDH_INIT, &input_kex_dh_init); + r = 0; + out: ++ if (r != 0) { ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ } + return r; + } + +@@ -101,6 +105,7 @@ input_kex_dh_init(int type, u_int32_t se + size_t sbloblen, slen; + size_t klen = 0, hashlen; + int kout, r; ++ const BIGNUM *pub_key; + + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) { +@@ -163,6 +168,7 @@ input_kex_dh_init(int type, u_int32_t se + goto out; + /* calc H */ + hashlen = sizeof(hash); ++ DH_get0_key(kex->dh, &pub_key, NULL); + if ((r = kex_dh_hash( + kex->hash_alg, + kex->client_version_string, +@@ -171,7 +177,7 @@ input_kex_dh_init(int type, u_int32_t se + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + server_host_key_blob, sbloblen, + dh_client_pub, +- kex->dh->pub_key, ++ pub_key, + shared_secret, + hash, &hashlen)) != 0) + goto out; +@@ -197,7 +203,7 @@ input_kex_dh_init(int type, u_int32_t se + /* send server hostkey, DH pubkey 'f' and signed H */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEXDH_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; +Index: openssh-7.8p1/kexgexc.c +=================================================================== +--- openssh-7.8p1.orig/kexgexc.c ++++ openssh-7.8p1/kexgexc.c +@@ -96,6 +96,7 @@ input_kex_dh_gex_group(int type, u_int32 + struct kex *kex = ssh->kex; + BIGNUM *p = NULL, *g = NULL; + int r, bits; ++ const BIGNUM *pub_key; + + debug("got SSH2_MSG_KEX_DH_GEX_GROUP"); + +@@ -126,16 +127,18 @@ input_kex_dh_gex_group(int type, u_int32 + p = g = NULL; /* belong to kex->dh now */ + + /* generate and send 'e', client DH public key */ +- if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0 || +- (r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || ++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0) ++ goto out; ++ DH_get0_key(kex->dh, &pub_key, NULL); ++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); + #ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, kex->dh); + fprintf(stderr, "pub= "); +- BN_print_fp(stderr, kex->dh->pub_key); ++ BN_print_fp(stderr, pub_key); + fprintf(stderr, "\n"); + #endif + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, NULL); +@@ -144,6 +147,10 @@ input_kex_dh_gex_group(int type, u_int32 + out: + BN_clear_free(p); + BN_clear_free(g); ++ if (r != 0) { ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ } + return r; + } + +@@ -157,6 +164,7 @@ input_kex_dh_gex_reply(int type, u_int32 + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t klen = 0, slen, sbloblen, hashlen; + int kout, r; ++ const BIGNUM *p, *g, *pub_key; + + debug("got SSH2_MSG_KEX_DH_GEX_REPLY"); + if (kex->verify_host_key == NULL) { +@@ -219,6 +227,8 @@ input_kex_dh_gex_reply(int type, u_int32 + kex->min = kex->max = -1; + + /* calc and verify H */ ++ DH_get0_pqg(kex->dh, &p, NULL, &g); ++ DH_get0_key(kex->dh, &pub_key, NULL); + hashlen = sizeof(hash); + if ((r = kexgex_hash( + kex->hash_alg, +@@ -228,8 +238,8 @@ input_kex_dh_gex_reply(int type, u_int32 + sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), + server_host_key_blob, sbloblen, + kex->min, kex->nbits, kex->max, +- kex->dh->p, kex->dh->g, +- kex->dh->pub_key, ++ p, g, ++ pub_key, + dh_server_pub, + shared_secret, + hash, &hashlen)) != 0) +Index: openssh-7.8p1/kexgexs.c +=================================================================== +--- openssh-7.8p1.orig/kexgexs.c ++++ openssh-7.8p1/kexgexs.c +@@ -74,6 +74,7 @@ input_kex_dh_gex_request(int type, u_int + struct kex *kex = ssh->kex; + int r; + u_int min = 0, max = 0, nbits = 0; ++ const BIGNUM *p, *g; + + debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || +@@ -109,9 +110,10 @@ input_kex_dh_gex_request(int type, u_int + goto out; + } + debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); ++ DH_get0_pqg(kex->dh, &p, NULL, &g); + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, p)) != 0 || ++ (r = sshpkt_put_bignum2(ssh, g)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; + +@@ -123,6 +125,10 @@ input_kex_dh_gex_request(int type, u_int + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init); + r = 0; + out: ++ if (r != 0) { ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ } + return r; + } + +@@ -137,6 +143,7 @@ input_kex_dh_gex_init(int type, u_int32_ + size_t sbloblen, slen; + size_t klen = 0, hashlen; + int kout, r; ++ const BIGNUM *p, *g, *pub_key; + + if (kex->load_host_public_key == NULL || + kex->load_host_private_key == NULL) { +@@ -199,6 +206,8 @@ input_kex_dh_gex_init(int type, u_int32_ + goto out; + /* calc H */ + hashlen = sizeof(hash); ++ DH_get0_pqg(kex->dh, &p, NULL, &g); ++ DH_get0_key(kex->dh, &pub_key, NULL); + if ((r = kexgex_hash( + kex->hash_alg, + kex->client_version_string, +@@ -207,9 +216,9 @@ input_kex_dh_gex_init(int type, u_int32_ + sshbuf_ptr(kex->my), sshbuf_len(kex->my), + server_host_key_blob, sbloblen, + kex->min, kex->nbits, kex->max, +- kex->dh->p, kex->dh->g, ++ p, g, + dh_client_pub, +- kex->dh->pub_key, ++ pub_key, + shared_secret, + hash, &hashlen)) != 0) + goto out; +@@ -235,7 +244,7 @@ input_kex_dh_gex_init(int type, u_int32_ + /* send server hostkey, DH pubkey 'f' and signed H */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 || + (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 || +- (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */ ++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 || /* f */ + (r = sshpkt_put_string(ssh, signature, slen)) != 0 || + (r = sshpkt_send(ssh)) != 0) + goto out; +Index: openssh-7.8p1/kexgssc.c +=================================================================== +--- openssh-7.8p1.orig/kexgssc.c ++++ openssh-7.8p1/kexgssc.c +@@ -61,6 +61,7 @@ kexgss_client(struct ssh *ssh) + BIGNUM *shared_secret = NULL; + BIGNUM *p = NULL; + BIGNUM *g = NULL; ++ const BIGNUM *pub_key, *p1, *g1; + u_char *kbuf; + u_char *serverhostkey = NULL; + u_char *empty = ""; +@@ -126,6 +127,7 @@ kexgss_client(struct ssh *ssh) + + /* Step 1 - e is dh->pub_key */ + dh_gen_key(dh, ssh->kex->we_need * 8); ++ DH_get0_key(dh, &pub_key, NULL); + + /* This is f, we initialise it now to make life easier */ + dh_server_pub = BN_new(); +@@ -173,7 +175,7 @@ kexgss_client(struct ssh *ssh) + packet_start(SSH2_MSG_KEXGSS_INIT); + packet_put_string(send_tok.value, + send_tok.length); +- packet_put_bignum2(dh->pub_key); ++ packet_put_bignum2((BIGNUM *)pub_key); + first = 0; + } else { + packet_start(SSH2_MSG_KEXGSS_CONTINUE); +@@ -284,13 +286,14 @@ kexgss_client(struct ssh *ssh) + sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), + sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), + (serverhostkey ? serverhostkey : empty), slen, +- dh->pub_key, /* e */ ++ pub_key, /* e */ + dh_server_pub, /* f */ + shared_secret, /* K */ + hash, &hashlen + ); + break; + case KEX_GSS_GEX_SHA1: ++ DH_get0_pqg(dh, &p1, NULL, &g1); + kexgex_hash( + ssh->kex->hash_alg, + ssh->kex->client_version_string, +@@ -299,8 +302,8 @@ kexgss_client(struct ssh *ssh) + sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), + (serverhostkey ? serverhostkey : empty), slen, + min, nbits, max, +- dh->p, dh->g, +- dh->pub_key, ++ p1, g1, ++ pub_key, + dh_server_pub, + shared_secret, + hash, &hashlen +Index: openssh-7.8p1/kexgsss.c +=================================================================== +--- openssh-7.8p1.orig/kexgsss.c ++++ openssh-7.8p1/kexgsss.c +@@ -78,6 +78,7 @@ kexgss_server(struct ssh *ssh) + char *mechs; + u_char hash[SSH_DIGEST_MAX_LENGTH]; + size_t hashlen; ++ const BIGNUM *p, *g, *pub_key; + + /* Initialise GSSAPI */ + +@@ -130,9 +131,10 @@ kexgss_server(struct ssh *ssh) + if (dh == NULL) + packet_disconnect("Protocol error: no matching group found"); + ++ DH_get0_pqg(dh, &p, NULL, &g); + packet_start(SSH2_MSG_KEXGSS_GROUP); +- packet_put_bignum2(dh->p); +- packet_put_bignum2(dh->g); ++ packet_put_bignum2((BIGNUM *)p); ++ packet_put_bignum2((BIGNUM *)g); + packet_send(); + + packet_write_wait(); +@@ -224,6 +226,7 @@ kexgss_server(struct ssh *ssh) + memset(kbuf, 0, klen); + free(kbuf); + ++ DH_get0_key(dh, &pub_key, NULL); + hashlen = sizeof(hash); + switch (ssh->kex->kex_type) { + case KEX_GSS_GRP1_SHA1: +@@ -234,7 +237,7 @@ kexgss_server(struct ssh *ssh) + sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), + sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), + NULL, 0, /* Change this if we start sending host keys */ +- dh_client_pub, dh->pub_key, shared_secret, ++ dh_client_pub, pub_key, shared_secret, + hash, &hashlen + ); + break; +@@ -246,9 +249,9 @@ kexgss_server(struct ssh *ssh) + sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), + NULL, 0, + cmin, nbits, cmax, +- dh->p, dh->g, ++ p, g, + dh_client_pub, +- dh->pub_key, ++ pub_key, + shared_secret, + hash, &hashlen + ); +@@ -272,7 +275,7 @@ kexgss_server(struct ssh *ssh) + fatal("Couldn't get MIC"); + + packet_start(SSH2_MSG_KEXGSS_COMPLETE); +- packet_put_bignum2(dh->pub_key); ++ packet_put_bignum2((BIGNUM *)pub_key); + packet_put_string(msg_tok.value,msg_tok.length); + + if (send_tok.length != 0) { +Index: openssh-7.8p1/libcrypto-compat.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/libcrypto-compat.c +@@ -0,0 +1,428 @@ ++/* ++ * Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. ++ * ++ * Licensed under the OpenSSL license (the "License"). You may not use ++ * this file except in compliance with the License. You can obtain a copy ++ * in the file LICENSE in the source distribution or at ++ * https://www.openssl.org/source/license.html ++ */ ++ ++#include "includes.h" ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ ++#include ++#include ++ ++static void *OPENSSL_zalloc(size_t num) ++{ ++ void *ret = OPENSSL_malloc(num); ++ ++ if (ret != NULL) ++ memset(ret, 0, num); ++ return ret; ++} ++ ++int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) ++{ ++ /* If the fields n and e in r are NULL, the corresponding input ++ * parameters MUST be non-NULL for n and e. d may be ++ * left NULL (in case only the public key is used). ++ */ ++ if ((r->n == NULL && n == NULL) ++ || (r->e == NULL && e == NULL)) ++ return 0; ++ ++ if (n != NULL) { ++ BN_clear_free(r->n); ++ r->n = n; ++ } ++ if (e != NULL) { ++ BN_clear_free(r->e); ++ r->e = e; ++ } ++ if (d != NULL) { ++ BN_clear_free(r->d); ++ r->d = d; ++ } ++ ++ return 1; ++} ++ ++int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) ++{ ++ /* If the fields p and q in r are NULL, the corresponding input ++ * parameters MUST be non-NULL. ++ */ ++ if ((r->p == NULL && p == NULL) ++ || (r->q == NULL && q == NULL)) ++ return 0; ++ ++ if (p != NULL) { ++ BN_clear_free(r->p); ++ r->p = p; ++ } ++ if (q != NULL) { ++ BN_clear_free(r->q); ++ r->q = q; ++ } ++ ++ return 1; ++} ++ ++int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) ++{ ++ /* If the fields dmp1, dmq1 and iqmp in r are NULL, the corresponding input ++ * parameters MUST be non-NULL. ++ */ ++ if ((r->dmp1 == NULL && dmp1 == NULL) ++ || (r->dmq1 == NULL && dmq1 == NULL) ++ || (r->iqmp == NULL && iqmp == NULL)) ++ return 0; ++ ++ if (dmp1 != NULL) { ++ BN_clear_free(r->dmp1); ++ r->dmp1 = dmp1; ++ } ++ if (dmq1 != NULL) { ++ BN_clear_free(r->dmq1); ++ r->dmq1 = dmq1; ++ } ++ if (iqmp != NULL) { ++ BN_clear_free(r->iqmp); ++ r->iqmp = iqmp; ++ } ++ ++ return 1; ++} ++ ++void RSA_get0_key(const RSA *r, ++ const BIGNUM **n, const BIGNUM **e, const BIGNUM **d) ++{ ++ if (n != NULL) ++ *n = r->n; ++ if (e != NULL) ++ *e = r->e; ++ if (d != NULL) ++ *d = r->d; ++} ++ ++void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q) ++{ ++ if (p != NULL) ++ *p = r->p; ++ if (q != NULL) ++ *q = r->q; ++} ++ ++void RSA_get0_crt_params(const RSA *r, ++ const BIGNUM **dmp1, const BIGNUM **dmq1, ++ const BIGNUM **iqmp) ++{ ++ if (dmp1 != NULL) ++ *dmp1 = r->dmp1; ++ if (dmq1 != NULL) ++ *dmq1 = r->dmq1; ++ if (iqmp != NULL) ++ *iqmp = r->iqmp; ++} ++ ++void DSA_get0_pqg(const DSA *d, ++ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) ++{ ++ if (p != NULL) ++ *p = d->p; ++ if (q != NULL) ++ *q = d->q; ++ if (g != NULL) ++ *g = d->g; ++} ++ ++int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) ++{ ++ /* If the fields p, q and g in d are NULL, the corresponding input ++ * parameters MUST be non-NULL. ++ */ ++ if ((d->p == NULL && p == NULL) ++ || (d->q == NULL && q == NULL) ++ || (d->g == NULL && g == NULL)) ++ return 0; ++ ++ if (p != NULL) { ++ BN_clear_free(d->p); ++ d->p = p; ++ } ++ if (q != NULL) { ++ BN_clear_free(d->q); ++ d->q = q; ++ } ++ if (g != NULL) { ++ BN_clear_free(d->g); ++ d->g = g; ++ } ++ ++ return 1; ++} ++ ++void DSA_get0_key(const DSA *d, ++ const BIGNUM **pub_key, const BIGNUM **priv_key) ++{ ++ if (pub_key != NULL) ++ *pub_key = d->pub_key; ++ if (priv_key != NULL) ++ *priv_key = d->priv_key; ++} ++ ++int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) ++{ ++ /* If the field pub_key in d is NULL, the corresponding input ++ * parameters MUST be non-NULL. The priv_key field may ++ * be left NULL. ++ */ ++ if (d->pub_key == NULL && pub_key == NULL) ++ return 0; ++ ++ if (pub_key != NULL) { ++ BN_clear_free(d->pub_key); ++ d->pub_key = pub_key; ++ } ++ if (priv_key != NULL) { ++ BN_clear_free(d->priv_key); ++ d->priv_key = priv_key; ++ } ++ ++ return 1; ++} ++ ++void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) ++{ ++ if (pr != NULL) ++ *pr = sig->r; ++ if (ps != NULL) ++ *ps = sig->s; ++} ++ ++int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) ++{ ++ if (r == NULL || s == NULL) ++ return 0; ++ BN_clear_free(sig->r); ++ BN_clear_free(sig->s); ++ sig->r = r; ++ sig->s = s; ++ return 1; ++} ++ ++void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) ++{ ++ if (pr != NULL) ++ *pr = sig->r; ++ if (ps != NULL) ++ *ps = sig->s; ++} ++ ++int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) ++{ ++ if (r == NULL || s == NULL) ++ return 0; ++ BN_clear_free(sig->r); ++ BN_clear_free(sig->s); ++ sig->r = r; ++ sig->s = s; ++ return 1; ++} ++ ++void DH_get0_pqg(const DH *dh, ++ const BIGNUM **p, const BIGNUM **q, const BIGNUM **g) ++{ ++ if (p != NULL) ++ *p = dh->p; ++ if (q != NULL) ++ *q = dh->q; ++ if (g != NULL) ++ *g = dh->g; ++} ++ ++int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) ++{ ++ /* If the fields p and g in d are NULL, the corresponding input ++ * parameters MUST be non-NULL. q may remain NULL. ++ */ ++ if ((dh->p == NULL && p == NULL) ++ || (dh->g == NULL && g == NULL)) ++ return 0; ++ ++ if (p != NULL) { ++ BN_clear_free(dh->p); ++ dh->p = p; ++ } ++ if (q != NULL) { ++ BN_clear_free(dh->q); ++ dh->q = q; ++ } ++ if (g != NULL) { ++ BN_clear_free(dh->g); ++ dh->g = g; ++ } ++ ++ if (q != NULL) { ++ dh->length = BN_num_bits(q); ++ } ++ ++ return 1; ++} ++ ++void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key) ++{ ++ if (pub_key != NULL) ++ *pub_key = dh->pub_key; ++ if (priv_key != NULL) ++ *priv_key = dh->priv_key; ++} ++ ++int DH_set_length(DH *dh, long length) ++{ ++ dh->length = length; ++ return 1; ++} ++ ++const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx) ++{ ++ return ctx->iv; ++} ++ ++unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx) ++{ ++ return ctx->iv; ++} ++ ++EVP_MD_CTX *EVP_MD_CTX_new(void) ++{ ++ return OPENSSL_zalloc(sizeof(EVP_MD_CTX)); ++} ++ ++static void OPENSSL_clear_free(void *str, size_t num) ++{ ++ if (str == NULL) ++ return; ++ if (num) ++ OPENSSL_cleanse(str, num); ++ OPENSSL_free(str); ++} ++ ++/* This call frees resources associated with the context */ ++int EVP_MD_CTX_reset(EVP_MD_CTX *ctx) ++{ ++ if (ctx == NULL) ++ return 1; ++ ++ /* ++ * Don't assume ctx->md_data was cleaned in EVP_Digest_Final, because ++ * sometimes only copies of the context are ever finalised. ++ */ ++ if (ctx->digest && ctx->digest->cleanup ++ && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_CLEANED)) ++ ctx->digest->cleanup(ctx); ++ if (ctx->digest && ctx->digest->ctx_size && ctx->md_data ++ && !EVP_MD_CTX_test_flags(ctx, EVP_MD_CTX_FLAG_REUSE)) { ++ OPENSSL_clear_free(ctx->md_data, ctx->digest->ctx_size); ++ } ++ EVP_PKEY_CTX_free(ctx->pctx); ++#ifndef OPENSSL_NO_ENGINE ++ ENGINE_finish(ctx->engine); ++#endif ++ OPENSSL_cleanse(ctx, sizeof(*ctx)); ++ ++ return 1; ++} ++ ++void EVP_MD_CTX_free(EVP_MD_CTX *ctx) ++{ ++ EVP_MD_CTX_reset(ctx); ++ OPENSSL_free(ctx); ++} ++ ++RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth) ++{ ++ RSA_METHOD *ret; ++ ++ ret = OPENSSL_malloc(sizeof(RSA_METHOD)); ++ ++ if (ret != NULL) { ++ memcpy(ret, meth, sizeof(*meth)); ++ ret->name = OPENSSL_strdup(meth->name); ++ if (ret->name == NULL) { ++ OPENSSL_free(ret); ++ return NULL; ++ } ++ } ++ ++ return ret; ++} ++ ++int RSA_meth_set1_name(RSA_METHOD *meth, const char *name) ++{ ++ char *tmpname; ++ ++ tmpname = OPENSSL_strdup(name); ++ if (tmpname == NULL) { ++ return 0; ++ } ++ ++ OPENSSL_free((char *)meth->name); ++ meth->name = tmpname; ++ ++ return 1; ++} ++ ++int RSA_meth_set_priv_enc(RSA_METHOD *meth, ++ int (*priv_enc) (int flen, const unsigned char *from, ++ unsigned char *to, RSA *rsa, ++ int padding)) ++{ ++ meth->rsa_priv_enc = priv_enc; ++ return 1; ++} ++ ++int RSA_meth_set_priv_dec(RSA_METHOD *meth, ++ int (*priv_dec) (int flen, const unsigned char *from, ++ unsigned char *to, RSA *rsa, ++ int padding)) ++{ ++ meth->rsa_priv_dec = priv_dec; ++ return 1; ++} ++ ++int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)) ++{ ++ meth->finish = finish; ++ return 1; ++} ++ ++void RSA_meth_free(RSA_METHOD *meth) ++{ ++ if (meth != NULL) { ++ OPENSSL_free((char *)meth->name); ++ OPENSSL_free(meth); ++ } ++} ++ ++int RSA_bits(const RSA *r) ++{ ++ return (BN_num_bits(r->n)); ++} ++ ++int DSA_bits(const DSA *dsa) ++{ ++ return BN_num_bits(dsa->p); ++} ++ ++RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey) ++{ ++ if (pkey->type != EVP_PKEY_RSA) { ++ return NULL; ++ } ++ return pkey->pkey.rsa; ++} ++ ++#endif /* OPENSSL_VERSION_NUMBER */ +Index: openssh-7.8p1/libcrypto-compat.h +=================================================================== +--- /dev/null ++++ openssh-7.8p1/libcrypto-compat.h +@@ -0,0 +1,59 @@ ++#ifndef LIBCRYPTO_COMPAT_H ++#define LIBCRYPTO_COMPAT_H ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ ++#include ++#include ++#include ++#include ++#include ++ ++int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); ++int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q); ++int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp); ++void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d); ++void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q); ++void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp); ++ ++void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); ++int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); ++void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key); ++int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); ++ ++void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); ++int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s); ++ ++void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps); ++int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); ++ ++void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g); ++int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g); ++void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key); ++int DH_set_length(DH *dh, long length); ++ ++const unsigned char *EVP_CIPHER_CTX_iv(const EVP_CIPHER_CTX *ctx); ++unsigned char *EVP_CIPHER_CTX_iv_noconst(EVP_CIPHER_CTX *ctx); ++int EVP_MD_CTX_reset(EVP_MD_CTX *ctx); ++EVP_MD_CTX *EVP_MD_CTX_new(void); ++void EVP_MD_CTX_free(EVP_MD_CTX *ctx); ++#define EVP_CIPHER_impl_ctx_size(e) e->ctx_size ++#define EVP_CIPHER_CTX_get_cipher_data(ctx) ctx->cipher_data ++ ++RSA_METHOD *RSA_meth_dup(const RSA_METHOD *meth); ++int RSA_meth_set1_name(RSA_METHOD *meth, const char *name); ++#define RSA_meth_get_finish(meth) meth->finish ++int RSA_meth_set_priv_enc(RSA_METHOD *meth, int (*priv_enc) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); ++int RSA_meth_set_priv_dec(RSA_METHOD *meth, int (*priv_dec) (int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding)); ++int RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)); ++void RSA_meth_free(RSA_METHOD *meth); ++ ++int RSA_bits(const RSA *r); ++int DSA_bits(const DSA *d); ++ ++RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey); ++ ++#endif /* OPENSSL_VERSION_NUMBER */ ++ ++#endif /* LIBCRYPTO_COMPAT_H */ ++ +Index: openssh-7.8p1/monitor.c +=================================================================== +--- openssh-7.8p1.orig/monitor.c ++++ openssh-7.8p1/monitor.c +@@ -624,9 +624,12 @@ mm_answer_moduli(int sock, struct sshbuf + return (0); + } else { + /* Send first bignum */ ++ const BIGNUM *p, *g; ++ ++ DH_get0_pqg(dh, &p, NULL, &g); + if ((r = sshbuf_put_u8(m, 1)) != 0 || +- (r = sshbuf_put_bignum2(m, dh->p)) != 0 || +- (r = sshbuf_put_bignum2(m, dh->g)) != 0) ++ (r = sshbuf_put_bignum2(m, p)) != 0 || ++ (r = sshbuf_put_bignum2(m, g)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + DH_free(dh); +Index: openssh-7.8p1/openbsd-compat/openssl-compat.c +=================================================================== +--- openssh-7.8p1.orig/openbsd-compat/openssl-compat.c ++++ openssh-7.8p1/openbsd-compat/openssl-compat.c +@@ -70,12 +70,19 @@ ssh_compatible_openssl(long headerver, l + void + ssh_OpenSSL_add_all_algorithms(void) + { ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + OpenSSL_add_all_algorithms(); + + /* Enable use of crypto hardware */ + ENGINE_load_builtin_engines(); ++#if OPENSSL_VERSION_NUMBER < 0x10001000L + ENGINE_register_all_complete(); ++#endif + OPENSSL_config(NULL); ++#else ++ OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_DIGESTS | ++ OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CONFIG, NULL); ++#endif + } + #endif + +Index: openssh-7.8p1/regress/unittests/sshkey/test_file.c +=================================================================== +--- openssh-7.8p1.orig/regress/unittests/sshkey/test_file.c ++++ openssh-7.8p1/regress/unittests/sshkey/test_file.c +@@ -46,6 +46,7 @@ sshkey_file_tests(void) + struct sshbuf *buf, *pw; + BIGNUM *a, *b, *c; + char *cp; ++ const BIGNUM *n, *p, *q, *g, *pub_key, *priv_key; + + TEST_START("load passphrase"); + pw = load_text_file("pw"); +@@ -60,9 +61,11 @@ sshkey_file_tests(void) + a = load_bignum("rsa_1.param.n"); + b = load_bignum("rsa_1.param.p"); + c = load_bignum("rsa_1.param.q"); +- ASSERT_BIGNUM_EQ(k1->rsa->n, a); +- ASSERT_BIGNUM_EQ(k1->rsa->p, b); +- ASSERT_BIGNUM_EQ(k1->rsa->q, c); ++ RSA_get0_key(k1->rsa, &n, NULL, NULL); ++ RSA_get0_factors(k1->rsa, &p, &q); ++ ASSERT_BIGNUM_EQ(n, a); ++ ASSERT_BIGNUM_EQ(p, b); ++ ASSERT_BIGNUM_EQ(q, c); + BN_free(a); + BN_free(b); + BN_free(c); +@@ -151,9 +154,11 @@ sshkey_file_tests(void) + a = load_bignum("dsa_1.param.g"); + b = load_bignum("dsa_1.param.priv"); + c = load_bignum("dsa_1.param.pub"); +- ASSERT_BIGNUM_EQ(k1->dsa->g, a); +- ASSERT_BIGNUM_EQ(k1->dsa->priv_key, b); +- ASSERT_BIGNUM_EQ(k1->dsa->pub_key, c); ++ DSA_get0_pqg(k1->dsa, NULL, NULL, &g); ++ DSA_get0_key(k1->dsa, &pub_key, &priv_key); ++ ASSERT_BIGNUM_EQ(g, a); ++ ASSERT_BIGNUM_EQ(priv_key, b); ++ ASSERT_BIGNUM_EQ(pub_key, c); + BN_free(a); + BN_free(b); + BN_free(c); +Index: openssh-7.8p1/regress/unittests/sshkey/test_sshkey.c +=================================================================== +--- openssh-7.8p1.orig/regress/unittests/sshkey/test_sshkey.c ++++ openssh-7.8p1/regress/unittests/sshkey/test_sshkey.c +@@ -197,9 +197,6 @@ sshkey_tests(void) + k1 = sshkey_new(KEY_RSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->rsa, NULL); +- ASSERT_PTR_NE(k1->rsa->n, NULL); +- ASSERT_PTR_NE(k1->rsa->e, NULL); +- ASSERT_PTR_EQ(k1->rsa->p, NULL); + sshkey_free(k1); + TEST_DONE(); + +@@ -207,8 +204,6 @@ sshkey_tests(void) + k1 = sshkey_new(KEY_DSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->dsa, NULL); +- ASSERT_PTR_NE(k1->dsa->g, NULL); +- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); + sshkey_free(k1); + TEST_DONE(); + +@@ -234,9 +229,6 @@ sshkey_tests(void) + k1 = sshkey_new_private(KEY_RSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->rsa, NULL); +- ASSERT_PTR_NE(k1->rsa->n, NULL); +- ASSERT_PTR_NE(k1->rsa->e, NULL); +- ASSERT_PTR_NE(k1->rsa->p, NULL); + ASSERT_INT_EQ(sshkey_add_private(k1), 0); + sshkey_free(k1); + TEST_DONE(); +@@ -245,8 +237,6 @@ sshkey_tests(void) + k1 = sshkey_new_private(KEY_DSA); + ASSERT_PTR_NE(k1, NULL); + ASSERT_PTR_NE(k1->dsa, NULL); +- ASSERT_PTR_NE(k1->dsa->g, NULL); +- ASSERT_PTR_NE(k1->dsa->priv_key, NULL); + ASSERT_INT_EQ(sshkey_add_private(k1), 0); + sshkey_free(k1); + TEST_DONE(); +@@ -285,18 +275,13 @@ sshkey_tests(void) + ASSERT_INT_EQ(sshkey_generate(KEY_RSA, 1024, &kr), 0); + ASSERT_PTR_NE(kr, NULL); + ASSERT_PTR_NE(kr->rsa, NULL); +- ASSERT_PTR_NE(kr->rsa->n, NULL); +- ASSERT_PTR_NE(kr->rsa->e, NULL); +- ASSERT_PTR_NE(kr->rsa->p, NULL); +- ASSERT_INT_EQ(BN_num_bits(kr->rsa->n), 1024); ++ ASSERT_INT_EQ(RSA_bits(kr->rsa), 1024); + TEST_DONE(); + + TEST_START("generate KEY_DSA"); + ASSERT_INT_EQ(sshkey_generate(KEY_DSA, 1024, &kd), 0); + ASSERT_PTR_NE(kd, NULL); + ASSERT_PTR_NE(kd->dsa, NULL); +- ASSERT_PTR_NE(kd->dsa->g, NULL); +- ASSERT_PTR_NE(kd->dsa->priv_key, NULL); + TEST_DONE(); + + #ifdef OPENSSL_HAS_ECC +@@ -323,9 +308,6 @@ sshkey_tests(void) + ASSERT_PTR_NE(kr, k1); + ASSERT_INT_EQ(k1->type, KEY_RSA); + ASSERT_PTR_NE(k1->rsa, NULL); +- ASSERT_PTR_NE(k1->rsa->n, NULL); +- ASSERT_PTR_NE(k1->rsa->e, NULL); +- ASSERT_PTR_EQ(k1->rsa->p, NULL); + TEST_DONE(); + + TEST_START("equal KEY_RSA/demoted KEY_RSA"); +@@ -339,8 +321,6 @@ sshkey_tests(void) + ASSERT_PTR_NE(kd, k1); + ASSERT_INT_EQ(k1->type, KEY_DSA); + ASSERT_PTR_NE(k1->dsa, NULL); +- ASSERT_PTR_NE(k1->dsa->g, NULL); +- ASSERT_PTR_EQ(k1->dsa->priv_key, NULL); + TEST_DONE(); + + TEST_START("equal KEY_DSA/demoted KEY_DSA"); +Index: openssh-7.8p1/ssh-dss.c +=================================================================== +--- openssh-7.8p1.orig/ssh-dss.c ++++ openssh-7.8p1/ssh-dss.c +@@ -55,6 +55,7 @@ ssh_dss_sign(const struct sshkey *key, u + size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1); + struct sshbuf *b = NULL; + int ret = SSH_ERR_INVALID_ARGUMENT; ++ const BIGNUM *r, *s; + + if (lenp != NULL) + *lenp = 0; +@@ -76,15 +77,16 @@ ssh_dss_sign(const struct sshkey *key, u + goto out; + } + +- rlen = BN_num_bytes(sig->r); +- slen = BN_num_bytes(sig->s); ++ DSA_SIG_get0(sig, &r, &s); ++ rlen = BN_num_bytes(r); ++ slen = BN_num_bytes(s); + if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) { + ret = SSH_ERR_INTERNAL_ERROR; + goto out; + } + explicit_bzero(sigblob, SIGBLOB_LEN); +- BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); +- BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen); ++ BN_bn2bin(r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen); ++ BN_bn2bin(s, sigblob + SIGBLOB_LEN - slen); + + if ((b = sshbuf_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; +@@ -123,6 +125,7 @@ ssh_dss_verify(const struct sshkey *key, + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL; + char *ktype = NULL; ++ BIGNUM *r = NULL, *s = NULL; + + if (key == NULL || key->dsa == NULL || + sshkey_type_plain(key->type) != KEY_DSA || +@@ -155,16 +158,19 @@ ssh_dss_verify(const struct sshkey *key, + + /* parse signature */ + if ((sig = DSA_SIG_new()) == NULL || +- (sig->r = BN_new()) == NULL || +- (sig->s = BN_new()) == NULL) { ++ (r = BN_new()) == NULL || ++ (s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) || +- (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) { ++ if ((BN_bin2bn(sigblob, INTBLOB_LEN, r) == NULL) || ++ (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, s) == NULL) || ++ (DSA_SIG_set0(sig, r, s) == 0)) { + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } ++ r = NULL; ++ s = NULL; + + /* sha1 the data */ + if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen, +@@ -185,6 +191,8 @@ ssh_dss_verify(const struct sshkey *key, + + out: + explicit_bzero(digest, sizeof(digest)); ++ BN_free(r); ++ BN_free(s); + DSA_SIG_free(sig); + sshbuf_free(b); + free(ktype); +Index: openssh-7.8p1/ssh-ecdsa.c +=================================================================== +--- openssh-7.8p1.orig/ssh-ecdsa.c ++++ openssh-7.8p1/ssh-ecdsa.c +@@ -54,6 +54,7 @@ ssh_ecdsa_sign(const struct sshkey *key, + size_t len, dlen; + struct sshbuf *b = NULL, *bb = NULL; + int ret = SSH_ERR_INTERNAL_ERROR; ++ const BIGNUM *r, *s; + + if (lenp != NULL) + *lenp = 0; +@@ -80,8 +81,9 @@ ssh_ecdsa_sign(const struct sshkey *key, + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 || +- (ret = sshbuf_put_bignum2(bb, sig->s)) != 0) ++ ECDSA_SIG_get0(sig, &r, &s); ++ if ((ret = sshbuf_put_bignum2(bb, r)) != 0 || ++ (ret = sshbuf_put_bignum2(bb, s)) != 0) + goto out; + if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 || + (ret = sshbuf_put_stringb(b, bb)) != 0) +@@ -118,6 +120,7 @@ ssh_ecdsa_verify(const struct sshkey *ke + int ret = SSH_ERR_INTERNAL_ERROR; + struct sshbuf *b = NULL, *sigbuf = NULL; + char *ktype = NULL; ++ BIGNUM *r = NULL, *s = NULL; + + if (key == NULL || key->ecdsa == NULL || + sshkey_type_plain(key->type) != KEY_ECDSA || +@@ -146,15 +149,23 @@ ssh_ecdsa_verify(const struct sshkey *ke + } + + /* parse signature */ +- if ((sig = ECDSA_SIG_new()) == NULL) { ++ if ((sig = ECDSA_SIG_new()) == NULL || ++ (r = BN_new()) == NULL || ++ (s = BN_new()) == NULL) { + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 || +- sshbuf_get_bignum2(sigbuf, sig->s) != 0) { ++ if (sshbuf_get_bignum2(sigbuf, r) != 0 || ++ sshbuf_get_bignum2(sigbuf, s) != 0) { + ret = SSH_ERR_INVALID_FORMAT; + goto out; + } ++ if (ECDSA_SIG_set0(sig, r, s) == 0) { ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ r = NULL; ++ s = NULL; + if (sshbuf_len(sigbuf) != 0) { + ret = SSH_ERR_UNEXPECTED_TRAILING_DATA; + goto out; +@@ -179,6 +190,8 @@ ssh_ecdsa_verify(const struct sshkey *ke + explicit_bzero(digest, sizeof(digest)); + sshbuf_free(sigbuf); + sshbuf_free(b); ++ BN_free(r); ++ BN_free(s); + ECDSA_SIG_free(sig); + free(ktype); + return ret; +Index: openssh-7.8p1/ssh-keygen.c +=================================================================== +--- openssh-7.8p1.orig/ssh-keygen.c ++++ openssh-7.8p1/ssh-keygen.c +@@ -495,40 +495,67 @@ do_convert_private_ssh2_from_blob(u_char + free(type); + + switch (key->type) { +- case KEY_DSA: +- buffer_get_bignum_bits(b, key->dsa->p); +- buffer_get_bignum_bits(b, key->dsa->g); +- buffer_get_bignum_bits(b, key->dsa->q); +- buffer_get_bignum_bits(b, key->dsa->pub_key); +- buffer_get_bignum_bits(b, key->dsa->priv_key); ++ case KEY_DSA: { ++ BIGNUM *p = NULL, *g = NULL, *q = NULL, *pub_key = NULL, *priv_key = NULL; ++ ++ if ((p = BN_new()) == NULL || ++ (g = BN_new()) == NULL || ++ (q = BN_new()) == NULL || ++ (pub_key = BN_new()) == NULL || ++ (priv_key = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ buffer_get_bignum_bits(b, p); ++ buffer_get_bignum_bits(b, g); ++ buffer_get_bignum_bits(b, q); ++ buffer_get_bignum_bits(b, pub_key); ++ buffer_get_bignum_bits(b, priv_key); ++ if (DSA_set0_pqg(key->dsa, p, q, g) == 0 || ++ DSA_set0_key(key->dsa, pub_key, priv_key) == 0) { ++ fatal("failed to set DSA key"); ++ } ++ } + break; +- case KEY_RSA: +- if ((r = sshbuf_get_u8(b, &e1)) != 0 || +- (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || +- (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) +- fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- e = e1; +- debug("e %lx", e); +- if (e < 30) { +- e <<= 8; +- e += e2; +- debug("e %lx", e); +- e <<= 8; +- e += e3; ++ case KEY_RSA: { ++ BIGNUM *bn_e = NULL, *bn_d = NULL, *bn_n = NULL, *bn_iqmp = NULL, *bn_p = NULL, *bn_q = NULL; ++ ++ if ((bn_e = BN_new()) == NULL || ++ (bn_d = BN_new()) == NULL || ++ (bn_n = BN_new()) == NULL || ++ (bn_iqmp = BN_new()) == NULL || ++ (bn_p = BN_new()) == NULL || ++ (bn_q = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ ++ if ((r = sshbuf_get_u8(b, &e1)) != 0 || ++ (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || ++ (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ e = e1; + debug("e %lx", e); ++ if (e < 30) { ++ e <<= 8; ++ e += e2; ++ debug("e %lx", e); ++ e <<= 8; ++ e += e3; ++ debug("e %lx", e); ++ } ++ if (!BN_set_word(bn_e, e)) { ++ sshbuf_free(b); ++ sshkey_free(key); ++ return NULL; ++ } ++ buffer_get_bignum_bits(b, bn_d); ++ buffer_get_bignum_bits(b, bn_n); ++ buffer_get_bignum_bits(b, bn_iqmp); ++ buffer_get_bignum_bits(b, bn_q); ++ buffer_get_bignum_bits(b, bn_p); ++ if (RSA_set0_key(key->rsa, bn_n, bn_e, bn_d) == 0 || ++ RSA_set0_factors(key->rsa, bn_p, bn_q) == 0) ++ fatal("Failed to set RSA parameters"); ++ if ((r = ssh_rsa_generate_additional_parameters(key, bn_iqmp)) != 0) ++ fatal("generate RSA parameters failed: %s", ssh_err(r)); + } +- if (!BN_set_word(key->rsa->e, e)) { +- sshbuf_free(b); +- sshkey_free(key); +- return NULL; +- } +- buffer_get_bignum_bits(b, key->rsa->d); +- buffer_get_bignum_bits(b, key->rsa->n); +- buffer_get_bignum_bits(b, key->rsa->iqmp); +- buffer_get_bignum_bits(b, key->rsa->q); +- buffer_get_bignum_bits(b, key->rsa->p); +- if ((r = ssh_rsa_generate_additional_parameters(key)) != 0) +- fatal("generate RSA parameters failed: %s", ssh_err(r)); + break; + } + rlen = sshbuf_len(b); +@@ -636,7 +663,7 @@ do_convert_from_pkcs8(struct sshkey **k, + identity_file); + } + fclose(fp); +- switch (EVP_PKEY_type(pubkey->type)) { ++ switch (EVP_PKEY_base_id(pubkey)) { + case EVP_PKEY_RSA: + if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); +@@ -660,7 +687,7 @@ do_convert_from_pkcs8(struct sshkey **k, + #endif + default: + fatal("%s: unsupported pubkey type %d", __func__, +- EVP_PKEY_type(pubkey->type)); ++ EVP_PKEY_base_id(pubkey)); + } + EVP_PKEY_free(pubkey); + return; +@@ -1806,6 +1833,7 @@ do_ca_sign(struct passwd *pw, int argc, + #ifdef ENABLE_PKCS11 + pkcs11_terminate(); + #endif ++ free(ca); + exit(0); + } + +Index: openssh-7.8p1/ssh-pkcs11-client.c +=================================================================== +--- openssh-7.8p1.orig/ssh-pkcs11-client.c ++++ openssh-7.8p1/ssh-pkcs11-client.c +@@ -156,12 +156,16 @@ pkcs11_rsa_private_encrypt(int flen, con + static int + wrap_key(RSA *rsa) + { +- static RSA_METHOD helper_rsa; ++ static RSA_METHOD *helper_rsa; + +- memcpy(&helper_rsa, RSA_get_default_method(), sizeof(helper_rsa)); +- helper_rsa.name = "ssh-pkcs11-helper"; +- helper_rsa.rsa_priv_enc = pkcs11_rsa_private_encrypt; +- RSA_set_method(rsa, &helper_rsa); ++ if (helper_rsa == NULL) { ++ helper_rsa = RSA_meth_dup(RSA_get_default_method()); ++ if (helper_rsa == NULL) ++ error("RSA_meth_dup failed"); ++ RSA_meth_set1_name(helper_rsa, "ssh-pkcs11-helper"); ++ RSA_meth_set_priv_enc(helper_rsa, pkcs11_rsa_private_encrypt); ++ } ++ RSA_set_method(rsa, helper_rsa); + return (0); + } + +Index: openssh-7.8p1/ssh-pkcs11.c +=================================================================== +--- openssh-7.8p1.orig/ssh-pkcs11.c ++++ openssh-7.8p1/ssh-pkcs11.c +@@ -67,7 +67,7 @@ struct pkcs11_key { + struct pkcs11_provider *provider; + CK_ULONG slotidx; + int (*orig_finish)(RSA *rsa); +- RSA_METHOD rsa_method; ++ RSA_METHOD *rsa_method; + char *keyid; + int keyid_len; + }; +@@ -183,6 +183,7 @@ pkcs11_rsa_finish(RSA *rsa) + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); ++ RSA_meth_free(k11->rsa_method); + free(k11); + } + return (rv); +@@ -326,13 +327,21 @@ pkcs11_rsa_wrap(struct pkcs11_provider * + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } +- k11->orig_finish = def->finish; +- memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); +- k11->rsa_method.name = "pkcs11"; +- k11->rsa_method.rsa_priv_enc = pkcs11_rsa_private_encrypt; +- k11->rsa_method.rsa_priv_dec = pkcs11_rsa_private_decrypt; +- k11->rsa_method.finish = pkcs11_rsa_finish; +- RSA_set_method(rsa, &k11->rsa_method); ++ k11->orig_finish = RSA_meth_get_finish(def); ++ if ((k11->rsa_method = RSA_meth_dup(def)) == NULL || ++ RSA_meth_set1_name(k11->rsa_method, "pkcs11") == 0 || ++ RSA_meth_set_priv_enc(k11->rsa_method, pkcs11_rsa_private_encrypt) == 0 || ++ RSA_meth_set_priv_dec(k11->rsa_method, pkcs11_rsa_private_decrypt) == 0 || ++ RSA_meth_set_finish(k11->rsa_method, pkcs11_rsa_finish) == 0) { ++ RSA_meth_free(k11->rsa_method); ++ k11->rsa_method = NULL; ++ pkcs11_provider_unref(k11->provider); ++ free(k11->keyid); ++ free(k11); ++ return (-1); ++ } ++ ++ RSA_set_method(rsa, k11->rsa_method); + RSA_set_app_data(rsa, k11); + return (0); + } +@@ -460,6 +469,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + CK_ULONG nfound; + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f; ++ const BIGNUM *n, *e; + + f = p->function_list; + session = p->slotinfo[slotidx].session; +@@ -512,10 +522,16 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { +- rsa->n = BN_bin2bn(attribs[1].pValue, ++ BIGNUM *rsa_n, *rsa_e; ++ ++ rsa_n = BN_bin2bn(attribs[1].pValue, + attribs[1].ulValueLen, NULL); +- rsa->e = BN_bin2bn(attribs[2].pValue, ++ rsa_e = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); ++ if (rsa_n == NULL || rsa_e == NULL) ++ error("BN_bin2bn failed"); ++ if (RSA_set0_key(rsa, rsa_n, rsa_e, NULL) == 0) ++ error("RSA_set0_key failed"); + } + } else { + cp = attribs[2].pValue; +@@ -525,16 +541,18 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + == NULL) { + error("d2i_X509 failed"); + } else if ((evp = X509_get_pubkey(x509)) == NULL || +- evp->type != EVP_PKEY_RSA || +- evp->pkey.rsa == NULL) { ++ EVP_PKEY_id(evp) != EVP_PKEY_RSA || ++ EVP_PKEY_get0_RSA(evp) == NULL) { + debug("X509_get_pubkey failed or no rsa"); +- } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) ++ } else if ((rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(evp))) + == NULL) { + error("RSAPublicKey_dup"); + } + X509_free(x509); + } +- if (rsa && rsa->n && rsa->e && ++ if (rsa) ++ RSA_get0_key(rsa, &n, &e, NULL); ++ if (rsa && n && e && + pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); +Index: openssh-7.8p1/ssh-rsa.c +=================================================================== +--- openssh-7.8p1.orig/ssh-rsa.c ++++ openssh-7.8p1/ssh-rsa.c +@@ -104,38 +104,50 @@ rsa_hash_alg_nid(int type) + } + + int +-ssh_rsa_generate_additional_parameters(struct sshkey *key) ++ssh_rsa_generate_additional_parameters(struct sshkey *key, BIGNUM *iqmp) + { + BIGNUM *aux = NULL; + BN_CTX *ctx = NULL; +- BIGNUM d; ++ BIGNUM *d = NULL; + int r; ++ const BIGNUM *p, *q, *rsa_d; ++ BIGNUM *dmp1 = NULL, *dmq1 = NULL; + + if (key == NULL || key->rsa == NULL || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; + +- if ((ctx = BN_CTX_new()) == NULL) +- return SSH_ERR_ALLOC_FAIL; +- if ((aux = BN_new()) == NULL) { ++ RSA_get0_factors(key->rsa, &p, &q); ++ RSA_get0_key(key->rsa, NULL, NULL, &rsa_d); ++ ++ if ((ctx = BN_CTX_new()) == NULL || ++ (aux = BN_new()) == NULL || ++ (d = BN_new()) == NULL || ++ (dmp1 = BN_new()) == NULL || ++ (dmq1 = BN_new()) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + BN_set_flags(aux, BN_FLG_CONSTTIME); + +- BN_init(&d); +- BN_with_flags(&d, key->rsa->d, BN_FLG_CONSTTIME); ++ BN_with_flags(d, rsa_d, BN_FLG_CONSTTIME); + +- if ((BN_sub(aux, key->rsa->q, BN_value_one()) == 0) || +- (BN_mod(key->rsa->dmq1, &d, aux, ctx) == 0) || +- (BN_sub(aux, key->rsa->p, BN_value_one()) == 0) || +- (BN_mod(key->rsa->dmp1, &d, aux, ctx) == 0)) { ++ if ((BN_sub(aux, q, BN_value_one()) == 0) || ++ (BN_mod(dmq1, d, aux, ctx) == 0) || ++ (BN_sub(aux, p, BN_value_one()) == 0) || ++ (BN_mod(dmp1, d, aux, ctx) == 0) || ++ (RSA_set0_crt_params(key->rsa, dmp1, dmq1, iqmp) == 0)) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } ++ dmp1 = NULL; ++ dmq1 = NULL; + r = 0; + out: ++ BN_free(d); + BN_clear_free(aux); ++ BN_clear_free(dmp1); ++ BN_clear_free(dmq1); + BN_CTX_free(ctx); + return r; + } +@@ -163,7 +175,7 @@ ssh_rsa_sign(const struct sshkey *key, u + if (key == NULL || key->rsa == NULL || hash_alg == -1 || + sshkey_type_plain(key->type) != KEY_RSA) + return SSH_ERR_INVALID_ARGUMENT; +- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) ++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + slen = RSA_size(key->rsa); + if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM) +@@ -235,7 +247,7 @@ ssh_rsa_verify(const struct sshkey *key, + sshkey_type_plain(key->type) != KEY_RSA || + sig == NULL || siglen == 0) + return SSH_ERR_INVALID_ARGUMENT; +- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) ++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) + return SSH_ERR_KEY_LENGTH; + + if ((b = sshbuf_from(sig, siglen)) == NULL) +Index: openssh-7.8p1/sshconnect.c +=================================================================== +--- openssh-7.8p1.orig/sshconnect.c ++++ openssh-7.8p1/sshconnect.c +@@ -1399,6 +1399,7 @@ ssh_login(Sensitive *sensitive, const ch + char *server_user, *local_user; + + local_user = xstrdup(pw->pw_name); ++ free(pw); + server_user = options.user ? options.user : local_user; + + /* Convert the user-supplied hostname into all lowercase. */ +Index: openssh-7.8p1/sshkey.c +=================================================================== +--- openssh-7.8p1.orig/sshkey.c ++++ openssh-7.8p1/sshkey.c +@@ -293,10 +293,10 @@ sshkey_size(const struct sshkey *k) + #ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: +- return BN_num_bits(k->rsa->n); ++ return RSA_bits(k->rsa); + case KEY_DSA: + case KEY_DSA_CERT: +- return BN_num_bits(k->dsa->p); ++ return DSA_bits(k->dsa); + case KEY_ECDSA: + case KEY_ECDSA_CERT: + return sshkey_curve_nid_to_bits(k->ecdsa_nid); +@@ -325,6 +325,31 @@ sshkey_type_is_valid_ca(int type) + } + } + ++ ++static int ++sshkey_is_private_rsa(const RSA *r) ++{ ++ const BIGNUM *d; ++ int rv; ++ ++ RSA_get0_key(r, NULL, NULL, &d); ++ rv = (d != NULL); ++ d = NULL; ++ return rv; ++} ++ ++static int ++sshkey_is_private_dsa(const DSA *d) ++{ ++ const BIGNUM *priv_key; ++ int rv; ++ ++ DSA_get0_key(d, NULL, &priv_key); ++ rv = (priv_key != NULL); ++ priv_key = NULL; ++ return rv; ++} ++ + int + sshkey_is_private(const struct sshkey *k) + { +@@ -332,10 +357,10 @@ sshkey_is_private(const struct sshkey *k + #ifdef WITH_OPENSSL + case KEY_RSA_CERT: + case KEY_RSA: +- return k->rsa->d != NULL; ++ return sshkey_is_private_rsa(k->rsa); + case KEY_DSA_CERT: + case KEY_DSA: +- return k->dsa->priv_key != NULL; ++ return sshkey_is_private_dsa(k->dsa); + #ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: +@@ -527,10 +552,7 @@ sshkey_new(int type) + #ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: +- if ((rsa = RSA_new()) == NULL || +- (rsa->n = BN_new()) == NULL || +- (rsa->e = BN_new()) == NULL) { +- RSA_free(rsa); ++ if ((rsa = RSA_new()) == NULL) { + free(k); + return NULL; + } +@@ -538,12 +560,7 @@ sshkey_new(int type) + break; + case KEY_DSA: + case KEY_DSA_CERT: +- if ((dsa = DSA_new()) == NULL || +- (dsa->p = BN_new()) == NULL || +- (dsa->q = BN_new()) == NULL || +- (dsa->g = BN_new()) == NULL || +- (dsa->pub_key = BN_new()) == NULL) { +- DSA_free(dsa); ++ if ((dsa = DSA_new()) == NULL) { + free(k); + return NULL; + } +@@ -584,21 +601,10 @@ sshkey_add_private(struct sshkey *k) + #ifdef WITH_OPENSSL + case KEY_RSA: + case KEY_RSA_CERT: +-#define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL) +- if (bn_maybe_alloc_failed(k->rsa->d) || +- bn_maybe_alloc_failed(k->rsa->iqmp) || +- bn_maybe_alloc_failed(k->rsa->q) || +- bn_maybe_alloc_failed(k->rsa->p) || +- bn_maybe_alloc_failed(k->rsa->dmq1) || +- bn_maybe_alloc_failed(k->rsa->dmp1)) +- return SSH_ERR_ALLOC_FAIL; + break; + case KEY_DSA: + case KEY_DSA_CERT: +- if (bn_maybe_alloc_failed(k->dsa->priv_key)) +- return SSH_ERR_ALLOC_FAIL; + break; +-#undef bn_maybe_alloc_failed + case KEY_ECDSA: + case KEY_ECDSA_CERT: + /* Cannot do anything until we know the group */ +@@ -721,17 +727,31 @@ sshkey_equal_public(const struct sshkey + switch (a->type) { + #ifdef WITH_OPENSSL + case KEY_RSA_CERT: +- case KEY_RSA: +- return a->rsa != NULL && b->rsa != NULL && +- BN_cmp(a->rsa->e, b->rsa->e) == 0 && +- BN_cmp(a->rsa->n, b->rsa->n) == 0; ++ case KEY_RSA: { ++ const BIGNUM *a_e, *a_n, *b_e, *b_n; ++ ++ if (a->rsa == NULL || b->rsa == NULL) ++ return 0; ++ RSA_get0_key(a->rsa, &a_n, &a_e, NULL); ++ RSA_get0_key(b->rsa, &b_n, &b_e, NULL); ++ return BN_cmp(a_e, b_e) == 0 && BN_cmp(a_n, b_n) == 0; ++ } + case KEY_DSA_CERT: +- case KEY_DSA: +- return a->dsa != NULL && b->dsa != NULL && +- BN_cmp(a->dsa->p, b->dsa->p) == 0 && +- BN_cmp(a->dsa->q, b->dsa->q) == 0 && +- BN_cmp(a->dsa->g, b->dsa->g) == 0 && +- BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; ++ case KEY_DSA: { ++ const BIGNUM *a_p, *a_q, *a_g, *a_pub_key; ++ const BIGNUM *b_p, *b_q, *b_g, *b_pub_key; ++ ++ if (a->dsa == NULL || b->dsa == NULL) ++ return 0; ++ DSA_get0_pqg(a->dsa, &a_p, &a_q, &a_g); ++ DSA_get0_key(a->dsa, &a_pub_key, NULL); ++ DSA_get0_pqg(b->dsa, &b_p, &b_q, &b_g); ++ DSA_get0_key(b->dsa, &b_pub_key, NULL); ++ return BN_cmp(a_p, b_p) == 0 && ++ BN_cmp(a_q, b_q) == 0 && ++ BN_cmp(a_g, b_g) == 0 && ++ BN_cmp(a_pub_key, b_pub_key) == 0; ++ } + # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: +@@ -817,15 +837,21 @@ to_blob_buf(const struct sshkey *key, st + return ret; + break; + #ifdef WITH_OPENSSL +- case KEY_DSA: +- if (key->dsa == NULL) +- return SSH_ERR_INVALID_ARGUMENT; +- if ((ret = sshbuf_put_cstring(b, typename)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) +- return ret; ++ case KEY_DSA: { ++ const BIGNUM *p, *q, *g, *pub_key; ++ ++ if (key->dsa == NULL) ++ return SSH_ERR_INVALID_ARGUMENT; ++ ++ DSA_get0_pqg(key->dsa, &p, &q, &g); ++ DSA_get0_key(key->dsa, &pub_key, NULL); ++ if ((ret = sshbuf_put_cstring(b, typename)) != 0 || ++ (ret = sshbuf_put_bignum2(b, p)) != 0 || ++ (ret = sshbuf_put_bignum2(b, q)) != 0 || ++ (ret = sshbuf_put_bignum2(b, g)) != 0 || ++ (ret = sshbuf_put_bignum2(b, pub_key)) != 0) ++ return ret; ++ } + break; + # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: +@@ -838,13 +864,18 @@ to_blob_buf(const struct sshkey *key, st + return ret; + break; + # endif +- case KEY_RSA: +- if (key->rsa == NULL) +- return SSH_ERR_INVALID_ARGUMENT; +- if ((ret = sshbuf_put_cstring(b, typename)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || +- (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) +- return ret; ++ case KEY_RSA: { ++ const BIGNUM *e, *n; ++ ++ if (key->rsa == NULL) ++ return SSH_ERR_INVALID_ARGUMENT; ++ ++ RSA_get0_key(key->rsa, &n, &e, NULL); ++ if ((ret = sshbuf_put_cstring(b, typename)) != 0 || ++ (ret = sshbuf_put_bignum2(b, e)) != 0 || ++ (ret = sshbuf_put_bignum2(b, n)) != 0) ++ return ret; ++ } + break; + #endif /* WITH_OPENSSL */ + case KEY_ED25519: +@@ -1782,15 +1813,32 @@ sshkey_from_private(const struct sshkey + switch (k->type) { + #ifdef WITH_OPENSSL + case KEY_DSA: +- case KEY_DSA_CERT: +- if ((n = sshkey_new(k->type)) == NULL) +- return SSH_ERR_ALLOC_FAIL; +- if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || +- (BN_copy(n->dsa->q, k->dsa->q) == NULL) || +- (BN_copy(n->dsa->g, k->dsa->g) == NULL) || +- (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { +- sshkey_free(n); +- return SSH_ERR_ALLOC_FAIL; ++ case KEY_DSA_CERT: { ++ const BIGNUM *k_p, *k_q, *k_g, *k_pub_key; ++ BIGNUM *n_p = NULL, *n_q = NULL, *n_g = NULL, *n_pub_key = NULL; ++ ++ if ((n = sshkey_new(k->type)) == NULL) ++ return SSH_ERR_ALLOC_FAIL; ++ ++ DSA_get0_pqg(k->dsa, &k_p, &k_q, &k_g); ++ DSA_get0_key(k->dsa, &k_pub_key, NULL); ++ ++ if (((n_p = BN_dup(k_p)) == NULL) || ++ ((n_q = BN_dup(k_q)) == NULL) || ++ ((n_g = BN_dup(k_g)) == NULL) || ++ (DSA_set0_pqg(n->dsa, n_p, n_q, n_g) == 0)) { ++ sshkey_free(n); ++ BN_free(n_p); ++ BN_free(n_q); ++ BN_free(n_g); ++ return SSH_ERR_ALLOC_FAIL; ++ } ++ if (((n_pub_key = BN_dup(k_pub_key)) == NULL) || ++ (DSA_set0_key(n->dsa, n_pub_key, NULL) == 0)) { ++ sshkey_free(n); ++ BN_free(n_pub_key); ++ return SSH_ERR_ALLOC_FAIL; ++ } + } + break; + # ifdef OPENSSL_HAS_ECC +@@ -1812,13 +1860,22 @@ sshkey_from_private(const struct sshkey + break; + # endif /* OPENSSL_HAS_ECC */ + case KEY_RSA: +- case KEY_RSA_CERT: +- if ((n = sshkey_new(k->type)) == NULL) +- return SSH_ERR_ALLOC_FAIL; +- if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || +- (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { +- sshkey_free(n); +- return SSH_ERR_ALLOC_FAIL; ++ case KEY_RSA_CERT: { ++ const BIGNUM *k_n, *k_e; ++ BIGNUM *n_n = NULL, *n_e = NULL; ++ ++ if ((n = sshkey_new(k->type)) == NULL) ++ return SSH_ERR_ALLOC_FAIL; ++ ++ RSA_get0_key(k->rsa, &k_n, &k_e, NULL); ++ if (((n_n = BN_dup(k_n)) == NULL) || ++ ((n_e = BN_dup(k_e)) == NULL) || ++ RSA_set0_key(n->rsa, n_n, n_e, NULL) == 0) { ++ sshkey_free(n); ++ BN_free(n_n); ++ BN_free(n_e); ++ return SSH_ERR_ALLOC_FAIL; ++ } + } + break; + #endif /* WITH_OPENSSL */ +@@ -2040,12 +2097,22 @@ sshkey_from_blob_internal(struct sshbuf + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if (sshbuf_get_bignum2(b, key->rsa->e) != 0 || +- sshbuf_get_bignum2(b, key->rsa->n) != 0) { +- ret = SSH_ERR_INVALID_FORMAT; +- goto out; ++ { ++ BIGNUM *e, *n; ++ ++ e = BN_new(); ++ n = BN_new(); ++ if (e == NULL || n == NULL || ++ sshbuf_get_bignum2(b, e) != 0 || ++ sshbuf_get_bignum2(b, n) != 0 || ++ RSA_set0_key(key->rsa, n, e, NULL) == 0) { ++ BN_free(e); ++ BN_free(n); ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } + } +- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { ++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + ret = SSH_ERR_KEY_LENGTH; + goto out; + } +@@ -2065,12 +2132,34 @@ sshkey_from_blob_internal(struct sshbuf + ret = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if (sshbuf_get_bignum2(b, key->dsa->p) != 0 || +- sshbuf_get_bignum2(b, key->dsa->q) != 0 || +- sshbuf_get_bignum2(b, key->dsa->g) != 0 || +- sshbuf_get_bignum2(b, key->dsa->pub_key) != 0) { +- ret = SSH_ERR_INVALID_FORMAT; +- goto out; ++ { ++ BIGNUM *p, *q, *g, *pub_key; ++ ++ p = BN_new(); ++ q = BN_new(); ++ g = BN_new(); ++ pub_key = BN_new(); ++ ++ if (p == NULL || q == NULL || g == NULL || ++ pub_key == NULL || ++ sshbuf_get_bignum2(b, p) != 0 || ++ sshbuf_get_bignum2(b, q) != 0 || ++ sshbuf_get_bignum2(b, g) != 0 || ++ sshbuf_get_bignum2(b, pub_key) != 0 || ++ DSA_set0_pqg(key->dsa, p, q, g) == 0) { ++ BN_free(p); ++ BN_free(q); ++ BN_free(g); ++ BN_free(pub_key); ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if (DSA_set0_key(key->dsa, pub_key, NULL) == 0) { ++ BN_free(pub_key); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } + } + #ifdef DEBUG_PK + DSA_print_fp(stderr, key->dsa, 8); +@@ -2415,26 +2504,53 @@ sshkey_demote(const struct sshkey *k, st + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ +- case KEY_RSA: +- if ((pk->rsa = RSA_new()) == NULL || +- (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || +- (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { +- ret = SSH_ERR_ALLOC_FAIL; +- goto fail; ++ case KEY_RSA: { ++ const BIGNUM *k_e, *k_n; ++ BIGNUM *pk_e = NULL, *pk_n = NULL; ++ ++ RSA_get0_key(k->rsa, &k_n, &k_e, NULL); ++ if ((pk->rsa = RSA_new()) == NULL || ++ (pk_e = BN_dup(k_e)) == NULL || ++ (pk_n = BN_dup(k_n)) == NULL || ++ RSA_set0_key(pk->rsa, pk_n, pk_e, NULL) == 0) { ++ BN_free(pk_e); ++ BN_free(pk_n); ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto fail; + } ++ } + break; + case KEY_DSA_CERT: + if ((ret = sshkey_cert_copy(k, pk)) != 0) + goto fail; + /* FALLTHROUGH */ +- case KEY_DSA: +- if ((pk->dsa = DSA_new()) == NULL || +- (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || +- (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || +- (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || +- (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { +- ret = SSH_ERR_ALLOC_FAIL; +- goto fail; ++ case KEY_DSA: { ++ const BIGNUM *k_p, *k_q, *k_g, *k_pub_key; ++ BIGNUM *pk_p = NULL, *pk_q = NULL, *pk_g = NULL; ++ BIGNUM *pk_pub_key = NULL; ++ ++ DSA_get0_pqg(k->dsa, &k_p, &k_q, &k_g); ++ DSA_get0_key(k->dsa, &k_pub_key, NULL); ++ ++ if ((pk->dsa = DSA_new()) == NULL || ++ (pk_p = BN_dup(k_p)) == NULL || ++ (pk_q = BN_dup(k_q)) == NULL || ++ (pk_g = BN_dup(k_g)) == NULL || ++ (pk_pub_key = BN_dup(k_pub_key)) == NULL || ++ DSA_set0_pqg(pk->dsa, pk_p, pk_q, pk_g) == 0) { ++ BN_free(pk_p); ++ BN_free(pk_q); ++ BN_free(pk_g); ++ BN_free(pk_pub_key); ++ ret = SSH_ERR_ALLOC_FAIL; ++ goto fail; ++ } ++ ++ if (DSA_set0_key(pk->dsa, pk_pub_key, NULL) == 0) { ++ BN_free(pk_pub_key); ++ ret = SSH_ERR_LIBCRYPTO_ERROR; ++ goto fail; ++ } + } + break; + case KEY_ECDSA_CERT: +@@ -2584,12 +2700,17 @@ sshkey_certify_custom(struct sshkey *k, + /* XXX this substantially duplicates to_blob(); refactor */ + switch (k->type) { + #ifdef WITH_OPENSSL +- case KEY_DSA_CERT: +- if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || +- (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || +- (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || +- (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) +- goto out; ++ case KEY_DSA_CERT: { ++ const BIGNUM *p, *q, *g, *pub_key; ++ ++ DSA_get0_pqg(k->dsa, &p, &q, &g); ++ DSA_get0_key(k->dsa, &pub_key, NULL); ++ if ((ret = sshbuf_put_bignum2(cert, p)) != 0 || ++ (ret = sshbuf_put_bignum2(cert, q)) != 0 || ++ (ret = sshbuf_put_bignum2(cert, g)) != 0 || ++ (ret = sshbuf_put_bignum2(cert, pub_key)) != 0) ++ goto out; ++ } + break; + # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: +@@ -2601,10 +2722,15 @@ sshkey_certify_custom(struct sshkey *k, + goto out; + break; + # endif /* OPENSSL_HAS_ECC */ +- case KEY_RSA_CERT: +- if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || +- (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0) +- goto out; ++ case KEY_RSA_CERT: { ++ const BIGNUM *e, *n; ++ ++ RSA_get0_key(k->rsa, &n, &e, NULL); ++ if (e == NULL || n == NULL || ++ (ret = sshbuf_put_bignum2(cert, e)) != 0 || ++ (ret = sshbuf_put_bignum2(cert, n)) != 0) ++ goto out; ++ } + break; + #endif /* WITH_OPENSSL */ + case KEY_ED25519_CERT: +@@ -2790,43 +2916,65 @@ sshkey_private_serialize_opt(const struc + goto out; + switch (key->type) { + #ifdef WITH_OPENSSL +- case KEY_RSA: +- if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) +- goto out; ++ case KEY_RSA: { ++ const BIGNUM *n, *e, *d, *iqmp, *p, *q; ++ RSA_get0_key(key->rsa, &n, &e, &d); ++ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); ++ RSA_get0_factors(key->rsa, &p, &q); ++ if ((r = sshbuf_put_bignum2(b, n)) != 0 || ++ (r = sshbuf_put_bignum2(b, e)) != 0 || ++ (r = sshbuf_put_bignum2(b, d)) != 0 || ++ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || ++ (r = sshbuf_put_bignum2(b, p)) != 0 || ++ (r = sshbuf_put_bignum2(b, q)) != 0) ++ goto out; ++ } + break; + case KEY_RSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } +- if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || +- (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) +- goto out; ++ { ++ const BIGNUM *d, *iqmp, *p, *q; ++ ++ RSA_get0_key(key->rsa, NULL, NULL, &d); ++ RSA_get0_factors(key->rsa, &p, &q); ++ RSA_get0_crt_params(key->rsa, NULL, NULL, &iqmp); ++ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || ++ (r = sshbuf_put_bignum2(b, d)) != 0 || ++ (r = sshbuf_put_bignum2(b, iqmp)) != 0 || ++ (r = sshbuf_put_bignum2(b, p)) != 0 || ++ (r = sshbuf_put_bignum2(b, q)) != 0) ++ goto out; ++ } + break; +- case KEY_DSA: +- if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || +- (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || +- (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || +- (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 || +- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) +- goto out; ++ case KEY_DSA: { ++ const BIGNUM *p, *q, *g, *pub_key, *priv_key; ++ ++ DSA_get0_pqg(key->dsa, &p, &q, &g); ++ DSA_get0_key(key->dsa, &pub_key, &priv_key); ++ if ((r = sshbuf_put_bignum2(b, p)) != 0 || ++ (r = sshbuf_put_bignum2(b, q)) != 0 || ++ (r = sshbuf_put_bignum2(b, g)) != 0 || ++ (r = sshbuf_put_bignum2(b, pub_key)) != 0 || ++ (r = sshbuf_put_bignum2(b, priv_key)) != 0) ++ goto out; ++ } + break; + case KEY_DSA_CERT: + if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { + r = SSH_ERR_INVALID_ARGUMENT; + goto out; + } +- if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || +- (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) +- goto out; ++ { ++ const BIGNUM *priv_key; ++ ++ DSA_get0_key(key->dsa, NULL, &priv_key); ++ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || ++ (r = sshbuf_put_bignum2(b, priv_key)) != 0) ++ goto out; ++ } + break; + # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: +@@ -2940,18 +3088,51 @@ sshkey_private_deserialize(struct sshbuf + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) +- goto out; ++ { ++ BIGNUM *p, *q, *g, *pub_key, *priv_key; ++ ++ p = BN_new(); ++ q = BN_new(); ++ g = BN_new(); ++ pub_key = BN_new(); ++ priv_key = BN_new(); ++ if (p == NULL || q == NULL || g == NULL || ++ pub_key == NULL || priv_key == NULL || ++ (r = sshbuf_get_bignum2(buf, p)) != 0 || ++ (r = sshbuf_get_bignum2(buf, q)) != 0 || ++ (r = sshbuf_get_bignum2(buf, g)) != 0 || ++ (r = sshbuf_get_bignum2(buf, pub_key)) != 0 || ++ (r = sshbuf_get_bignum2(buf, priv_key)) != 0 || ++ (r = ((DSA_set0_pqg(k->dsa, p, q, g) == 0) ++ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { ++ BN_free(p); ++ BN_free(q); ++ BN_free(g); ++ BN_free(pub_key); ++ BN_free(priv_key); ++ goto out; ++ } ++ if (DSA_set0_key(k->dsa, pub_key, priv_key) == 0) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ BN_free(pub_key); ++ BN_free(priv_key); ++ goto out; ++ } ++ } + break; +- case KEY_DSA_CERT: +- if ((r = sshkey_froms(buf, &k)) != 0 || +- (r = sshkey_add_private(k)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) +- goto out; ++ case KEY_DSA_CERT: { ++ BIGNUM *priv_key = BN_new(); ++ ++ if (priv_key == NULL || ++ (r = sshkey_froms(buf, &k)) != 0 || ++ (r = sshkey_add_private(k)) != 0 || ++ (r = sshbuf_get_bignum2(buf, priv_key)) != 0 || ++ (r = ((DSA_set0_key(k->dsa, NULL, priv_key) == 0) ++ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { ++ BN_free(priv_key); ++ goto out; ++ } ++ } + break; + # ifdef OPENSSL_HAS_ECC + case KEY_ECDSA: +@@ -3010,29 +3191,89 @@ sshkey_private_deserialize(struct sshbuf + r = SSH_ERR_ALLOC_FAIL; + goto out; + } +- if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || +- (r = ssh_rsa_generate_additional_parameters(k)) != 0) +- goto out; +- if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { ++ { ++ BIGNUM *n, *e, *d, *iqmp, *p, *q; ++ ++ n = BN_new(); ++ e = BN_new(); ++ d = BN_new(); ++ iqmp = BN_new(); ++ p = BN_new(); ++ q = BN_new(); ++ ++ if (n == NULL || e == NULL || d == NULL || ++ iqmp == NULL || p == NULL || q == NULL || ++ (r = sshbuf_get_bignum2(buf, n)) != 0 || ++ (r = sshbuf_get_bignum2(buf, e)) != 0 || ++ (r = sshbuf_get_bignum2(buf, d)) != 0 || ++ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || ++ (r = sshbuf_get_bignum2(buf, p)) != 0 || ++ (r = sshbuf_get_bignum2(buf, q)) != 0 || ++ (r = ((RSA_set0_key(k->rsa, n, e, d) == 0) ++ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { ++ BN_free(n); ++ BN_free(e); ++ BN_free(d); ++ BN_free(iqmp); ++ BN_free(p); ++ BN_free(q); ++ goto out; ++ } ++ if (RSA_set0_factors(k->rsa, p, q) == 0) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ BN_free(iqmp); ++ BN_free(p); ++ BN_free(q); ++ goto out; ++ } ++ if ((r = ssh_rsa_generate_additional_parameters(k, iqmp)) != 0) { ++ BN_free(iqmp); ++ goto out; ++ } ++ } ++ if (RSA_bits(k->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + r = SSH_ERR_KEY_LENGTH; + goto out; + } + break; +- case KEY_RSA_CERT: +- if ((r = sshkey_froms(buf, &k)) != 0 || +- (r = sshkey_add_private(k)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || +- (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || +- (r = ssh_rsa_generate_additional_parameters(k)) != 0) +- goto out; +- if (BN_num_bits(k->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { ++ case KEY_RSA_CERT: { ++ BIGNUM *d, *iqmp, *p, *q; ++ ++ /* N and E are already set so make sure we will not overwrite them */ ++ d = BN_new(); ++ iqmp = BN_new(); ++ p = BN_new(); ++ q = BN_new(); ++ ++ if (d == NULL || iqmp == NULL || p == NULL || ++ q == NULL || ++ (r = sshkey_froms(buf, &k)) != 0 || ++ (r = sshkey_add_private(k)) != 0 || ++ (r = sshbuf_get_bignum2(buf, d)) != 0 || ++ (r = sshbuf_get_bignum2(buf, iqmp)) != 0 || ++ (r = sshbuf_get_bignum2(buf, p)) != 0 || ++ (r = sshbuf_get_bignum2(buf, q)) != 0 || ++ (r = ((RSA_set0_key(k->rsa, NULL, NULL, d) == 0) ++ ? SSH_ERR_LIBCRYPTO_ERROR : 0)) != 0) { ++ BN_free(d); ++ BN_free(iqmp); ++ BN_free(p); ++ BN_free(q); ++ goto out; ++ } ++ if (RSA_set0_factors(k->rsa, p, q) == 0) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ BN_free(p); ++ BN_free(q); ++ goto out; ++ } ++ if (ssh_rsa_generate_additional_parameters(k, iqmp) != 0) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ free(iqmp); ++ goto out; ++ } ++ } ++ if (RSA_bits(k->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + r = SSH_ERR_KEY_LENGTH; + goto out; + } +@@ -3796,7 +4037,9 @@ translate_libcrypto_error(unsigned long + switch (pem_reason) { + case EVP_R_BAD_DECRYPT: + return SSH_ERR_KEY_WRONG_PASSPHRASE; ++#ifdef EVP_R_BN_DECODE_ERROR + case EVP_R_BN_DECODE_ERROR: ++#endif + case EVP_R_DECODE_ERROR: + #ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + case EVP_R_PRIVATE_KEY_DECODE_ERROR: +@@ -3861,7 +4104,7 @@ sshkey_parse_private_pem_fileblob(struct + r = convert_libcrypto_error(); + goto out; + } +- if (pk->type == EVP_PKEY_RSA && ++ if (EVP_PKEY_id(pk) == EVP_PKEY_RSA && + (type == KEY_UNSPEC || type == KEY_RSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; +@@ -3876,11 +4119,11 @@ sshkey_parse_private_pem_fileblob(struct + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- if (BN_num_bits(prv->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { ++ if (RSA_bits(prv->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + r = SSH_ERR_KEY_LENGTH; + goto out; + } +- } else if (pk->type == EVP_PKEY_DSA && ++ } else if (EVP_PKEY_id(pk) == EVP_PKEY_DSA && + (type == KEY_UNSPEC || type == KEY_DSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; +@@ -3892,7 +4135,7 @@ sshkey_parse_private_pem_fileblob(struct + DSA_print_fp(stderr, prv->dsa, 8); + #endif + #ifdef OPENSSL_HAS_ECC +- } else if (pk->type == EVP_PKEY_EC && ++ } else if (EVP_PKEY_id(pk) == EVP_PKEY_EC && + (type == KEY_UNSPEC || type == KEY_ECDSA)) { + if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; +Index: openssh-7.8p1/sshkey.h +=================================================================== +--- openssh-7.8p1.orig/sshkey.h ++++ openssh-7.8p1/sshkey.h +@@ -220,7 +220,7 @@ int sshkey_parse_private_fileblob_type(s + const char *passphrase, struct sshkey **keyp, char **commentp); + + /* XXX should be internal, but used by ssh-keygen */ +-int ssh_rsa_generate_additional_parameters(struct sshkey *); ++int ssh_rsa_generate_additional_parameters(struct sshkey *, BIGNUM *iqmp); + + /* stateful keys (e.g. XMSS) */ + #ifdef NO_ATTRIBUTE_ON_PROTOTYPE_ARGS +Index: openssh-7.8p1/ssh.c +=================================================================== +--- openssh-7.8p1.orig/ssh.c ++++ openssh-7.8p1/ssh.c +@@ -1332,6 +1332,7 @@ main(int ac, char **av) + (char *)NULL); + free(cp); + } ++ free(host_arg); + + if (config_test) { + dump_client_config(&options, host); +Index: openssh-7.8p1/gss-serv.c +=================================================================== +--- openssh-7.8p1.orig/gss-serv.c ++++ openssh-7.8p1/gss-serv.c +@@ -148,7 +148,8 @@ ssh_gssapi_server_mechanisms() { + if (supported_oids == NULL) + ssh_gssapi_prepare_supported_oids(); + return (ssh_gssapi_kex_mechs(supported_oids, +- &ssh_gssapi_server_check_mech, NULL, NULL)); ++ ssh_gssapi_server_check_mech, NULL, NULL, ++ options.gss_kex_algorithms)); + } + + /* Unprivileged */ +Index: openssh-7.8p1/kex.c +=================================================================== +--- openssh-7.8p1.orig/kex.c ++++ openssh-7.8p1/kex.c +@@ -49,6 +49,7 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" ++#include "xmalloc.h" + + #include "ssherr.h" + #include "sshbuf.h" +@@ -359,6 +360,28 @@ kex_assemble_names(char **listp, const c + return r; + } + ++/* Validate GSS KEX method name list */ ++int ++gss_kex_names_valid(const char *names) ++{ ++ char *s, *cp, *p; ++ if (names == NULL || *names == '\0') ++ return 0; ++ s = cp = xstrdup(names); ++ for ((p = strsep(&cp, ",")); p && *p != '\0'; ++ (p = strsep(&cp, ","))) { ++ if (strncmp(p, "gss-", 4) != 0 ++ || kex_alg_by_name(p) == NULL) { ++ error("Unsupported KEX algorithm \"%.100s\"", p); ++ free(s); ++ return 0; ++ } ++ } ++ debug3("gss kex names ok: [%s]", names); ++ free(s); ++ return 1; ++} ++ + /* put algorithm proposal into buffer */ + int + kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) +@@ -795,7 +818,7 @@ choose_mac(struct ssh *ssh, struct sshma + #ifdef SSH_AUDIT_EVENTS + audit_unsupported(SSH_AUDIT_UNSUPPORTED_MAC); + #endif +- return SSH_ERR_NO_MAC_ALG_MATCH; ++ return SSH_ERR_NO_MAC_ALG_MATCH; + } + if (mac_setup(mac, name) < 0) { + free(name); +Index: openssh-7.8p1/kex.h +=================================================================== +--- openssh-7.8p1.orig/kex.h ++++ openssh-7.8p1/kex.h +@@ -182,6 +182,7 @@ int kex_names_valid(const char *); + char *kex_alg_list(char); + char *kex_names_cat(const char *, const char *); + int kex_assemble_names(char **, const char *, const char *); ++int gss_kex_names_valid(const char *); + + int kex_new(struct ssh *, char *[PROPOSAL_MAX], struct kex **); + int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); +Index: openssh-7.8p1/readconf.c +=================================================================== +--- openssh-7.8p1.orig/readconf.c ++++ openssh-7.8p1/readconf.c +@@ -69,6 +69,7 @@ + #include "digest.h" + #include "dh.h" + #include "fips.h" ++#include "ssh-gss.h" + + /* Format of the configuration file: + +@@ -164,7 +165,7 @@ typedef enum { + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, + oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, +- oGssServerIdentity, ++ oGssServerIdentity, oGssKexAlgorithms, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, +@@ -211,6 +212,7 @@ static struct { + { "gssapiclientidentity", oGssClientIdentity }, + { "gssapiserveridentity", oGssServerIdentity }, + { "gssapirenewalforcesrekey", oGssRenewalRekey }, ++ { "gssapikexalgorithms", oGssKexAlgorithms }, + # else + { "gssapiauthentication", oUnsupported }, + { "gssapikeyexchange", oUnsupported }, +@@ -219,6 +221,7 @@ static struct { + { "gssapiclientidentity", oUnsupported }, + { "gssapiserveridentity", oUnsupported }, + { "gssapirenewalforcesrekey", oUnsupported }, ++ { "gssapikexalgorithms", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, +@@ -1015,6 +1018,18 @@ parse_time: + intptr = &options->gss_renewal_rekey; + goto parse_flag; + ++ case oGssKexAlgorithms: ++ arg = strdelim(&s); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!gss_kex_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1879,9 +1894,10 @@ initialize_options(Options * options) + options->gss_keyex = -1; + options->gss_deleg_creds = -1; + options->gss_trust_dns = -1; +- options->gss_renewal_rekey = -1; +- options->gss_client_identity = NULL; +- options->gss_server_identity = NULL; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; ++ options->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -2035,6 +2051,10 @@ fill_default_options(Options * options) + options->gss_trust_dns = 0; + if (options->gss_renewal_rekey == -1) + options->gss_renewal_rekey = 0; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +Index: openssh-7.8p1/readconf.h +=================================================================== +--- openssh-7.8p1.orig/readconf.h ++++ openssh-7.8p1/readconf.h +@@ -46,6 +46,7 @@ typedef struct { + int gss_renewal_rekey; /* Credential renewal forces rekey */ + char *gss_client_identity; /* Principal to initiate GSSAPI with */ + char *gss_server_identity; /* GSSAPI target principal */ ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* Try password + * authentication. */ + int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ +Index: openssh-7.8p1/servconf.c +=================================================================== +--- openssh-7.8p1.orig/servconf.c ++++ openssh-7.8p1/servconf.c +@@ -64,6 +64,7 @@ + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + #include "dh.h" + #include "fips.h" + +@@ -134,6 +135,7 @@ initialize_server_options(ServerOptions + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; + options->gss_store_rekey = -1; ++ options->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -379,6 +381,10 @@ fill_default_server_options(ServerOption + options->gss_strict_acceptor = 1; + if (options->gss_store_rekey == -1) + options->gss_store_rekey = 0; ++#ifdef GSSAPI ++ if (options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); ++#endif + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -525,8 +531,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, +- sGssKeyEx, sGssStoreRekey, +- sAcceptEnv, sSetEnv, sPermitTunnel, ++ sGssKeyEx, sGssStoreRekey, sGssKexAlgorithms, sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, +@@ -605,12 +610,14 @@ static struct { + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, ++ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, + { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, +@@ -1532,6 +1539,18 @@ process_server_config_line(ServerOptions + intptr = &options->gss_store_rekey; + goto parse_flag; + ++ case sGssKexAlgorithms: ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!gss_kex_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2629,6 +2648,7 @@ dump_config(ServerOptions *o) + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); + dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); + dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); ++ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, +Index: openssh-7.8p1/servconf.h +=================================================================== +--- openssh-7.8p1.orig/servconf.h ++++ openssh-7.8p1/servconf.h +@@ -131,6 +131,7 @@ typedef struct { + int gss_cleanup_creds; /* If true, destroy cred cache on logout */ + int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ + int gss_store_rekey; ++ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ + int password_authentication; /* If true, permit password + * authentication. */ + int kbd_interactive_authentication; /* If true, permit */ +Index: openssh-7.8p1/ssh-gss.h +=================================================================== +--- openssh-7.8p1.orig/ssh-gss.h ++++ openssh-7.8p1/ssh-gss.h +@@ -72,6 +72,10 @@ + #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" + #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" + ++#define GSS_KEX_DEFAULT_KEX \ ++ KEX_GSS_GEX_SHA1_ID "," \ ++ KEX_GSS_GRP14_SHA1_ID ++ + typedef struct { + char *filename; + char *envvar; +@@ -148,7 +152,7 @@ int ssh_gssapi_credentials_updated(Gssct + typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, const char *); + char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *); + char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, +- const char *); ++ const char *, const char *); + gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); + int ssh_gssapi_server_check_mech(Gssctxt **, gss_OID, const char *, const char *); + OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); +Index: openssh-7.8p1/ssh.1 +=================================================================== +--- openssh-7.8p1.orig/ssh.1 ++++ openssh-7.8p1/ssh.1 +@@ -489,6 +489,7 @@ For full details of the options listed b + .It GlobalKnownHostsFile + .It GSSAPIAuthentication + .It GSSAPIDelegateCredentials ++.It GSSAPIKexAlgorithms + .It HashKnownHosts + .It Host + .It HostbasedAuthentication +Index: openssh-7.8p1/ssh_config.5 +=================================================================== +--- openssh-7.8p1.orig/ssh_config.5 ++++ openssh-7.8p1/ssh_config.5 +@@ -754,6 +754,18 @@ the name of the host being connected to. + command line will be passed untouched to the GSSAPI library. + The default is + .Dq no . ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are offered for GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1-, ++gss-group1-sha1-, ++gss-group14-sha1- ++.Ed ++.Pp ++The default is ++.Dq gss-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +Index: openssh-7.8p1/sshconnect2.c +=================================================================== +--- openssh-7.8p1.orig/sshconnect2.c ++++ openssh-7.8p1/sshconnect2.c +@@ -301,8 +301,9 @@ ssh_kex2(char *host, struct sockaddr *ho + else + gss_host = host; + +- gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity, +- options.kex_algorithms); ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity, ++ options.kex_algorithms); + if (gss) { + debug("Offering GSSAPI proposal: %s", gss); + xasprintf(&options.kex_algorithms, +Index: openssh-7.8p1/sshd_config.5 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.5 ++++ openssh-7.8p1/sshd_config.5 +@@ -668,6 +668,18 @@ Controls whether the user's GSSAPI crede + successful connection rekeying. This option can be used to accepted renewed + or updated credentials from a compatible client. The default is + .Dq no . ++.It Cm GSSAPIKexAlgorithms ++The list of key exchange algorithms that are accepted by GSSAPI ++key exchange. Possible values are ++.Bd -literal -offset 3n ++gss-gex-sha1-, ++gss-group1-sha1-, ++gss-group14-sha1- ++.Ed ++.Pp ++The default is ++.Dq gss-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. diff --git a/openssh-7.7p1-pam_check_locks.patch b/openssh-7.7p1-pam_check_locks.patch new file mode 100644 index 0000000..d4638f9 --- /dev/null +++ b/openssh-7.7p1-pam_check_locks.patch @@ -0,0 +1,134 @@ +# HG changeset patch +# Parent 089f4fba0112d410a1bfa74398941f076681d446 +new option UsePAMCheckLocks to enforce checking for locked accounts while +UsePAM is used + +bnc#708678, FATE#312033 + +Index: openssh-7.8p1/auth.c +=================================================================== +--- openssh-7.8p1.orig/auth.c ++++ openssh-7.8p1/auth.c +@@ -112,7 +112,7 @@ allowed_user(struct passwd * pw) + return 0; + + #ifdef USE_SHADOW +- if (!options.use_pam) ++ if (!options.use_pam || options.use_pam_check_locks) + spw = getspnam(pw->pw_name); + #ifdef HAS_SHADOW_EXPIRE + if (!options.use_pam && spw != NULL && auth_shadow_acctexpired(spw)) +@@ -132,7 +132,7 @@ allowed_user(struct passwd * pw) + #endif + + /* check for locked account */ +- if (!options.use_pam && passwd && *passwd) { ++ if ((!options.use_pam || options.use_pam_check_locks) && passwd && *passwd) { + int locked = 0; + + #ifdef LOCKED_PASSWD_STRING +Index: openssh-7.8p1/servconf.c +=================================================================== +--- openssh-7.8p1.orig/servconf.c ++++ openssh-7.8p1/servconf.c +@@ -83,6 +83,7 @@ initialize_server_options(ServerOptions + + /* Portable-specific options */ + options->use_pam = -1; ++ options->use_pam_check_locks = -1; + + /* Standard Options */ + options->num_ports = 0; +@@ -259,6 +260,8 @@ fill_default_server_options(ServerOption + /* Portable-specific options */ + if (options->use_pam == -1) + options->use_pam = 0; ++ if (options->use_pam_check_locks == -1) ++ options->use_pam_check_locks = 0; + + /* Standard Options */ + if (options->num_host_key_files == 0) { +@@ -459,7 +462,7 @@ fill_default_server_options(ServerOption + typedef enum { + sBadOption, /* == unknown option */ + /* Portable-specific options */ +- sUsePAM, ++ sUsePAM, sUsePAMChecklocks, + /* Standard Options */ + sPort, sHostKeyFile, sLoginGraceTime, + sPermitRootLogin, sLogFacility, sLogLevel, +@@ -509,8 +512,10 @@ static struct { + /* Portable-specific options */ + #ifdef USE_PAM + { "usepam", sUsePAM, SSHCFG_GLOBAL }, ++ { "usepamchecklocks", sUsePAMChecklocks, SSHCFG_GLOBAL }, + #else + { "usepam", sUnsupported, SSHCFG_GLOBAL }, ++ { "usepamchecklocks", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, + /* Standard Options */ +@@ -1250,6 +1255,9 @@ process_server_config_line(ServerOptions + case sUsePAM: + intptr = &options->use_pam; + goto parse_flag; ++ case sUsePAMChecklocks: ++ intptr = &options->use_pam_check_locks; ++ goto parse_flag; + + /* Standard Options */ + case sBadOption: +Index: openssh-7.8p1/servconf.h +=================================================================== +--- openssh-7.8p1.orig/servconf.h ++++ openssh-7.8p1/servconf.h +@@ -181,6 +181,7 @@ typedef struct { + char *adm_forced_command; + + int use_pam; /* Enable auth via PAM */ ++ int use_pam_check_locks; /* internally check for locked accounts even when using PAM */ + + int permit_tun; + +Index: openssh-7.8p1/sshd_config.0 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.0 ++++ openssh-7.8p1/sshd_config.0 +@@ -961,6 +961,14 @@ DESCRIPTION + If UsePAM is enabled, you will not be able to run sshd(8) as a + non-root user. The default is no. + ++ UsePAMCheckLocks ++ When set to ``yes'', the checks whether the account has been ++ locked with `passwd -l' are performed even when PAM authentication ++ is enabled via UsePAM. This is to ensure that it is not possible ++ to log in with e.g. a public key (in such a case PAM is used only ++ to set up the session and some PAM modules will not check whether ++ the account is locked in this scenario). The default is ``no''. ++ + VersionAddendum + Optionally specifies additional text to append to the SSH + protocol banner sent by the server upon connection. The default +Index: openssh-7.8p1/sshd_config.5 +=================================================================== +--- openssh-7.8p1.orig/sshd_config.5 ++++ openssh-7.8p1/sshd_config.5 +@@ -1613,6 +1613,18 @@ is enabled, you will not be able to run + as a non-root user. + The default is + .Cm no . ++.It Cm UsePAMCheckLocks ++When set to ++.Dq yes ++, the checks whether the account has been locked with ++.Pa passwd -l ++are performed even when PAM authentication is enabled via ++.Cm UsePAM . ++This is to ensure that it is not possible to log in with e.g. a ++public key (in such a case PAM is used only to set up the session and some PAM ++modules will not check whether the account is locked in this scenario). The ++default is ++.Dq no . + .It Cm VersionAddendum + Optionally specifies additional text to append to the SSH protocol banner + sent by the server upon connection. diff --git a/openssh-7.7p1-pts_names_formatting.patch b/openssh-7.7p1-pts_names_formatting.patch new file mode 100644 index 0000000..5d984e8 --- /dev/null +++ b/openssh-7.7p1-pts_names_formatting.patch @@ -0,0 +1,49 @@ +# HG changeset patch +# Parent 7cd948c83939479d1ba88a3161991cb561306f3f +# 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.7p1/loginrec.c b/openssh-7.7p1/loginrec.c +--- openssh-7.7p1/loginrec.c ++++ openssh-7.7p1/loginrec.c +@@ -541,17 +541,17 @@ getlast_entry(struct logininfo *li) + /* + * 'line' string utility functions + * + * These functions process the 'line' string into one of three forms: + * + * 1. The full filename (including '/dev') + * 2. The stripped name (excluding '/dev') + * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 +- * /dev/pts/1 -> ts/1 ) ++ * /dev/pts/1 -> /1 ) + * + * Form 3 is used on some systems to identify a .tmp.? entry when + * attempting to remove it. Typically both addition and removal is + * performed by one application - say, sshd - so as long as the choice + * uniquely identifies a terminal it's ok. + */ + + +@@ -602,16 +602,20 @@ line_abbrevname(char *dst, const char *s + /* Always skip prefix if present */ + if (strncmp(src, "/dev/", 5) == 0) + src += 5; + + #ifdef WITH_ABBREV_NO_TTY + if (strncmp(src, "tty", 3) == 0) + src += 3; + #endif ++ if (strncmp(src, "pts/", 4) == 0) { ++ src += 3; ++ if (strlen(src) > 4) src++; ++ } + + len = strlen(src); + + if (len > 0) { + if (((int)len - dstsize) > 0) + src += ((int)len - dstsize); + + /* note: _don't_ change this to strlcpy */ diff --git a/openssh-7.7p1-remove_xauth_cookies_on_exit.patch b/openssh-7.7p1-remove_xauth_cookies_on_exit.patch new file mode 100644 index 0000000..58d0f9d --- /dev/null +++ b/openssh-7.7p1-remove_xauth_cookies_on_exit.patch @@ -0,0 +1,55 @@ +# HG changeset patch +# Parent a60c0d88667efe0a64c030168950b69476af1622 +# --used to be called '-xauth' +try to remove xauth cookies on logout + +bnc#98815 + +diff --git a/openssh-7.7p1/session.c b/openssh-7.7p1/session.c +--- openssh-7.7p1/session.c ++++ openssh-7.7p1/session.c +@@ -2302,16 +2302,44 @@ session_close(struct ssh *ssh, Session * + u_int i; + + verbose("Close session: user %s from %.200s port %d id %d", + s->pw->pw_name, + ssh_remote_ipaddr(ssh), + ssh_remote_port(ssh), + s->self); + ++ if ((s->display != NULL) && (s->auth_proto != NULL) && ++ (s->auth_data != NULL) && (options.xauth_location != NULL)) { ++ pid_t pid; ++ FILE *f; ++ char cmd[1024]; ++ struct passwd * pw = s->pw; ++ ++ if (!(pid = fork())) { ++ permanently_set_uid(pw); ++ ++ /* Remove authority data from .Xauthority if appropriate. */ ++ debug("Running %.500s remove %.100s\n", ++ options.xauth_location, s->auth_display); ++ ++ snprintf(cmd, sizeof cmd, "unset XAUTHORITY && HOME=\"%.200s\" %s -q -", ++ s->pw->pw_dir, options.xauth_location); ++ f = popen(cmd, "w"); ++ if (f) { ++ fprintf(f, "remove %s\n", s->auth_display); ++ pclose(f); ++ } else ++ error("Could not run %s\n", cmd); ++ exit(0); ++ } else if (pid > 0) { ++ waitpid(pid, NULL, 0); ++ } ++ } ++ + if (s->ttyfd != -1) + session_pty_cleanup(s); + free(s->term); + free(s->display); + free(s->x11_chanids); + free(s->auth_display); + free(s->auth_data); + free(s->auth_proto); diff --git a/openssh-7.7p1-seccomp_ioctl_s390_EP11.patch b/openssh-7.7p1-seccomp_ioctl_s390_EP11.patch new file mode 100644 index 0000000..c6290a0 --- /dev/null +++ b/openssh-7.7p1-seccomp_ioctl_s390_EP11.patch @@ -0,0 +1,36 @@ +# HG changeset patch +# Parent a7b18fdd68dba10349e59a9085fd822343311f45 +Patch from IBM enabling use of EP11 hw crypto accelerator, submitted upstreams: + +From: Eduardo Barretto +To: openssh-unix-dev@mindrot.org +Subject: [PATCH 3/3] Enable specific ioctl call for EP11 crypto card (s390) +Date: Tue, 9 May 2017 14:27:15 -0300 + +The EP11 crypto card needs to make an ioctl call, which receives an +specific argument. This crypto card is for s390 only. + +Signed-off-by: Eduardo Barretto + +diff --git a/openssh-7.7p1/sandbox-seccomp-filter.c b/openssh-7.7p1/sandbox-seccomp-filter.c +--- openssh-7.7p1/sandbox-seccomp-filter.c ++++ openssh-7.7p1/sandbox-seccomp-filter.c +@@ -248,16 +248,18 @@ static const struct sock_filter preauth_ + SC_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN), + SC_DENY(__NR_socketcall, EACCES), + #endif + #if defined(__NR_ioctl) && defined(__s390__) + /* Allow ioctls for ICA crypto card on s390 */ + SC_ALLOW_ARG(__NR_ioctl, 1, Z90STAT_STATUS_MASK), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSAMODEXPO), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSACRT), ++ /* Allow ioctls for EP11 crypto card on s390 */ ++ SC_ALLOW_ARG(__NR_ioctl, 1, ZSENDEP11CPRB), + #endif + #if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT) + /* + * On Linux x32, the clock_gettime VDSO falls back to the + * x86-64 syscall under some circumstances, e.g. + * https://bugs.debian.org/849923 + */ + SC_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT), diff --git a/openssh-7.7p1-seccomp_ipc_flock.patch b/openssh-7.7p1-seccomp_ipc_flock.patch new file mode 100644 index 0000000..4607628 --- /dev/null +++ b/openssh-7.7p1-seccomp_ipc_flock.patch @@ -0,0 +1,60 @@ +# HG changeset patch +# Parent 9d38b7292619a6d5faf554b1a88888fdfa535de7 +Patch from IBM enabling the use of OpenCryptoki, submitted upstreams: + +From: Eduardo Barretto +To: openssh-unix-dev@mindrot.org +Subject: [PATCH 1/3] Allow flock and ipc syscall for s390 architecture +Date: Tue, 9 May 2017 14:27:13 -0300 + +In order to use the OpenSSL-ibmpkcs11 engine it is needed to allow flock +and ipc calls, because this engine calls OpenCryptoki (a PKCS#11 +implementation) which calls the libraries that will communicate with the +crypto cards. OpenCryptoki makes use of flock and ipc and, as of now, +this is only need on s390 architecture. + +Signed-off-by: Eduardo Barretto + +diff --git a/openssh-7.7p1/sandbox-seccomp-filter.c b/openssh-7.7p1/sandbox-seccomp-filter.c +--- openssh-7.7p1/sandbox-seccomp-filter.c ++++ openssh-7.7p1/sandbox-seccomp-filter.c +@@ -167,16 +167,19 @@ static const struct sock_filter preauth_ + SC_ALLOW(__NR_exit_group), + #endif + #ifdef __NR_geteuid + SC_ALLOW(__NR_geteuid), + #endif + #ifdef __NR_geteuid32 + SC_ALLOW(__NR_geteuid32), + #endif ++#if defined(__NR_flock) && defined(__s390__) ++ SC_ALLOW(__NR_flock), ++#endif + #ifdef __NR_getpgid + SC_ALLOW(__NR_getpgid), + #endif + #ifdef __NR_getpid + SC_ALLOW(__NR_getpid), + #endif + #ifdef __NR_getrandom + SC_ALLOW(__NR_getrandom), +@@ -185,16 +188,19 @@ static const struct sock_filter preauth_ + SC_ALLOW(__NR_gettimeofday), + #endif + #ifdef __NR_getuid + SC_ALLOW(__NR_getuid), + #endif + #ifdef __NR_getuid32 + SC_ALLOW(__NR_getuid32), + #endif ++#if defined(__NR_ipc) && defined(__s390__) ++ SC_ALLOW(__NR_ipc), ++#endif + #ifdef __NR_madvise + SC_ALLOW(__NR_madvise), + #endif + #ifdef __NR_mmap + SC_ALLOW(__NR_mmap), + #endif + #ifdef __NR_mmap2 + SC_ALLOW(__NR_mmap2), diff --git a/openssh-7.7p1-seccomp_stat.patch b/openssh-7.7p1-seccomp_stat.patch new file mode 100644 index 0000000..2e0acff --- /dev/null +++ b/openssh-7.7p1-seccomp_stat.patch @@ -0,0 +1,30 @@ +# HG changeset patch +# Parent 5034ae16f6a5c9c7151d931dc1cce2a541fe010e +Allow the stat() syscall for OpenSSL re-seed patch +(which causes OpenSSL use stat() on some file) + +bnc#912436 + +diff --git a/openssh-7.7p1/sandbox-seccomp-filter.c b/openssh-7.7p1/sandbox-seccomp-filter.c +--- openssh-7.7p1/sandbox-seccomp-filter.c ++++ openssh-7.7p1/sandbox-seccomp-filter.c +@@ -224,16 +224,19 @@ static const struct sock_filter preauth_ + SC_ALLOW(__NR_select), + #endif + #ifdef __NR_shutdown + SC_ALLOW(__NR_shutdown), + #endif + #ifdef __NR_sigprocmask + SC_ALLOW(__NR_sigprocmask), + #endif ++#ifdef __NR_stat ++ SC_ALLOW(__NR_stat), ++#endif + #ifdef __NR_time + SC_ALLOW(__NR_time), + #endif + #ifdef __NR_write + SC_ALLOW(__NR_write), + #endif + #ifdef __NR_socketcall + SC_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN), diff --git a/openssh-7.7p1-seed-prng.patch b/openssh-7.7p1-seed-prng.patch new file mode 100644 index 0000000..e9b7f60 --- /dev/null +++ b/openssh-7.7p1-seed-prng.patch @@ -0,0 +1,332 @@ +# HG changeset patch +# Parent e655fcb8e89d19ce9e954d6fc330e5e3e093a848 +# extended support for (re-)seeding the OpenSSL PRNG from /dev/random +# bnc#703221, FATE#312172 + +Index: openssh-7.8p1/entropy.c +=================================================================== +--- openssh-7.8p1.orig/entropy.c ++++ openssh-7.8p1/entropy.c +@@ -235,6 +235,9 @@ seed_rng(void) + memset(buf, '\0', sizeof(buf)); + + #endif /* OPENSSL_PRNG_ONLY */ ++ ++ linux_seed(); ++ + if (RAND_status() != 1) + fatal("PRNG is not seeded"); + } +Index: openssh-7.8p1/openbsd-compat/Makefile.in +=================================================================== +--- openssh-7.8p1.orig/openbsd-compat/Makefile.in ++++ openssh-7.8p1/openbsd-compat/Makefile.in +@@ -90,6 +90,7 @@ COMPAT= arc4random.o \ + PORTS= port-aix.o \ + port-irix.o \ + port-linux.o \ ++ port-linux-prng.o \ + port-solaris.o \ + port-net.o \ + port-uw.o +Index: openssh-7.8p1/openbsd-compat/port-linux-prng.c +=================================================================== +--- /dev/null ++++ openssh-7.8p1/openbsd-compat/port-linux-prng.c +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (c) 2011 Jan F. Chadima ++ * (c) 2011 Petr Cerny ++ * ++ * 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. ++ */ ++ ++/* ++ * Linux-specific portability code - prng support ++ */ ++ ++#include "includes.h" ++#include "defines.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "log.h" ++#include "port-linux.h" ++#include "fips.h" ++ ++#define RNG_BYTES_DEFAULT 6L ++#define RNG_ENV_VAR "SSH_USE_STRONG_RNG" ++ ++long rand_bytes = 0; ++char *rand_file = NULL; ++ ++static void ++linux_seed_init(void) ++{ ++ long elen = 0; ++ char *env = getenv(RNG_ENV_VAR); ++ ++ if (env) { ++ errno = 0; ++ elen = strtol(env, NULL, 10); ++ if (errno) { ++ elen = RNG_BYTES_DEFAULT; ++ debug("bogus value in the %s environment variable, " ++ "using %li bytes from /dev/random\n", ++ RNG_ENV_VAR, RNG_BYTES_DEFAULT); ++ } ++ } ++ ++ if (elen || fips_mode()) ++ rand_file = "/dev/random"; ++ else ++ rand_file = "/dev/urandom"; ++ ++ rand_bytes = MAX(elen, RNG_BYTES_DEFAULT); ++} ++ ++void ++linux_seed(void) ++{ ++ long len; ++ if (!rand_file) ++ linux_seed_init(); ++ ++ errno = 0; ++ len = RAND_load_file(rand_file, rand_bytes); ++ if (len != rand_bytes) { ++ if (errno) ++ fatal ("cannot read from %s, %s", rand_file, strerror(errno)); ++ else ++ fatal ("EOF reading %s", rand_file); ++ } ++} +Index: openssh-7.8p1/openbsd-compat/port-linux.h +=================================================================== +--- openssh-7.8p1.orig/openbsd-compat/port-linux.h ++++ openssh-7.8p1/openbsd-compat/port-linux.h +@@ -17,6 +17,10 @@ + #ifndef _PORT_LINUX_H + #define _PORT_LINUX_H + ++extern long rand_bytes; ++extern char *rand_file; ++void linux_seed(void); ++ + #ifdef WITH_SELINUX + int ssh_selinux_enabled(void); + void ssh_selinux_setup_pty(char *, const char *); +Index: openssh-7.8p1/ssh-add.1 +=================================================================== +--- openssh-7.8p1.orig/ssh-add.1 ++++ openssh-7.8p1/ssh-add.1 +@@ -172,6 +172,20 @@ to make this work.) + Identifies the path of a + .Ux Ns -domain + socket used to communicate with the agent. ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .El + .Sh FILES + .Bl -tag -width Ds +Index: openssh-7.8p1/ssh-agent.1 +=================================================================== +--- openssh-7.8p1.orig/ssh-agent.1 ++++ openssh-7.8p1/ssh-agent.1 +@@ -214,6 +214,23 @@ sockets used to contain the connection t + These sockets should only be readable by the owner. + The sockets should get automatically removed when the agent exits. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , +Index: openssh-7.8p1/ssh-keygen.1 +=================================================================== +--- openssh-7.8p1.orig/ssh-keygen.1 ++++ openssh-7.8p1/ssh-keygen.1 +@@ -869,6 +869,23 @@ Contains Diffie-Hellman groups used for + The file format is described in + .Xr moduli 5 . + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , +Index: openssh-7.8p1/ssh-keysign.8 +=================================================================== +--- openssh-7.8p1.orig/ssh-keysign.8 ++++ openssh-7.8p1/ssh-keysign.8 +@@ -80,6 +80,23 @@ must be set-uid root if host-based authe + If these files exist they are assumed to contain public certificate + information corresponding with the private keys above. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-keygen 1 , +Index: openssh-7.8p1/ssh.1 +=================================================================== +--- openssh-7.8p1.orig/ssh.1 ++++ openssh-7.8p1/ssh.1 +@@ -1432,6 +1432,20 @@ For more information, see the + .Cm PermitUserEnvironment + option in + .Xr sshd_config 5 . ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh FILES + .Bl -tag -width Ds -compact + .It Pa ~/.rhosts +Index: openssh-7.8p1/sshd.8 +=================================================================== +--- openssh-7.8p1.orig/sshd.8 ++++ openssh-7.8p1/sshd.8 +@@ -966,6 +966,23 @@ concurrently for different ports, this c + started last). + The content of this file is not sensitive; it can be world-readable. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr scp 1 , + .Xr sftp 1 , +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -55,6 +55,8 @@ + #endif + #include "openbsd-compat/sys-tree.h" + #include "openbsd-compat/sys-queue.h" ++#include "openbsd-compat/port-linux.h" ++ + #include + + #include +@@ -208,6 +210,13 @@ struct { + int have_ssh2_key; + } sensitive_data; + ++/* ++ * Every RESEED_AFTERth connection triggers call to linux_seed() to re-seed the ++ * random pool. ++ */ ++#define RESEED_AFTER 100 ++static int re_seeding_counter = RESEED_AFTER; ++ + /* This is set to true when a signal is received. */ + static volatile sig_atomic_t received_sighup = 0; + static volatile sig_atomic_t received_sigterm = 0; +@@ -1252,6 +1261,10 @@ server_accept_loop(int *sock_in, int *so + startups++; + break; + } ++ if(!(--re_seeding_counter)) { ++ re_seeding_counter = RESEED_AFTER; ++ linux_seed(); ++ } + + /* + * Got connection. Fork a child to handle it, unless diff --git a/openssh-7.7p1-send_locale.patch b/openssh-7.7p1-send_locale.patch new file mode 100644 index 0000000..10dcb46 --- /dev/null +++ b/openssh-7.7p1-send_locale.patch @@ -0,0 +1,53 @@ +# HG changeset patch +# Parent db426aecefd1f4f8a7f9b9b6e8936cd8dd2f17fa +send locales in default configuration +bnc#65747 + +diff --git a/openssh-7.7p1/ssh_config b/openssh-7.7p1/ssh_config +--- openssh-7.7p1/ssh_config ++++ openssh-7.7p1/ssh_config +@@ -26,16 +26,21 @@ Host * + # security reasons: Someone stealing the authentification data on the + # remote side (the "spoofed" X-server by the remote sshd) can read your + # keystrokes as you type, just like any other X11 client could do. + # Set this to "no" here for global effect or in your own ~/.ssh/config + # file if you want to have the remote X11 authentification data to + # expire after twenty minutes after remote login. + ForwardX11Trusted yes + ++# This enables sending locale enviroment variables LC_* LANG, see ssh_config(5). ++ SendEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES ++ SendEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT ++ SendEnv LC_IDENTIFICATION LC_ALL ++ + # PasswordAuthentication yes + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no + # BatchMode no + # CheckHostIP yes + # AddressFamily any + # ConnectTimeout 0 +diff --git a/openssh-7.7p1/sshd_config b/openssh-7.7p1/sshd_config +--- openssh-7.7p1/sshd_config ++++ openssh-7.7p1/sshd_config +@@ -104,14 +104,19 @@ X11Forwarding yes + #VersionAddendum none + + # no default banner path + #Banner none + + # override default of no subsystems + Subsystem sftp /usr/libexec/sftp-server + ++# This enables accepting locale enviroment variables LC_* LANG, see sshd_config(5). ++AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES ++AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT ++AcceptEnv LC_IDENTIFICATION LC_ALL ++ + # Example of overriding settings on a per-user basis + #Match User anoncvs + # X11Forwarding no + # AllowTcpForwarding no + # PermitTTY no + # ForceCommand cvs server diff --git a/openssh-7.7p1-sftp_force_permissions.patch b/openssh-7.7p1-sftp_force_permissions.patch new file mode 100644 index 0000000..31f2113 --- /dev/null +++ b/openssh-7.7p1-sftp_force_permissions.patch @@ -0,0 +1,157 @@ +# HG changeset patch +# Parent 37bba3ff816d9ab93ddcf23389a4eb29d7716006 +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.7p1/sftp-server.8 b/openssh-7.7p1/sftp-server.8 +--- openssh-7.7p1/sftp-server.8 ++++ openssh-7.7p1/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.7p1/sftp-server.c b/openssh-7.7p1/sftp-server.c +--- openssh-7.7p1/sftp-server.c ++++ openssh-7.7p1/sftp-server.c +@@ -71,16 +71,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; + }; +@@ -685,16 +689,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_RDONLY || + (flags & (O_CREAT|O_TRUNC)) != 0)) { + verbose("Refusing open request in read-only mode"); + status = SSH2_FX_PERMISSION_DENIED; + } else { +@@ -1487,17 +1495,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) + { +@@ -1516,17 +1525,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); +@@ -1576,16 +1585,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.7p1-sftp_print_diagnostic_messages.patch b/openssh-7.7p1-sftp_print_diagnostic_messages.patch new file mode 100644 index 0000000..ad61a2d --- /dev/null +++ b/openssh-7.7p1-sftp_print_diagnostic_messages.patch @@ -0,0 +1,76 @@ +# HG changeset patch +# Parent 60bdbe6dd8d6bc011883472363d56e1d97f68835 +Put back sftp client diagnostic messages in batch mode + +bsc#1023275 + +Index: openssh-7.8p1/sftp.0 +=================================================================== +--- openssh-7.8p1.orig/sftp.0 ++++ openssh-7.8p1/sftp.0 +@@ -160,6 +160,9 @@ DESCRIPTION + -p Preserves modification times, access times, and modes from the + original files transferred. + ++ -Q Not-so-quiet batch mode: forces printing of diagnostic messages ++ in batch mode. ++ + -q Quiet mode: disables the progress meter as well as warning and + diagnostic messages from ssh(1). + +Index: openssh-7.8p1/sftp.1 +=================================================================== +--- openssh-7.8p1.orig/sftp.1 ++++ openssh-7.8p1/sftp.1 +@@ -256,6 +256,9 @@ Specifies the port to connect to on the + .It Fl p + Preserves modification times, access times, and modes from the + original files transferred. ++.It Fl Q ++Not-so-quiet batch mode: forces printing of diagnostic messages ++in batch mode. + .It Fl q + Quiet mode: disables the progress meter as well as warning and + diagnostic messages from +Index: openssh-7.8p1/sftp.c +=================================================================== +--- openssh-7.8p1.orig/sftp.c ++++ openssh-7.8p1/sftp.c +@@ -86,6 +86,9 @@ static volatile pid_t sshpid = -1; + /* Suppress diagnositic messages */ + int quiet = 0; + ++/* Force diagnositic messages in batch mode */ ++int loud = 0; ++ + /* This is set to 0 if the progressmeter is not desired. */ + int showprogress = 1; + +@@ -2373,7 +2376,7 @@ main(int argc, char **argv) + infile = stdin; + + while ((ch = getopt(argc, argv, +- "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { ++ "1246afhpQqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { + switch (ch) { + /* Passed through to ssh(1) */ + case '4': +@@ -2389,6 +2392,9 @@ main(int argc, char **argv) + addargs(&args, "-%c", ch); + addargs(&args, "%s", optarg); + break; ++ case 'Q': ++ loud = 1; ++ break; + case 'q': + ll = SYSLOG_LEVEL_ERROR; + quiet = 1; +@@ -2472,6 +2478,8 @@ main(int argc, char **argv) + usage(); + } + } ++ if (batchmode && loud) ++ quiet = 0; + + if (!isatty(STDERR_FILENO)) + showprogress = 0; diff --git a/openssh-7.7p1-systemd-notify.patch b/openssh-7.7p1-systemd-notify.patch new file mode 100644 index 0000000..5ae8b30 --- /dev/null +++ b/openssh-7.7p1-systemd-notify.patch @@ -0,0 +1,86 @@ +# HG changeset patch +# Parent d296e85dc414b8cd1b4b55ad03d8216feb26531a +Send signals to systemd to prevent various race conditions +bsc#1048367 + +Index: openssh-7.8p1/configure.ac +=================================================================== +--- openssh-7.8p1.orig/configure.ac ++++ openssh-7.8p1/configure.ac +@@ -4378,6 +4378,30 @@ AC_ARG_WITH([kerberos5], + AC_SUBST([GSSLIBS]) + AC_SUBST([K5LIBS]) + ++# Check whether user wants systemd support ++SYSTEMD_MSG="no" ++AC_ARG_WITH(systemd, ++ [ --with-systemd Enable systemd support], ++ [ if test "x$withval" != "xno" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "$PKGCONFIG" != "no"; then ++ AC_MSG_CHECKING([for libsystemd]) ++ if $PKGCONFIG --exists libsystemd; then ++ SYSTEMD_CFLAGS=`$PKGCONFIG --cflags libsystemd` ++ SYSTEMD_LIBS=`$PKGCONFIG --libs libsystemd` ++ CPPFLAGS="$CPPFLAGS $SYSTEMD_CFLAGS" ++ SSHDLIBS="$SSHDLIBS $SYSTEMD_LIBS" ++ AC_MSG_RESULT([yes]) ++ AC_DEFINE(HAVE_SYSTEMD, 1, [Define if you want systemd support.]) ++ SYSTEMD_MSG="yes" ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ fi ] ++) ++ ++ + # Looking for programs, paths and files + + PRIVSEP_PATH=/var/empty +@@ -5183,6 +5207,7 @@ echo " libldns support + echo " Solaris process contract support: $SPC_MSG" + echo " Solaris project support: $SP_MSG" + echo " Solaris privilege support: $SPP_MSG" ++echo " systemd support: $SYSTEMD_MSG" + echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" + echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" + echo " BSD Auth support: $BSD_AUTH_MSG" +Index: openssh-7.8p1/sshd.c +=================================================================== +--- openssh-7.8p1.orig/sshd.c ++++ openssh-7.8p1/sshd.c +@@ -87,6 +87,10 @@ + #include + #endif + ++#ifdef HAVE_SYSTEMD ++#include ++#endif ++ + #include "xmalloc.h" + #include "ssh.h" + #include "ssh2.h" +@@ -308,6 +312,10 @@ sighup_handler(int sig) + static void + sighup_restart(void) + { ++#ifdef HAVE_SYSTEMD ++ /* Signal systemd that we are reloading */ ++ sd_notify(0, "RELOADING=1"); ++#endif + logit("Received SIGHUP; restarting."); + if (options.pid_file != NULL) + unlink(options.pid_file); +@@ -1995,6 +2003,11 @@ main(int ac, char **av) + } + } + ++#ifdef HAVE_SYSTEMD ++ /* Signal systemd that we are ready to accept connections */ ++ sd_notify(0, "READY=1"); ++#endif ++ + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); diff --git a/openssh-7.7p1.tar.gz b/openssh-7.7p1.tar.gz deleted file mode 100644 index c518c6f..0000000 --- a/openssh-7.7p1.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d73be7e684e99efcd024be15a30bffcbe41b012b2f7b3c9084aed621775e6b8f -size 1536900 diff --git a/openssh-7.7p1.tar.gz.asc b/openssh-7.7p1.tar.gz.asc deleted file mode 100644 index c3c3b95..0000000 --- a/openssh-7.7p1.tar.gz.asc +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQHDBAABCgAdFiEEWcIRjtIG2SfmZ+vj0+X1a22SDTAFAlrBwh4ACgkQ0+X1a22S -DTCqGwyAgQuR+5b6dAEK3PV3WnzuPSJ8KKnw3/HlqQw40QfWotVOX4+On3+yOYy+ -txjAWkbocjHa5/6IzKVU0y9GD3A0H7XwJAwjqqQg3pKD3kXyl7Lz5nkwWWICN0z+ -fU8HUwJv3SOhilD7XRZqWHUfSL69AR5CbYPraurMQWDNwHY0i4n3vDFp1WrSJx8q -mcSgAEwucKavr3+PDm0MbmYINAqgqn1USVDalGy8U6ICnCyzXvu4o8gMuiGGwwKR -Jlt2zCs5CBnF2LAaFgawwNh6NO/TOLvvNrW3zUm3s3DzLKqYtl4Jfs39Coii9LEE -PqF8YFhgbzm+JPPe9/k5zBSEZOWwkzu33cXm7nC1rypt4PQVZLB8BvRE5HXE9QOx -xpGi+BFVeMIMqjsW+nOAAdl4S+FNtzR/OABAhwRveLGMPMFRQ9/GqN5B1L9Wezut -V/6SUUzQUyf5Kn6Gjo+ktJB1i7ufPTLSjH9eYjS/7Fn5cMdjF5iezOAzp3FNWXln -cDZzHkVgrwqYqTKkekDFTwJD+q/QJQ== -=gz3x ------END PGP SIGNATURE----- diff --git a/openssh-7.8p1.tar.gz b/openssh-7.8p1.tar.gz new file mode 100644 index 0000000..754639c --- /dev/null +++ b/openssh-7.8p1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1a484bb15152c183bb2514e112aa30dd34138c3cfb032eee5490a66c507144ca +size 1548026 diff --git a/openssh-7.8p1.tar.gz.asc b/openssh-7.8p1.tar.gz.asc new file mode 100644 index 0000000..9942bbf --- /dev/null +++ b/openssh-7.8p1.tar.gz.asc @@ -0,0 +1,14 @@ +-----BEGIN PGP SIGNATURE----- + +iQHDBAABCgAdFiEEWcIRjtIG2SfmZ+vj0+X1a22SDTAFAlt+Xa8ACgkQ0+X1a22S +DTAJPwx9HIW/obxNJYTU7M8trpalBekdl1SqUjxdDwInIsKTLSOpJCsnynBai/3c +SuvZkBwcKwZZFe+xCvRQDHkf/YYLT+d7slUQolb0OJmzFKbvu6xwuv7q12ag9hQj +/8BUfdYRKb63uemfKuVAHfcnUm9WlwSbif+Au/j1yg/MlETY47ezYA9/q75wignx +3g38JVHVgKDenDd8o9/hgjeQpEHKNdCQo71nN2h3MYRlh4xrR9ENZj7y8x65Kp1j +WoZEhlvjYkka4deSGwj2MIAJnzsc39uppEoEjkB7F9SUo4O7CxbWFein70Ct7Xbs +VDWXQibnJGHKatHIecaPLUYexGWO1XYNZErDhY7fPw0ChfMGbz3+0eDfDJqGY49r +Lo6wzsrgv2kDJMqwciT/D/Zb3ocHnCrq1Isnz/Ug2lW58LMk7Y1HisPteZFQ/pkC +xKeO+K1RkaRUSCrB5iToqF+7i8eRNVROYmkKLgKcMrC0WYEjnbEoFdr4bktAS9QM +BS6aIsh2cyg2H0FjDKmYvcKOUf0IgA== +=ZiYm +-----END PGP SIGNATURE----- diff --git a/openssh-askpass-gnome.changes b/openssh-askpass-gnome.changes index 593fbee..aa197b9 100644 --- a/openssh-askpass-gnome.changes +++ b/openssh-askpass-gnome.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Tue Oct 9 10:52:15 UTC 2018 - Tomáš Chvátal + +- Update to 7.8p1: + * no actual changes for the askpass +- Format with spec-cleaner +- Respect cflags +- Use gtk3 rather than gtk2 which is being phased out + ------------------------------------------------------------------- Mon May 21 15:19:03 UTC 2018 - pcerny@suse.com diff --git a/openssh-askpass-gnome.spec b/openssh-askpass-gnome.spec index cae8063..05039d0 100644 --- a/openssh-askpass-gnome.spec +++ b/openssh-askpass-gnome.spec @@ -12,23 +12,22 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # %define _name openssh Name: openssh-askpass-gnome -BuildRequires: gtk2-devel -Version: 7.7p1 +Version: 7.8p1 Release: 0 -Requires: %{_name} = %{version} Summary: A GNOME-Based Passphrase Dialog for OpenSSH License: BSD-2-Clause Group: Productivity/Networking/SSH -Url: http://www.openssh.com/ +URL: http://www.openssh.com/ Source: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/%{_name}-%{version}.tar.gz Source42: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/%{_name}-%{version}.tar.gz.asc -BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildRequires: gtk3-devel +Requires: %{_name} = %{version} %description SSH (Secure Shell) is a program for logging into a remote machine and @@ -40,14 +39,14 @@ GNOME-based passphrase dialog for OpenSSH. %build cd contrib -make %{?_smp_mflags} gnome-ssh-askpass2 +export CFLAGS="%{optflags}" +make %{?_smp_mflags} gnome-ssh-askpass3 %install install -d -m 755 %{buildroot}%{_libexecdir}/ssh/ -install contrib/gnome-ssh-askpass2 %{buildroot}%{_libexecdir}/ssh/gnome-ssh-askpass +install contrib/gnome-ssh-askpass3 %{buildroot}%{_libexecdir}/ssh/gnome-ssh-askpass %files -%defattr(-,root,root) %dir %{_libexecdir}/ssh %attr(0755,root,root) %{_libexecdir}/ssh/gnome-ssh-askpass diff --git a/openssh.changes b/openssh.changes index a7e87bd..366ef78 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,80 @@ +------------------------------------------------------------------- +Wed Oct 17 08:42:12 UTC 2018 - Tomáš Chvátal + +- Remove the mention of the SLE12 in the README.SUSE +- Install firewall rules only when really needed ( + +- Version update to 7.8p1: + * For most details see release notes file + * ssh-keygen(1): write OpenSSH format private keys by default + instead of using OpenSSL's PEM format +- Rebase patches to apply on 7.8p1 release: + * openssh-7.7p1-fips.patch + * openssh-7.7p1-cavstest-kdf.patch + * openssh-7.7p1-fips_checks.patch + * openssh-7.7p1-gssapi_key_exchange.patch + * openssh-7.7p1-audit.patch + * openssh-7.7p1-openssl_1.1.0.patch + * openssh-7.7p1-ldap.patch + * openssh-7.7p1-IPv6_X_forwarding.patch + * openssh-7.7p1-sftp_print_diagnostic_messages.patch + * openssh-7.7p1-disable_short_DH_parameters.patch + * openssh-7.7p1-hostname_changes_when_forwarding_X.patch + * openssh-7.7p1-pam_check_locks.patch + * openssh-7.7p1-seed-prng.patch + * openssh-7.7p1-systemd-notify.patch + * openssh-7.7p1-X11_trusted_forwarding.patch +- Dropped patches: + * openssh-7.7p1-lastlog.patch + * openssh-7.7p1-blocksigalrm.patch +- Do not use env in script cavs_driver-ssh.pl +- Added pam_keyinit to pam configuration file [bsc#1081947] + +------------------------------------------------------------------- +Tue Oct 9 11:01:40 UTC 2018 - Tomáš Chvátal + +- Format with spec-cleaner +- Reduce conditionals to support SLE12+ only +- Split out bundled patches to be normal patches applied over + the package (use -p1 for patches): + * openssh-7.7p1-allow_root_password_login.patch + * openssh-7.7p1-X11_trusted_forwarding.patch + * openssh-7.7p1-lastlog.patch + * openssh-7.7p1-enable_PAM_by_default.patch + * openssh-7.7p1-eal3.patch + * openssh-7.7p1-blocksigalrm.patch + * openssh-7.7p1-send_locale.patch + * openssh-7.7p1-hostname_changes_when_forwarding_X.patch + * openssh-7.7p1-remove_xauth_cookies_on_exit.patch + * openssh-7.7p1-pts_names_formatting.patch + * openssh-7.7p1-pam_check_locks.patch + * openssh-7.7p1-disable_short_DH_parameters.patch + * openssh-7.7p1-seccomp_getuid.patch + * openssh-7.7p1-seccomp_geteuid.patch + * openssh-7.7p1-seccomp_stat.patch + * openssh-7.7p1-seccomp_ipc_flock.patch + * openssh-7.7p1-seccomp_ioctl_s390_EP11.patch + * openssh-7.7p1-fips.patch + * openssh-7.7p1-cavstest-ctr.patch + * openssh-7.7p1-cavstest-kdf.patch + * openssh-7.7p1-fips_checks.patch + * openssh-7.7p1-seed-prng.patch + * openssh-7.7p1-systemd-notify.patch + * openssh-7.7p1-gssapi_key_exchange.patch + * openssh-7.7p1-audit.patch + * openssh-7.7p1-openssl_1.1.0.patch + * openssh-7.7p1-disable_openssl_abi_check.patch + * openssh-7.7p1-no_fork-no_pid_file.patch + * openssh-7.7p1-host_ident.patch + * openssh-7.7p1-sftp_force_permissions.patch + * openssh-7.7p1-X_forward_with_disabled_ipv6.patch + * openssh-7.7p1-ldap.patch + * openssh-7.7p1-IPv6_X_forwarding.patch + * openssh-7.7p1-sftp_print_diagnostic_messages.patch + ------------------------------------------------------------------- Tue Sep 18 09:22:23 UTC 2018 - schwab@suse.de @@ -1628,6 +1705,7 @@ Mon Mar 31 01:22:21 UTC 2014 - pcerny@suse.com - FIPS checks during ssh client and daemon startup (-fips-checks.patch) + ------------------------------------------------------------------- Tue Mar 25 10:07:18 UTC 2014 - idonmez@suse.com diff --git a/openssh.spec b/openssh.spec index 5de6d4f..354e64f 100644 --- a/openssh.spec +++ b/openssh.spec @@ -12,97 +12,39 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # -#Compat macro for new _fillupdir macro introduced in Nov 2017 -%if ! %{defined _fillupdir} - %define _fillupdir /var/adm/fillup-templates -%endif - -%if 0%{suse_version} >= 1100 -%define has_fw_dir 1 -%else -%define has_fw_dir 0 -%endif - -%if 0%{suse_version} >= 1110 -%define has_libselinux 1 -%else -%define has_libselinux 0 -%endif - -%if 0%{?suse_version} >= 1130 -%define needs_all_dirs 1 -%else -%define needs_all_dirs 0 -%endif - -%if 0%{?suse_version} >= 1140 -%define needs_libedit 1 -%else -%define needs_libedit 0 -%endif - -%if 0%{?suse_version} > 1220 -%define uses_systemd 1 -%else -%define uses_systemd 0 -%endif - %define sandbox_seccomp 0 %ifnarch ppc -%if 0%{?suse_version} > 1220 %define sandbox_seccomp 1 %endif -%endif - %if 0%{?suse_version} >= 1500 -%define use_tirpc 1 +%bcond_with susefirewall +%bcond_without tirpc +%else +%bcond_without susefirewall +%bcond_with tirpc %endif - %define _fwdir %{_sysconfdir}/sysconfig/SuSEfirewall2.d %define _fwdefdir %{_fwdir}/services %define _appdefdir %( grep "configdirspec=" $( which xmkmf ) | sed -r 's,^[^=]+=.*-I(.*)/config.*$,\\1/app-defaults,' ) -%{!?_initddir:%global _initddir %{_initrddir}} - +%define CHECKSUM_SUFFIX .hmac +%define CHECKSUM_HMAC_KEY "HMAC_KEY:OpenSSH-FIPS@SLE" +#Compat macro for new _fillupdir macro introduced in Nov 2017 +%if ! %{defined _fillupdir} + %define _fillupdir %{_localstatedir}/adm/fillup-templates +%endif Name: openssh -BuildRequires: audit-devel -BuildRequires: autoconf -BuildRequires: groff -BuildRequires: zlib-devel -BuildRequires: pkgconfig(krb5) -%if %{needs_libedit} -BuildRequires: libedit-devel -%endif -%if %{has_libselinux} -BuildRequires: libselinux-devel -%endif -BuildRequires: openldap2-devel -BuildRequires: openssl-devel -BuildRequires: pam-devel -%if 0%{?use_tirpc} -BuildRequires: libtirpc-devel -%endif -%if %{uses_systemd} -BuildRequires: pkgconfig(libsystemd) -BuildRequires: pkgconfig(systemd) -%{?systemd_requires} -%endif -PreReq: pwdutils %{fillup_prereq} coreutils -%if ! %{uses_systemd} -PreReq: %{insserv_prereq} -%endif -Version: 7.7p1 +Version: 7.8p1 Release: 0 Summary: Secure Shell Client and Server (Remote Login Program) License: BSD-2-Clause AND MIT Group: Productivity/Networking/SSH -Url: http://www.openssh.com/ -Source: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz -Source42: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc -Source1: sshd.init +URL: http://www.openssh.com/ +Source0: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz +Source1: http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc Source2: sshd.pamd Source3: README.SUSE Source4: README.kerberos @@ -114,15 +56,61 @@ Source9: sshd-gen-keys-start Source10: sshd.service Source11: README.FIPS Source12: cavs_driver-ssh.pl -Source100: openssh-%{version}-SUSE_patches.tar.gz -BuildRoot: %{_tmppath}/%{name}-%{version}-build -Conflicts: nonfreessh +Patch0: openssh-7.7p1-allow_root_password_login.patch +Patch1: openssh-7.7p1-X11_trusted_forwarding.patch +Patch3: openssh-7.7p1-enable_PAM_by_default.patch +Patch4: openssh-7.7p1-eal3.patch +Patch5: openssh-7.7p1-blocksigalrm.patch +Patch6: openssh-7.7p1-send_locale.patch +Patch7: openssh-7.7p1-hostname_changes_when_forwarding_X.patch +Patch8: openssh-7.7p1-remove_xauth_cookies_on_exit.patch +Patch9: openssh-7.7p1-pts_names_formatting.patch +Patch10: openssh-7.7p1-pam_check_locks.patch +Patch11: openssh-7.7p1-disable_short_DH_parameters.patch +Patch14: openssh-7.7p1-seccomp_stat.patch +Patch15: openssh-7.7p1-seccomp_ipc_flock.patch +Patch16: openssh-7.7p1-seccomp_ioctl_s390_EP11.patch +Patch17: openssh-7.7p1-fips.patch +Patch18: openssh-7.7p1-cavstest-ctr.patch +Patch19: openssh-7.7p1-cavstest-kdf.patch +Patch20: openssh-7.7p1-fips_checks.patch +Patch21: openssh-7.7p1-seed-prng.patch +Patch22: openssh-7.7p1-systemd-notify.patch +Patch23: openssh-7.7p1-gssapi_key_exchange.patch +Patch24: openssh-7.7p1-audit.patch +Patch25: openssh-7.7p1-openssl_1.1.0.patch +Patch26: openssh-7.7p1-disable_openssl_abi_check.patch +Patch27: openssh-7.7p1-no_fork-no_pid_file.patch +Patch28: openssh-7.7p1-host_ident.patch +Patch29: openssh-7.7p1-sftp_force_permissions.patch +Patch30: openssh-7.7p1-X_forward_with_disabled_ipv6.patch +Patch31: openssh-7.7p1-ldap.patch +Patch32: openssh-7.7p1-IPv6_X_forwarding.patch +Patch33: openssh-7.7p1-sftp_print_diagnostic_messages.patch +BuildRequires: audit-devel +BuildRequires: autoconf +BuildRequires: groff +BuildRequires: libedit-devel +BuildRequires: libselinux-devel +BuildRequires: openldap2-devel +BuildRequires: openssl-devel +BuildRequires: pam-devel +BuildRequires: pkgconfig +BuildRequires: zlib-devel +BuildRequires: pkgconfig(krb5) +BuildRequires: pkgconfig(libsystemd) +Requires(post): %fillup_prereq +Requires(pre): pwdutils +Recommends: %{name}-helpers = %{version}-%{release} Recommends: audit Recommends: xauth -Recommends: %{name}-helpers = %{version}-%{release} -Conflicts: %{name}-fips < %{version}-%{release} , %{name}-fips > %{version}-%{release} -%define CHECKSUM_SUFFIX .hmac -%define CHECKSUM_HMAC_KEY "HMAC_KEY:OpenSSH-FIPS@SLE" +Conflicts: %{name}-fips < %{version}-%{release} +Conflicts: %{name}-fips > %{version}-%{release} +Conflicts: nonfreessh +%{?systemd_requires} +%if %{with tirpc} +BuildRequires: libtirpc-devel +%endif %description SSH (Secure Shell) is a program for logging into and executing commands @@ -133,7 +121,6 @@ hosts over an insecure network. xorg-x11 (X Window System) connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. - %package helpers Summary: OpenSSH AuthorizedKeysCommand helpers Group: Productivity/Networking/SSH @@ -142,19 +129,18 @@ Requires: %{name} = %{version}-%{release} %description helpers Helper applications for OpenSSH which retrieve keys from various sources. - %package fips Summary: OpenSSH FIPS cryptomodule HMACs Group: Productivity/Networking/SSH Requires: %{name} = %{version}-%{release} -Conflicts: %{name} < %{version}-%{release} , %{name} > %{version}-%{release} +Conflicts: %{name} < %{version}-%{release} +Conflicts: %{name} > %{version}-%{release} Obsoletes: %{name}-hmac %description fips Hashes that together with the main package form the FIPS certifiable cryptomodule. - %package cavs Summary: OpenSSH FIPS cryptomodule CAVS tests Group: Productivity/Networking/SSH @@ -163,32 +149,21 @@ Requires: %{name} = %{version}-%{release} %description cavs FIPS140 CAVS tests related parts of the OpenSSH package - %prep -%setup -q -b 100 +%setup -q cp %{SOURCE3} %{SOURCE4} %{SOURCE11} . -# patch sources -PATCH_DIR="../SUSE_patches" -cat $PATCH_DIR/patch.series | while read p; do -%if %{suse_version} < 1330 - if echo "$p" | grep openssl_1.1.0.patch ; then - printf ">> skipping '$p'" - continue - fi -%endif - printf ">> applying '$p'\n" - patch -p2 < "${PATCH_DIR}/$p" -done -# set libexec dir in the LDAP patch +%autopatch -p1 + +#set libexec dir in the LDAP patch sed -i.libexec 's,@LIBEXECDIR@,%{_libexecdir}/ssh,' \ $( grep -Rl @LIBEXECDIR@ \ - $( grep "^+++" $PATCH_DIR/openssh-7.7p1-ldap.patch | sed -r 's@^.+/([^/\t ]+).*$@\1@' ) + $( grep "^+++" openssh-7.7p1-ldap.patch | sed -r 's@^.+/([^/\t ]+).*$@\1@' ) ) %build autoreconf -fiv -%ifarch s390 s390x %sparc +%ifarch s390 s390x %{sparc} PIEFLAGS="-fPIE" %else PIEFLAGS="-fpie" @@ -196,26 +171,19 @@ PIEFLAGS="-fpie" CFLAGS="%{optflags} $PIEFLAGS -fstack-protector" CXXFLAGS="%{optflags} $PIEFLAGS -fstack-protector" LDFLAGS="-pie -Wl,--as-needed" -#CPPFLAGS="%{optflags} -DUSE_INTERNAL_B64" +#CPPFLAGS="% {optflags} -DUSE_INTERNAL_B64" export LDFLAGS CFLAGS CXXFLAGS CPPFLAGS %configure \ - --prefix=%{_prefix} \ - --mandir=%{_mandir} \ - --infodir=%{_infodir} \ --sysconfdir=%{_sysconfdir}/ssh \ --libexecdir=%{_libexecdir}/ssh \ --with-tcp-wrappers \ -%if %{has_libselinux} --with-selinux \ -%endif -%if %{uses_systemd} --with-pid-dir=/run \ --with-systemd \ -%endif --with-ssl-engine \ --with-pam \ --with-kerberos5=%{_prefix} \ - --with-privsep-path=/var/lib/empty \ + --with-privsep-path=%{_localstatedir}/lib/empty \ %if %{sandbox_seccomp} --with-sandbox=seccomp_filter \ %else @@ -228,46 +196,32 @@ export LDFLAGS CFLAGS CXXFLAGS CPPFLAGS --with-audit=linux \ --with-ldap \ --with-xauth=%{_bindir}/xauth \ -%if %{needs_libedit} --with-libedit \ -%endif --with-ssh1 \ --target=%{_target_cpu}-suse-linux \ ### configure end make %{?_smp_mflags} -#make %{?_smp_mflags} -C converter - %install -make install DESTDIR=%{buildroot} -#make install DESTDIR=%{buildroot} -C converter +%make_install install -d -m 755 %{buildroot}%{_sysconfdir}/pam.d -install -d -m 755 %{buildroot}/var/lib/sshd +install -d -m 755 %{buildroot}%{_localstatedir}/lib/sshd install -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/pam.d/sshd install -d -m 755 %{buildroot}%{_sysconfdir}/slp.reg.d/ install -m 644 %{SOURCE5} %{buildroot}%{_sysconfdir}/slp.reg.d/ -install -d -m 755 %{buildroot}%{_initddir} -%if %{uses_systemd} -install -m 0755 %{SOURCE1} . install -D -m 0644 %{SOURCE10} %{buildroot}%{_unitdir}/sshd.service -ln -s /sbin/service %{buildroot}%{_sbindir}/rcsshd -%else -install -D -m 0755 %{SOURCE1} %{buildroot}%{_initddir}/sshd -install -m 0644 %{SOURCE10} . -ln -s ../..%{_initddir}/sshd %{buildroot}%{_sbindir}/rcsshd -%endif +ln -s service %{buildroot}%{_sbindir}/rcsshd install -d -m 755 %{buildroot}%{_fillupdir} install -m 644 %{SOURCE8} %{buildroot}%{_fillupdir} # install shell script to automate the process of adding your public key to a remote machine install -m 755 contrib/ssh-copy-id %{buildroot}%{_bindir} install -m 644 contrib/ssh-copy-id.1 %{buildroot}%{_mandir}/man1 -sed -i -e s@/usr/libexec@%{_libexecdir}@g %{buildroot}%{_sysconfdir}/ssh/sshd_config +sed -i -e s@%{_prefix}/libexec@%{_libexecdir}@g %{buildroot}%{_sysconfdir}/ssh/sshd_config -%if %{has_fw_dir} -#install firewall definitions format is described here: -#%{_datadir}/SuSEfirewall2/services/TEMPLATE +%if %{with susefirewall} +#install firewall definitions mkdir -p %{buildroot}%{_fwdefdir} install -m 644 %{SOURCE7} %{buildroot}%{_fwdefdir}/sshd %endif @@ -284,7 +238,7 @@ install -D -m 0755 %{SOURCE9} %{buildroot}%{_sbindir}/sshd-gen-keys-start # re-define the __os_install_post macro: the macro strips # the binaries and thereby invalidates any hashes created earlier. # -# this shows up earlier because otherwise the %expand of +# this shows up earlier because otherwise the % expand of # the macro is too late. %{expand:%%global __os_install_post {%__os_install_post for b in \ @@ -299,52 +253,36 @@ done %pre getent group sshd >/dev/null || %{_sbindir}/groupadd -r sshd -getent passwd sshd >/dev/null || %{_sbindir}/useradd -r -g sshd -d /var/lib/sshd -s /bin/false -c "SSH daemon" sshd -%if %{uses_systemd} +getent passwd sshd >/dev/null || %{_sbindir}/useradd -r -g sshd -d %{_localstatedir}/lib/sshd -s /bin/false -c "SSH daemon" sshd %service_add_pre sshd.service -%endif %post -%if %{uses_systemd} %{fillup_only -n ssh sshd} %service_add_post sshd.service -%else -%{fillup_and_insserv -n ssh sshd} -%endif -%set_permissions /etc/ssh/sshd_config +%set_permissions %{_sysconfdir}/ssh/sshd_config %preun -%if %{uses_systemd} %service_del_preun sshd.service -%else -%stop_on_removal sshd -%endif %postun # The openssh-fips trigger script for openssh will normally restart sshd once # it gets installed, so only restart the service here is openssh-fips is not # present rpm -q openssh-fips >& /dev/null && DISABLE_RESTART_ON_UPDATE=yes -%if %{uses_systemd} %service_del_postun sshd.service -%else -%restart_on_update sshd -%{insserv_cleanup} -%endif %triggerin -n openssh-fips -- %{name} = %{version}-%{release} %restart_on_update sshd %verifyscript -%verify_permissions -e /etc/ssh/sshd_config +%verify_permissions -e %{_sysconfdir}/ssh/sshd_config %files -%defattr(-,root,root) %exclude %{_bindir}/ssh%{CHECKSUM_SUFFIX} %exclude %{_sbindir}/sshd%{CHECKSUM_SUFFIX} %exclude %{_libexecdir}/ssh/sftp-server%{CHECKSUM_SUFFIX} %exclude %{_libexecdir}/ssh/cavs* -%dir %attr(755,root,root) /var/lib/sshd +%dir %attr(755,root,root) %{_localstatedir}/lib/sshd %license LICENCE %doc README.SUSE README.kerberos README.FIPS ChangeLog OVERVIEW README TODO CREDITS %attr(0755,root,root) %dir %{_sysconfdir}/ssh @@ -352,34 +290,23 @@ rpm -q openssh-fips >& /dev/null && DISABLE_RESTART_ON_UPDATE=yes %verify(not mode) %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config %verify(not mode) %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/pam.d/sshd -%if %{uses_systemd} -%doc sshd.init -%attr(0644,root,root) %config %{_unitdir}/sshd.service -%else -%attr(0755,root,root) %config %{_initddir}/sshd -%doc sshd.service -%endif +%attr(0644,root,root) %{_unitdir}/sshd.service %attr(0755,root,root) %{_bindir}/* %attr(0755,root,root) %{_sbindir}/* %attr(0755,root,root) %dir %{_libexecdir}/ssh %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/* -%attr(0444,root,root) %doc %{_mandir}/man8/* +%attr(0444,root,root) %{_mandir}/man1/* +%attr(0444,root,root) %{_mandir}/man5/* +%attr(0444,root,root) %{_mandir}/man8/* %dir %{_sysconfdir}/slp.reg.d %config %{_sysconfdir}/slp.reg.d/ssh.reg %{_fillupdir}/sysconfig.ssh -%if %{has_fw_dir} -%if %{needs_all_dirs} -%dir %{_fwdir} -%dir %{_fwdefdir} -%endif +%if %{with susefirewall} %config %{_fwdefdir}/sshd %endif %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 %attr(0755,root,root) %dir %{_libexecdir}/ssh @@ -387,13 +314,11 @@ rpm -q openssh-fips >& /dev/null && DISABLE_RESTART_ON_UPDATE=yes %doc HOWTO.ldap-keys openssh-lpk-openldap.schema openssh-lpk-sun.schema %files fips -%defattr(-,root,root) %attr(0444,root,root) %{_bindir}/ssh%{CHECKSUM_SUFFIX} %attr(0444,root,root) %{_sbindir}/sshd%{CHECKSUM_SUFFIX} %attr(0444,root,root) %{_libexecdir}/ssh/sftp-server%{CHECKSUM_SUFFIX} %files cavs -%defattr(-,root,root) %attr(0755,root,root) %{_libexecdir}/ssh/cavs* %changelog diff --git a/sshd.init b/sshd.init deleted file mode 100644 index 7a92141..0000000 --- a/sshd.init +++ /dev/null @@ -1,150 +0,0 @@ -#! /bin/sh -# Copyright (c) 1995-2013 SUSE -# -# Author: Jiri Smid -# -# /etc/init.d/sshd -# -# and symbolic its link -# -# /usr/sbin/rcsshd -# -### BEGIN INIT INFO -# Provides: sshd -# Required-Start: $network $remote_fs -# Required-Stop: $network $remote_fs -# Should-Start: haveged auditd -# Default-Start: 3 5 -# Default-Stop: 0 1 2 6 -# Description: Start the sshd daemon -### END INIT INFO - -SSHD_BIN=/usr/sbin/sshd -test -x $SSHD_BIN || exit 5 - -SSHD_SYSCONFIG=/etc/sysconfig/ssh -test -r $SSHD_SYSCONFIG || exit 6 -. $SSHD_SYSCONFIG - -SSHD_PIDFILE=/var/run/sshd.init.pid - -. /etc/rc.status - -# Shell functions sourced from /etc/rc.status: -# rc_check check and set local and overall rc status -# rc_status check and set local and overall rc status -# rc_status -v ditto but be verbose in local rc status -# rc_status -v -r ditto and clear the local rc status -# rc_failed set local and overall rc status to failed -# rc_reset clear local rc status (overall remains) -# rc_exit exit appropriate to overall rc status - -function soft_stop () { - echo -n "Shutting down the listening SSH daemon" - killproc -p $SSHD_PIDFILE -TERM $SSHD_BIN -} - -function force_stop () { - echo -n "Shutting down SSH daemon *with all active connections*" - trap '' TERM - killall sshd 2>/dev/null - trap - TERM -} - -# First reset status of this service -rc_reset - -case "$1" in - start) - /usr/sbin/sshd-gen-keys-start - echo -n "Starting SSH daemon" - ## Start daemon with startproc(8). If this fails - ## the echo return value is set appropriate. - startproc -f -p $SSHD_PIDFILE $SSHD_BIN $SSHD_OPTS -o "PidFile=$SSHD_PIDFILE" - - # Remember status and be verbose - rc_status -v - ;; - stop) - # If we're shutting down, kill active sshd connections so they're not - # left hanging. - runlevel=$(set -- $(runlevel); eval "echo \$$#") - if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then - force_stop - else - soft_stop - fi - - # Remember status and be verbose - rc_status -v - ;; - soft-stop) - ## Stop the listener daemon process with killproc(8) and if this - ## fails set echo the echo return value. - soft_stop - - # Remember status and be verbose - rc_status -v - ;; - force-stop) - ## stop all running ssh - force_stop - - # Remember status and be verbose - rc_status -v - ;; - try-restart) - ## Stop the service and if this succeeds (i.e. the - ## service was running before), start it again. - $0 status >/dev/null && $0 restart - - # Remember status and be quiet - rc_status - ;; - restart) - ## Stop the service without closing live connections - ## and start it again regardless of whether it was - ## running or not - $0 soft-stop - $0 start - - # Remember status and be quiet - rc_status - ;; - force-reload|reload) - ## Signal the daemon to reload its config. Most daemons - ## do this on signal 1 (SIGHUP). - echo -n "Reload service sshd" - - killproc -p $SSHD_PIDFILE -HUP $SSHD_BIN - - rc_status -v - - ;; - status) - echo -n "Checking for service sshd " - ## Check status with checkproc(8), if process is running - ## checkproc will return with exit status 0. - - # Status has a slightly different for the status command: - # 0 - service running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running - - checkproc -p $SSHD_PIDFILE $SSHD_BIN - - rc_status -v - ;; - probe) - ## Optional: Probe for the necessity of a reload, - ## give out the argument which is required for a reload. - - test /etc/ssh/sshd_config -nt $SSHD_PIDFILE && echo reload - ;; - *) - echo "Usage: $0 {start|stop|soft-stop|force-stop|status|try-restart|restart|force-reload|reload|probe}" - exit 1 - ;; -esac -rc_exit diff --git a/sshd.pamd b/sshd.pamd index f27032c..cb4d696 100644 --- a/sshd.pamd +++ b/sshd.pamd @@ -6,4 +6,5 @@ account include common-account password include common-password session required pam_loginuid.so session include common-session -session optional pam_lastlog.so silent noupdate showfailed +session optional pam_lastlog.so silent noupdate showfailed +session optional pam_keyinit.so force revoke