From b830398e1843f2af0dcabb13ee45802c1a0103ec Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 7 May 2019 02:34:56 -0400 Subject: [PATCH] import openssh-7.8p1-4.el8 --- .gitignore | 3 + .openssh.metadata | 3 + SOURCES/openssh-4.3p2-askpass-grab-info.patch | 19 + SOURCES/openssh-5.1p1-askpass-progress.patch | 81 + SOURCES/openssh-5.8p2-sigpipe.patch | 12 + SOURCES/openssh-5.9p1-ipv6man.patch | 24 + SOURCES/openssh-5.9p1-wIm.patch | 78 + SOURCES/openssh-6.1p1-gssapi-canohost.patch | 21 + SOURCES/openssh-6.2p1-vendor.patch | 142 + SOURCES/openssh-6.3p1-ctr-evp-fast.patch | 101 + SOURCES/openssh-6.4p1-fromto-remote.patch | 16 + SOURCES/openssh-6.6.1p1-log-in-chroot.patch | 263 + ...h-6.6.1p1-scp-non-existing-directory.patch | 14 + .../openssh-6.6.1p1-selinux-contexts.patch | 126 + .../openssh-6.6p1-GSSAPIEnablek5users.patch | 129 + SOURCES/openssh-6.6p1-allow-ip-opts.patch | 39 + SOURCES/openssh-6.6p1-ctr-cavstest.patch | 257 + SOURCES/openssh-6.6p1-force_krb.patch | 280 + SOURCES/openssh-6.6p1-keycat.patch | 485 ++ SOURCES/openssh-6.6p1-keyperm.patch | 26 + SOURCES/openssh-6.6p1-kuserok.patch | 289 + SOURCES/openssh-6.6p1-privsep-selinux.patch | 121 + SOURCES/openssh-6.7p1-coverity.patch | 185 + SOURCES/openssh-6.7p1-kdf-cavs.patch | 608 +++ SOURCES/openssh-6.7p1-ldap.patch | 2742 ++++++++++ .../openssh-6.7p1-sftp-force-permission.patch | 98 + SOURCES/openssh-6.8p1-sshdT-output.patch | 27 + SOURCES/openssh-6.9p1-permit-root-login.patch | 12 + SOURCES/openssh-7.0p1-gssKexAlgorithms.patch | 431 ++ .../openssh-7.1p1-gssapi-documentation.patch | 52 + .../openssh-7.1p2-audit-race-condition.patch | 187 + SOURCES/openssh-7.2p2-k5login_directory.patch | 87 + SOURCES/openssh-7.2p2-s390-closefrom.patch | 52 + SOURCES/openssh-7.2p2-x11.patch | 53 + SOURCES/openssh-7.3p1-openssl-1.1.0.patch | 2688 ++++++++++ SOURCES/openssh-7.3p1-x11-max-displays.patch | 213 + SOURCES/openssh-7.4p1-systemd.patch | 98 + .../openssh-7.5p1-gssapi-kex-with-ec.patch | 1377 +++++ SOURCES/openssh-7.5p1-sandbox.patch | 108 + SOURCES/openssh-7.6p1-audit.patch | 2172 ++++++++ SOURCES/openssh-7.6p1-cleanup-selinux.patch | 253 + SOURCES/openssh-7.6p1-pkcs11-ecdsa.patch | 801 +++ SOURCES/openssh-7.6p1-pkcs11-uri.patch | 4771 +++++++++++++++++ SOURCES/openssh-7.7p1-fips.patch | 709 +++ SOURCES/openssh-7.7p1-gssapi-new-unique.patch | 648 +++ SOURCES/openssh-7.7p1-redhat.patch | 164 + SOURCES/openssh-7.8p1-UsePAM-warning.patch | 26 + SOURCES/openssh-7.8p1-gsskex.patch | 2907 ++++++++++ .../openssh-7.8p1-ip-port-config-parser.patch | 72 + SOURCES/openssh-7.8p1-role-mls.patch | 885 +++ SOURCES/openssh-7.8p1-scp-ipv6.patch | 16 + SOURCES/openssh-7.8p1.tar.gz.asc | 14 + SOURCES/openssh-7.9p1-disable-sha1.patch | 62 + SOURCES/openssh-7.9p1-gsskex-method.patch | 150 + SOURCES/pam_ssh_agent-rmheaders | 37 + .../pam_ssh_agent_auth-0.10.2-compat.patch | 939 ++++ ...am_ssh_agent_auth-0.10.2-dereference.patch | 20 + .../pam_ssh_agent_auth-0.10.3-seteuid.patch | 37 + .../pam_ssh_agent_auth-0.9.2-visibility.patch | 21 + ...ssh_agent_auth-0.9.3-agent_structure.patch | 96 + SOURCES/pam_ssh_agent_auth-0.9.3-build.patch | 196 + SOURCES/ssh-keycat.pam | 8 + SOURCES/sshd-keygen | 40 + SOURCES/sshd-keygen.target | 5 + SOURCES/sshd-keygen@.service | 11 + SOURCES/sshd.pam | 17 + SOURCES/sshd.service | 18 + SOURCES/sshd.socket | 11 + SOURCES/sshd.sysconfig | 11 + SOURCES/sshd.tmpfiles | 1 + SOURCES/sshd@.service | 11 + SPECS/openssh.spec | 2817 ++++++++++ 72 files changed, 29493 insertions(+) create mode 100644 .gitignore create mode 100644 .openssh.metadata create mode 100644 SOURCES/openssh-4.3p2-askpass-grab-info.patch create mode 100644 SOURCES/openssh-5.1p1-askpass-progress.patch create mode 100644 SOURCES/openssh-5.8p2-sigpipe.patch create mode 100644 SOURCES/openssh-5.9p1-ipv6man.patch create mode 100644 SOURCES/openssh-5.9p1-wIm.patch create mode 100644 SOURCES/openssh-6.1p1-gssapi-canohost.patch create mode 100644 SOURCES/openssh-6.2p1-vendor.patch create mode 100644 SOURCES/openssh-6.3p1-ctr-evp-fast.patch create mode 100644 SOURCES/openssh-6.4p1-fromto-remote.patch create mode 100644 SOURCES/openssh-6.6.1p1-log-in-chroot.patch create mode 100644 SOURCES/openssh-6.6.1p1-scp-non-existing-directory.patch create mode 100644 SOURCES/openssh-6.6.1p1-selinux-contexts.patch create mode 100644 SOURCES/openssh-6.6p1-GSSAPIEnablek5users.patch create mode 100644 SOURCES/openssh-6.6p1-allow-ip-opts.patch create mode 100644 SOURCES/openssh-6.6p1-ctr-cavstest.patch create mode 100644 SOURCES/openssh-6.6p1-force_krb.patch create mode 100644 SOURCES/openssh-6.6p1-keycat.patch create mode 100644 SOURCES/openssh-6.6p1-keyperm.patch create mode 100644 SOURCES/openssh-6.6p1-kuserok.patch create mode 100644 SOURCES/openssh-6.6p1-privsep-selinux.patch create mode 100644 SOURCES/openssh-6.7p1-coverity.patch create mode 100644 SOURCES/openssh-6.7p1-kdf-cavs.patch create mode 100644 SOURCES/openssh-6.7p1-ldap.patch create mode 100644 SOURCES/openssh-6.7p1-sftp-force-permission.patch create mode 100644 SOURCES/openssh-6.8p1-sshdT-output.patch create mode 100644 SOURCES/openssh-6.9p1-permit-root-login.patch create mode 100644 SOURCES/openssh-7.0p1-gssKexAlgorithms.patch create mode 100644 SOURCES/openssh-7.1p1-gssapi-documentation.patch create mode 100644 SOURCES/openssh-7.1p2-audit-race-condition.patch create mode 100644 SOURCES/openssh-7.2p2-k5login_directory.patch create mode 100644 SOURCES/openssh-7.2p2-s390-closefrom.patch create mode 100644 SOURCES/openssh-7.2p2-x11.patch create mode 100644 SOURCES/openssh-7.3p1-openssl-1.1.0.patch create mode 100644 SOURCES/openssh-7.3p1-x11-max-displays.patch create mode 100644 SOURCES/openssh-7.4p1-systemd.patch create mode 100644 SOURCES/openssh-7.5p1-gssapi-kex-with-ec.patch create mode 100644 SOURCES/openssh-7.5p1-sandbox.patch create mode 100644 SOURCES/openssh-7.6p1-audit.patch create mode 100644 SOURCES/openssh-7.6p1-cleanup-selinux.patch create mode 100644 SOURCES/openssh-7.6p1-pkcs11-ecdsa.patch create mode 100644 SOURCES/openssh-7.6p1-pkcs11-uri.patch create mode 100644 SOURCES/openssh-7.7p1-fips.patch create mode 100644 SOURCES/openssh-7.7p1-gssapi-new-unique.patch create mode 100644 SOURCES/openssh-7.7p1-redhat.patch create mode 100644 SOURCES/openssh-7.8p1-UsePAM-warning.patch create mode 100644 SOURCES/openssh-7.8p1-gsskex.patch create mode 100644 SOURCES/openssh-7.8p1-ip-port-config-parser.patch create mode 100644 SOURCES/openssh-7.8p1-role-mls.patch create mode 100644 SOURCES/openssh-7.8p1-scp-ipv6.patch create mode 100644 SOURCES/openssh-7.8p1.tar.gz.asc create mode 100644 SOURCES/openssh-7.9p1-disable-sha1.patch create mode 100644 SOURCES/openssh-7.9p1-gsskex-method.patch create mode 100644 SOURCES/pam_ssh_agent-rmheaders create mode 100644 SOURCES/pam_ssh_agent_auth-0.10.2-compat.patch create mode 100644 SOURCES/pam_ssh_agent_auth-0.10.2-dereference.patch create mode 100644 SOURCES/pam_ssh_agent_auth-0.10.3-seteuid.patch create mode 100644 SOURCES/pam_ssh_agent_auth-0.9.2-visibility.patch create mode 100644 SOURCES/pam_ssh_agent_auth-0.9.3-agent_structure.patch create mode 100644 SOURCES/pam_ssh_agent_auth-0.9.3-build.patch create mode 100644 SOURCES/ssh-keycat.pam create mode 100644 SOURCES/sshd-keygen create mode 100644 SOURCES/sshd-keygen.target create mode 100644 SOURCES/sshd-keygen@.service create mode 100644 SOURCES/sshd.pam create mode 100644 SOURCES/sshd.service create mode 100644 SOURCES/sshd.socket create mode 100644 SOURCES/sshd.sysconfig create mode 100644 SOURCES/sshd.tmpfiles create mode 100644 SOURCES/sshd@.service create mode 100644 SPECS/openssh.spec diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8594728 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +SOURCES/DJM-GPG-KEY.gpg +SOURCES/openssh-7.8p1.tar.gz +SOURCES/pam_ssh_agent_auth-0.10.3.tar.bz2 diff --git a/.openssh.metadata b/.openssh.metadata new file mode 100644 index 0000000..15d8406 --- /dev/null +++ b/.openssh.metadata @@ -0,0 +1,3 @@ +bed7240bb17840b451b8f8457791c33456814d93 SOURCES/DJM-GPG-KEY.gpg +27e267e370315561de96577fccae563bc2c37a60 SOURCES/openssh-7.8p1.tar.gz +a4482a050fdad1d012427e45799564136708cf6b SOURCES/pam_ssh_agent_auth-0.10.3.tar.bz2 diff --git a/SOURCES/openssh-4.3p2-askpass-grab-info.patch b/SOURCES/openssh-4.3p2-askpass-grab-info.patch new file mode 100644 index 0000000..e9a0b0d --- /dev/null +++ b/SOURCES/openssh-4.3p2-askpass-grab-info.patch @@ -0,0 +1,19 @@ +diff -up openssh-7.4p1/contrib/gnome-ssh-askpass2.c.grab-info openssh-7.4p1/contrib/gnome-ssh-askpass2.c +--- openssh-7.4p1/contrib/gnome-ssh-askpass2.c.grab-info 2016-12-23 13:31:22.645213115 +0100 ++++ openssh-7.4p1/contrib/gnome-ssh-askpass2.c 2016-12-23 13:31:40.997216691 +0100 +@@ -65,9 +65,12 @@ report_failed_grab (GtkWidget *parent_wi + err = gtk_message_dialog_new(GTK_WINDOW(parent_window), 0, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, +- "Could not grab %s. " +- "A malicious client may be eavesdropping " +- "on your session.", what); ++ "SSH password dialog could not grab the %s input.\n" ++ "This might be caused by application such as screensaver, " ++ "however it could also mean that someone may be eavesdropping " ++ "on your session.\n" ++ "Either close the application which grabs the %s or " ++ "log out and log in again to prevent this from happening.", what, what); + gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); + + gtk_dialog_run(GTK_DIALOG(err)); diff --git a/SOURCES/openssh-5.1p1-askpass-progress.patch b/SOURCES/openssh-5.1p1-askpass-progress.patch new file mode 100644 index 0000000..6601fbf --- /dev/null +++ b/SOURCES/openssh-5.1p1-askpass-progress.patch @@ -0,0 +1,81 @@ +diff -up openssh-7.4p1/contrib/gnome-ssh-askpass2.c.progress openssh-7.4p1/contrib/gnome-ssh-askpass2.c +--- openssh-7.4p1/contrib/gnome-ssh-askpass2.c.progress 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/contrib/gnome-ssh-askpass2.c 2016-12-23 13:31:16.545211926 +0100 +@@ -53,6 +53,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -81,13 +82,24 @@ ok_dialog(GtkWidget *entry, gpointer dia + gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + } + ++static void ++move_progress(GtkWidget *entry, gpointer progress) ++{ ++ gdouble step; ++ g_return_if_fail(GTK_IS_PROGRESS_BAR(progress)); ++ ++ step = g_random_double_range(0.03, 0.1); ++ gtk_progress_bar_set_pulse_step(GTK_PROGRESS_BAR(progress), step); ++ gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progress)); ++} ++ + static int + passphrase_dialog(char *message) + { + const char *failed; + char *passphrase, *local; + int result, grab_tries, grab_server, grab_pointer; +- GtkWidget *parent_window, *dialog, *entry; ++ GtkWidget *parent_window, *dialog, *entry, *progress, *hbox; + GdkGrabStatus status; + + grab_server = (getenv("GNOME_SSH_ASKPASS_GRAB_SERVER") != NULL); +@@ -104,14 +116,32 @@ passphrase_dialog(char *message) + "%s", + message); + ++ hbox = gtk_hbox_new(FALSE, 0); ++ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, ++ FALSE, 0); ++ gtk_widget_show(hbox); ++ + entry = gtk_entry_new(); + gtk_box_pack_start( +- GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), entry, +- FALSE, FALSE, 0); ++ GTK_BOX(hbox), entry, ++ TRUE, FALSE, 0); ++ gtk_entry_set_width_chars(GTK_ENTRY(entry), 2); + gtk_entry_set_visibility(GTK_ENTRY(entry), FALSE); + gtk_widget_grab_focus(entry); + gtk_widget_show(entry); + ++ hbox = gtk_hbox_new(FALSE, 0); ++ gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, ++ FALSE, 8); ++ gtk_widget_show(hbox); ++ ++ progress = gtk_progress_bar_new(); ++ ++ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), "Passphrase length hidden intentionally"); ++ gtk_box_pack_start(GTK_BOX(hbox), progress, TRUE, ++ TRUE, 5); ++ gtk_widget_show(progress); ++ + gtk_window_set_title(GTK_WINDOW(dialog), "OpenSSH"); + gtk_window_set_position (GTK_WINDOW(dialog), GTK_WIN_POS_CENTER); + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); +@@ -120,6 +150,8 @@ passphrase_dialog(char *message) + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK); + g_signal_connect(G_OBJECT(entry), "activate", + G_CALLBACK(ok_dialog), dialog); ++ g_signal_connect(G_OBJECT(entry), "changed", ++ G_CALLBACK(move_progress), progress); + + gtk_window_set_keep_above(GTK_WINDOW(dialog), TRUE); + diff --git a/SOURCES/openssh-5.8p2-sigpipe.patch b/SOURCES/openssh-5.8p2-sigpipe.patch new file mode 100644 index 0000000..56af045 --- /dev/null +++ b/SOURCES/openssh-5.8p2-sigpipe.patch @@ -0,0 +1,12 @@ +diff -up openssh-5.8p2/ssh-keyscan.c.sigpipe openssh-5.8p2/ssh-keyscan.c +--- openssh-5.8p2/ssh-keyscan.c.sigpipe 2011-08-23 18:30:33.873025916 +0200 ++++ openssh-5.8p2/ssh-keyscan.c 2011-08-23 18:32:24.574025362 +0200 +@@ -715,6 +715,8 @@ main(int argc, char **argv) + fdlim_set(maxfd); + fdcon = xcalloc(maxfd, sizeof(con)); + ++ signal(SIGPIPE, SIG_IGN); ++ + read_wait_nfdset = howmany(maxfd, NFDBITS); + read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); + diff --git a/SOURCES/openssh-5.9p1-ipv6man.patch b/SOURCES/openssh-5.9p1-ipv6man.patch new file mode 100644 index 0000000..ece1a73 --- /dev/null +++ b/SOURCES/openssh-5.9p1-ipv6man.patch @@ -0,0 +1,24 @@ +diff -up openssh-5.9p0/ssh.1.ipv6man openssh-5.9p0/ssh.1 +--- openssh-5.9p0/ssh.1.ipv6man 2011-08-05 22:17:32.000000000 +0200 ++++ openssh-5.9p0/ssh.1 2011-08-31 13:08:34.880024485 +0200 +@@ -1400,6 +1400,8 @@ manual page for more information. + .Nm + exits with the exit status of the remote command or with 255 + if an error occurred. ++.Sh IPV6 ++IPv6 address can be used everywhere where IPv4 address. In all entries must be the IPv6 address enclosed in square brackets. Note: The square brackets are metacharacters for the shell and must be escaped in shell. + .Sh SEE ALSO + .Xr scp 1 , + .Xr sftp 1 , +diff -up openssh-5.9p0/sshd.8.ipv6man openssh-5.9p0/sshd.8 +--- openssh-5.9p0/sshd.8.ipv6man 2011-08-05 22:17:32.000000000 +0200 ++++ openssh-5.9p0/sshd.8 2011-08-31 13:10:34.129039094 +0200 +@@ -940,6 +940,8 @@ concurrently for different ports, this c + started last). + The content of this file is not sensitive; it can be world-readable. + .El ++.Sh IPV6 ++IPv6 address can be used everywhere where IPv4 address. In all entries must be the IPv6 address enclosed in square brackets. Note: The square brackets are metacharacters for the shell and must be escaped in shell. + .Sh SEE ALSO + .Xr scp 1 , + .Xr sftp 1 , diff --git a/SOURCES/openssh-5.9p1-wIm.patch b/SOURCES/openssh-5.9p1-wIm.patch new file mode 100644 index 0000000..a00046a --- /dev/null +++ b/SOURCES/openssh-5.9p1-wIm.patch @@ -0,0 +1,78 @@ +diff -up openssh-5.9p1/Makefile.in.wIm openssh-5.9p1/Makefile.in +--- openssh-5.9p1/Makefile.in.wIm 2011-08-05 22:15:18.000000000 +0200 ++++ openssh-5.9p1/Makefile.in 2011-09-12 16:24:18.643674014 +0200 +@@ -66,7 +66,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o b + cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ + compat.o compress.o crc32.o deattack.o fatal.o hostfile.o \ + log.o match.o md-sha256.o moduli.o nchan.o packet.o \ +- readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ ++ readpass.o rsa.o ttymodes.o whereIam.o xmalloc.o addrmatch.o \ + atomicio.o key.o dispatch.o kex.o mac.o uidswap.o uuencode.o misc.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + kexdh.o kexgex.o kexdhc.o kexgexc.o bufec.o kexecdh.o kexecdhc.o \ +diff -up openssh-5.9p1/log.h.wIm openssh-5.9p1/log.h +--- openssh-5.9p1/log.h.wIm 2011-06-20 06:42:23.000000000 +0200 ++++ openssh-5.9p1/log.h 2011-09-12 16:34:52.984674326 +0200 +@@ -65,6 +65,8 @@ void verbose(const char *, ...) __at + void debug(const char *, ...) __attribute__((format(printf, 1, 2))); + void debug2(const char *, ...) __attribute__((format(printf, 1, 2))); + void debug3(const char *, ...) __attribute__((format(printf, 1, 2))); ++void _debug_wIm_body(const char *, int, const char *, const char *, int); ++#define debug_wIm(a,b) _debug_wIm_body(a,b,__func__,__FILE__,__LINE__) + + + void set_log_handler(log_handler_fn *, void *); +diff -up openssh-5.9p1/sshd.c.wIm openssh-5.9p1/sshd.c +--- openssh-5.9p1/sshd.c.wIm 2011-06-23 11:45:51.000000000 +0200 ++++ openssh-5.9p1/sshd.c 2011-09-12 16:38:35.787816490 +0200 +@@ -140,6 +140,9 @@ int deny_severity; + + extern char *__progname; + ++/* trace of fork processes */ ++extern int whereIam; ++ + /* Server configuration options. */ + ServerOptions options; + +@@ -666,6 +669,7 @@ privsep_preauth(Authctxt *authctxt) + return 1; + } else { + /* child */ ++ whereIam = 1; + close(pmonitor->m_sendfd); + close(pmonitor->m_log_recvfd); + +@@ -715,6 +719,7 @@ privsep_postauth(Authctxt *authctxt) + + /* child */ + ++ whereIam = 2; + close(pmonitor->m_sendfd); + pmonitor->m_sendfd = -1; + +@@ -1325,6 +1330,8 @@ main(int ac, char **av) + Key *key; + Authctxt *authctxt; + ++ whereIam = 0; ++ + #ifdef HAVE_SECUREWARE + (void)set_auth_parameters(ac, av); + #endif +diff -up openssh-5.9p1/whereIam.c.wIm openssh-5.9p1/whereIam.c +--- openssh-5.9p1/whereIam.c.wIm 2011-09-12 16:24:18.722674167 +0200 ++++ openssh-5.9p1/whereIam.c 2011-09-12 16:24:18.724674418 +0200 +@@ -0,0 +1,12 @@ ++ ++int whereIam = -1; ++ ++void _debug_wIm_body(const char *txt, int val, const char *func, const char *file, int line) ++{ ++ if (txt) ++ debug("%s=%d, %s(%s:%d) wIm = %d, uid=%d, euid=%d", txt, val, func, file, line, whereIam, getuid(), geteuid()); ++ else ++ debug("%s(%s:%d) wIm = %d, uid=%d, euid=%d", func, file, line, whereIam, getuid(), geteuid()); ++} ++ ++ diff --git a/SOURCES/openssh-6.1p1-gssapi-canohost.patch b/SOURCES/openssh-6.1p1-gssapi-canohost.patch new file mode 100644 index 0000000..3e6c9cc --- /dev/null +++ b/SOURCES/openssh-6.1p1-gssapi-canohost.patch @@ -0,0 +1,21 @@ +diff -up openssh-6.1p1/sshconnect2.c.canohost openssh-6.1p1/sshconnect2.c +--- openssh-6.1p1/sshconnect2.c.canohost 2012-10-30 10:52:59.593301692 +0100 ++++ openssh-6.1p1/sshconnect2.c 2012-10-30 11:01:12.870301632 +0100 +@@ -699,12 +699,15 @@ userauth_gssapi(Authctxt *authctxt) + static u_int mech = 0; + OM_uint32 min; + int r, ok = 0; +- const char *gss_host; ++ const char *gss_host = NULL; + + if (options.gss_server_identity) + gss_host = options.gss_server_identity; +- else if (options.gss_trust_dns) ++ else if (options.gss_trust_dns) { + gss_host = get_canonical_hostname(active_state, 1); ++ if (strcmp(gss_host, "UNKNOWN") == 0) ++ gss_host = authctxt->host; ++ } + else + gss_host = authctxt->host; + diff --git a/SOURCES/openssh-6.2p1-vendor.patch b/SOURCES/openssh-6.2p1-vendor.patch new file mode 100644 index 0000000..1af5e9d --- /dev/null +++ b/SOURCES/openssh-6.2p1-vendor.patch @@ -0,0 +1,142 @@ +diff -up openssh-7.4p1/configure.ac.vendor openssh-7.4p1/configure.ac +--- openssh-7.4p1/configure.ac.vendor 2016-12-23 13:34:51.681253844 +0100 ++++ openssh-7.4p1/configure.ac 2016-12-23 13:34:51.694253847 +0100 +@@ -4930,6 +4930,12 @@ AC_ARG_WITH([lastlog], + fi + ] + ) ++AC_ARG_ENABLE(vendor-patchlevel, ++ [ --enable-vendor-patchlevel=TAG specify a vendor patch level], ++ [AC_DEFINE_UNQUOTED(SSH_VENDOR_PATCHLEVEL,[SSH_RELEASE "-" "$enableval"],[Define to your vendor patch level, if it has been modified from the upstream source release.]) ++ SSH_VENDOR_PATCHLEVEL="$enableval"], ++ [AC_DEFINE(SSH_VENDOR_PATCHLEVEL,SSH_RELEASE,[Define to your vendor patch level, if it has been modified from the upstream source release.]) ++ SSH_VENDOR_PATCHLEVEL=none]) + + dnl lastlog, [uw]tmpx? detection + dnl NOTE: set the paths in the platform section to avoid the +@@ -5194,6 +5200,7 @@ echo " Translate v4 in v6 hack + echo " BSD Auth support: $BSD_AUTH_MSG" + echo " Random number source: $RAND_MSG" + echo " Privsep sandbox style: $SANDBOX_STYLE" ++echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" + + echo "" + +diff -up openssh-7.4p1/servconf.c.vendor openssh-7.4p1/servconf.c +--- openssh-7.4p1/servconf.c.vendor 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/servconf.c 2016-12-23 13:36:07.555268628 +0100 +@@ -143,6 +143,7 @@ initialize_server_options(ServerOptions + options->max_authtries = -1; + options->max_sessions = -1; + options->banner = NULL; ++ options->show_patchlevel = -1; + options->use_dns = -1; + options->client_alive_interval = -1; + options->client_alive_count_max = -1; +@@ -325,6 +326,8 @@ fill_default_server_options(ServerOption + options->ip_qos_bulk = IPTOS_DSCP_CS1; + if (options->version_addendum == NULL) + options->version_addendum = xstrdup(""); ++ if (options->show_patchlevel == -1) ++ options->show_patchlevel = 0; + if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) + options->fwd_opts.streamlocal_bind_mask = 0177; + if (options->fwd_opts.streamlocal_bind_unlink == -1) +@@ -402,7 +405,7 @@ typedef enum { + sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, + sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedKeyTypes, + sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, +- sBanner, sUseDNS, sHostbasedAuthentication, ++ sBanner, sShowPatchLevel, sUseDNS, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, +@@ -528,6 +531,7 @@ static struct { + { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, + { "maxsessions", sMaxSessions, SSHCFG_ALL }, + { "banner", sBanner, SSHCFG_ALL }, ++ { "showpatchlevel", sShowPatchLevel, SSHCFG_GLOBAL }, + { "usedns", sUseDNS, SSHCFG_GLOBAL }, + { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, + { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, +@@ -1369,6 +1373,10 @@ process_server_config_line(ServerOptions + intptr = &options->disable_forwarding; + goto parse_flag; + ++ case sShowPatchLevel: ++ intptr = &options->show_patchlevel; ++ goto parse_flag; ++ + case sAllowUsers: + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (match_user(NULL, NULL, NULL, arg) == -1) +@@ -2269,6 +2277,7 @@ dump_config(ServerOptions *o) + dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); + dump_cfg_fmtint(sCompression, o->compression); + dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); ++ dump_cfg_fmtint(sShowPatchLevel, o->show_patchlevel); + dump_cfg_fmtint(sUseDNS, o->use_dns); + dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); + dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); +diff -up openssh-7.4p1/servconf.h.vendor openssh-7.4p1/servconf.h +--- openssh-7.4p1/servconf.h.vendor 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/servconf.h 2016-12-23 13:34:51.694253847 +0100 +@@ -149,6 +149,7 @@ typedef struct { + int max_authtries; + int max_sessions; + char *banner; /* SSH-2 banner message */ ++ int show_patchlevel; /* Show vendor patch level to clients */ + int use_dns; + int client_alive_interval; /* + * poke the client this often to +diff -up openssh-7.4p1/sshd_config.5.vendor openssh-7.4p1/sshd_config.5 +--- openssh-7.4p1/sshd_config.5.vendor 2016-12-23 13:34:51.695253847 +0100 ++++ openssh-7.4p1/sshd_config.5 2016-12-23 13:37:17.482282253 +0100 +@@ -1334,6 +1334,13 @@ an OpenSSH Key Revocation List (KRL) as + .Cm AcceptEnv + or + .Cm PermitUserEnvironment . ++.It Cm ShowPatchLevel ++Specifies whether ++.Nm sshd ++will display the patch level of the binary in the identification string. ++The patch level is set at compile-time. ++The default is ++.Dq no . + .It Cm StreamLocalBindMask + Sets the octal file creation mode mask + .Pq umask +diff -up openssh-7.4p1/sshd_config.vendor openssh-7.4p1/sshd_config +--- openssh-7.4p1/sshd_config.vendor 2016-12-23 13:34:51.690253846 +0100 ++++ openssh-7.4p1/sshd_config 2016-12-23 13:34:51.695253847 +0100 +@@ -105,6 +105,7 @@ X11Forwarding yes + #Compression delayed + #ClientAliveInterval 0 + #ClientAliveCountMax 3 ++#ShowPatchLevel no + #UseDNS no + #PidFile /var/run/sshd.pid + #MaxStartups 10:30:100 +diff -up openssh-7.4p1/sshd.c.vendor openssh-7.4p1/sshd.c +--- openssh-7.4p1/sshd.c.vendor 2016-12-23 13:34:51.682253844 +0100 ++++ openssh-7.4p1/sshd.c 2016-12-23 13:38:32.434296856 +0100 +@@ -367,7 +367,8 @@ sshd_exchange_identification(struct ssh + char remote_version[256]; /* Must be at least as big as buf. */ + + xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s\r\n", +- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, ++ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, ++ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, + *options.version_addendum == '\0' ? "" : " ", + options.version_addendum); + +@@ -1650,7 +1651,8 @@ main(int ac, char **av) + exit(1); + } + +- debug("sshd version %s, %s", SSH_VERSION, ++ debug("sshd version %s, %s", ++ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, + #ifdef WITH_OPENSSL + SSLeay_version(SSLEAY_VERSION) + #else diff --git a/SOURCES/openssh-6.3p1-ctr-evp-fast.patch b/SOURCES/openssh-6.3p1-ctr-evp-fast.patch new file mode 100644 index 0000000..ddcb7f1 --- /dev/null +++ b/SOURCES/openssh-6.3p1-ctr-evp-fast.patch @@ -0,0 +1,101 @@ +diff -up openssh-5.9p1/cipher-ctr.c.ctr-evp openssh-5.9p1/cipher-ctr.c +--- openssh-5.9p1/cipher-ctr.c.ctr-evp 2012-01-11 09:24:06.000000000 +0100 ++++ openssh-5.9p1/cipher-ctr.c 2012-01-11 15:54:04.675956600 +0100 +@@ -38,7 +38,7 @@ void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, in + + struct ssh_aes_ctr_ctx + { +- AES_KEY aes_ctx; ++ EVP_CIPHER_CTX ecbctx; + u_char aes_counter[AES_BLOCK_SIZE]; + }; + +@@ -63,21 +63,42 @@ ssh_aes_ctr(EVP_CIPHER_CTX *ctx, u_char + { + struct ssh_aes_ctr_ctx *c; + size_t n = 0; +- u_char buf[AES_BLOCK_SIZE]; ++ u_char ctrbuf[AES_BLOCK_SIZE*256]; ++ u_char buf[AES_BLOCK_SIZE*256]; + + if (len == 0) + return (1); + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) + return (0); + +- while ((len--) > 0) { ++ for (; len > 0; len -= sizeof(u_int)) { ++ u_int r,a,b; ++ + if (n == 0) { +- AES_encrypt(c->aes_counter, buf, &c->aes_ctx); +- ssh_ctr_inc(c->aes_counter, AES_BLOCK_SIZE); ++ int outl, i, buflen; ++ ++ buflen = MIN(len, sizeof(ctrbuf)); ++ ++ for(i = 0; i < buflen; i += AES_BLOCK_SIZE) { ++ memcpy(&ctrbuf[i], c->aes_counter, AES_BLOCK_SIZE); ++ ssh_ctr_inc(c->aes_counter, AES_BLOCK_SIZE); ++ } ++ ++ EVP_EncryptUpdate(&c->ecbctx, buf, &outl, ++ ctrbuf, buflen); + } +- *(dest++) = *(src++) ^ buf[n]; +- n = (n + 1) % AES_BLOCK_SIZE; ++ ++ memcpy(&a, src, sizeof(a)); ++ memcpy(&b, &buf[n], sizeof(b)); ++ r = a ^ b; ++ memcpy(dest, &r, sizeof(r)); ++ src += sizeof(a); ++ dest += sizeof(r); ++ ++ n = (n + sizeof(b)) % sizeof(buf); + } ++ memset(ctrbuf, '\0', sizeof(ctrbuf)); ++ memset(buf, '\0', sizeof(buf)); + return (1); + } + +@@ -91,9 +112,28 @@ ssh_aes_ctr_init(EVP_CIPHER_CTX *ctx, co + c = xmalloc(sizeof(*c)); + EVP_CIPHER_CTX_set_app_data(ctx, c); + } +- if (key != NULL) +- AES_set_encrypt_key(key, EVP_CIPHER_CTX_key_length(ctx) * 8, +- &c->aes_ctx); ++ ++ EVP_CIPHER_CTX_init(&c->ecbctx); ++ ++ if (key != NULL) { ++ const EVP_CIPHER *cipher; ++ switch(EVP_CIPHER_CTX_key_length(ctx)*8) { ++ case 128: ++ cipher = EVP_aes_128_ecb(); ++ break; ++ case 192: ++ cipher = EVP_aes_192_ecb(); ++ break; ++ case 256: ++ cipher = EVP_aes_256_ecb(); ++ break; ++ default: ++ fatal("ssh_aes_ctr_init: wrong aes key length"); ++ } ++ if(!EVP_EncryptInit_ex(&c->ecbctx, cipher, NULL, key, NULL)) ++ fatal("ssh_aes_ctr_init: cannot initialize aes encryption"); ++ EVP_CIPHER_CTX_set_padding(&c->ecbctx, 0); ++ } + if (iv != NULL) + memcpy(c->aes_counter, iv, AES_BLOCK_SIZE); + return (1); +@@ -105,6 +145,7 @@ ssh_aes_ctr_cleanup(EVP_CIPHER_CTX *ctx) + struct ssh_aes_ctr_ctx *c; + + if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) != NULL) { ++ EVP_CIPHER_CTX_cleanup(&c->ecbctx); + memset(c, 0, sizeof(*c)); + free(c); + EVP_CIPHER_CTX_set_app_data(ctx, NULL); diff --git a/SOURCES/openssh-6.4p1-fromto-remote.patch b/SOURCES/openssh-6.4p1-fromto-remote.patch new file mode 100644 index 0000000..4a7d849 --- /dev/null +++ b/SOURCES/openssh-6.4p1-fromto-remote.patch @@ -0,0 +1,16 @@ +diff --git a/scp.c b/scp.c +index d98fa67..25d347b 100644 +--- a/scp.c ++++ b/scp.c +@@ -638,7 +638,10 @@ toremote(char *targ, int argc, char **argv) + addargs(&alist, "%s", ssh_program); + addargs(&alist, "-x"); + addargs(&alist, "-oClearAllForwardings=yes"); +- addargs(&alist, "-n"); ++ if (isatty(fileno(stdin))) ++ addargs(&alist, "-t"); ++ else ++ addargs(&alist, "-n"); + for (j = 0; j < remote_remote_args.num; j++) { + addargs(&alist, "%s", + remote_remote_args.list[j]); diff --git a/SOURCES/openssh-6.6.1p1-log-in-chroot.patch b/SOURCES/openssh-6.6.1p1-log-in-chroot.patch new file mode 100644 index 0000000..7f822ab --- /dev/null +++ b/SOURCES/openssh-6.6.1p1-log-in-chroot.patch @@ -0,0 +1,263 @@ +diff -up openssh-7.4p1/log.c.log-in-chroot openssh-7.4p1/log.c +--- openssh-7.4p1/log.c.log-in-chroot 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/log.c 2016-12-23 15:14:33.330168088 +0100 +@@ -250,6 +250,11 @@ debug3(const char *fmt,...) + void + log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) + { ++ log_init_handler(av0, level, facility, on_stderr, 1); ++} ++ ++void ++log_init_handler(char *av0, LogLevel level, SyslogFacility facility, int on_stderr, int reset_handler) { + #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) + struct syslog_data sdata = SYSLOG_DATA_INIT; + #endif +@@ -273,8 +278,10 @@ log_init(char *av0, LogLevel level, Sysl + exit(1); + } + +- log_handler = NULL; +- log_handler_ctx = NULL; ++ if (reset_handler) { ++ log_handler = NULL; ++ log_handler_ctx = NULL; ++ } + + log_on_stderr = on_stderr; + if (on_stderr) +diff -up openssh-7.4p1/log.h.log-in-chroot openssh-7.4p1/log.h +--- openssh-7.4p1/log.h.log-in-chroot 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/log.h 2016-12-23 15:14:33.330168088 +0100 +@@ -49,6 +49,7 @@ typedef enum { + typedef void (log_handler_fn)(LogLevel, const char *, void *); + + void log_init(char *, LogLevel, SyslogFacility, int); ++void log_init_handler(char *, LogLevel, SyslogFacility, int, int); + LogLevel log_level_get(void); + int log_change_level(LogLevel); + int log_is_on_stderr(void); +diff -up openssh-7.4p1/monitor.c.log-in-chroot openssh-7.4p1/monitor.c +--- openssh-7.4p1/monitor.c.log-in-chroot 2016-12-23 15:14:33.311168085 +0100 ++++ openssh-7.4p1/monitor.c 2016-12-23 15:16:42.154193100 +0100 +@@ -307,6 +307,8 @@ monitor_child_preauth(Authctxt *_authctx + close(pmonitor->m_log_sendfd); + pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1; + ++ pmonitor->m_state = "preauth"; ++ + authctxt = _authctxt; + memset(authctxt, 0, sizeof(*authctxt)); + ssh->authctxt = authctxt; +@@ -405,6 +407,8 @@ monitor_child_postauth(struct monitor *p + close(pmonitor->m_recvfd); + pmonitor->m_recvfd = -1; + ++ pmonitor->m_state = "postauth"; ++ + monitor_set_child_handler(pmonitor->m_pid); + signal(SIGHUP, &monitor_child_handler); + signal(SIGTERM, &monitor_child_handler); +@@ -472,7 +476,7 @@ monitor_read_log(struct monitor *pmonito + if (log_level_name(level) == NULL) + fatal("%s: invalid log level %u (corrupted message?)", + __func__, level); +- do_log2(level, "%s [preauth]", msg); ++ do_log2(level, "%s [%s]", msg, pmonitor->m_state); + + sshbuf_free(logmsg); + free(msg); +@@ -1719,13 +1723,28 @@ monitor_init(void) + mon = xcalloc(1, sizeof(*mon)); + monitor_openfds(mon, 1); + ++ mon->m_state = ""; ++ + return mon; + } + + void +-monitor_reinit(struct monitor *mon) ++monitor_reinit(struct monitor *mon, const char *chroot_dir) + { +- monitor_openfds(mon, 0); ++ struct stat dev_log_stat; ++ char *dev_log_path; ++ int do_logfds = 0; ++ ++ if (chroot_dir != NULL) { ++ xasprintf(&dev_log_path, "%s/dev/log", chroot_dir); ++ ++ if (stat(dev_log_path, &dev_log_stat) != 0) { ++ debug("%s: /dev/log doesn't exist in %s chroot - will try to log via monitor using [postauth] suffix", __func__, chroot_dir); ++ do_logfds = 1; ++ } ++ free(dev_log_path); ++ } ++ monitor_openfds(mon, do_logfds); + } + + #ifdef GSSAPI +diff -up openssh-7.4p1/monitor.h.log-in-chroot openssh-7.4p1/monitor.h +--- openssh-7.4p1/monitor.h.log-in-chroot 2016-12-23 15:14:33.330168088 +0100 ++++ openssh-7.4p1/monitor.h 2016-12-23 15:16:28.372190424 +0100 +@@ -83,10 +83,11 @@ struct monitor { + int m_log_sendfd; + struct kex **m_pkex; + pid_t m_pid; ++ char *m_state; + }; + + struct monitor *monitor_init(void); +-void monitor_reinit(struct monitor *); ++void monitor_reinit(struct monitor *, const char *); + + struct Authctxt; + void monitor_child_preauth(struct Authctxt *, struct monitor *); +diff -up openssh-7.4p1/session.c.log-in-chroot openssh-7.4p1/session.c +--- openssh-7.4p1/session.c.log-in-chroot 2016-12-23 15:14:33.319168086 +0100 ++++ openssh-7.4p1/session.c 2016-12-23 15:18:18.742211853 +0100 +@@ -160,6 +160,7 @@ login_cap_t *lc; + + static int is_child = 0; + static int in_chroot = 0; ++static int have_dev_log = 1; + + /* File containing userauth info, if ExposeAuthInfo set */ + static char *auth_info_file = NULL; +@@ -619,6 +620,7 @@ do_exec(Session *s, const char *command) + int ret; + const char *forced = NULL, *tty = NULL; + char session_type[1024]; ++ struct stat dev_log_stat; + + if (options.adm_forced_command) { + original_command = command; +@@ -676,6 +678,10 @@ do_exec(Session *s, const char *command) + tty += 5; + } + ++ if (lstat("/dev/log", &dev_log_stat) != 0) { ++ have_dev_log = 0; ++ } ++ + verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", + session_type, + tty == NULL ? "" : " on ", +@@ -1486,14 +1492,6 @@ child_close_fds(void) + * descriptors left by system functions. They will be closed later. + */ + endpwent(); +- +- /* +- * Close any extra open file descriptors so that we don't have them +- * hanging around in clients. Note that we want to do this after +- * initgroups, because at least on Solaris 2.3 it leaves file +- * descriptors open. +- */ +- closefrom(STDERR_FILENO + 1); + } + + /* +@@ -1629,8 +1627,6 @@ do_child(Session *s, const char *command + exit(1); + } + +- closefrom(STDERR_FILENO + 1); +- + do_rc_files(ssh, s, shell); + + /* restore SIGPIPE for child */ +@@ -1653,9 +1649,17 @@ do_child(Session *s, const char *command + argv[i] = NULL; + optind = optreset = 1; + __progname = argv[0]; +- exit(sftp_server_main(i, argv, s->pw)); ++ exit(sftp_server_main(i, argv, s->pw, have_dev_log)); + } + ++ /* ++ * Close any extra open file descriptors so that we don't have them ++ * hanging around in clients. Note that we want to do this after ++ * initgroups, because at least on Solaris 2.3 it leaves file ++ * descriptors open. ++ */ ++ closefrom(STDERR_FILENO + 1); ++ + fflush(NULL); + + /* Get the last component of the shell name. */ +diff -up openssh-7.4p1/sftp.h.log-in-chroot openssh-7.4p1/sftp.h +--- openssh-7.4p1/sftp.h.log-in-chroot 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/sftp.h 2016-12-23 15:14:33.331168088 +0100 +@@ -97,5 +97,5 @@ + + struct passwd; + +-int sftp_server_main(int, char **, struct passwd *); ++int sftp_server_main(int, char **, struct passwd *, int); + void sftp_server_cleanup_exit(int) __attribute__((noreturn)); +diff -up openssh-7.4p1/sftp-server.c.log-in-chroot openssh-7.4p1/sftp-server.c +--- openssh-7.4p1/sftp-server.c.log-in-chroot 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/sftp-server.c 2016-12-23 15:14:33.331168088 +0100 +@@ -1497,7 +1497,7 @@ sftp_server_usage(void) + } + + int +-sftp_server_main(int argc, char **argv, struct passwd *user_pw) ++sftp_server_main(int argc, char **argv, struct passwd *user_pw, int reset_handler) + { + fd_set *rset, *wset; + int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0; +@@ -1511,7 +1511,7 @@ 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); ++ log_init_handler(__progname, log_level, log_facility, log_stderr, reset_handler); + + pw = pwcopy(user_pw); + +@@ -1582,7 +1582,7 @@ sftp_server_main(int argc, char **argv, + } + } + +- log_init(__progname, log_level, log_facility, log_stderr); ++ log_init_handler(__progname, log_level, log_facility, log_stderr, reset_handler); + + /* + * On platforms where we can, avoid making /proc/self/{mem,maps} +diff -up openssh-7.4p1/sftp-server-main.c.log-in-chroot openssh-7.4p1/sftp-server-main.c +--- openssh-7.4p1/sftp-server-main.c.log-in-chroot 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/sftp-server-main.c 2016-12-23 15:14:33.331168088 +0100 +@@ -49,5 +49,5 @@ main(int argc, char **argv) + return 1; + } + +- return (sftp_server_main(argc, argv, user_pw)); ++ return (sftp_server_main(argc, argv, user_pw, 0)); + } +diff -up openssh-7.4p1/sshd.c.log-in-chroot openssh-7.4p1/sshd.c +--- openssh-7.4p1/sshd.c.log-in-chroot 2016-12-23 15:14:33.328168088 +0100 ++++ openssh-7.4p1/sshd.c 2016-12-23 15:14:33.332168088 +0100 +@@ -650,7 +650,7 @@ privsep_postauth(Authctxt *authctxt) + } + + /* New socket pair */ +- monitor_reinit(pmonitor); ++ monitor_reinit(pmonitor, options.chroot_directory); + + pmonitor->m_pid = fork(); + if (pmonitor->m_pid == -1) +@@ -668,6 +668,11 @@ privsep_postauth(Authctxt *authctxt) + + close(pmonitor->m_sendfd); + pmonitor->m_sendfd = -1; ++ close(pmonitor->m_log_recvfd); ++ pmonitor->m_log_recvfd = -1; ++ ++ if (pmonitor->m_log_sendfd != -1) ++ set_log_handler(mm_log_handler, pmonitor); + + /* Demote the private keys to public keys. */ + demote_sensitive_data(); diff --git a/SOURCES/openssh-6.6.1p1-scp-non-existing-directory.patch b/SOURCES/openssh-6.6.1p1-scp-non-existing-directory.patch new file mode 100644 index 0000000..5412bc5 --- /dev/null +++ b/SOURCES/openssh-6.6.1p1-scp-non-existing-directory.patch @@ -0,0 +1,14 @@ +--- a/scp.c ++++ a/scp.c +@@ -1084,6 +1084,10 @@ sink(int argc, char **argv) + free(vect[0]); + continue; + } ++ if (buf[0] == 'C' && ! exists && np[strlen(np)-1] == '/') { ++ errno = ENOTDIR; ++ goto bad; ++ } + omode = mode; + mode |= S_IWUSR; + if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { +-- diff --git a/SOURCES/openssh-6.6.1p1-selinux-contexts.patch b/SOURCES/openssh-6.6.1p1-selinux-contexts.patch new file mode 100644 index 0000000..8bf6c78 --- /dev/null +++ b/SOURCES/openssh-6.6.1p1-selinux-contexts.patch @@ -0,0 +1,126 @@ +diff --git a/openbsd-compat/port-linux-sshd.c b/openbsd-compat/port-linux-sshd.c +index 8f32464..18a2ca4 100644 +--- a/openbsd-compat/port-linux-sshd.c ++++ b/openbsd-compat/port-linux-sshd.c +@@ -32,6 +32,7 @@ + #include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ + #include "servconf.h" + #include "port-linux.h" ++#include "misc.h" + #include "sshkey.h" + #include "hostfile.h" + #include "auth.h" +@@ -445,7 +446,7 @@ sshd_selinux_setup_exec_context(char *pwname) + void + sshd_selinux_copy_context(void) + { +- security_context_t *ctx; ++ char *ctx; + + if (!sshd_selinux_enabled()) + return; +@@ -461,6 +462,58 @@ sshd_selinux_copy_context(void) + } + } + ++void ++sshd_selinux_change_privsep_preauth_context(void) ++{ ++ int len; ++ char line[1024], *preauth_context = NULL, *cp, *arg; ++ const char *contexts_path; ++ FILE *contexts_file; ++ ++ contexts_path = selinux_openssh_contexts_path(); ++ if (contexts_path != NULL) { ++ if ((contexts_file = fopen(contexts_path, "r")) != NULL) { ++ struct stat sb; ++ ++ if (fstat(fileno(contexts_file), &sb) == 0 && ((sb.st_uid == 0) && ((sb.st_mode & 022) == 0))) { ++ while (fgets(line, sizeof(line), contexts_file)) { ++ /* Strip trailing whitespace */ ++ for (len = strlen(line) - 1; len > 0; len--) { ++ if (strchr(" \t\r\n", line[len]) == NULL) ++ break; ++ line[len] = '\0'; ++ } ++ ++ if (line[0] == '\0') ++ continue; ++ ++ cp = line; ++ arg = strdelim(&cp); ++ if (arg && *arg == '\0') ++ arg = strdelim(&cp); ++ ++ if (arg && strcmp(arg, "privsep_preauth") == 0) { ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') { ++ debug("%s: privsep_preauth is empty", __func__); ++ fclose(contexts_file); ++ return; ++ } ++ preauth_context = xstrdup(arg); ++ } ++ } ++ } ++ fclose(contexts_file); ++ } ++ } ++ ++ if (preauth_context == NULL) ++ preauth_context = xstrdup("sshd_net_t"); ++ ++ ssh_selinux_change_context(preauth_context); ++ free(preauth_context); ++} ++ + #endif + #endif + +diff --git a/openbsd-compat/port-linux.c b/openbsd-compat/port-linux.c +index 22ea8ef..1fc963d 100644 +--- a/openbsd-compat/port-linux.c ++++ b/openbsd-compat/port-linux.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include "log.h" + #include "xmalloc.h" +@@ -179,7 +179,7 @@ ssh_selinux_change_context(const char *newname) + strlcpy(newctx + len, newname, newlen - len); + if ((cx = index(cx + 1, ':'))) + strlcat(newctx, cx, newlen); +- debug3("%s: setting context from '%s' to '%s'", __func__, ++ debug("%s: setting context from '%s' to '%s'", __func__, + oldctx, newctx); + if (setcon(newctx) < 0) + switchlog("%s: setcon %s from %s failed with %s", __func__, +diff --git a/openbsd-compat/port-linux.h b/openbsd-compat/port-linux.h +index cb51f99..8b7cda2 100644 +--- a/openbsd-compat/port-linux.h ++++ b/openbsd-compat/port-linux.h +@@ -29,6 +29,7 @@ int sshd_selinux_enabled(void); + void sshd_selinux_copy_context(void); + void sshd_selinux_setup_exec_context(char *); + int sshd_selinux_setup_env_variables(void); ++void sshd_selinux_change_privsep_preauth_context(void); + #endif + + #ifdef LINUX_OOM_ADJUST +diff --git a/sshd.c b/sshd.c +index 2871fe9..39b9c08 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -629,7 +629,7 @@ privsep_preauth_child(void) + demote_sensitive_data(); + + #ifdef WITH_SELINUX +- ssh_selinux_change_context("sshd_net_t"); ++ sshd_selinux_change_privsep_preauth_context(); + #endif + + /* Demote the child */ diff --git a/SOURCES/openssh-6.6p1-GSSAPIEnablek5users.patch b/SOURCES/openssh-6.6p1-GSSAPIEnablek5users.patch new file mode 100644 index 0000000..c33bdac --- /dev/null +++ b/SOURCES/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -0,0 +1,129 @@ +diff -up openssh-7.4p1/gss-serv-krb5.c.GSSAPIEnablek5users openssh-7.4p1/gss-serv-krb5.c +--- openssh-7.4p1/gss-serv-krb5.c.GSSAPIEnablek5users 2016-12-23 15:18:40.615216100 +0100 ++++ openssh-7.4p1/gss-serv-krb5.c 2016-12-23 15:18:40.628216102 +0100 +@@ -279,7 +279,6 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri + FILE *fp; + char file[MAXPATHLEN]; + char *line = NULL; +- char kuser[65]; /* match krb5_kuserok() */ + struct stat st; + struct passwd *pw = the_authctxt->pw; + int found_principal = 0; +@@ -288,7 +287,7 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri + + snprintf(file, sizeof(file), "%s/.k5users", pw->pw_dir); + /* If both .k5login and .k5users DNE, self-login is ok. */ +- if (!k5login_exists && (access(file, F_OK) == -1)) { ++ if ( !options.enable_k5users || (!k5login_exists && (access(file, F_OK) == -1))) { + return ssh_krb5_kuserok(krb_context, principal, luser, + k5login_exists); + } +diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c +--- openssh-7.4p1/servconf.c.GSSAPIEnablek5users 2016-12-23 15:18:40.615216100 +0100 ++++ openssh-7.4p1/servconf.c 2016-12-23 15:35:36.354401156 +0100 +@@ -168,6 +168,7 @@ initialize_server_options(ServerOptions + options->gss_strict_acceptor = -1; + options->gss_store_rekey = -1; + options->use_kuserok = -1; ++ options->enable_k5users = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -345,6 +346,8 @@ fill_default_server_options(ServerOption + options->gss_store_rekey = 0; + if (options->use_kuserok == -1) + options->use_kuserok = 1; ++ if (options->enable_k5users == -1) ++ options->enable_k5users = 0; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -418,7 +421,7 @@ typedef enum { + sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, +- sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, + sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -497,12 +500,14 @@ static struct { + { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, ++ { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, + { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, ++ { "gssapienablek5users", sUnsupported, SSHCFG_ALL }, + #endif + { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, + { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, +@@ -1653,6 +1658,10 @@ process_server_config_line(ServerOptions + intptr = &options->use_kuserok; + goto parse_flag; + ++ case sGssEnablek5users: ++ intptr = &options->enable_k5users; ++ goto parse_flag; ++ + case sPermitListen: + case sPermitOpen: + if (opcode == sPermitListen) { +@@ -2026,6 +2035,7 @@ copy_set_server_options(ServerOptions *d + M_CP_INTOPT(ip_qos_interactive); + M_CP_INTOPT(ip_qos_bulk); + M_CP_INTOPT(use_kuserok); ++ M_CP_INTOPT(enable_k5users); + M_CP_INTOPT(rekey_limit); + M_CP_INTOPT(rekey_interval); + M_CP_INTOPT(log_level); +@@ -2320,6 +2330,7 @@ dump_config(ServerOptions *o) + # endif + dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); + dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); ++ dump_cfg_fmtint(sGssEnablek5users, o->enable_k5users); + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); +diff -up openssh-7.4p1/servconf.h.GSSAPIEnablek5users openssh-7.4p1/servconf.h +--- openssh-7.4p1/servconf.h.GSSAPIEnablek5users 2016-12-23 15:18:40.616216100 +0100 ++++ openssh-7.4p1/servconf.h 2016-12-23 15:18:40.629216102 +0100 +@@ -174,6 +174,7 @@ typedef struct { + int kerberos_unique_ccache; /* If true, the acquired ticket will + * be stored in per-session ccache */ + int use_kuserok; ++ int enable_k5users; + 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 */ +diff -up openssh-7.4p1/sshd_config.5.GSSAPIEnablek5users openssh-7.4p1/sshd_config.5 +--- openssh-7.4p1/sshd_config.5.GSSAPIEnablek5users 2016-12-23 15:18:40.630216103 +0100 ++++ openssh-7.4p1/sshd_config.5 2016-12-23 15:36:21.607408435 +0100 +@@ -628,6 +628,12 @@ Specifies whether to automatically destr + on logout. + The default is + .Cm yes . ++.It Cm GSSAPIEnablek5users ++Specifies whether to look at .k5users file for GSSAPI authentication ++access control. Further details are described in ++.Xr ksu 1 . ++The default is ++.Cm no . + .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. +diff -up openssh-7.4p1/sshd_config.GSSAPIEnablek5users openssh-7.4p1/sshd_config +--- openssh-7.4p1/sshd_config.GSSAPIEnablek5users 2016-12-23 15:18:40.616216100 +0100 ++++ openssh-7.4p1/sshd_config 2016-12-23 15:18:40.631216103 +0100 +@@ -80,6 +80,7 @@ GSSAPIAuthentication yes + GSSAPICleanupCredentials no + #GSSAPIStrictAcceptorCheck yes + #GSSAPIKeyExchange no ++#GSSAPIEnablek5users no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will diff --git a/SOURCES/openssh-6.6p1-allow-ip-opts.patch b/SOURCES/openssh-6.6p1-allow-ip-opts.patch new file mode 100644 index 0000000..953d613 --- /dev/null +++ b/SOURCES/openssh-6.6p1-allow-ip-opts.patch @@ -0,0 +1,39 @@ +diff -up openssh/sshd.c.ip-opts openssh/sshd.c +--- openssh/sshd.c.ip-opts 2016-07-25 13:58:48.998507834 +0200 ++++ openssh/sshd.c 2016-07-25 14:01:28.346469878 +0200 +@@ -1507,12 +1507,29 @@ check_ip_options(struct ssh *ssh) + + if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, + &option_size) >= 0 && option_size != 0) { +- text[0] = '\0'; +- for (i = 0; i < option_size; i++) +- snprintf(text + i*3, sizeof(text) - i*3, +- " %2.2x", opts[i]); +- fatal("Connection from %.100s port %d with IP opts: %.800s", +- ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); ++ i = 0; ++ do { ++ switch (opts[i]) { ++ case 0: ++ case 1: ++ ++i; ++ break; ++ case 130: ++ case 133: ++ case 134: ++ i += opts[i + 1]; ++ break; ++ default: ++ /* Fail, fatally, if we detect either loose or strict ++ * source routing options. */ ++ text[0] = '\0'; ++ for (i = 0; i < option_size; i++) ++ snprintf(text + i*3, sizeof(text) - i*3, ++ " %2.2x", opts[i]); ++ fatal("Connection from %.100s port %d with IP options:%.800s", ++ ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); ++ } ++ } while (i < option_size); + } + return; + #endif /* IP_OPTIONS */ diff --git a/SOURCES/openssh-6.6p1-ctr-cavstest.patch b/SOURCES/openssh-6.6p1-ctr-cavstest.patch new file mode 100644 index 0000000..c56313d --- /dev/null +++ b/SOURCES/openssh-6.6p1-ctr-cavstest.patch @@ -0,0 +1,257 @@ +diff -up openssh-6.8p1/Makefile.in.ctr-cavs openssh-6.8p1/Makefile.in +--- openssh-6.8p1/Makefile.in.ctr-cavs 2015-03-18 11:22:05.493289018 +0100 ++++ openssh-6.8p1/Makefile.in 2015-03-18 11:22:44.504196316 +0100 +@@ -28,6 +28,7 @@ SSH_KEYSIGN=$(libexecdir)/ssh-keysign + SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper + SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper + SSH_KEYCAT=$(libexecdir)/ssh-keycat ++CTR_CAVSTEST=$(libexecdir)/ctr-cavstest + SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +@@ -66,7 +67,7 @@ EXEEXT=@EXEEXT@ + MKDIR_P=@MKDIR_P@ + INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) ctr-cavstest$(EXEEXT) + + XMSS_OBJS=\ + ssh-xmss.o \ +@@ -194,6 +195,9 @@ ssh-ldap-helper$(EXEEXT): $(LIBCOMPAT) l + ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o + $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(KEYCATLIBS) $(LIBS) + ++ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o ++ $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) ++ + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +@@ -326,6 +330,7 @@ install-files: + $(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ + fi + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keycat$(EXEEXT) $(DESTDIR)$(libexecdir)/ssh-keycat$(EXEEXT) ++ $(INSTALL) -m 0755 $(STRIP_OPT) ctr-cavstest$(EXEEXT) $(DESTDIR)$(libexecdir)/ctr-cavstest$(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 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 +diff -up openssh-6.8p1/ctr-cavstest.c.ctr-cavs openssh-6.8p1/ctr-cavstest.c +--- openssh-6.8p1/ctr-cavstest.c.ctr-cavs 2015-03-18 11:22:05.521288952 +0100 ++++ openssh-6.8p1/ctr-cavstest.c 2015-03-18 11:22:05.521288952 +0100 +@@ -0,0 +1,215 @@ ++/* ++ * ++ * 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 "ssherr.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[]) ++{ ++ ++ const struct sshcipher *c; ++ struct sshcipher_ctx *cc; ++ char *algo = "aes128-ctr"; ++ char *hexkey = NULL; ++ char *hexiv = "00000000000000000000000000000000"; ++ char *hexdata = NULL; ++ char *p; ++ int i, r; ++ 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; ++ } ++ ++ if ((r = cipher_init(&cc, c, key, keylen, iv, ivlen, encrypt)) != 0) { ++ fprintf(stderr, "Error: cipher_init failed: %s\n", ssh_err(r)); ++ return 2; ++ } ++ ++ free(key); ++ free(iv); ++ ++ outdata = malloc(datalen); ++ if(outdata == NULL) { ++ fprintf(stderr, "Error: memory allocation failure\n"); ++ return 2; ++ } ++ ++ if ((r = cipher_crypt(cc, 0, outdata, data, datalen, 0, 0)) != 0) { ++ fprintf(stderr, "Error: cipher_crypt failed: %s\n", ssh_err(r)); ++ return 2; ++ } ++ ++ 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/SOURCES/openssh-6.6p1-force_krb.patch b/SOURCES/openssh-6.6p1-force_krb.patch new file mode 100644 index 0000000..aeee77f --- /dev/null +++ b/SOURCES/openssh-6.6p1-force_krb.patch @@ -0,0 +1,280 @@ +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index 413b845..54dd383 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -32,7 +32,9 @@ + #include + + #include ++#include + #include ++#include + + #include "xmalloc.h" + #include "sshkey.h" +@@ -45,6 +47,7 @@ + + #include "ssh-gss.h" + ++extern Authctxt *the_authctxt; + extern ServerOptions options; + + #ifdef HEIMDAL +@@ -56,6 +59,13 @@ extern ServerOptions options; + # include + #endif + ++/* all commands are allowed by default */ ++char **k5users_allowed_cmds = NULL; ++ ++static int ssh_gssapi_k5login_exists(); ++static int ssh_gssapi_krb5_cmdok(krb5_principal, const char *, const char *, ++ int); ++ + static krb5_context krb_context = NULL; + + /* Initialise the krb5 library, for the stuff that GSSAPI won't do */ +@@ -88,6 +98,7 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + krb5_principal princ; + int retval; + const char *errmsg; ++ int k5login_exists; + + if (ssh_gssapi_krb5_init() == 0) + return 0; +@@ -99,10 +110,22 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + krb5_free_error_message(krb_context, errmsg); + return 0; + } +- if (krb5_kuserok(krb_context, princ, name)) { ++ /* krb5_kuserok() returns 1 if .k5login DNE and this is self-login. ++ * We have to make sure to check .k5users in that case. */ ++ k5login_exists = ssh_gssapi_k5login_exists(); ++ /* NOTE: .k5login and .k5users must opened as root, not the user, ++ * because if they are on a krb5-protected filesystem, user credentials ++ * to access these files aren't available yet. */ ++ if (krb5_kuserok(krb_context, princ, name) && k5login_exists) { + retval = 1; + logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", + name, (char *)client->displayname.value); ++ } else if (ssh_gssapi_krb5_cmdok(princ, client->exportedname.value, ++ name, k5login_exists)) { ++ retval = 1; ++ logit("Authorized to %s, krb5 principal %s " ++ "(ssh_gssapi_krb5_cmdok)", ++ name, (char *)client->displayname.value); + } else + retval = 0; + +@@ -110,6 +133,137 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + return retval; + } + ++/* Test for existence of .k5login. ++ * We need this as part of our .k5users check, because krb5_kuserok() ++ * returns success if .k5login DNE and user is logging in as himself. ++ * With .k5login absent and .k5users present, we don't want absence ++ * of .k5login to authorize self-login. (absence of both is required) ++ * Returns 1 if .k5login is available, 0 otherwise. ++ */ ++static int ++ssh_gssapi_k5login_exists() ++{ ++ char file[MAXPATHLEN]; ++ struct passwd *pw = the_authctxt->pw; ++ ++ snprintf(file, sizeof(file), "%s/.k5login", pw->pw_dir); ++ return access(file, F_OK) == 0; ++} ++ ++/* check .k5users for login or command authorization ++ * Returns 1 if principal is authorized, 0 otherwise. ++ * If principal is authorized, (global) k5users_allowed_cmds may be populated. ++ */ ++static int ++ssh_gssapi_krb5_cmdok(krb5_principal principal, const char *name, ++ const char *luser, int k5login_exists) ++{ ++ FILE *fp; ++ char file[MAXPATHLEN]; ++ char *line = NULL; ++ char kuser[65]; /* match krb5_kuserok() */ ++ struct stat st; ++ struct passwd *pw = the_authctxt->pw; ++ int found_principal = 0; ++ int ncommands = 0, allcommands = 0; ++ u_long linenum = 0; ++ size_t linesize = 0; ++ ++ snprintf(file, sizeof(file), "%s/.k5users", pw->pw_dir); ++ /* If both .k5login and .k5users DNE, self-login is ok. */ ++ if (!k5login_exists && (access(file, F_OK) == -1)) { ++ return (krb5_aname_to_localname(krb_context, principal, ++ sizeof(kuser), kuser) == 0) && ++ (strcmp(kuser, luser) == 0); ++ } ++ if ((fp = fopen(file, "r")) == NULL) { ++ int saved_errno = errno; ++ /* 2nd access check to ease debugging if file perms are wrong. ++ * But we don't want to report this if .k5users simply DNE. */ ++ if (access(file, F_OK) == 0) { ++ logit("User %s fopen %s failed: %s", ++ pw->pw_name, file, strerror(saved_errno)); ++ } ++ return 0; ++ } ++ /* .k5users must be owned either by the user or by root */ ++ if (fstat(fileno(fp), &st) == -1) { ++ /* can happen, but very wierd error so report it */ ++ logit("User %s fstat %s failed: %s", ++ pw->pw_name, file, strerror(errno)); ++ fclose(fp); ++ return 0; ++ } ++ if (!(st.st_uid == pw->pw_uid || st.st_uid == 0)) { ++ logit("User %s %s is not owned by root or user", ++ pw->pw_name, file); ++ fclose(fp); ++ return 0; ++ } ++ /* .k5users must be a regular file. krb5_kuserok() doesn't do this ++ * check, but we don't want to be deficient if they add a check. */ ++ if (!S_ISREG(st.st_mode)) { ++ logit("User %s %s is not a regular file", pw->pw_name, file); ++ fclose(fp); ++ return 0; ++ } ++ /* file exists; initialize k5users_allowed_cmds (to none!) */ ++ k5users_allowed_cmds = xcalloc(++ncommands, ++ sizeof(*k5users_allowed_cmds)); ++ ++ /* Check each line. ksu allows unlimited length lines. */ ++ while (!allcommands && getline(&line, &linesize, fp) != -1) { ++ linenum++; ++ char *token; ++ ++ /* we parse just like ksu, even though we could do better */ ++ if ((token = strtok(line, " \t\n")) == NULL) ++ continue; ++ if (strcmp(name, token) == 0) { ++ /* we matched on client principal */ ++ found_principal = 1; ++ if ((token = strtok(NULL, " \t\n")) == NULL) { ++ /* only shell is allowed */ ++ k5users_allowed_cmds[ncommands-1] = ++ xstrdup(pw->pw_shell); ++ k5users_allowed_cmds = ++ xreallocarray(k5users_allowed_cmds, ++ncommands, ++ sizeof(*k5users_allowed_cmds)); ++ break; ++ } ++ /* process the allowed commands */ ++ while (token) { ++ if (strcmp(token, "*") == 0) { ++ allcommands = 1; ++ break; ++ } ++ k5users_allowed_cmds[ncommands-1] = ++ xstrdup(token); ++ k5users_allowed_cmds = ++ xreallocarray(k5users_allowed_cmds, ++ncommands, ++ sizeof(*k5users_allowed_cmds)); ++ token = strtok(NULL, " \t\n"); ++ } ++ } ++ } ++ free(line); ++ if (k5users_allowed_cmds) { ++ /* terminate vector */ ++ k5users_allowed_cmds[ncommands-1] = NULL; ++ /* if all commands are allowed, free vector */ ++ if (allcommands) { ++ int i; ++ for (i = 0; i < ncommands; i++) { ++ free(k5users_allowed_cmds[i]); ++ } ++ free(k5users_allowed_cmds); ++ k5users_allowed_cmds = NULL; ++ } ++ } ++ fclose(fp); ++ return found_principal; ++} ++ + + /* This writes out any forwarded credentials from the structure populated + * during userauth. Called after we have setuid to the user */ +diff --git a/session.c b/session.c +index 28659ec..9c94d8e 100644 +--- a/session.c ++++ b/session.c +@@ -789,6 +789,29 @@ do_exec(Session *s, const char *command) + command = auth_opts->force_command; + forced = "(key-option)"; + } ++#ifdef GSSAPI ++#ifdef KRB5 /* k5users_allowed_cmds only available w/ GSSAPI+KRB5 */ ++ else if (k5users_allowed_cmds) { ++ const char *match = command; ++ int allowed = 0, i = 0; ++ ++ if (!match) ++ match = s->pw->pw_shell; ++ while (k5users_allowed_cmds[i]) { ++ if (strcmp(match, k5users_allowed_cmds[i++]) == 0) { ++ debug("Allowed command '%.900s'", match); ++ allowed = 1; ++ break; ++ } ++ } ++ if (!allowed) { ++ debug("command '%.900s' not allowed", match); ++ return 1; ++ } ++ } ++#endif ++#endif ++ + if (forced != NULL) { + if (IS_INTERNAL_SFTP(command)) { + s->is_subsystem = s->is_subsystem ? +diff --git a/ssh-gss.h b/ssh-gss.h +index 0374c88..509109a 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -49,6 +49,10 @@ + # endif /* !HAVE_DECL_GSS_C_NT_... */ + + # endif /* !HEIMDAL */ ++ ++/* .k5users support */ ++extern char **k5users_allowed_cmds; ++ + #endif /* KRB5 */ + + /* draft-ietf-secsh-gsskeyex-06 */ +diff --git a/sshd.8 b/sshd.8 +index adcaaf9..824163b 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -324,6 +324,7 @@ Finally, the server and the client enter an authentication dialog. + The client tries to authenticate itself using + host-based authentication, + public key authentication, ++GSSAPI authentication, + challenge-response authentication, + or password authentication. + .Pp +@@ -800,6 +801,12 @@ This file is used in exactly the same way as + but allows host-based authentication without permitting login with + rlogin/rsh. + .Pp ++.It Pa ~/.k5login ++.It Pa ~/.k5users ++These files enforce GSSAPI/Kerberos authentication access control. ++Further details are described in ++.Xr ksu 1 . ++.Pp + .It Pa ~/.ssh/ + This directory is the default location for all user-specific configuration + and authentication information. diff --git a/SOURCES/openssh-6.6p1-keycat.patch b/SOURCES/openssh-6.6p1-keycat.patch new file mode 100644 index 0000000..e22a5f3 --- /dev/null +++ b/SOURCES/openssh-6.6p1-keycat.patch @@ -0,0 +1,485 @@ +diff -up openssh/auth.c.keycat openssh/misc.c +--- openssh/auth.c.keycat 2015-06-24 10:57:50.158849606 +0200 ++++ openssh/auth.c 2015-06-24 11:04:23.989868638 +0200 +@@ -966,6 +966,14 @@ subprocess(const char *tag, struct passw + _exit(1); + } + ++#ifdef WITH_SELINUX ++ if (sshd_selinux_setup_env_variables() < 0) { ++ error ("failed to copy environment: %s", ++ strerror(errno)); ++ _exit(127); ++ } ++#endif ++ + execve(av[0], av, child_env); + error("%s exec \"%s\": %s", tag, command, strerror(errno)); + _exit(127); +diff -up openssh/HOWTO.ssh-keycat.keycat openssh/HOWTO.ssh-keycat +--- openssh/HOWTO.ssh-keycat.keycat 2015-06-24 10:57:50.157849608 +0200 ++++ openssh/HOWTO.ssh-keycat 2015-06-24 10:57:50.157849608 +0200 +@@ -0,0 +1,12 @@ ++The ssh-keycat retrieves the content of the ~/.ssh/authorized_keys ++of an user in any environment. This includes environments with ++polyinstantiation of home directories and SELinux MLS policy enabled. ++ ++To use ssh-keycat, set these options in /etc/ssh/sshd_config file: ++ AuthorizedKeysCommand /usr/libexec/openssh/ssh-keycat ++ AuthorizedKeysCommandUser root ++ ++Do not forget to enable public key authentication: ++ PubkeyAuthentication yes ++ ++ +diff -up openssh/Makefile.in.keycat openssh/Makefile.in +--- openssh/Makefile.in.keycat 2015-06-24 10:57:50.152849621 +0200 ++++ openssh/Makefile.in 2015-06-24 10:57:50.157849608 +0200 +@@ -27,6 +27,7 @@ SFTP_SERVER=$(libexecdir)/sftp-server + SSH_KEYSIGN=$(libexecdir)/ssh-keysign + SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper + SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper ++SSH_KEYCAT=$(libexecdir)/ssh-keycat + SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +@@ -52,6 +52,7 @@ K5LIBS=@K5LIBS@ + GSSLIBS=@GSSLIBS@ + SSHLIBS=@SSHLIBS@ + SSHDLIBS=@SSHDLIBS@ ++KEYCATLIBS=@KEYCATLIBS@ + LIBEDIT=@LIBEDIT@ + AR=@AR@ + AWK=@AWK@ +@@ -65,7 +66,7 @@ EXEEXT=@EXEEXT@ + MKDIR_P=@MKDIR_P@ + INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) + + XMSS_OBJS=\ + ssh-xmss.o \ +@@ -190,6 +191,9 @@ ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) + 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 -lfipscheck $(LIBS) $(LDAPLIBS) + ++ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o ++ $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(KEYCATLIBS) $(LIBS) ++ + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +@@ -321,6 +325,7 @@ install-files: + $(INSTALL) -m 0700 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \ + $(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ + fi ++ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keycat$(EXEEXT) $(DESTDIR)$(libexecdir)/ssh-keycat$(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 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 +diff -up openssh/openbsd-compat/port-linux.h.keycat openssh/openbsd-compat/port-linux.h +--- openssh/openbsd-compat/port-linux.h.keycat 2015-06-24 10:57:50.150849626 +0200 ++++ openssh/openbsd-compat/port-linux.h 2015-06-24 10:57:50.160849601 +0200 +@@ -25,8 +25,10 @@ void ssh_selinux_setup_pty(char *, const + void ssh_selinux_change_context(const char *); + void ssh_selinux_setfscreatecon(const char *); + ++int sshd_selinux_enabled(void); + void sshd_selinux_copy_context(void); + void sshd_selinux_setup_exec_context(char *); ++int sshd_selinux_setup_env_variables(void); + #endif + + #ifdef LINUX_OOM_ADJUST +diff -up openssh/openbsd-compat/port-linux-sshd.c.keycat openssh/openbsd-compat/port-linux-sshd.c +--- openssh/openbsd-compat/port-linux-sshd.c.keycat 2015-06-24 10:57:50.150849626 +0200 ++++ openssh/openbsd-compat/port-linux-sshd.c 2015-06-24 10:57:50.159849603 +0200 +@@ -54,6 +54,20 @@ extern Authctxt *the_authctxt; + extern int inetd_flag; + extern int rexeced_flag; + ++/* Wrapper around is_selinux_enabled() to log its return value once only */ ++int ++sshd_selinux_enabled(void) ++{ ++ static int enabled = -1; ++ ++ if (enabled == -1) { ++ enabled = (is_selinux_enabled() == 1); ++ debug("SELinux support %s", enabled ? "enabled" : "disabled"); ++ } ++ ++ return (enabled); ++} ++ + /* Send audit message */ + static int + sshd_selinux_send_audit_message(int success, security_context_t default_context, +@@ -308,7 +322,7 @@ sshd_selinux_getctxbyname(char *pwname, + + /* Setup environment variables for pam_selinux */ + static int +-sshd_selinux_setup_pam_variables(void) ++sshd_selinux_setup_variables(int(*set_it)(char *, const char *)) + { + const char *reqlvl; + char *role; +@@ -319,16 +333,16 @@ sshd_selinux_setup_pam_variables(void) + + ssh_selinux_get_role_level(&role, &reqlvl); + +- rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : ""); ++ rv = set_it("SELINUX_ROLE_REQUESTED", role ? role : ""); + + if (inetd_flag && !rexeced_flag) { + use_current = "1"; + } else { + use_current = ""; +- rv = rv || do_pam_putenv("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: ""); ++ rv = rv || set_it("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: ""); + } + +- rv = rv || do_pam_putenv("SELINUX_USE_CURRENT_RANGE", use_current); ++ rv = rv || set_it("SELINUX_USE_CURRENT_RANGE", use_current); + + if (role != NULL) + free(role); +@@ -336,6 +350,24 @@ sshd_selinux_setup_pam_variables(void) + return rv; + } + ++static int ++sshd_selinux_setup_pam_variables(void) ++{ ++ return sshd_selinux_setup_variables(do_pam_putenv); ++} ++ ++static int ++do_setenv(char *name, const char *value) ++{ ++ return setenv(name, value, 1); ++} ++ ++int ++sshd_selinux_setup_env_variables(void) ++{ ++ return sshd_selinux_setup_variables(do_setenv); ++} ++ + /* Set the execution context to the default for the specified user */ + void + sshd_selinux_setup_exec_context(char *pwname) +@@ -344,7 +376,7 @@ sshd_selinux_setup_exec_context(char *pw + int r = 0; + security_context_t default_ctx = NULL; + +- if (!ssh_selinux_enabled()) ++ if (!sshd_selinux_enabled()) + return; + + if (options.use_pam) { +@@ -415,7 +447,7 @@ sshd_selinux_copy_context(void) + { + security_context_t *ctx; + +- if (!ssh_selinux_enabled()) ++ if (!sshd_selinux_enabled()) + return; + + if (getexeccon((security_context_t *)&ctx) != 0) { +diff -up openssh/platform.c.keycat openssh/platform.c +--- openssh/platform.c.keycat 2015-06-24 10:57:50.147849633 +0200 ++++ openssh/platform.c 2015-06-24 10:57:50.160849601 +0200 +@@ -103,7 +103,7 @@ platform_setusercontext(struct passwd *p + { + #ifdef WITH_SELINUX + /* Cache selinux status for later use */ +- (void)ssh_selinux_enabled(); ++ (void)sshd_selinux_enabled(); + #endif + + #ifdef USE_SOLARIS_PROJECTS +diff -up openssh/ssh-keycat.c.keycat openssh/ssh-keycat.c +--- openssh/ssh-keycat.c.keycat 2015-06-24 10:57:50.161849599 +0200 ++++ openssh/ssh-keycat.c 2015-06-24 10:57:50.161849599 +0200 +@@ -0,0 +1,241 @@ ++/* ++ * 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 Public License, in which case the provisions of the GPL 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 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. ++ */ ++ ++/* ++ * Copyright (c) 2011 Red Hat, Inc. ++ * Written by Tomas Mraz ++*/ ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef HAVE_STDINT_H ++#include ++#endif ++ ++#include ++ ++#include "uidswap.h" ++#include "misc.h" ++ ++#define ERR_USAGE 1 ++#define ERR_PAM_START 2 ++#define ERR_OPEN_SESSION 3 ++#define ERR_CLOSE_SESSION 4 ++#define ERR_PAM_END 5 ++#define ERR_GETPWNAM 6 ++#define ERR_MEMORY 7 ++#define ERR_OPEN 8 ++#define ERR_FILE_MODE 9 ++#define ERR_FDOPEN 10 ++#define ERR_STAT 11 ++#define ERR_WRITE 12 ++#define ERR_PAM_PUTENV 13 ++#define BUFLEN 4096 ++ ++/* Just ignore the messages in the conversation function */ ++static int ++dummy_conv(int num_msg, const struct pam_message **msgm, ++ struct pam_response **response, void *appdata_ptr) ++{ ++ struct pam_response *rsp; ++ ++ (void)msgm; ++ (void)appdata_ptr; ++ ++ if (num_msg <= 0) ++ return PAM_CONV_ERR; ++ ++ /* Just allocate the array as empty responses */ ++ rsp = calloc (num_msg, sizeof (struct pam_response)); ++ if (rsp == NULL) ++ return PAM_CONV_ERR; ++ ++ *response = rsp; ++ return PAM_SUCCESS; ++} ++ ++static struct pam_conv conv = { ++ dummy_conv, ++ NULL ++}; ++ ++char * ++make_auth_keys_name(const struct passwd *pwd) ++{ ++ char *fname; ++ ++ if (asprintf(&fname, "%s/.ssh/authorized_keys", pwd->pw_dir) < 0) ++ return NULL; ++ ++ return fname; ++} ++ ++int ++dump_keys(const char *user) ++{ ++ struct passwd *pwd; ++ int fd = -1; ++ FILE *f = NULL; ++ char *fname = NULL; ++ int rv = 0; ++ char buf[BUFLEN]; ++ size_t len; ++ struct stat st; ++ ++ if ((pwd = getpwnam(user)) == NULL) { ++ return ERR_GETPWNAM; ++ } ++ ++ if ((fname = make_auth_keys_name(pwd)) == NULL) { ++ return ERR_MEMORY; ++ } ++ ++ temporarily_use_uid(pwd); ++ ++ if ((fd = open(fname, O_RDONLY|O_NONBLOCK|O_NOFOLLOW, 0)) < 0) { ++ rv = ERR_OPEN; ++ goto fail; ++ } ++ ++ if (fstat(fd, &st) < 0) { ++ rv = ERR_STAT; ++ goto fail; ++ } ++ ++ if (!S_ISREG(st.st_mode) || ++ (st.st_uid != pwd->pw_uid && st.st_uid != 0)) { ++ rv = ERR_FILE_MODE; ++ goto fail; ++ } ++ ++ unset_nonblock(fd); ++ ++ if ((f = fdopen(fd, "r")) == NULL) { ++ rv = ERR_FDOPEN; ++ goto fail; ++ } ++ ++ fd = -1; ++ ++ while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { ++ rv = fwrite(buf, 1, len, stdout) != len ? ERR_WRITE : 0; ++ } ++ ++fail: ++ if (fd != -1) ++ close(fd); ++ if (f != NULL) ++ fclose(f); ++ free(fname); ++ restore_uid(); ++ return rv; ++} ++ ++static const char *env_names[] = { "SELINUX_ROLE_REQUESTED", ++ "SELINUX_LEVEL_REQUESTED", ++ "SELINUX_USE_CURRENT_RANGE" ++}; ++ ++extern char **environ; ++ ++int ++set_pam_environment(pam_handle_t *pamh) ++{ ++ int i; ++ size_t j; ++ ++ for (j = 0; j < sizeof(env_names)/sizeof(env_names[0]); ++j) { ++ int len = strlen(env_names[j]); ++ ++ for (i = 0; environ[i] != NULL; ++i) { ++ if (strncmp(env_names[j], environ[i], len) == 0 && ++ environ[i][len] == '=') { ++ if (pam_putenv(pamh, environ[i]) != PAM_SUCCESS) ++ return ERR_PAM_PUTENV; ++ } ++ } ++ } ++ ++ return 0; ++} ++ ++int ++main(int argc, char *argv[]) ++{ ++ pam_handle_t *pamh = NULL; ++ int retval; ++ int ev = 0; ++ ++ if (argc != 2) { ++ fprintf(stderr, "Usage: %s \n", argv[0]); ++ return ERR_USAGE; ++ } ++ ++ retval = pam_start("ssh-keycat", argv[1], &conv, &pamh); ++ if (retval != PAM_SUCCESS) { ++ return ERR_PAM_START; ++ } ++ ++ ev = set_pam_environment(pamh); ++ if (ev != 0) ++ goto finish; ++ ++ retval = pam_open_session(pamh, PAM_SILENT); ++ if (retval != PAM_SUCCESS) { ++ ev = ERR_OPEN_SESSION; ++ goto finish; ++ } ++ ++ ev = dump_keys(argv[1]); ++ ++ retval = pam_close_session(pamh, PAM_SILENT); ++ if (retval != PAM_SUCCESS) { ++ ev = ERR_CLOSE_SESSION; ++ } ++ ++finish: ++ retval = pam_end (pamh,retval); ++ if (retval != PAM_SUCCESS) { ++ ev = ERR_PAM_END; ++ } ++ return ev; ++} +diff --git a/configure.ac b/configure.ac +index 3bbccfd..6481f1f 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -2952,6 +2952,7 @@ AC_ARG_WITH([pam], + PAM_MSG="yes" + + SSHDLIBS="$SSHDLIBS -lpam" ++ KEYCATLIBS="$KEYCATLIBS -lpam" + AC_DEFINE([USE_PAM], [1], + [Define if you want to enable PAM support]) + +@@ -3105,6 +3106,7 @@ + ;; + *) + SSHDLIBS="$SSHDLIBS -ldl" ++ KEYCATLIBS="$KEYCATLIBS -ldl" + ;; + esac + fi +@@ -4042,6 +4044,7 @@ AC_ARG_WITH([selinux], + ) + AC_SUBST([SSHLIBS]) + AC_SUBST([SSHDLIBS]) ++AC_SUBST([KEYCATLIBS]) + + # Check whether user wants Kerberos 5 support + KRB5_MSG="no" +@@ -5031,6 +5034,9 @@ fi + if test ! -z "${SSHLIBS}"; then + echo " +for ssh: ${SSHLIBS}" + fi ++if test ! -z "${KEYCATLIBS}"; then ++echo " +for ssh-keycat: ${KEYCATLIBS}" ++fi + + echo "" + diff --git a/SOURCES/openssh-6.6p1-keyperm.patch b/SOURCES/openssh-6.6p1-keyperm.patch new file mode 100644 index 0000000..fbe33b0 --- /dev/null +++ b/SOURCES/openssh-6.6p1-keyperm.patch @@ -0,0 +1,26 @@ +diff --git a/authfile.c b/authfile.c +index e93d867..4fc5b3d 100644 +--- a/authfile.c ++++ b/authfile.c +@@ -32,6 +32,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -207,6 +208,13 @@ sshkey_perm_ok(int fd, const char *filename) + #ifdef HAVE_CYGWIN + if (check_ntsec(filename)) + #endif ++ if (st.st_mode & 040) { ++ struct group *gr; ++ ++ if ((gr = getgrnam("ssh_keys")) && (st.st_gid == gr->gr_gid)) ++ st.st_mode &= ~040; ++ } ++ + if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) { + error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); + error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); diff --git a/SOURCES/openssh-6.6p1-kuserok.patch b/SOURCES/openssh-6.6p1-kuserok.patch new file mode 100644 index 0000000..4b681ff --- /dev/null +++ b/SOURCES/openssh-6.6p1-kuserok.patch @@ -0,0 +1,289 @@ +diff -up openssh-7.4p1/auth-krb5.c.kuserok openssh-7.4p1/auth-krb5.c +--- openssh-7.4p1/auth-krb5.c.kuserok 2016-12-23 14:36:07.640465939 +0100 ++++ openssh-7.4p1/auth-krb5.c 2016-12-23 14:36:07.644465936 +0100 +@@ -56,6 +56,21 @@ + + extern ServerOptions options; + ++int ++ssh_krb5_kuserok(krb5_context krb5_ctx, krb5_principal krb5_user, const char *client, ++ int k5login_exists) ++{ ++ if (options.use_kuserok || !k5login_exists) ++ return krb5_kuserok(krb5_ctx, krb5_user, client); ++ else { ++ char kuser[65]; ++ ++ if (krb5_aname_to_localname(krb5_ctx, krb5_user, sizeof(kuser), kuser)) ++ return 0; ++ return strcmp(kuser, client) == 0; ++ } ++} ++ + static int + krb5_init(void *context) + { +@@ -160,8 +175,9 @@ auth_krb5_password(Authctxt *authctxt, c + if (problem) + goto out; + +- if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, +- authctxt->pw->pw_name)) { ++ /* Use !options.use_kuserok here to make ssh_krb5_kuserok() not ++ * depend on the existance of .k5login */ ++ if (!ssh_krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name, !options.use_kuserok)) { + problem = -1; + goto out; + } +diff -up openssh-7.4p1/gss-serv-krb5.c.kuserok openssh-7.4p1/gss-serv-krb5.c +--- openssh-7.4p1/gss-serv-krb5.c.kuserok 2016-12-23 14:36:07.640465939 +0100 ++++ openssh-7.4p1/gss-serv-krb5.c 2016-12-23 14:36:07.644465936 +0100 +@@ -67,6 +67,7 @@ static int ssh_gssapi_krb5_cmdok(krb5_pr + int); + + static krb5_context krb_context = NULL; ++extern int ssh_krb5_kuserok(krb5_context, krb5_principal, const char *, int); + + /* Initialise the krb5 library, for the stuff that GSSAPI won't do */ + +@@ -92,6 +93,103 @@ ssh_gssapi_krb5_init(void) + * Returns true if the user is OK to log in, otherwise returns 0 + */ + ++/* The purpose of the function is to find out if a Kerberos principal is ++ * allowed to log in as the given local user. This is a general problem with ++ * Kerberized services because by design the Kerberos principals are ++ * completely independent from the local user names. This is one of the ++ * reasons why Kerberos is working well on different operating systems like ++ * Windows and UNIX/Linux. Nevertheless a relationship between a Kerberos ++ * principal and a local user name must be established because otherwise every ++ * access would be granted for every principal with a valid ticket. ++ * ++ * Since it is a general issue libkrb5 provides some functions for ++ * applications to find out about the relationship between the Kerberos ++ * principal and a local user name. They are krb5_kuserok() and ++ * krb5_aname_to_localname(). ++ * ++ * krb5_kuserok() can be used to "Determine if a principal is authorized to ++ * log in as a local user" (from the MIT Kerberos documentation of this ++ * function). Which is exactly what we are looking for and should be the ++ * preferred choice. It accepts the Kerberos principal and a local user name ++ * and let libkrb5 or its plugins determine if they relate to each other or ++ * not. ++ * ++ * krb5_aname_to_localname() can use used to "Convert a principal name to a ++ * local name" (from the MIT Kerberos documentation of this function). It ++ * accepts a Kerberos principle and returns a local name and it is up to the ++ * application to do any additional checks. There are two issues using ++ * krb5_aname_to_localname(). First, since POSIX user names are case ++ * sensitive, the calling application in general has no other choice than ++ * doing a case-sensitive string comparison between the name returned by ++ * krb5_aname_to_localname() and the name used at the login prompt. When the ++ * users are provided by a case in-sensitive server, e.g. Active Directory, ++ * this might lead to login failures because the user typing the name at the ++ * login prompt might not be aware of the right case. Another issue might be ++ * caused if there are multiple alias names available for a single user. E.g. ++ * the canonical name of a user is user@group.department.example.com but there ++ * exists a shorter login name, e.g. user@example.com, to safe typing at the ++ * login prompt. Here krb5_aname_to_localname() can only return the canonical ++ * name, but if the short alias is used at the login prompt authentication ++ * will fail as well. All this can be avoided by using krb5_kuserok() and ++ * configuring krb5.conf or using a suitable plugin to meet the needs of the ++ * given environment. ++ * ++ * The Fedora and RHEL version of openssh contain two patches which modify the ++ * access control behavior: ++ * - openssh-6.6p1-kuserok.patch ++ * - openssh-6.6p1-force_krb.patch ++ * ++ * openssh-6.6p1-kuserok.patch adds a new option KerberosUseKuserok for ++ * sshd_config which controls if krb5_kuserok() is used to check if the ++ * principle is authorized or if krb5_aname_to_localname() should be used. ++ * The reason to add this patch was that krb5_kuserok() by default checks if ++ * a .k5login file exits in the users home-directory. With this the user can ++ * give access to his account for any given principal which might be ++ * in violation with company policies and it would be useful if this can be ++ * rejected. Nevertheless the patch ignores the fact that krb5_kuserok() does ++ * no only check .k5login but other sources as well and checking .k5login can ++ * be disabled for all applications in krb5.conf as well. With this new ++ * option KerberosUseKuserok set to 'no' (and this is the default for RHEL7 ++ * and Fedora 21) openssh can only use krb5_aname_to_localname() with the ++ * restrictions mentioned above. ++ * ++ * openssh-6.6p1-force_krb.patch adds a ksu like behaviour to ssh, i.e. when ++ * using GSSAPI authentication only commands configured in the .k5user can be ++ * executed. Here the wrong assumption that krb5_kuserok() only checks ++ * .k5login is made as well. In contrast ksu checks .k5login directly and ++ * does not use krb5_kuserok() which might be more useful for the given ++ * purpose. Additionally this patch is not synced with ++ * openssh-6.6p1-kuserok.patch. ++ * ++ * The current patch tries to restore the usage of krb5_kuserok() so that e.g. ++ * localauth plugins can be used. It does so by adding a forth parameter to ++ * ssh_krb5_kuserok() which indicates whether .k5login exists or not. If it ++ * does not exists krb5_kuserok() is called even if KerberosUseKuserok is set ++ * to 'no' because the intent of the option is to not check .k5login and if it ++ * does not exists krb5_kuserok() returns a result without checking .k5login. ++ * If .k5login does exists and KerberosUseKuserok is 'no' we fall back to ++ * krb5_aname_to_localname(). This is in my point of view an acceptable ++ * limitation and does not break the current behaviour. ++ * ++ * Additionally with this patch ssh_krb5_kuserok() is called in ++ * ssh_gssapi_krb5_cmdok() instead of only krb5_aname_to_localname() is ++ * neither .k5login nor .k5users exists to allow plugin evaluation via ++ * krb5_kuserok() as well. ++ * ++ * I tried to keep the patch as minimal as possible, nevertheless I see some ++ * areas for improvement which, if they make sense, have to be evaluated ++ * carefully because they might change existing behaviour and cause breaks ++ * during upgrade: ++ * - I wonder if disabling .k5login usage make sense in sshd or if it should ++ * be better disabled globally in krb5.conf ++ * - if really needed openssh-6.6p1-kuserok.patch should be fixed to really ++ * only disable checking .k5login and maybe .k5users ++ * - the ksu behaviour should be configurable and maybe check the .k5login and ++ * .k5users files directly like ksu itself does ++ * - to make krb5_aname_to_localname() more useful an option for sshd to use ++ * the canonical name (the one returned by getpwnam()) instead of the name ++ * given at the login prompt might be useful */ ++ + static int + ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + { +@@ -116,7 +214,8 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client + /* NOTE: .k5login and .k5users must opened as root, not the user, + * because if they are on a krb5-protected filesystem, user credentials + * to access these files aren't available yet. */ +- if (krb5_kuserok(krb_context, princ, name) && k5login_exists) { ++ if (ssh_krb5_kuserok(krb_context, princ, name, k5login_exists) ++ && k5login_exists) { + retval = 1; + logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", + name, (char *)client->displayname.value); +@@ -190,9 +289,8 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri + snprintf(file, sizeof(file), "%s/.k5users", pw->pw_dir); + /* If both .k5login and .k5users DNE, self-login is ok. */ + if (!k5login_exists && (access(file, F_OK) == -1)) { +- return (krb5_aname_to_localname(krb_context, principal, +- sizeof(kuser), kuser) == 0) && +- (strcmp(kuser, luser) == 0); ++ return ssh_krb5_kuserok(krb_context, principal, luser, ++ k5login_exists); + } + if ((fp = fopen(file, "r")) == NULL) { + int saved_errno = errno; +diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c +--- openssh-7.4p1/servconf.c.kuserok 2016-12-23 14:36:07.630465944 +0100 ++++ openssh-7.4p1/servconf.c 2016-12-23 15:11:52.278133344 +0100 +@@ -116,6 +116,7 @@ initialize_server_options(ServerOptions + options->gss_cleanup_creds = -1; + options->gss_strict_acceptor = -1; + options->gss_store_rekey = -1; ++ options->use_kuserok = -1; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->challenge_response_authentication = -1; +@@ -278,6 +279,8 @@ fill_default_server_options(ServerOption + options->gss_strict_acceptor = 1; + if (options->gss_store_rekey == -1) + options->gss_store_rekey = 0; ++ if (options->use_kuserok == -1) ++ options->use_kuserok = 1; + if (options->password_authentication == -1) + options->password_authentication = 1; + if (options->kbd_interactive_authentication == -1) +@@ -399,7 +402,7 @@ typedef enum { + sPermitRootLogin, sLogFacility, sLogLevel, + sRhostsRSAAuthentication, sRSAAuthentication, + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, +- sKerberosGetAFSToken, sKerberosUniqueCCache, ++ sKerberosGetAFSToken, sKerberosUniqueCCache, sKerberosUseKuserok, + sChallengeResponseAuthentication, + sPasswordAuthentication, sKbdInteractiveAuthentication, + sListenAddress, sAddressFamily, +@@ -478,12 +481,14 @@ static struct { + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "kerberosuniqueccache", sKerberosUniqueCCache, SSHCFG_GLOBAL }, ++ { "kerberosusekuserok", sKerberosUseKuserok, SSHCFG_ALL }, + #else + { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, + { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosuniqueccache", sUnsupported, SSHCFG_GLOBAL }, ++ { "kerberosusekuserok", sUnsupported, SSHCFG_ALL }, + #endif + { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, + { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, +@@ -1644,6 +1649,10 @@ process_server_config_line(ServerOptions + *activep = value; + break; + ++ case sKerberosUseKuserok: ++ intptr = &options->use_kuserok; ++ goto parse_flag; ++ + case sPermitListen: + case sPermitOpen: + if (opcode == sPermitListen) { +@@ -2016,6 +2025,7 @@ copy_set_server_options(ServerOptions *d + M_CP_INTOPT(client_alive_interval); + M_CP_INTOPT(ip_qos_interactive); + M_CP_INTOPT(ip_qos_bulk); ++ M_CP_INTOPT(use_kuserok); + M_CP_INTOPT(rekey_limit); + M_CP_INTOPT(rekey_interval); + M_CP_INTOPT(log_level); +@@ -2309,6 +2319,7 @@ dump_config(ServerOptions *o) + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); + # endif + dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); ++ dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); +diff -up openssh-7.4p1/servconf.h.kuserok openssh-7.4p1/servconf.h +--- openssh-7.4p1/servconf.h.kuserok 2016-12-23 14:36:07.630465944 +0100 ++++ openssh-7.4p1/servconf.h 2016-12-23 14:36:07.645465936 +0100 +@@ -118,6 +118,7 @@ typedef struct { + * authenticated with Kerberos. */ + int kerberos_unique_ccache; /* If true, the acquired ticket will + * be stored in per-session ccache */ ++ int use_kuserok; + 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 */ +diff -up openssh-7.4p1/sshd_config.5.kuserok openssh-7.4p1/sshd_config.5 +--- openssh-7.4p1/sshd_config.5.kuserok 2016-12-23 14:36:07.637465940 +0100 ++++ openssh-7.4p1/sshd_config.5 2016-12-23 15:14:03.117162222 +0100 +@@ -850,6 +850,10 @@ Specifies whether to automatically destr + .Cm no + can lead to overwriting previous tickets by subseqent connections to the same + user account. ++.It Cm KerberosUseKuserok ++Specifies whether to look at .k5login file for user's aliases. ++The default is ++.Cm yes . + .It Cm KexAlgorithms + Specifies the available KEX (Key Exchange) algorithms. + Multiple algorithms must be comma-separated. +@@ -1078,6 +1082,7 @@ Available keywords are + .Cm IPQoS , + .Cm KbdInteractiveAuthentication , + .Cm KerberosAuthentication , ++.Cm KerberosUseKuserok , + .Cm LogLevel , + .Cm MaxAuthTries , + .Cm MaxSessions , +diff -up openssh-7.4p1/sshd_config.kuserok openssh-7.4p1/sshd_config +--- openssh-7.4p1/sshd_config.kuserok 2016-12-23 14:36:07.631465943 +0100 ++++ openssh-7.4p1/sshd_config 2016-12-23 14:36:07.646465935 +0100 +@@ -73,6 +73,7 @@ ChallengeResponseAuthentication no + #KerberosOrLocalPasswd yes + #KerberosTicketCleanup yes + #KerberosGetAFSToken no ++#KerberosUseKuserok yes + + # GSSAPI options + GSSAPIAuthentication yes diff --git a/SOURCES/openssh-6.6p1-privsep-selinux.patch b/SOURCES/openssh-6.6p1-privsep-selinux.patch new file mode 100644 index 0000000..1365506 --- /dev/null +++ b/SOURCES/openssh-6.6p1-privsep-selinux.patch @@ -0,0 +1,121 @@ +diff -up openssh-7.4p1/openbsd-compat/port-linux.h.privsep-selinux openssh-7.4p1/openbsd-compat/port-linux.h +--- openssh-7.4p1/openbsd-compat/port-linux.h.privsep-selinux 2016-12-23 18:58:52.972122201 +0100 ++++ openssh-7.4p1/openbsd-compat/port-linux.h 2016-12-23 18:58:52.974122201 +0100 +@@ -23,6 +23,7 @@ void ssh_selinux_setup_pty(char *, const + void ssh_selinux_change_context(const char *); + void ssh_selinux_setfscreatecon(const char *); + ++void sshd_selinux_copy_context(void); + void sshd_selinux_setup_exec_context(char *); + #endif + +diff -up openssh-7.4p1/openbsd-compat/port-linux-sshd.c.privsep-selinux openssh-7.4p1/openbsd-compat/port-linux-sshd.c +--- openssh-7.4p1/openbsd-compat/port-linux-sshd.c.privsep-selinux 2016-12-23 18:58:52.973122201 +0100 ++++ openssh-7.4p1/openbsd-compat/port-linux-sshd.c 2016-12-23 18:58:52.974122201 +0100 +@@ -419,6 +419,28 @@ sshd_selinux_setup_exec_context(char *pw + debug3("%s: done", __func__); + } + ++void ++sshd_selinux_copy_context(void) ++{ ++ security_context_t *ctx; ++ ++ if (!ssh_selinux_enabled()) ++ return; ++ ++ if (getexeccon((security_context_t *)&ctx) != 0) { ++ logit("%s: getcon failed with %s", __func__, strerror (errno)); ++ return; ++ } ++ if (ctx != NULL) { ++ /* unset exec context before we will lose this capabililty */ ++ if (setexeccon(NULL) != 0) ++ fatal("%s: setexeccon failed with %s", __func__, strerror (errno)); ++ if (setcon(ctx) != 0) ++ fatal("%s: setcon failed with %s", __func__, strerror (errno)); ++ freecon(ctx); ++ } ++} ++ + #endif + #endif + +diff -up openssh-7.4p1/session.c.privsep-selinux openssh-7.4p1/session.c +--- openssh-7.4p1/session.c.privsep-selinux 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/session.c 2016-12-23 18:58:52.974122201 +0100 +@@ -1331,7 +1331,7 @@ do_setusercontext(struct passwd *pw) + + platform_setusercontext(pw); + +- if (platform_privileged_uidswap()) { ++ if (platform_privileged_uidswap() && (!is_child || !use_privsep)) { + #ifdef HAVE_LOGIN_CAP + if (setusercontext(lc, pw, pw->pw_uid, + (LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { +@@ -1361,6 +1361,9 @@ do_setusercontext(struct passwd *pw) + (unsigned long long)pw->pw_uid); + chroot_path = percent_expand(tmp, "h", pw->pw_dir, + "u", pw->pw_name, "U", uidstr, (char *)NULL); ++#ifdef WITH_SELINUX ++ sshd_selinux_copy_context(); ++#endif + safely_chroot(chroot_path, pw->pw_uid); + free(tmp); + free(chroot_path); +@@ -1396,6 +1399,11 @@ do_setusercontext(struct passwd *pw) + /* Permanently switch to the desired uid. */ + permanently_set_uid(pw); + #endif ++ ++#ifdef WITH_SELINUX ++ if (in_chroot == 0) ++ sshd_selinux_copy_context(); ++#endif + } else if (options.chroot_directory != NULL && + strcasecmp(options.chroot_directory, "none") != 0) { + fatal("server lacks privileges to chroot to ChrootDirectory"); +@@ -1413,9 +1421,6 @@ do_pwchange(Session *s) + if (s->ttyfd != -1) { + fprintf(stderr, + "You must change your password now and login again!\n"); +-#ifdef WITH_SELINUX +- setexeccon(NULL); +-#endif + #ifdef PASSWD_NEEDS_USERNAME + execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, + (char *)NULL); +@@ -1625,9 +1630,6 @@ do_child(Session *s, const char *command + argv[i] = NULL; + optind = optreset = 1; + __progname = argv[0]; +-#ifdef WITH_SELINUX +- ssh_selinux_change_context("sftpd_t"); +-#endif + exit(sftp_server_main(i, argv, s->pw)); + } + +diff -up openssh-7.4p1/sshd.c.privsep-selinux openssh-7.4p1/sshd.c +--- openssh-7.4p1/sshd.c.privsep-selinux 2016-12-23 18:58:52.973122201 +0100 ++++ openssh-7.4p1/sshd.c 2016-12-23 18:59:13.808124269 +0100 +@@ -540,6 +540,10 @@ privsep_preauth_child(void) + /* Demote the private keys to public keys. */ + demote_sensitive_data(); + ++#ifdef WITH_SELINUX ++ ssh_selinux_change_context("sshd_net_t"); ++#endif ++ + /* Demote the child */ + if (privsep_chroot) { + /* Change our root directory */ +@@ -633,6 +637,9 @@ privsep_postauth(Authctxt *authctxt) + { + #ifdef DISABLE_FD_PASSING + if (1) { ++#elif defined(WITH_SELINUX) ++ if (0) { ++ /* even root user can be confined by SELinux */ + #else + if (authctxt->pw->pw_uid == 0) { + #endif diff --git a/SOURCES/openssh-6.7p1-coverity.patch b/SOURCES/openssh-6.7p1-coverity.patch new file mode 100644 index 0000000..0d238dd --- /dev/null +++ b/SOURCES/openssh-6.7p1-coverity.patch @@ -0,0 +1,185 @@ +diff -up openssh-7.4p1/channels.c.coverity openssh-7.4p1/channels.c +--- openssh-7.4p1/channels.c.coverity 2016-12-23 16:40:26.881788686 +0100 ++++ openssh-7.4p1/channels.c 2016-12-23 16:42:36.244818763 +0100 +@@ -288,11 +288,11 @@ channel_register_fds(Channel *c, int rfd + + /* enable nonblocking mode */ + if (nonblock) { +- if (rfd != -1) ++ if (rfd >= 0) + set_nonblock(rfd); +- if (wfd != -1) ++ if (wfd >= 0) + set_nonblock(wfd); +- if (efd != -1) ++ if (efd >= 0) + set_nonblock(efd); + } + } +diff -up openssh-7.4p1/monitor.c.coverity openssh-7.4p1/monitor.c +--- openssh-7.4p1/monitor.c.coverity 2016-12-23 16:40:26.888788688 +0100 ++++ openssh-7.4p1/monitor.c 2016-12-23 16:40:26.900788691 +0100 +@@ -411,7 +411,7 @@ monitor_child_preauth(Authctxt *_authctx + mm_get_keystate(pmonitor); + + /* Drain any buffered messages from the child */ +- while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) ++ while (pmonitor->m_log_recvfd >= 0 && monitor_read_log(pmonitor) == 0) + ; + + if (pmonitor->m_recvfd >= 0) +diff -up openssh-7.4p1/monitor_wrap.c.coverity openssh-7.4p1/monitor_wrap.c +--- openssh-7.4p1/monitor_wrap.c.coverity 2016-12-23 16:40:26.892788689 +0100 ++++ openssh-7.4p1/monitor_wrap.c 2016-12-23 16:40:26.900788691 +0100 +@@ -525,10 +525,10 @@ mm_pty_allocate(int *ptyfd, int *ttyfd, + if ((tmp1 = dup(pmonitor->m_recvfd)) == -1 || + (tmp2 = dup(pmonitor->m_recvfd)) == -1) { + error("%s: cannot allocate fds for pty", __func__); +- if (tmp1 > 0) ++ if (tmp1 >= 0) + close(tmp1); +- if (tmp2 > 0) +- close(tmp2); ++ /*DEAD CODE if (tmp2 >= 0) ++ close(tmp2);*/ + return 0; + } + close(tmp1); +diff -up openssh-7.4p1/openbsd-compat/bindresvport.c.coverity openssh-7.4p1/openbsd-compat/bindresvport.c +--- openssh-7.4p1/openbsd-compat/bindresvport.c.coverity 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/openbsd-compat/bindresvport.c 2016-12-23 16:40:26.901788691 +0100 +@@ -58,7 +58,7 @@ bindresvport_sa(int sd, struct sockaddr + struct sockaddr_in6 *in6; + u_int16_t *portp; + u_int16_t port; +- socklen_t salen; ++ socklen_t salen = sizeof(struct sockaddr_storage); + int i; + + if (sa == NULL) { +diff -up openssh-7.4p1/scp.c.coverity openssh-7.4p1/scp.c +--- openssh-7.4p1/scp.c.coverity 2016-12-23 16:40:26.856788681 +0100 ++++ openssh-7.4p1/scp.c 2016-12-23 16:40:26.901788691 +0100 +@@ -157,7 +157,7 @@ killchild(int signo) + { + if (do_cmd_pid > 1) { + kill(do_cmd_pid, signo ? signo : SIGTERM); +- waitpid(do_cmd_pid, NULL, 0); ++ (void) waitpid(do_cmd_pid, NULL, 0); + } + + if (signo) +diff -up openssh-7.4p1/servconf.c.coverity openssh-7.4p1/servconf.c +--- openssh-7.4p1/servconf.c.coverity 2016-12-23 16:40:26.896788690 +0100 ++++ openssh-7.4p1/servconf.c 2016-12-23 16:40:26.901788691 +0100 +@@ -1547,7 +1547,7 @@ process_server_config_line(ServerOptions + fatal("%s line %d: Missing subsystem name.", + filename, linenum); + if (!*activep) { +- arg = strdelim(&cp); ++ /*arg =*/ (void) strdelim(&cp); + break; + } + for (i = 0; i < options->num_subsystems; i++) +@@ -1638,8 +1638,9 @@ process_server_config_line(ServerOptions + if (*activep && *charptr == NULL) { + *charptr = tilde_expand_filename(arg, getuid()); + /* increase optional counter */ +- if (intptr != NULL) +- *intptr = *intptr + 1; ++ /* DEAD CODE intptr is still NULL ;) ++ if (intptr != NULL) ++ *intptr = *intptr + 1; */ + } + break; + +diff -up openssh-7.4p1/serverloop.c.coverity openssh-7.4p1/serverloop.c +--- openssh-7.4p1/serverloop.c.coverity 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/serverloop.c 2016-12-23 16:40:26.902788691 +0100 +@@ -125,13 +125,13 @@ notify_setup(void) + static void + notify_parent(void) + { +- if (notify_pipe[1] != -1) ++ if (notify_pipe[1] >= 0) + (void)write(notify_pipe[1], "", 1); + } + static void + notify_prepare(fd_set *readset) + { +- if (notify_pipe[0] != -1) ++ if (notify_pipe[0] >= 0) + FD_SET(notify_pipe[0], readset); + } + static void +@@ -139,8 +139,8 @@ notify_done(fd_set *readset) + { + char c; + +- if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) +- while (read(notify_pipe[0], &c, 1) != -1) ++ if (notify_pipe[0] >= 0 && FD_ISSET(notify_pipe[0], readset)) ++ while (read(notify_pipe[0], &c, 1) >= 0) + debug2("%s: reading", __func__); + } + +@@ -518,7 +518,7 @@ server_request_tun(void) + } + + tun = packet_get_int(); +- if (auth_opts->force_tun_device != -1) { ++ if (auth_opts->force_tun_device >= 0) { + if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun) + goto done; + tun = auth_opts->force_tun_device; +diff -up openssh-7.4p1/sftp.c.coverity openssh-7.4p1/sftp.c +--- openssh-7.4p1/sftp.c.coverity 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/sftp.c 2016-12-23 16:40:26.903788691 +0100 +@@ -224,7 +224,7 @@ killchild(int signo) + { + if (sshpid > 1) { + kill(sshpid, SIGTERM); +- waitpid(sshpid, NULL, 0); ++ (void) waitpid(sshpid, NULL, 0); + } + + _exit(1); +diff -up openssh-7.4p1/ssh-agent.c.coverity openssh-7.4p1/ssh-agent.c +--- openssh-7.4p1/ssh-agent.c.coverity 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/ssh-agent.c 2016-12-23 16:40:26.903788691 +0100 +@@ -1220,8 +1220,8 @@ main(int ac, char **av) + sanitise_stdfd(); + + /* drop */ +- setegid(getgid()); +- setgid(getgid()); ++ (void) setegid(getgid()); ++ (void) setgid(getgid()); + + platform_disable_tracing(0); /* strict=no */ + +diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c +--- openssh-7.4p1/sshd.c.coverity 2016-12-23 16:40:26.897788690 +0100 ++++ openssh-7.4p1/sshd.c 2016-12-23 16:40:26.904788692 +0100 +@@ -691,8 +691,10 @@ privsep_preauth(Authctxt *authctxt) + + privsep_preauth_child(); + setproctitle("%s", "[net]"); +- if (box != NULL) ++ if (box != NULL) { + ssh_sandbox_child(box); ++ free(box); ++ } + + return 0; + } +@@ -1386,6 +1388,9 @@ server_accept_loop(int *sock_in, int *so + if (num_listen_socks < 0) + break; + } ++ ++ if (fdset != NULL) ++ free(fdset); + } + + /* diff --git a/SOURCES/openssh-6.7p1-kdf-cavs.patch b/SOURCES/openssh-6.7p1-kdf-cavs.patch new file mode 100644 index 0000000..181267c --- /dev/null +++ b/SOURCES/openssh-6.7p1-kdf-cavs.patch @@ -0,0 +1,608 @@ +diff -up openssh-6.8p1/Makefile.in.kdf-cavs openssh-6.8p1/Makefile.in +--- openssh-6.8p1/Makefile.in.kdf-cavs 2015-03-18 11:23:46.346049359 +0100 ++++ openssh-6.8p1/Makefile.in 2015-03-18 11:24:20.395968445 +0100 +@@ -29,6 +29,7 @@ SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-h + SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper + SSH_KEYCAT=$(libexecdir)/ssh-keycat + CTR_CAVSTEST=$(libexecdir)/ctr-cavstest ++SSH_CAVS=$(libexecdir)/ssh-cavs + SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +@@ -67,7 +68,7 @@ EXEEXT=@EXEEXT@ + MKDIR_P=@MKDIR_P@ + INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) ctr-cavstest$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) ssh-keycat$(EXEEXT) ctr-cavstest$(EXEEXT) ssh-cavs$(EXEEXT) + + XMSS_OBJS=\ + ssh-xmss.o \ +@@ -198,6 +199,9 @@ ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHD + ctr-cavstest$(EXEEXT): $(LIBCOMPAT) libssh.a ctr-cavstest.o + $(LD) -o $@ ctr-cavstest.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(LIBS) + ++ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-cavs.o ++ $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +@@ -331,6 +335,8 @@ install-files: + fi + $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keycat$(EXEEXT) $(DESTDIR)$(libexecdir)/ssh-keycat$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) ctr-cavstest$(EXEEXT) $(DESTDIR)$(libexecdir)/ctr-cavstest$(EXEEXT) ++ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-cavs$(EXEEXT) $(DESTDIR)$(libexecdir)/ssh-cavs$(EXEEXT) ++ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-cavs_driver.pl $(DESTDIR)$(libexecdir)/ssh-cavs_driver.pl + $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 +diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c +--- openssh-6.8p1/ssh-cavs.c.kdf-cavs 2015-03-18 11:23:46.348049354 +0100 ++++ openssh-6.8p1/ssh-cavs.c 2015-03-18 11:23:46.348049354 +0100 +@@ -0,0 +1,377 @@ ++/* ++ * 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" ++#include "digest.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 *ctoskeys; ++ struct newkeys *stockeys; ++ 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 = SSH_DIGEST_SHA1; ++ break; ++ case 32: ++ kex.hash_alg = SSH_DIGEST_SHA256; ++ break; ++ case 48: ++ kex.hash_alg = SSH_DIGEST_SHA384; ++ break; ++ case 64: ++ kex.hash_alg = SSH_DIGEST_SHA512; ++ 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); ++ ++ ctoskeys = kex.newkeys[0]; ++ stockeys = kex.newkeys[1]; ++ ++ /* get data */ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(ctoskeys->enc.iv, (size_t)ctoskeys->enc.iv_len, ++ hex, HEXOUTLEN, 0); ++ printf("Initial IV (client to server) = %s\n", hex); ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(stockeys->enc.iv, (size_t)stockeys->enc.iv_len, ++ hex, HEXOUTLEN, 0); ++ printf("Initial IV (server to client) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(ctoskeys->enc.key, (size_t)ctoskeys->enc.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Encryption key (client to server) = %s\n", hex); ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(stockeys->enc.key, (size_t)stockeys->enc.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Encryption key (server to client) = %s\n", hex); ++ ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(ctoskeys->mac.key, (size_t)ctoskeys->mac.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Integrity key (client to server) = %s\n", hex); ++ memset(hex, 0, HEXOUTLEN); ++ bin2hex(stockeys->mac.key, (size_t)stockeys->mac.key_len, ++ hex, HEXOUTLEN, 0); ++ printf("Integrity key (server to client) = %s\n", hex); ++ ++out: ++ if (Kbn) ++ BN_free(Kbn); ++ if (ssh) ++ ssh_packet_close(ssh); ++ 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 ++ * ++ * Initial IV (client to server) = 4bb320d1679dfd3a ++ * Initial IV (server to client) = 43dea6fdf263a308 ++ * Encryption key (client to server) = 13048cc600b9d3cf9095aa6cf8e2ff9cf1c54ca0520c89ed ++ * Encryption key (server to client) = 1e483c5134e901aa11fc4e0a524e7ec7b75556148a222bb0 ++ * Integrity key (client to server) = ecef63a092b0dcc585bdc757e01b2740af57d640 ++ * 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 -up openssh-6.8p1/ssh-cavs_driver.pl.kdf-cavs openssh-6.8p1/ssh-cavs_driver.pl +--- openssh-6.8p1/ssh-cavs_driver.pl.kdf-cavs 2015-03-18 11:23:46.348049354 +0100 ++++ openssh-6.8p1/ssh-cavs_driver.pl 2015-03-18 11:23:46.348049354 +0100 +@@ -0,0 +1,184 @@ ++#!/usr/bin/env perl ++# ++# CAVS test driver for OpenSSH ++# ++# Copyright (C) 2015, Stephan Mueller ++# ++# Permission is hereby granted, free of charge, to any person obtaining a copy ++# of this software and associated documentation files (the "Software"), to deal ++# in the Software without restriction, including without limitation the rights ++# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++# copies of the Software, and to permit persons to whom the Software is ++# furnished to do so, subject to the following conditions: ++# ++# The above copyright notice and this permission notice shall be included in ++# all copies or substantial portions of the Software. ++# ++# NO WARRANTY ++# ++# BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY ++# FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN ++# OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES ++# PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED ++# OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF ++# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS ++# TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE ++# PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, ++# REPAIR OR CORRECTION. ++# ++# IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++# WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++# REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, ++# INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING ++# OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED ++# TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY ++# YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER ++# PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE ++# POSSIBILITY OF SUCH DAMAGES. ++# ++use strict; ++use warnings; ++use IPC::Open2; ++ ++# Executing a program by feeding STDIN and retrieving ++# STDOUT ++# $1: data string to be piped to the app on STDIN ++# rest: program and args ++# returns: STDOUT of program as string ++sub pipe_through_program($@) { ++ my $in = shift; ++ my @args = @_; ++ ++ my ($CO, $CI); ++ my $pid = open2($CO, $CI, @args); ++ ++ my $out = ""; ++ my $len = length($in); ++ my $first = 1; ++ while (1) { ++ my $rin = ""; ++ my $win = ""; ++ # Output of prog is FD that we read ++ vec($rin,fileno($CO),1) = 1; ++ # Input of prog is FD that we write ++ # check for $first is needed because we can have NULL input ++ # that is to be written to the app ++ if ( $len > 0 || $first) { ++ (vec($win,fileno($CI),1) = 1); ++ $first=0; ++ } ++ # Let us wait for 100ms ++ my $nfound = select(my $rout=$rin, my $wout=$win, undef, 0.1); ++ if ( $wout ) { ++ my $written = syswrite($CI, $in, $len); ++ die "broken pipe" if !defined $written; ++ $len -= $written; ++ substr($in, 0, $written) = ""; ++ if ($len <= 0) { ++ close $CI or die "broken pipe: $!"; ++ } ++ } ++ if ( $rout ) { ++ my $tmp_out = ""; ++ my $bytes_read = sysread($CO, $tmp_out, 4096); ++ $out .= $tmp_out; ++ last if ($bytes_read == 0); ++ } ++ } ++ close $CO or die "broken pipe: $!"; ++ waitpid $pid, 0; ++ ++ return $out; ++} ++ ++# Parser of CAVS test vector file ++# $1: Test vector file ++# $2: Output file for test results ++# return: nothing ++sub parse($$) { ++ my $infile = shift; ++ my $outfile = shift; ++ ++ my $out = ""; ++ ++ my $K = ""; ++ my $H = ""; ++ my $session_id = ""; ++ my $ivlen = 0; ++ my $eklen = ""; ++ my $iklen = ""; ++ ++ open(IN, "<$infile"); ++ while() { ++ ++ my $line = $_; ++ chomp($line); ++ $line =~ s/\r//; ++ ++ if ($line =~ /\[SHA-1\]/) { ++ $iklen = 20; ++ } elsif ($line =~ /\[SHA-256\]/) { ++ $iklen = 32; ++ } elsif ($line =~ /\[SHA-384\]/) { ++ $iklen = 48; ++ } elsif ($line =~ /\[SHA-512\]/) { ++ $iklen = 64; ++ } elsif ($line =~ /^\[IV length\s*=\s*(.*)\]/) { ++ $ivlen = $1; ++ $ivlen = $ivlen / 8; ++ } elsif ($line =~ /^\[encryption key length\s*=\s*(.*)\]/) { ++ $eklen = $1; ++ $eklen = $eklen / 8; ++ } elsif ($line =~ /^K\s*=\s*(.*)/) { ++ $K = $1; ++ $K = substr($K, 8); ++ $K = "00" . $K; ++ } elsif ($line =~ /^H\s*=\s*(.*)/) { ++ $H = $1; ++ } elsif ($line =~ /^session_id\s*=\s*(.*)/) { ++ $session_id = $1; ++ } ++ $out .= $line . "\n"; ++ ++ if ($K ne "" && $H ne "" && $session_id ne "" && ++ $ivlen ne "" && $eklen ne "" && $iklen > 0) { ++ $out .= pipe_through_program("", "./ssh-cavs -H $H -K $K -s $session_id -i $ivlen -e $eklen -m $iklen"); ++ ++ $K = ""; ++ $H = ""; ++ $session_id = ""; ++ } ++ } ++ close IN; ++ $out =~ s/\n/\r\n/g; # make it a dos file ++ open(OUT, ">$outfile") or die "Cannot create output file $outfile: $?"; ++ print OUT $out; ++ close OUT; ++} ++ ++############################################################ ++# ++# let us pretend to be C :-) ++sub main() { ++ ++ my $infile=$ARGV[0]; ++ die "Error: Test vector file $infile not found" if (! -f $infile); ++ ++ my $outfile = $infile; ++ # let us add .rsp regardless whether we could strip .req ++ $outfile =~ s/\.req$//; ++ $outfile .= ".rsp"; ++ if (-f $outfile) { ++ die "Output file $outfile could not be removed: $?" ++ unless unlink($outfile); ++ } ++ print STDERR "Performing tests from source file $infile with results stored in destination file $outfile\n"; ++ ++ # Do the job ++ parse($infile, $outfile); ++} ++ ++########################################### ++# Call it ++main(); ++1; diff --git a/SOURCES/openssh-6.7p1-ldap.patch b/SOURCES/openssh-6.7p1-ldap.patch new file mode 100644 index 0000000..ee44fdb --- /dev/null +++ b/SOURCES/openssh-6.7p1-ldap.patch @@ -0,0 +1,2742 @@ +diff -up openssh-6.8p1/HOWTO.ldap-keys.ldap openssh-6.8p1/HOWTO.ldap-keys +--- openssh-6.8p1/HOWTO.ldap-keys.ldap 2015-03-18 11:11:29.029801467 +0100 ++++ openssh-6.8p1/HOWTO.ldap-keys 2015-03-18 11:11:29.029801467 +0100 +@@ -0,0 +1,122 @@ ++ ++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 /usr/libexec/openssh/ssh-ldap-wrapper ++ AuthorizedKeysCommandUser ++ * 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 ++ /usr/libexec/openssh -s -f /etc/ldap.conf -w -d >> /tmp/ldapdebuglog.txt ++7) Configure SELinux boolean which allows ldap-helper to bind ldap server ++ Run this command ++ # setsebool -P authlogin_nsswitch_use_ldap on ++ ++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. ++ ++HOW TO CONFIGURE SSH FOR OTHER LDAP CONFIGURATION / SERVER /SCHEMA ++ ++You can adjust search format string in /etc/ldap.conf using ++ 1) SSH_Filter option to limit results for only specified users ++ (this appends search condition after original query) ++ 2) Search_Format option to define your own search string using expansion ++ characters %u for username, %c for objectclass and %f for above mentioned filter. ++ ++Example: ++Search_Format (&(objectclass=posixAccount)(objectclass=ldapPublicKey)(uid=%u)%f) ++ ++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. ++ - Robin H. Johnson. ++ - Adrian Bridgett. ++ ++5) Author ++ Jan F. Chadima ++ +diff -up openssh-6.8p1/Makefile.in.ldap openssh-6.8p1/Makefile.in +--- openssh-6.8p1/Makefile.in.ldap 2015-03-17 06:49:20.000000000 +0100 ++++ openssh-6.8p1/Makefile.in 2015-03-18 11:13:10.147561177 +0100 +@@ -25,6 +25,8 @@ SSH_PROGRAM=@bindir@/ssh + ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass + SFTP_SERVER=$(libexecdir)/sftp-server + SSH_KEYSIGN=$(libexecdir)/ssh-keysign ++SSH_LDAP_HELPER=$(libexecdir)/ssh-ldap-helper ++SSH_LDAP_WRAPPER=$(libexecdir)/ssh-ldap-wrapper + SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper + PRIVSEP_PATH=@PRIVSEP_PATH@ + SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ +@@ -50,6 +50,7 @@ + CFLAGS=@CFLAGS@ + CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ + LIBS=@LIBS@ ++LDAPLIBS=@LDAPLIBS@ + K5LIBS=@K5LIBS@ + GSSLIBS=@GSSLIBS@ + SSHLIBS=@SSHLIBS@ +@@ -61,8 +63,9 @@ XAUTH_PATH=@XAUTH_PATH@ + EXEEXT=@EXEEXT@ + MANFMT=@MANFMT@ + MKDIR_P=@MKDIR_P@ ++INSTALL_SSH_LDAP_HELPER=@INSTALL_SSH_LDAP_HELPER@ + +-TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ++TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) ssh-ldap-helper$(EXEEXT) + + XMSS_OBJS=\ + ssh-xmss.o \ +@@ -112,8 +115,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 +@@ -184,6 +187,9 @@ ssh-keysign$(EXEEXT): $(LIBCOMPAT) libss + ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o + $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++ssh-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 -lfipscheck $(LIBS) $(LDAPLIBS) ++ + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o + $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) + +@@ -311,6 +317,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 0700 $(STRIP_OPT) ssh-ldap-helper $(DESTDIR)$(SSH_LDAP_HELPER) ; \ ++ $(INSTALL) -m 0700 ssh-ldap-wrapper $(DESTDIR)$(SSH_LDAP_WRAPPER) ; \ ++ fi + $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) + $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) + $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 +@@ -327,6 +337,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) +@@ -356,6 +370,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 \ +@@ -419,6 +440,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 +@@ -430,6 +453,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 +diff -up openssh-6.8p1/configure.ac.ldap openssh-6.8p1/configure.ac +--- openssh-6.8p1/configure.ac.ldap 2015-03-17 06:49:20.000000000 +0100 ++++ openssh-6.8p1/configure.ac 2015-03-18 11:11:29.030801464 +0100 +@@ -1605,6 +1605,110 @@ if test "x$use_pie" != "xno"; then + fi + fi + ++# 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, LDAPLIBS="-llber $LDAPLIBS" found_ldap_lib=yes) ++ AC_CHECK_LIB(ldap, main, LDAPLIBS="-lldap $LDAPLIBS" 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, LDAPLIBS="-lldap50 -lssldap50 -lssl3 -lnss3 -lnspr4 -lprldap50 -lplc4 -lplds4 $LDAPLIBS" 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, LDAPLIBS="-lldapssl41 -lplc3 -lplds3 -lnspr3 $LDAPLIBS" found_ldap_lib=yes) ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldapssl40, main, LDAPLIBS="-lldapssl40 $LDAPLIBS" found_ldap_lib=yes) ++ fi ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldap41, main, LDAPLIBS="-lldap41 $LDAPLIBS" found_ldap_lib=yes) ++ fi ++ if test -z "$found_ldap_lib"; then ++ AC_CHECK_LIB(ldap40, main, LDAPLIBS="-lldap40 $LDAPLIBS" 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, LDAPLIBS="-lldapssl30 $LDAPLIBS" found_ldap_lib=yes) ++ fi ++ ++ if test -z "$found_ldap_lib"; then ++ AC_MSG_ERROR(could not locate a valid LDAP library) ++ fi ++ ++ saved_LIBS="$LIBS" ++ LIBS="$LIBS $LDAPLIBS" ++ 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]) ++ ) ++ LIBS="$saved_LIBS" ++ fi ++ ] ++) ++AC_SUBST(INSTALL_SSH_LDAP_HELPER) ++AC_SUBST(LDAPLIBS) ++ + dnl Checks for library functions. Please keep in alphabetical order + AC_CHECK_FUNCS([ \ + Blowfish_initstate \ +@@ -5227,6 +5352,9 @@ + echo "Preprocessor flags: ${CPPFLAGS}" + echo " Linker flags: ${LDFLAGS}" + echo " Libraries: ${LIBS}" ++if test ! -z "${LDAPLIBS}"; then ++echo " +for ldap: ${LDAPLIBS}" ++fi + if test ! -z "${SSHDLIBS}"; then + echo " +for sshd: ${SSHDLIBS}" + fi +diff -up openssh-6.8p1/ldap-helper.c.ldap openssh-6.8p1/ldap-helper.c +--- openssh-6.8p1/ldap-helper.c.ldap 2015-03-18 11:11:29.030801464 +0100 ++++ openssh-6.8p1/ldap-helper.c 2015-03-18 11:11:29.030801464 +0100 +@@ -0,0 +1,151 @@ ++/* $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 ++#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; ++} +diff -up openssh-6.8p1/ldap-helper.h.ldap openssh-6.8p1/ldap-helper.h +--- openssh-6.8p1/ldap-helper.h.ldap 2015-03-18 11:11:29.031801462 +0100 ++++ openssh-6.8p1/ldap-helper.h 2015-03-18 11:11:29.031801462 +0100 +@@ -0,0 +1,32 @@ ++/* $OpenBSD: ldap-helper.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef LDAP_HELPER_H ++#define LDAP_HELPER_H ++ ++extern int config_exclusive_config_file; ++extern int config_warning_config_file; ++ ++#endif /* LDAP_HELPER_H */ +diff -up openssh-6.8p1/ldap.conf.ldap openssh-6.8p1/ldap.conf +--- openssh-6.8p1/ldap.conf.ldap 2015-03-18 11:11:29.031801462 +0100 ++++ openssh-6.8p1/ldap.conf 2015-03-18 11:11:29.031801462 +0100 +@@ -0,0 +1,95 @@ ++# $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 ++ ++# OpenLDAP search_format ++# format used to search for users in LDAP directory using substitution ++# for %u for user name and %f for SSH_Filter option (optional, empty by default) ++#search_format (&(objectclass=%c)(objectclass=ldapPublicKey)(uid=%u)%f) ++ ++#AccountClass posixAccount ++ +diff -up openssh-6.8p1/ldapbody.c.ldap openssh-6.8p1/ldapbody.c +--- openssh-6.8p1/ldapbody.c.ldap 2015-03-18 11:11:29.031801462 +0100 ++++ openssh-6.8p1/ldapbody.c 2015-03-18 11:11:29.031801462 +0100 +@@ -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 ++#include ++#include "misc.h" ++ ++#define LDAPSEARCH_FORMAT "(&(objectclass=%c)(objectclass=ldapPublicKey)(uid=%u)%f)" ++#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, 0, &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 ++ (void) 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, 1)) == 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, 0, &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, 1)) != 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, 1); ++#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, *format; ++ int 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 */ ++ format = LDAPSEARCH_FORMAT; ++ if (options.search_format != NULL) ++ format = options.search_format; ++ buffer = percent_expand(format, "c", options.account_class, "u", user, "f", options.ssh_filter, (char *)NULL); ++ ++ debug3 ("LDAP search scope = %d %s", options.scope, buffer); ++ ++ timeout.tv_sec = options.timelimit; ++ timeout.tv_usec = 0; ++ if ((rc = ldap_search_st(ld, options.base, options.scope, buffer, attrs, 0, &timeout, &res)) != LDAP_SUCCESS) { ++ error ("ldap_search_st(): %s", ldap_err2string (rc)); ++ free (buffer); ++ return; ++ } ++ ++ /* free */ ++ free (buffer); ++ ++ for (e = ldap_first_entry(ld, res); e != NULL; e = ldap_next_entry(ld, e)) { ++ int num; ++ struct berval **keys; ++ ++ keys = ldap_get_values_len(ld, e, PUBKEYATTR); ++ num = ldap_count_values_len(keys); ++ for (i = 0 ; i < num ; i++) { ++ char *cp; //, *options = NULL; ++ ++ for (cp = keys[i]->bv_val; *cp == ' ' || *cp == '\t'; cp++); ++ if (!*cp || *cp == '\n' || *cp == '#') ++ continue; ++ ++ /* We have found the desired key. */ ++ fprintf (output, "%s\n", keys[i]->bv_val); ++ } ++ ++ ldap_value_free_len(keys); ++ } ++ ++ ldap_msgfree(res); ++ debug2 ("LDAP process user finished"); ++} ++ ++void ++ldap_do_close(void) ++{ ++ int rc; ++ ++ debug ("LDAP do close"); ++ if ((rc = ldap_unbind_ext(ld, NULL, NULL)) != LDAP_SUCCESS) ++ fatal ("ldap_unbind_ext: %s", ++ ldap_err2string (rc)); ++ ++ ld = NULL; ++ debug2 ("LDAP do close OK"); ++ return; ++} ++ +diff -up openssh-6.8p1/ldapbody.h.ldap openssh-6.8p1/ldapbody.h +--- openssh-6.8p1/ldapbody.h.ldap 2015-03-18 11:11:29.031801462 +0100 ++++ openssh-6.8p1/ldapbody.h 2015-03-18 11:11:29.031801462 +0100 +@@ -0,0 +1,37 @@ ++/* $OpenBSD: ldapbody.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef LDAPBODY_H ++#define LDAPBODY_H ++ ++#include ++ ++void ldap_checkconfig(void); ++void ldap_do_connect(void); ++void process_user(const char *, FILE *); ++void ldap_do_close(void); ++ ++#endif /* LDAPBODY_H */ ++ +diff -up openssh-6.8p1/ldapconf.c.ldap openssh-6.8p1/ldapconf.c +--- openssh-6.8p1/ldapconf.c.ldap 2015-03-18 11:11:29.032801460 +0100 ++++ openssh-6.8p1/ldapconf.c 2015-03-18 11:11:29.032801460 +0100 +@@ -0,0 +1,729 @@ ++/* $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 ++#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, lSearch_Format, ++ lAccountClass, 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 }, ++ { "search_format", lSearch_Format }, ++ { "AccountClass", lAccountClass }, ++ { 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; ++} ++ ++/* Characters considered whitespace in strsep calls. */ ++#define WHITESPACE " \t\r\n" ++ ++/* return next token in configuration line */ ++static char * ++ldap_strdelim(char **s) ++{ ++ char *old; ++ int wspace = 0; ++ ++ if (*s == NULL) ++ return NULL; ++ ++ old = *s; ++ ++ *s = strpbrk(*s, WHITESPACE); ++ if (*s == NULL) ++ return (old); ++ ++ *s[0] = '\0'; ++ ++ /* Skip any extra whitespace after first token */ ++ *s += strspn(*s + 1, WHITESPACE) + 1; ++ if (*s[0] == '=' && !wspace) ++ *s += strspn(*s + 1, WHITESPACE) + 1; ++ ++ return (old); ++} ++ ++/* ++ * 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 = ldap_strdelim(&s)) == NULL) ++ return 0; ++ /* Ignore leading whitespace. */ ++ if (*keyword == '\0') ++ keyword = ldap_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 = ldap_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 = ldap_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 = ldap_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 = ldap_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 = ldap_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 = ldap_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) ++ *intptr = value; ++ break; ++ ++ case lSSLPath: ++ charptr = &options.sslpath; ++ goto parse_string; ++ ++ case lSSL: ++ intptr = &options.ssl; ++ arg = ldap_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 = ldap_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 = ldap_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) ++ *intptr = value; ++ 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 lSearch_Format: ++ charptr = &options.search_format; ++ goto parse_string; ++ ++ case lAccountClass: ++ charptr = &options.account_class; ++ goto parse_string; ++ ++ 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 = ldap_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 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. ++ */ ++ 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; ++ options.search_format = NULL; ++ options.account_class = 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 = ""; ++ if (options.account_class == NULL) ++ options.account_class = "posixAccount"; ++} ++ ++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); ++ dump_cfg_string(lSearch_Format, options.search_format); ++ dump_cfg_string(lAccountClass, options.account_class); ++} ++ +diff -up openssh-6.8p1/ldapconf.h.ldap openssh-6.8p1/ldapconf.h +--- openssh-6.8p1/ldapconf.h.ldap 2015-03-18 11:11:29.032801460 +0100 ++++ openssh-6.8p1/ldapconf.h 2015-03-18 11:11:29.032801460 +0100 +@@ -0,0 +1,73 @@ ++/* $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; ++ char *search_format; ++ char *account_class; ++} Options; ++ ++extern Options options; ++ ++void read_config_file(const char *); ++void initialize_options(void); ++void fill_default_options(void); ++void dump_config(void); ++ ++#endif /* LDAPCONF_H */ +diff -up openssh-6.8p1/ldapincludes.h.ldap openssh-6.8p1/ldapincludes.h +--- openssh-6.8p1/ldapincludes.h.ldap 2015-03-18 11:11:29.032801460 +0100 ++++ openssh-6.8p1/ldapincludes.h 2015-03-18 11:11:29.032801460 +0100 +@@ -0,0 +1,41 @@ ++/* $OpenBSD: ldapconf.c,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef LDAPINCLUDES_H ++#define LDAPINCLUDES_H ++ ++#include "includes.h" ++ ++#ifdef HAVE_LBER_H ++#include ++#endif ++#ifdef HAVE_LDAP_H ++#include ++#endif ++#ifdef HAVE_LDAP_SSL_H ++#include ++#endif ++ ++#endif /* LDAPINCLUDES_H */ +diff -up openssh-6.8p1/ldapmisc.c.ldap openssh-6.8p1/ldapmisc.c +--- openssh-6.8p1/ldapmisc.c.ldap 2015-03-18 11:11:29.032801460 +0100 ++++ openssh-6.8p1/ldapmisc.c 2015-03-18 11:11:29.032801460 +0100 +@@ -0,0 +1,79 @@ ++ ++#include "ldapincludes.h" ++#include "ldapmisc.h" ++ ++#ifndef HAVE_LDAP_GET_LDERRNO ++int ++ldap_get_lderrno (LDAP * ld, char **m, char **s) ++{ ++#ifdef HAVE_LDAP_GET_OPTION ++ int rc; ++#endif ++ int lderrno; ++ ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno)) != LDAP_SUCCESS) ++ return rc; ++#else ++ lderrno = ld->ld_errno; ++#endif ++ ++ if (s != NULL) { ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_ERROR_STRING) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_ERROR_STRING, s)) != LDAP_SUCCESS) ++ return rc; ++#else ++ *s = ld->ld_error; ++#endif ++ } ++ ++ if (m != NULL) { ++#if defined(HAVE_LDAP_GET_OPTION) && defined(LDAP_OPT_MATCHED_DN) ++ if ((rc = ldap_get_option (ld, LDAP_OPT_MATCHED_DN, m)) != LDAP_SUCCESS) ++ return rc; ++#else ++ *m = ld->ld_matched; ++#endif ++ } ++ ++ return lderrno; ++} ++#endif ++ ++#ifndef HAVE_LDAP_SET_LDERRNO ++int ++ldap_set_lderrno (LDAP * ld, int lderrno, const char *m, const char *s) ++{ ++#ifdef HAVE_LDAP_SET_OPTION ++ int rc; ++#endif ++ ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_NUMBER) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_ERROR_NUMBER, &lderrno)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_errno = lderrno; ++#endif ++ ++ if (s != NULL) { ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_ERROR_STRING) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_ERROR_STRING, s)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_error = s; ++#endif ++ } ++ ++ if (m != NULL) { ++#if defined(HAVE_LDAP_SET_OPTION) && defined(LDAP_OPT_MATCHED_DN) ++ if ((rc = ldap_set_option (ld, LDAP_OPT_MATCHED_DN, m)) != LDAP_SUCCESS) ++ return rc; ++#else ++ ld->ld_matched = m; ++#endif ++ } ++ ++ return LDAP_SUCCESS; ++} ++#endif ++ +diff -up openssh-6.8p1/ldapmisc.h.ldap openssh-6.8p1/ldapmisc.h +--- openssh-6.8p1/ldapmisc.h.ldap 2015-03-18 11:11:29.032801460 +0100 ++++ openssh-6.8p1/ldapmisc.h 2015-03-18 11:11:29.032801460 +0100 +@@ -0,0 +1,35 @@ ++/* $OpenBSD: ldapbody.h,v 1.1 2009/12/03 03:34:42 jfch Exp $ */ ++/* ++ * Copyright (c) 2009 Jan F. Chadima. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef LDAPMISC_H ++#define LDAPMISC_H ++ ++#include "ldapincludes.h" ++ ++int ldap_get_lderrno (LDAP *, char **, char **); ++int ldap_set_lderrno (LDAP *, int, const char *, const char *); ++ ++#endif /* LDAPMISC_H */ ++ +diff -up openssh-6.8p1/openssh-lpk-openldap.schema.ldap openssh-6.8p1/openssh-lpk-openldap.schema +--- openssh-6.8p1/openssh-lpk-openldap.schema.ldap 2015-03-18 11:11:29.033801457 +0100 ++++ openssh-6.8p1/openssh-lpk-openldap.schema 2015-03-18 11:11:29.033801457 +0100 +@@ -0,0 +1,21 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# Based on the proposal of : Mark Ruijter ++# ++ ++ ++# octetString SYNTAX ++attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' ++ DESC 'MANDATORY: OpenSSH Public key' ++ EQUALITY octetStringMatch ++ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) ++ ++# printableString SYNTAX yes|no ++objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY ++ DESC 'MANDATORY: OpenSSH LPK objectclass' ++ MUST ( sshPublicKey $ uid ) ++ ) +diff -up openssh-6.8p1/openssh-lpk-sun.schema.ldap openssh-6.8p1/openssh-lpk-sun.schema +--- openssh-6.8p1/openssh-lpk-sun.schema.ldap 2015-03-18 11:11:29.033801457 +0100 ++++ openssh-6.8p1/openssh-lpk-sun.schema 2015-03-18 11:11:29.033801457 +0100 +@@ -0,0 +1,23 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# Schema for Sun Directory Server. ++# Based on the original schema, modified by Stefan Fischer. ++# ++ ++dn: cn=schema ++ ++# octetString SYNTAX ++attributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' ++ DESC 'MANDATORY: OpenSSH Public key' ++ EQUALITY octetStringMatch ++ SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) ++ ++# printableString SYNTAX yes|no ++objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY ++ DESC 'MANDATORY: OpenSSH LPK objectclass' ++ MUST ( sshPublicKey $ uid ) ++ ) +diff -up openssh-6.8p1/ssh-ldap-helper.8.ldap openssh-6.8p1/ssh-ldap-helper.8 +--- openssh-6.8p1/ssh-ldap-helper.8.ldap 2015-03-18 11:11:29.033801457 +0100 ++++ openssh-6.8p1/ssh-ldap-helper.8 2015-03-18 11:11:29.033801457 +0100 +@@ -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 /usr/libexec/ssh-ldap-wrapper . ++.Pp ++.Nm ++is not intended to be invoked by the user, but from ++.Xr sshd 8 via ++.Xr ssh-ldap-wrapper . ++.Pp ++The options are as follows: ++.Bl -tag -width Ds ++.It Fl d ++Set the debug mode; ++.Nm ++prints all logs to stderr instead of syslog. ++.It Fl e ++Implies \-w; ++.Nm ++halts if it encounters an unknown item in the ldap.conf file. ++.It Fl f ++.Nm ++uses this file as the ldap configuration file instead of /etc/ssh/ldap.conf (default). ++.It Fl s ++.Nm ++prints out the user's keys to stdout and exits. ++.It Fl v ++Implies \-d; ++increases verbosity. ++.It Fl w ++.Nm ++writes warnings about unknown items in the ldap.conf configuration file. ++.El ++.Sh SEE ALSO ++.Xr sshd 8 , ++.Xr sshd_config 5 , ++.Xr ssh-ldap.conf 5 , ++.Sh HISTORY ++.Nm ++first appeared in ++OpenSSH 5.5 + PKA-LDAP . ++.Sh AUTHORS ++.An Jan F. Chadima Aq jchadima@redhat.com +diff -up openssh-6.8p1/ssh-ldap-wrapper.ldap openssh-6.8p1/ssh-ldap-wrapper +--- openssh-6.8p1/ssh-ldap-wrapper.ldap 2015-03-18 11:11:29.033801457 +0100 ++++ openssh-6.8p1/ssh-ldap-wrapper 2015-03-18 11:11:29.033801457 +0100 +@@ -0,0 +1,4 @@ ++#!/bin/sh ++ ++exec /usr/libexec/openssh/ssh-ldap-helper -s "$1" ++ +diff -up openssh-6.8p1/ssh-ldap.conf.5.ldap openssh-6.8p1/ssh-ldap.conf.5 +--- openssh-6.8p1/ssh-ldap.conf.5.ldap 2015-03-18 11:11:29.033801457 +0100 ++++ openssh-6.8p1/ssh-ldap.conf.5 2015-03-18 11:11:29.033801457 +0100 +@@ -0,0 +1,385 @@ ++.\" $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 search. ++The default is no filter. ++.It Cm AccountClass ++Specifies the LDAP class used to find user accounts. ++The default is posixAccount. ++.It Cm search_format ++Specifies the user format of search string in LDAP substituting %u for user name ++and %f for additional ssh filter ++.Cm SSH_Filter ++(optional). ++The default value is (&(objectclass=%c)(objectclass=ldapPublicKey)(uid=%u)%f) ++.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-lpk-openldap.ldif b/openssh-lpk-openldap.ldif +new file mode 100644 +index 0000000..9adf4b8 +--- /dev/null ++++ b/openssh-lpk-openldap.ldif +@@ -0,0 +1,19 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# LDIF for openLDAP Directory Server. ++# Based on the original schema, modified by Jakub Jelen. ++# ++ ++dn: cn=openssh-lpk,cn=schema,cn=config ++objectClass: olcSchemaConfig ++cn: openssh-lpk ++olcAttributeTypes: {0}( 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 ) ++olcObjectClasses: {0}( 1.3.6.1.4.1.24552.500.1.1.2.0 ++ NAME 'ldapPublicKey' DESC 'MANDATORY: OpenSSH LPK objectclass' ++ SUP top AUXILIARY MUST ( sshPublicKey $ uid ) ) +diff --git a/openssh-lpk-sun.ldif b/openssh-lpk-sun.ldif +new file mode 100644 +index 0000000..9adf4b8 +--- /dev/null ++++ b/openssh-lpk-sun.ldif +@@ -0,0 +1,17 @@ ++# ++# LDAP Public Key Patch schema for use with openssh-ldappubkey ++# useful with PKA-LDAP also ++# ++# Author: Eric AUGE ++# ++# LDIF for Sun Directory Server. ++# Based on the original schema, modified by Jakub Jelen. ++# ++ ++dn: cn=schema ++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 ) ++objectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 ++ NAME 'ldapPublicKey' DESC 'MANDATORY: OpenSSH LPK objectclass' ++ SUP top AUXILIARY MUST ( sshPublicKey $ uid ) ) diff --git a/SOURCES/openssh-6.7p1-sftp-force-permission.patch b/SOURCES/openssh-6.7p1-sftp-force-permission.patch new file mode 100644 index 0000000..2d6e730 --- /dev/null +++ b/SOURCES/openssh-6.7p1-sftp-force-permission.patch @@ -0,0 +1,98 @@ +diff -up openssh-7.2p2/sftp-server.8.sftp-force-mode openssh-7.2p2/sftp-server.8 +--- openssh-7.2p2/sftp-server.8.sftp-force-mode 2016-03-09 19:04:48.000000000 +0100 ++++ openssh-7.2p2/sftp-server.8 2016-06-23 16:18:20.463854117 +0200 +@@ -38,6 +38,7 @@ + .Op Fl P Ar blacklisted_requests + .Op Fl p Ar whitelisted_requests + .Op Fl u Ar umask ++.Op Fl m Ar force_file_perms + .Ek + .Nm + .Fl Q Ar protocol_feature +@@ -138,6 +139,10 @@ 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_perms ++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, +diff -up openssh-7.2p2/sftp-server.c.sftp-force-mode openssh-7.2p2/sftp-server.c +--- openssh-7.2p2/sftp-server.c.sftp-force-mode 2016-06-23 16:18:20.446854128 +0200 ++++ openssh-7.2p2/sftp-server.c 2016-06-23 16:20:37.950766082 +0200 +@@ -69,6 +69,10 @@ struct sshbuf *oqueue; + /* Version of client */ + static u_int version; + ++/* Force file permissions */ ++int permforce = 0; ++long permforcemode; ++ + /* SSH2_FXP_INIT received */ + static int init_done; + +@@ -683,6 +687,7 @@ process_open(u_int32_t id) + Attrib a; + char *name; + int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; ++ mode_t old_umask = 0; + + if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || + (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ +@@ -692,6 +697,10 @@ process_open(u_int32_t id) + 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) { /* Force perm if -m is set */ ++ mode = permforcemode; ++ old_umask = umask(0); /* so umask does not interfere */ ++ } + logit("open \"%s\" flags %s mode 0%o", + name, string_from_portable(pflags), mode); + if (readonly && +@@ -713,6 +722,8 @@ process_open(u_int32_t id) + } + } + } ++ if (permforce == 1) ++ (void) umask(old_umask); /* restore umask to something sane */ + if (status != SSH2_FX_OK) + send_status(id, status); + free(name); +@@ -1494,7 +1505,7 @@ sftp_server_usage(void) + 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] [-m force_file_perms]\n" + " %s -Q protocol_feature\n", + __progname, __progname); + exit(1); +@@ -1520,7 +1531,7 @@ sftp_server_main(int argc, char **argv, + 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) { +@@ -1580,6 +1591,15 @@ sftp_server_main(int argc, char **argv, + fatal("Invalid umask \"%s\"", optarg); + (void)umask((mode_t)mask); + break; ++ case 'm': ++ /* Force permissions on file received via sftp */ ++ permforce = 1; ++ permforcemode = strtol(optarg, &cp, 8); ++ if (permforcemode < 0 || permforcemode > 0777 || ++ *cp != '\0' || (permforcemode == 0 && ++ errno != 0)) ++ fatal("Invalid file mode \"%s\"", optarg); ++ break; + case 'h': + default: + sftp_server_usage(); diff --git a/SOURCES/openssh-6.8p1-sshdT-output.patch b/SOURCES/openssh-6.8p1-sshdT-output.patch new file mode 100644 index 0000000..7a843e7 --- /dev/null +++ b/SOURCES/openssh-6.8p1-sshdT-output.patch @@ -0,0 +1,27 @@ +diff -up openssh/servconf.c.sshdt openssh/servconf.c +--- openssh/servconf.c.sshdt 2015-06-24 11:42:29.041078704 +0200 ++++ openssh/servconf.c 2015-06-24 11:44:39.734745802 +0200 +@@ -2317,7 +2317,7 @@ dump_config(ServerOptions *o) + dump_cfg_string(sXAuthLocation, o->xauth_location); + dump_cfg_string(sCiphers, o->ciphers ? o->ciphers : KEX_SERVER_ENCRYPT); + dump_cfg_string(sMacs, o->macs ? o->macs : KEX_SERVER_MAC); +- dump_cfg_string(sBanner, o->banner); ++ dump_cfg_string(sBanner, o->banner != NULL ? o->banner : "none"); + dump_cfg_string(sForceCommand, o->adm_forced_command); + dump_cfg_string(sChrootDirectory, o->chroot_directory); + dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); +diff -up openssh/ssh.1.sshdt openssh/ssh.1 +--- openssh/ssh.1.sshdt 2015-06-24 11:42:19.565102807 +0200 ++++ openssh/ssh.1 2015-06-24 11:42:29.042078701 +0200 +@@ -441,7 +441,11 @@ For full details of the options listed b + .It GatewayPorts + .It GlobalKnownHostsFile + .It GSSAPIAuthentication ++.It GSSAPIKeyExchange ++.It GSSAPIClientIdentity + .It GSSAPIDelegateCredentials ++.It GSSAPIRenewalForcesRekey ++.It GSSAPITrustDNS + .It HashKnownHosts + .It Host + .It HostbasedAuthentication diff --git a/SOURCES/openssh-6.9p1-permit-root-login.patch b/SOURCES/openssh-6.9p1-permit-root-login.patch new file mode 100644 index 0000000..6b4c4da --- /dev/null +++ b/SOURCES/openssh-6.9p1-permit-root-login.patch @@ -0,0 +1,12 @@ +diff -up openssh-7.0p1/sshd_config.root-login openssh-7.0p1/sshd_config +--- openssh-7.0p1/sshd_config.root-login 2015-08-12 11:29:12.919269245 +0200 ++++ openssh-7.0p1/sshd_config 2015-08-12 11:31:03.653096466 +0200 +@@ -46,7 +46,7 @@ SyslogFacility AUTHPRIV + # Authentication: + + #LoginGraceTime 2m +-#PermitRootLogin prohibit-password ++PermitRootLogin yes + #StrictModes yes + #MaxAuthTries 6 + #MaxSessions 10 diff --git a/SOURCES/openssh-7.0p1-gssKexAlgorithms.patch b/SOURCES/openssh-7.0p1-gssKexAlgorithms.patch new file mode 100644 index 0000000..34066f6 --- /dev/null +++ b/SOURCES/openssh-7.0p1-gssKexAlgorithms.patch @@ -0,0 +1,431 @@ +diff -up openssh-7.0p1/gss-genr.c.gsskexalg openssh-7.0p1/gss-genr.c +--- openssh-7.0p1/gss-genr.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 ++++ openssh-7.0p1/gss-genr.c 2015-08-19 12:28:38.078518839 +0200 +@@ -78,7 +78,8 @@ ssh_gssapi_oid_table_ok() { + */ + + char * +-ssh_gssapi_client_mechanisms(const char *host, const char *client) { ++ssh_gssapi_client_mechanisms(const char *host, const char *client, ++ const char *kex) { + gss_OID_set gss_supported; + OM_uint32 min_status; + +@@ -86,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,6 +101,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + char deroid[2]; + const EVP_MD *evp_md = EVP_md5(); + EVP_MD_CTX md; ++ char *s, *cp, *p; + + if (gss_enc2oid != NULL) { + for (i = 0; gss_enc2oid[i].encoded != NULL; i++) +@@ -113,6 +115,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + fatal("%s: sshbuf_new failed", __func__); + + 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)) { +@@ -131,28 +134,25 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + enclen = __b64_ntop(digest, EVP_MD_size(evp_md), + encoded, EVP_MD_size(evp_md) * 2); + +- if (oidpos != 0) +- if ((r = sshbuf_put_u8(buf, ',')) != 0) +- fatal("%s: buffer error: %s", __func__, ssh_err(r)); +- +- if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, +- sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || +- (r = sshbuf_put(buf, encoded, enclen)) != 0 || +- (r = sshbuf_put_u8(buf, ',')) != 0 || +- (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, +- sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || +- (r = sshbuf_put(buf, encoded, enclen)) != 0 || +- (r = sshbuf_put_u8(buf, ',')) != 0 || +- (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, +- sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 || +- (r = sshbuf_put(buf, encoded, enclen)) != 0) +- fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ 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; + +diff -up openssh-7.0p1/gss-serv.c.gsskexalg openssh-7.0p1/gss-serv.c +--- openssh-7.0p1/gss-serv.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 ++++ openssh-7.0p1/gss-serv.c 2015-08-19 12:28:38.078518839 +0200 +@@ -149,7 +149,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 */ +diff -up openssh-7.0p1/kex.c.gsskexalg openssh-7.0p1/kex.c +--- openssh-7.0p1/kex.c.gsskexalg 2015-08-19 12:28:38.078518839 +0200 ++++ openssh-7.0p1/kex.c 2015-08-19 12:30:13.249306371 +0200 +@@ -50,6 +50,7 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" ++#include "xmalloc.h" + + #include "ssherr.h" + #include "sshbuf.h" +@@ -232,6 +232,29 @@ kex_assemble_names(const char *def, char + 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]) +diff -up openssh-7.0p1/kex.h.gsskexalg openssh-7.0p1/kex.h +--- openssh-7.0p1/kex.h.gsskexalg 2015-08-19 12:28:38.078518839 +0200 ++++ openssh-7.0p1/kex.h 2015-08-19 12:30:52.404218958 +0200 +@@ -173,6 +173,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]); +diff -up openssh-7.0p1/readconf.c.gsskexalg openssh-7.0p1/readconf.c +--- openssh-7.0p1/readconf.c.gsskexalg 2015-08-19 12:28:38.026518955 +0200 ++++ openssh-7.0p1/readconf.c 2015-08-19 12:31:28.333138747 +0200 +@@ -61,6 +61,7 @@ + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + /* Format of the configuration file: + +@@ -148,7 +149,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, +@@ -200,6 +201,7 @@ static struct { + { "gssapiclientidentity", oGssClientIdentity }, + { "gssapiserveridentity", oGssServerIdentity }, + { "gssapirenewalforcesrekey", oGssRenewalRekey }, ++ { "gssapikexalgorithms", oGssKexAlgorithms }, + # else + { "gssapiauthentication", oUnsupported }, + { "gssapikeyexchange", oUnsupported }, +@@ -207,6 +209,7 @@ static struct { + { "gssapitrustdns", oUnsupported }, + { "gssapiclientidentity", oUnsupported }, + { "gssapirenewalforcesrekey", oUnsupported }, ++ { "gssapikexalgorithms", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, +@@ -929,6 +932,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; +@@ -1638,6 +1653,7 @@ initialize_options(Options * options) + 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; +@@ -1773,6 +1789,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) +@@ -2651,6 +2671,8 @@ dump_client_config(Options *o, const cha + dump_cfg_string(oGssClientIdentity, o->gss_client_identity); + dump_cfg_string(oGssServerIdentity, o->gss_client_identity); + dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); ++ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ? ++ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX); + #endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); +diff -up openssh-7.9p1/readconf.h.gsskexalg openssh-7.9p1/readconf.h +--- openssh-7.9p1/readconf.h.gsskexalg 2018-11-14 09:20:06.616350574 +0100 ++++ openssh-7.9p1/readconf.h 2018-11-14 09:20:06.647350828 +0100 +@@ -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. */ +diff -up openssh-7.0p1/servconf.c.gsskexalg openssh-7.0p1/servconf.c +--- openssh-7.0p1/servconf.c.gsskexalg 2015-08-19 12:28:38.074518847 +0200 ++++ openssh-7.0p1/servconf.c 2015-08-19 12:33:13.599902732 +0200 +@@ -57,6 +57,7 @@ + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + static void add_listen_addr(ServerOptions *, const char *, + const char *, int); +@@ -121,6 +122,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->use_kuserok = -1; + options->enable_k5users = -1; + options->password_authentication = -1; +@@ -288,6 +290,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->use_kuserok == -1) + options->use_kuserok = 1; + if (options->enable_k5users == -1) +@@ -427,7 +431,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, +- sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, ++ sGssKeyEx, sGssStoreRekey, sGssKexAlgorithms, sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, +@@ -506,6 +510,7 @@ static struct { + { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, + { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, ++ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, + #else + { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, + { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, +@@ -513,6 +518,7 @@ static struct { + { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, + { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, + { "gssapienablek5users", sUnsupported, SSHCFG_ALL }, ++ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, + { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, +@@ -1273,6 +1279,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; +@@ -2304,6 +2322,7 @@ dump_config(ServerOptions *o) + dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); + 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, +diff -up openssh-7.0p1/servconf.h.gsskexalg openssh-7.0p1/servconf.h +--- openssh-7.0p1/servconf.h.gsskexalg 2015-08-19 12:28:38.080518834 +0200 ++++ openssh-7.0p1/servconf.h 2015-08-19 12:34:46.328693944 +0200 +@@ -122,6 +122,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 */ +diff -up openssh-7.0p1/ssh.1.gsskexalg openssh-7.0p1/ssh.1 +--- openssh-7.0p1/ssh.1.gsskexalg 2015-08-19 12:28:38.081518832 +0200 ++++ openssh-7.0p1/ssh.1 2015-08-19 12:35:31.741591692 +0200 +@@ -496,6 +496,7 @@ For full details of the options listed b + .It GSSAPIDelegateCredentials + .It GSSAPIRenewalForcesRekey + .It GSSAPITrustDNS ++.It GSSAPIKexAlgorithms + .It HashKnownHosts + .It Host + .It HostbasedAuthentication +diff -up openssh-7.0p1/ssh_config.5.gsskexalg openssh-7.0p1/ssh_config.5 +--- openssh-7.0p1/ssh_config.5.gsskexalg 2015-08-19 12:28:38.028518950 +0200 ++++ openssh-7.0p1/ssh_config.5 2015-08-19 12:28:38.082518830 +0200 +@@ -786,6 +786,18 @@ command line will be passed untouched 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 +diff -up openssh-7.0p1/sshconnect2.c.gsskexalg openssh-7.0p1/sshconnect2.c +--- openssh-7.0p1/sshconnect2.c.gsskexalg 2015-08-19 12:28:38.045518912 +0200 ++++ openssh-7.0p1/sshconnect2.c 2015-08-19 12:28:38.081518832 +0200 +@@ -179,7 +179,8 @@ ssh_kex2(char *host, struct sockaddr *ho + else + gss_host = host; + +- gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); ++ gss = ssh_gssapi_client_mechanisms(gss_host, ++ options.gss_client_identity, options.gss_kex_algorithms); + if (gss) { + debug("Offering GSSAPI proposal: %s", gss); + xasprintf(&options.kex_algorithms, +--- openssh-7.1p1/sshd_config.5.gsskexalg 2015-12-10 15:32:48.105418092 +0100 ++++ openssh-7.1p1/sshd_config.5 2015-12-10 15:33:47.771279548 +0100 +@@ -663,6 +663,18 @@ or updated credentials from a compatible + For this to work + .Cm GSSAPIKeyExchange + needs to be enabled in the server and also used by the client. ++.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 -up openssh-7.0p1/ssh-gss.h.gsskexalg openssh-7.0p1/ssh-gss.h +--- openssh-7.0p1/ssh-gss.h.gsskexalg 2015-08-19 12:28:38.031518944 +0200 ++++ openssh-7.0p1/ssh-gss.h 2015-08-19 12:28:38.081518832 +0200 +@@ -76,6 +76,10 @@ extern char **k5users_allowed_cmds; + #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 *envvar; + char *envval; +@@ -147,9 +151,9 @@ int ssh_gssapi_credentials_updated(Gssct + /* 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 *); ++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 *); diff --git a/SOURCES/openssh-7.1p1-gssapi-documentation.patch b/SOURCES/openssh-7.1p1-gssapi-documentation.patch new file mode 100644 index 0000000..db689d4 --- /dev/null +++ b/SOURCES/openssh-7.1p1-gssapi-documentation.patch @@ -0,0 +1,52 @@ +diff -up openssh-7.4p1/ssh_config.5.gss-docs openssh-7.4p1/ssh_config.5 +--- openssh-7.4p1/ssh_config.5.gss-docs 2016-12-23 14:28:34.051714486 +0100 ++++ openssh-7.4p1/ssh_config.5 2016-12-23 14:34:24.568522417 +0100 +@@ -765,10 +765,19 @@ The default is + 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 ++ssh connection. With a compatible server, this will delegate the renewed + credentials to a session on the server. ++.Pp ++Checks are made to ensure that credentials are only propagated when the new ++credentials match the old ones on the originating client and where the ++receiving server still has the old set in its cache. ++.Pp + The default is + .Dq no . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. + .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 +@@ -776,9 +785,11 @@ expected GSSAPI server identity will be + hostname. + .It Cm GSSAPITrustDns + Set to +-.Dq yes to indicate that the DNS is trusted to securely canonicalize ++.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 ++.Dq no , ++the hostname entered on the + command line will be passed untouched to the GSSAPI library. + The default is + .Dq no . +diff -up openssh-7.4p1/sshd_config.5.gss-docs openssh-7.4p1/sshd_config.5 +--- openssh-7.4p1/sshd_config.5.gss-docs 2016-12-23 14:28:34.043714490 +0100 ++++ openssh-7.4p1/sshd_config.5 2016-12-23 14:28:34.051714486 +0100 +@@ -652,6 +652,10 @@ 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 . ++.Pp ++For this to work ++.Cm GSSAPIKeyExchange ++needs to be enabled in the server and also used by the client. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. diff --git a/SOURCES/openssh-7.1p2-audit-race-condition.patch b/SOURCES/openssh-7.1p2-audit-race-condition.patch new file mode 100644 index 0000000..9a0d917 --- /dev/null +++ b/SOURCES/openssh-7.1p2-audit-race-condition.patch @@ -0,0 +1,187 @@ +diff -up openssh-7.4p1/monitor_wrap.c.audit-race openssh-7.4p1/monitor_wrap.c +--- openssh-7.4p1/monitor_wrap.c.audit-race 2016-12-23 16:35:52.694685771 +0100 ++++ openssh-7.4p1/monitor_wrap.c 2016-12-23 16:35:52.697685772 +0100 +@@ -1107,4 +1107,50 @@ mm_audit_destroy_sensitive_data(const ch + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, m); + sshbuf_free(m); + } ++ ++int mm_forward_audit_messages(int fdin) ++{ ++ u_char buf[4]; ++ u_int blen, msg_len; ++ struct sshbuf *m; ++ int r, ret = 0; ++ ++ debug3("%s: entering", __func__); ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ do { ++ blen = atomicio(read, fdin, buf, sizeof(buf)); ++ if (blen == 0) /* closed pipe */ ++ break; ++ if (blen != sizeof(buf)) { ++ error("%s: Failed to read the buffer from child", __func__); ++ ret = -1; ++ break; ++ } ++ ++ msg_len = get_u32(buf); ++ if (msg_len > 256 * 1024) ++ fatal("%s: read: bad msg_len %d", __func__, msg_len); ++ sshbuf_reset(m); ++ if ((r = sshbuf_reserve(m, msg_len, NULL)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ if (atomicio(read, fdin, sshbuf_mutable_ptr(m), msg_len) != msg_len) { ++ error("%s: Failed to read the the buffer content from the child", __func__); ++ ret = -1; ++ break; ++ } ++ if (atomicio(vwrite, pmonitor->m_recvfd, buf, blen) != blen || ++ atomicio(vwrite, pmonitor->m_recvfd, sshbuf_mutable_ptr(m), msg_len) != msg_len) { ++ error("%s: Failed to write the message to the monitor", __func__); ++ ret = -1; ++ break; ++ } ++ } while (1); ++ sshbuf_free(m); ++ return ret; ++} ++void mm_set_monitor_pipe(int fd) ++{ ++ pmonitor->m_recvfd = fd; ++} + #endif /* SSH_AUDIT_EVENTS */ +diff -up openssh-7.4p1/monitor_wrap.h.audit-race openssh-7.4p1/monitor_wrap.h +--- openssh-7.4p1/monitor_wrap.h.audit-race 2016-12-23 16:35:52.694685771 +0100 ++++ openssh-7.4p1/monitor_wrap.h 2016-12-23 16:35:52.698685772 +0100 +@@ -83,6 +83,8 @@ void mm_audit_unsupported_body(int); + void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); + void mm_audit_session_key_free_body(int, pid_t, uid_t); + void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++int mm_forward_audit_messages(int); ++void mm_set_monitor_pipe(int); + #endif + + struct Session; +diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c +--- openssh-7.4p1/session.c.audit-race 2016-12-23 16:35:52.695685771 +0100 ++++ openssh-7.4p1/session.c 2016-12-23 16:37:26.339730596 +0100 +@@ -162,6 +162,10 @@ static Session *sessions = NULL; + login_cap_t *lc; + #endif + ++#ifdef SSH_AUDIT_EVENTS ++int paudit[2]; ++#endif ++ + static int is_child = 0; + static int in_chroot = 0; + static int have_dev_log = 1; +@@ -289,6 +293,8 @@ xauth_valid_string(const char *s) + return 1; + } + ++void child_destory_sensitive_data(); ++ + #define USE_PIPES 1 + /* + * This is called to fork and execute a command when we have no tty. This +@@ -424,6 +430,8 @@ do_exec_no_pty(Session *s, const char *c + close(err[0]); + #endif + ++ child_destory_sensitive_data(); ++ + /* Do processing for the child (exec command etc). */ + do_child(ssh, s, command); + /* NOTREACHED */ +@@ -547,6 +555,9 @@ do_exec_pty(Session *s, const char *comm + /* Close the extra descriptor for the pseudo tty. */ + close(ttyfd); + ++ /* Do this early, so we will not block large MOTDs */ ++ child_destory_sensitive_data(); ++ + /* record login, etc. similar to login(1) */ + #ifndef HAVE_OSF_SIA + do_login(ssh, s, command); +@@ -717,6 +728,8 @@ do_exec(Session *s, const char *command) + } + if (s->command != NULL && s->ptyfd == -1) + s->command_handle = PRIVSEP(audit_run_command(s->command)); ++ if (pipe(paudit) < 0) ++ fatal("pipe: %s", strerror(errno)); + #endif + if (s->ttyfd != -1) + ret = do_exec_pty(ssh, s, command); +@@ -732,6 +745,20 @@ do_exec(Session *s, const char *command) + */ + sshbuf_reset(loginmsg); + ++#ifdef SSH_AUDIT_EVENTS ++ close(paudit[1]); ++ if (use_privsep && ret == 0) { ++ /* ++ * Read the audit messages from forked child and send them ++ * back to monitor. We don't want to communicate directly, ++ * because the messages might get mixed up. ++ * Continue after the pipe gets closed (all messages sent). ++ */ ++ ret = mm_forward_audit_messages(paudit[0]); ++ } ++ close(paudit[0]); ++#endif /* SSH_AUDIT_EVENTS */ ++ + return ret; + } + +@@ -1538,6 +1565,34 @@ child_close_fds(void) + endpwent(); + } + ++void ++child_destory_sensitive_data() ++{ ++#ifdef SSH_AUDIT_EVENTS ++ int pparent = paudit[1]; ++ close(paudit[0]); ++ /* Hack the monitor pipe to avoid race condition with parent */ ++ if (use_privsep) ++ mm_set_monitor_pipe(pparent); ++#endif ++ ++ /* remove hostkey from the child's memory */ ++ destroy_sensitive_data(use_privsep); ++ /* ++ * We can audit this, because we hacked the pipe to direct the ++ * messages over postauth child. But this message requires answer ++ * which we can't do using one-way pipe. ++ */ ++ packet_destroy_all(0, 1); ++ /* XXX this will clean the rest but should not audit anymore */ ++ /* packet_clear_keys(); */ ++ ++#ifdef SSH_AUDIT_EVENTS ++ /* Notify parent that we are done */ ++ close(pparent); ++#endif ++} ++ + /* + * Performs common processing for the child, such as setting up the + * environment, closing extra file descriptors, setting the user and group +@@ -1554,13 +1608,6 @@ do_child(Session *s, const char *command + struct passwd *pw = s->pw; + int r = 0; + +- /* remove hostkey from the child's memory */ +- 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) { + do_setusercontext(pw); diff --git a/SOURCES/openssh-7.2p2-k5login_directory.patch b/SOURCES/openssh-7.2p2-k5login_directory.patch new file mode 100644 index 0000000..09369aa --- /dev/null +++ b/SOURCES/openssh-7.2p2-k5login_directory.patch @@ -0,0 +1,87 @@ +diff --git a/auth-krb5.c b/auth-krb5.c +index 2b02a04..19b9364 100644 +--- a/auth-krb5.c ++++ b/auth-krb5.c +@@ -375,6 +375,22 @@ cleanup: + return -1; + } + ++/* ++ * Reads k5login_directory option from the krb5.conf ++ */ ++krb5_error_code ++ssh_krb5_get_k5login_directory(krb5_context ctx, char **k5login_directory) { ++ profile_t p; ++ int ret = 0; ++ ++ ret = krb5_get_profile(ctx, &p); ++ if (ret) ++ return ret; ++ ++ return profile_get_string(p, "libdefaults", "k5login_directory", NULL, NULL, ++ k5login_directory); ++} ++ + krb5_error_code + ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { + profile_t p; +diff --git a/auth.h b/auth.h +index f9d191c..c432d2f 100644 +--- a/auth.h ++++ b/auth.h +@@ -222,5 +222,7 @@ int sys_auth_passwd(Authctxt *, const char *); + #if defined(KRB5) && !defined(HEIMDAL) + #include + krb5_error_code ssh_krb5_cc_new_unique(krb5_context, krb5_ccache *, int *); ++krb5_error_code ssh_krb5_get_k5login_directory(krb5_context ctx, ++ char **k5login_directory); + #endif + #endif +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index a7c0c5f..df8cc9a 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -244,8 +244,27 @@ ssh_gssapi_k5login_exists() + { + char file[MAXPATHLEN]; + struct passwd *pw = the_authctxt->pw; ++ char *k5login_directory = NULL; ++ int ret = 0; ++ ++ ret = ssh_krb5_get_k5login_directory(krb_context, &k5login_directory); ++ debug3("%s: k5login_directory = %s (rv=%d)", __func__, k5login_directory, ret); ++ if (k5login_directory == NULL || ret != 0) { ++ /* If not set, the library will look for k5login ++ * files in the user's home directory, with the filename .k5login. ++ */ ++ snprintf(file, sizeof(file), "%s/.k5login", pw->pw_dir); ++ } else { ++ /* If set, the library will look for a local user's k5login file ++ * within the named directory, with a filename corresponding to the ++ * local username. ++ */ ++ snprintf(file, sizeof(file), "%s%s%s", k5login_directory, ++ k5login_directory[strlen(k5login_directory)-1] != '/' ? "/" : "", ++ pw->pw_name); ++ } ++ debug("%s: Checking existence of file %s", __func__, file); + +- snprintf(file, sizeof(file), "%s/.k5login", pw->pw_dir); + return access(file, F_OK) == 0; + } + +diff --git a/sshd.8 b/sshd.8 +index 5c4f15b..135e290 100644 +--- a/sshd.8 ++++ b/sshd.8 +@@ -806,6 +806,10 @@ rlogin/rsh. + These files enforce GSSAPI/Kerberos authentication access control. + Further details are described in + .Xr ksu 1 . ++The location of the k5login file depends on the configuration option ++.Cm k5login_directory ++in the ++.Xr krb5.conf 5 . + .Pp + .It Pa ~/.ssh/ + This directory is the default location for all user-specific configuration diff --git a/SOURCES/openssh-7.2p2-s390-closefrom.patch b/SOURCES/openssh-7.2p2-s390-closefrom.patch new file mode 100644 index 0000000..301a523 --- /dev/null +++ b/SOURCES/openssh-7.2p2-s390-closefrom.patch @@ -0,0 +1,52 @@ +Zseries only: Leave the hardware filedescriptors open. + +All filedescriptors above 2 are getting closed when a new +sshd process to handle a new client connection is +spawned. As the process also chroot into an empty filesystem +without any device nodes, there is no chance to reopen the +files. This patch filters out the reqired fds in the +closefrom function so these are skipped in the close loop. + +Author: Harald Freudenberger + +--- + openbsd-compat/bsd-closefrom.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +--- a/openbsd-compat/bsd-closefrom.c ++++ b/openbsd-compat/bsd-closefrom.c +@@ -82,7 +82,33 @@ closefrom(int lowfd) + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) ++#ifdef __s390__ ++ { ++ /* ++ * the filedescriptors used to communicate with ++ * the device drivers to provide hardware support ++ * should survive. HF ++ */ ++ char fpath[PATH_MAX], lpath[PATH_MAX]; ++ len = snprintf(fpath, sizeof(fpath), "%s/%s", ++ fdpath, dent->d_name); ++ if (len > 0 && (size_t)len <= sizeof(fpath)) { ++ len = readlink(fpath, lpath, sizeof(lpath)); ++ if (len > 0) { ++ lpath[len] = 0; ++ if (strstr(lpath, "dev/z90crypt") ++ || strstr(lpath, "dev/zcrypt") ++ || strstr(lpath, "dev/prandom") ++ || strstr(lpath, "dev/shm/icastats")) ++ fd = -1; ++ } ++ } ++ if (fd >= 0) ++ (void) close((int) fd); ++ } ++#else + (void) close((int) fd); ++#endif + } + (void) closedir(dirp); + } else + diff --git a/SOURCES/openssh-7.2p2-x11.patch b/SOURCES/openssh-7.2p2-x11.patch new file mode 100644 index 0000000..48ce840 --- /dev/null +++ b/SOURCES/openssh-7.2p2-x11.patch @@ -0,0 +1,53 @@ +diff -up openssh-7.2p2/channels.c.x11 openssh-7.2p2/channels.c +--- openssh-7.2p2/channels.c.x11 2016-03-09 19:04:48.000000000 +0100 ++++ openssh-7.2p2/channels.c 2016-06-03 10:42:04.775164520 +0200 +@@ -3990,21 +3990,24 @@ x11_create_display_inet(int x11_display_ + } + + static int +-connect_local_xsocket_path(const char *pathname) ++connect_local_xsocket_path(const char *pathname, int len) + { + int sock; + struct sockaddr_un addr; + ++ if (len <= 0) ++ return -1; + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) + error("socket: %.100s", strerror(errno)); + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; +- strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); +- if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) ++ if (len > sizeof addr.sun_path) ++ len = sizeof addr.sun_path; ++ memcpy(addr.sun_path, pathname, len); ++ if (connect(sock, (struct sockaddr *)&addr, sizeof addr - (sizeof addr.sun_path - len) ) == 0) + return sock; + close(sock); +- error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); + return -1; + } + +@@ -4012,8 +4015,18 @@ static int + connect_local_xsocket(u_int dnr) + { + char buf[1024]; +- snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); +- return connect_local_xsocket_path(buf); ++ int len, ret; ++ len = snprintf(buf + 1, sizeof (buf) - 1, _PATH_UNIX_X, dnr); ++#ifdef linux ++ /* try abstract socket first */ ++ buf[0] = '\0'; ++ if ((ret = connect_local_xsocket_path(buf, len + 1)) >= 0) ++ return ret; ++#endif ++ if ((ret = connect_local_xsocket_path(buf + 1, len)) >= 0) ++ return ret; ++ error("connect %.100s: %.100s", buf + 1, strerror(errno)); ++ return -1; + } + + #ifdef __APPLE__ diff --git a/SOURCES/openssh-7.3p1-openssl-1.1.0.patch b/SOURCES/openssh-7.3p1-openssl-1.1.0.patch new file mode 100644 index 0000000..42ede48 --- /dev/null +++ b/SOURCES/openssh-7.3p1-openssl-1.1.0.patch @@ -0,0 +1,2688 @@ +diff -up openssh/auth-pam.c.openssl openssh/auth-pam.c +--- openssh/auth-pam.c.openssl 2017-09-26 13:19:31.662248869 +0200 ++++ openssh/auth-pam.c 2017-09-26 13:19:31.793249672 +0200 +@@ -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 { +diff -up openssh/cipher.c.openssl openssh/cipher.c +--- openssh/cipher.c.openssl 2017-09-26 13:19:31.782249605 +0200 ++++ openssh/cipher.c 2017-09-26 13:27:37.424040367 +0200 +@@ -283,7 +283,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; +@@ -301,10 +301,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: +@@ -490,7 +486,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; + } +@@ -524,14 +520,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 +diff -up openssh/ctr-cavstest.c.openssl openssh/ctr-cavstest.c +--- openssh/ctr-cavstest.c.openssl 2017-09-26 13:19:31.707249145 +0200 ++++ openssh/ctr-cavstest.c 2017-09-26 13:19:31.794249679 +0200 +@@ -144,7 +144,7 @@ int main (int argc, char *argv[]) + usage(); + } + +- SSLeay_add_all_algorithms(); ++ OpenSSL_add_all_algorithms(); + + c = cipher_by_name(algo); + if (c == NULL) { +diff -up openssh/dh.c.openssl openssh/dh.c +--- openssh/dh.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/dh.c 2017-09-26 13:19:31.794249679 +0200 +@@ -212,14 +212,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; + } +@@ -232,7 +233,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"); +@@ -243,14 +245,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; +@@ -260,9 +262,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) +@@ -271,11 +275,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; +@@ -284,15 +289,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; + } + + /* +@@ -307,8 +319,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); + } +diff -up openssh/dh.h.openssl openssh/dh.h +--- openssh/dh.h.openssl 2017-09-26 13:19:31.783249611 +0200 ++++ openssh/dh.h 2017-09-26 13:19:31.794249679 +0200 +@@ -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); + +diff -up openssh/digest-openssl.c.openssl openssh/digest-openssl.c +--- openssh/digest-openssl.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/digest-openssl.c 2017-09-26 13:19:31.795249685 +0200 +@@ -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 +133,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 +141,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 +162,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 +173,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); + } +diff -up openssh/entropy.c.openssl openssh/entropy.c +--- openssh/entropy.c.openssl 2017-09-26 13:19:31.783249611 +0200 ++++ openssh/entropy.c 2017-09-26 13:19:31.795249685 +0200 +@@ -218,7 +218,9 @@ seed_rng(void) + "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); + + /* clean the PRNG status when exiting the program */ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + atexit(RAND_cleanup); ++#endif + + #ifndef OPENSSL_PRNG_ONLY + if (RAND_status() == 1) { +diff -up openssh/gss-genr.c.openssl openssh/gss-genr.c +--- openssh/gss-genr.c.openssl 2017-09-26 13:19:31.773249550 +0200 ++++ openssh/gss-genr.c 2017-09-26 13:19:31.796249691 +0200 +@@ -99,7 +99,7 @@ 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) { +@@ -113,6 +113,7 @@ 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++) { +@@ -122,12 +123,13 @@ 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), +@@ -149,6 +151,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup + } + } + free(s); ++ EVP_MD_CTX_free(md); + gss_enc2oid[oidpos].oid = NULL; + gss_enc2oid[oidpos].encoded = NULL; + +diff -up openssh/includes.h.openssl openssh/includes.h +--- openssh/includes.h.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/includes.h 2017-09-26 13:19:31.796249691 +0200 +@@ -166,6 +166,7 @@ + + #ifdef WITH_OPENSSL + #include /* For OPENSSL_VERSION_NUMBER */ ++#include "libcrypto-compat.h" + #endif + + #include "defines.h" +diff -up openssh/kexdhc.c.openssl openssh/kexdhc.c +--- openssh/kexdhc.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/kexdhc.c 2017-09-26 13:19:31.797249697 +0200 +@@ -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) +diff -up openssh/kexdhs.c.openssl openssh/kexdhs.c +--- openssh/kexdhs.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/kexdhs.c 2017-09-26 13:19:31.797249697 +0200 +@@ -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; +diff -up openssh/kexgexc.c.openssl openssh/kexgexc.c +--- openssh/kexgexc.c.openssl 2017-09-26 13:19:31.783249611 +0200 ++++ openssh/kexgexc.c 2017-09-26 13:19:31.797249697 +0200 +@@ -95,6 +95,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"); + +@@ -119,24 +120,30 @@ 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); + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply); + r = 0; + out: + BN_clear_free(p); + BN_clear_free(g); ++ if (r != 0) { ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ } + return r; + } + +@@ -152,6 +157,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) { +@@ -214,6 +220,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, +@@ -223,8 +231,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) +diff -up openssh/kexgexs.c.openssl openssh/kexgexs.c +--- openssh/kexgexs.c.openssl 2017-09-26 13:19:31.783249611 +0200 ++++ openssh/kexgexs.c 2017-09-26 13:19:31.797249697 +0200 +@@ -72,6 +72,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 || +@@ -101,9 +102,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; + +@@ -115,6 +117,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; + } + +@@ -129,6 +135,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) { +@@ -191,6 +198,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, +@@ -199,9 +208,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; +@@ -227,7 +236,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; +diff -up openssh/kexgssc.c.openssl openssh/kexgssc.c +--- openssh/kexgssc.c.openssl 2017-09-26 13:19:31.771249537 +0200 ++++ openssh/kexgssc.c 2017-09-26 13:19:31.797249697 +0200 +@@ -59,6 +59,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); +@@ -282,13 +284,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, +@@ -297,8 +300,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 +diff -up openssh/kexgsss.c.openssl openssh/kexgsss.c +--- openssh/kexgsss.c.openssl 2017-09-26 13:19:31.771249537 +0200 ++++ openssh/kexgsss.c 2017-09-26 13:19:31.798249703 +0200 +@@ -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 */ + +@@ -127,9 +128,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(); +@@ -221,6 +223,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: +@@ -232,7 +235,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; +@@ -244,9 +247,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 + ); +@@ -270,7 +273,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) { +diff -up openssh/libcrypto-compat.c.openssl openssh/libcrypto-compat.c +--- openssh/libcrypto-compat.c.openssl 2017-09-26 13:19:31.798249703 +0200 ++++ openssh/libcrypto-compat.c 2017-09-26 13:19:31.798249703 +0200 +@@ -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_free(r->n); ++ r->n = n; ++ } ++ if (e != NULL) { ++ BN_free(r->e); ++ r->e = e; ++ } ++ if (d != NULL) { ++ BN_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_free(r->p); ++ r->p = p; ++ } ++ if (q != NULL) { ++ BN_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_free(r->dmp1); ++ r->dmp1 = dmp1; ++ } ++ if (dmq1 != NULL) { ++ BN_free(r->dmq1); ++ r->dmq1 = dmq1; ++ } ++ if (iqmp != NULL) { ++ BN_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_free(d->p); ++ d->p = p; ++ } ++ if (q != NULL) { ++ BN_free(d->q); ++ d->q = q; ++ } ++ if (g != NULL) { ++ BN_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_free(d->pub_key); ++ d->pub_key = pub_key; ++ } ++ if (priv_key != NULL) { ++ BN_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_free(dh->p); ++ dh->p = p; ++ } ++ if (q != NULL) { ++ BN_free(dh->q); ++ dh->q = q; ++ } ++ if (g != NULL) { ++ BN_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 */ +diff -up openssh/libcrypto-compat.h.openssl openssh/libcrypto-compat.h +--- openssh/libcrypto-compat.h.openssl 2017-09-26 13:19:31.798249703 +0200 ++++ openssh/libcrypto-compat.h 2017-09-26 13:19:31.798249703 +0200 +@@ -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 */ ++ +diff -up openssh/Makefile.in.openssl openssh/Makefile.in +--- openssh/Makefile.in.openssl 2017-09-26 13:19:31.784249617 +0200 ++++ openssh/Makefile.in 2017-09-26 13:19:31.798249703 +0200 +@@ -101,7 +101,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ + platform-pledge.o platform-tracing.o platform-misc.o \ +- auditstub.o ++ auditstub.o libcrypto-compat.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o +diff -up openssh/monitor.c.openssl openssh/monitor.c +--- openssh/monitor.c.openssl 2017-09-26 13:19:31.789249648 +0200 ++++ openssh/monitor.c 2017-09-26 13:19:31.799249709 +0200 +@@ -631,9 +631,12 @@ mm_answer_moduli(int sock, Buffer *m) + 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); +diff -up openssh/openbsd-compat/openssl-compat.c.openssl openssh/openbsd-compat/openssl-compat.c +--- openssh/openbsd-compat/openssl-compat.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/openbsd-compat/openssl-compat.c 2017-09-26 13:19:31.799249709 +0200 +@@ -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 + +diff -up openssh/pam_ssh_agent_auth-0.10.3/configure.ac.openssl openssh/pam_ssh_agent_auth-0.10.3/configure.ac +--- openssh/pam_ssh_agent_auth-0.10.3/configure.ac.openssl 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/configure.ac 2017-09-26 13:19:31.799249709 +0200 +@@ -1829,6 +1829,7 @@ AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ + #include + #include ++#include + int main(void) { exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1); } + ]])], + [ +diff -up openssh/regress/unittests/sshkey/test_file.c.openssl openssh/regress/unittests/sshkey/test_file.c +--- openssh/regress/unittests/sshkey/test_file.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/regress/unittests/sshkey/test_file.c 2017-09-26 13:19:31.799249709 +0200 +@@ -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); +diff -up openssh/regress/unittests/sshkey/test_sshkey.c.openssl openssh/regress/unittests/sshkey/test_sshkey.c +--- openssh/regress/unittests/sshkey/test_sshkey.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/regress/unittests/sshkey/test_sshkey.c 2017-09-26 13:19:31.800249715 +0200 +@@ -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"); +diff -up openssh/ssh.c.openssl openssh/ssh.c +--- openssh/ssh.c.openssl 2017-09-26 13:19:31.786249629 +0200 ++++ openssh/ssh.c 2017-09-26 13:19:31.800249715 +0200 +@@ -530,7 +530,9 @@ main(int ac, char **av) + sanitise_stdfd(); + + __progname = ssh_get_progname(av[0]); ++#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSLeay_add_all_algorithms(); ++#endif + if (access("/etc/system-fips", F_OK) == 0) + if (! FIPSCHECK_verify(NULL, NULL)){ + if (FIPS_mode()) +diff -up openssh/sshd.c.openssl openssh/sshd.c +--- openssh/sshd.c.openssl 2017-09-26 13:19:31.792249666 +0200 ++++ openssh/sshd.c 2017-09-26 13:19:31.801249721 +0200 +@@ -1485,7 +1485,7 @@ main(int ac, char **av) + #endif + __progname = ssh_get_progname(av[0]); + +- SSLeay_add_all_algorithms(); ++ OpenSSL_add_all_algorithms(); + if (access("/etc/system-fips", F_OK) == 0) + if (! FIPSCHECK_verify(NULL, NULL)) { + openlog(__progname, LOG_PID, LOG_AUTHPRIV); +diff -up openssh/ssh-dss.c.openssl openssh/ssh-dss.c +--- openssh/ssh-dss.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/ssh-dss.c 2017-09-26 13:19:31.801249721 +0200 +@@ -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; +@@ -137,6 +139,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 || +@@ -177,16 +180,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, +@@ -207,7 +213,9 @@ 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); + if (sigblob != NULL) { +diff -up openssh/ssh-ecdsa.c.openssl openssh/ssh-ecdsa.c +--- openssh/ssh-ecdsa.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/ssh-ecdsa.c 2017-09-26 13:19:31.801249721 +0200 +@@ -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) +@@ -119,6 +121,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 || +@@ -147,15 +150,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; +@@ -180,7 +191,9 @@ 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; + } +diff -up openssh/sshkey.c.openssl openssh/sshkey.c +--- openssh/sshkey.c.openssl 2017-09-26 13:19:31.786249629 +0200 ++++ openssh/sshkey.c 2017-09-26 13:19:31.802249728 +0200 +@@ -267,10 +267,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); +@@ -302,11 +302,17 @@ 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_RSA: { ++ const BIGNUM *d; ++ RSA_get0_key(k->rsa, NULL, NULL, &d); ++ return d != NULL; ++ } + case KEY_DSA_CERT: +- case KEY_DSA: +- return k->dsa->priv_key != NULL; ++ case KEY_DSA: { ++ const BIGNUM *priv_key; ++ DSA_get0_key(k->dsa, NULL, &priv_key); ++ return priv_key != NULL; ++ } + #ifdef OPENSSL_HAS_ECC + case KEY_ECDSA_CERT: + case KEY_ECDSA: +@@ -496,10 +501,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; + } +@@ -508,12 +509,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; + } +@@ -553,21 +548,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 */ +@@ -684,17 +668,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: +@@ -769,15 +767,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: +@@ -790,13 +794,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: +@@ -1672,15 +1681,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 +@@ -1702,13 +1728,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 */ +@@ -1907,12 +1942,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; + } +@@ -1932,12 +1977,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); +@@ -2171,26 +2238,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: +@@ -2312,12 +2406,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: +@@ -2329,10 +2428,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: +@@ -2505,43 +2609,65 @@ sshkey_private_serialize(const struct ss + 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: +@@ -2617,18 +2743,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: +@@ -2687,29 +2846,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; + } +@@ -3427,7 +3646,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: +@@ -3492,7 +3713,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; +@@ -3507,11 +3728,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; +@@ -3523,7 +3744,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; +diff -up openssh/ssh-keygen.c.openssl openssh/ssh-keygen.c +--- openssh/ssh-keygen.c.openssl 2017-09-26 13:19:31.787249636 +0200 ++++ openssh/ssh-keygen.c 2017-09-26 13:19:31.802249728 +0200 +@@ -501,40 +501,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); +@@ -642,7 +669,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"); +@@ -666,7 +693,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; +@@ -1798,6 +1825,7 @@ do_ca_sign(struct passwd *pw, int argc, + #ifdef ENABLE_PKCS11 + pkcs11_terminate(); + #endif ++ free(ca); + exit(0); + } + +diff -up openssh/sshkey.h.openssl openssh/sshkey.h +--- openssh/sshkey.h.openssl 2017-09-26 13:19:31.780249593 +0200 ++++ openssh/sshkey.h 2017-09-26 13:19:31.803249734 +0200 +@@ -199,7 +199,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 +diff -up openssh/ssh-pkcs11-client.c.openssl openssh/ssh-pkcs11-client.c +--- openssh/ssh-pkcs11-client.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/ssh-pkcs11-client.c 2017-09-26 13:19:31.803249734 +0200 +@@ -143,12 +143,16 @@ pkcs11_rsa_private_encrypt(int flen, con + static int + wrap_rsa_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); + } + +diff -up openssh/ssh-pkcs11.c.openssl openssh/ssh-pkcs11.c +--- openssh/ssh-pkcs11.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/ssh-pkcs11.c 2017-09-26 13:19:31.803249734 +0200 +@@ -67,7 +67,7 @@ struct pkcs11_key { + CK_ULONG slotidx; + CK_ULONG key_type; + int (*orig_finish)(RSA *rsa); +- RSA_METHOD rsa_method; ++ RSA_METHOD *rsa_method; + char *keyid; + int keyid_len; + char *label; +@@ -183,6 +183,7 @@ pkcs11_rsa_finish(RSA *rsa) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); + free(k11->label); ++ RSA_meth_free(k11->rsa_method); + free(k11); + } + return (rv); +@@ -326,13 +326,21 @@ pkcs11_rsa_wrap(struct pkcs11_provider * + memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); + k11->label[label_attrib->ulValueLen] = 0; + } +- 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 +468,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->module->function_list; + session = p->module->slotinfo[slotidx].session; +@@ -512,10 +521,16 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { +- rsa->n = BN_bin2bn(attribs[2].pValue, ++ BIGNUM *rsa_n, *rsa_e; ++ ++ rsa_n = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); +- rsa->e = BN_bin2bn(attribs[3].pValue, ++ rsa_e = BN_bin2bn(attribs[3].pValue, + attribs[3].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"); + } + #ifdef ENABLE_PKCS11_ECDSA + } else if (attribs[2].type == CKA_EC_PARAMS ) { +@@ -920,19 +936,19 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + } else if ((evp = X509_get_pubkey(x509)) == NULL) { + debug("X509_get_pubkey failed"); + } else { +- switch (evp->type) { ++ switch (EVP_PKEY_id(evp)) { + case EVP_PKEY_RSA: +- if (evp->pkey.rsa == NULL) ++ if (EVP_PKEY_get0_RSA(evp) == NULL) + debug("Missing RSA key"); +- else if ((rsa = RSAPublicKey_dup( +- evp->pkey.rsa)) == NULL) ++ else if ((rsa = RSAPublicKey_dup( ++ EVP_PKEY_get0_RSA(evp))) == NULL) + error("RSAPublicKey_dup failed"); + break; + case EVP_PKEY_EC: +- if (evp->pkey.ecdsa == NULL) ++ if (EVP_PKEY_get0_EC_KEY(evp) == NULL) + debug("Missing ECDSA key"); +- else if ((ecdsa = EC_KEY_dup( +- evp->pkey.ecdsa)) == NULL) ++ else if ((ecdsa = EC_KEY_dup( ++ EVP_PKEY_get0_EC_KEY(evp))) == NULL) + error("EC_KEY_dup failed"); + break; + default: +@@ -538,7 +551,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + } + key = NULL; + if (rsa || ecdsa) { +- 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], &attribs[1], rsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); +diff -up openssh/ssh-rsa.c.openssl openssh/ssh-rsa.c +--- openssh/ssh-rsa.c.openssl 2017-09-19 06:26:43.000000000 +0200 ++++ openssh/ssh-rsa.c 2017-09-26 13:19:31.803249734 +0200 +@@ -78,38 +78,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; + } +@@ -136,7 +145,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) +@@ -210,7 +219,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) +--- openssh-7.6p1/configure.ac.openssl 2017-10-04 15:47:48.440672023 +0200 ++++ openssh-7.6p1/configure.ac 2017-10-04 15:47:23.327531495 +0200 +@@ -2730,6 +2730,7 @@ + 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")]) diff --git a/SOURCES/openssh-7.3p1-x11-max-displays.patch b/SOURCES/openssh-7.3p1-x11-max-displays.patch new file mode 100644 index 0000000..94dac8f --- /dev/null +++ b/SOURCES/openssh-7.3p1-x11-max-displays.patch @@ -0,0 +1,213 @@ +diff -up openssh-7.4p1/channels.c.x11max openssh-7.4p1/channels.c +--- openssh-7.4p1/channels.c.x11max 2016-12-23 15:46:32.071506625 +0100 ++++ openssh-7.4p1/channels.c 2016-12-23 15:46:32.139506636 +0100 +@@ -152,8 +152,8 @@ static int all_opens_permitted = 0; + #define FWD_PERMIT_ANY_HOST "*" + + /* -- X11 forwarding */ +-/* Maximum number of fake X11 displays to try. */ +-#define MAX_DISPLAYS 1000 ++/* Minimum port number for X11 forwarding */ ++#define X11_PORT_MIN 6000 + + /* Per-channel callback for pre/post select() actions */ + typedef void chan_fn(struct ssh *, Channel *c, +@@ -4228,7 +4228,7 @@ channel_send_window_changes(void) + */ + int + x11_create_display_inet(struct ssh *ssh, int x11_display_offset, +- int x11_use_localhost, int single_connection, ++ int x11_use_localhost, int x11_max_displays, int single_connection, + u_int *display_numberp, int **chanids) + { + Channel *nc = NULL; +@@ -4240,10 +4241,15 @@ x11_create_display_inet(int x11_display_ + if (chanids == NULL) + return -1; + ++ /* Try to bind ports starting at 6000+X11DisplayOffset */ ++ x11_max_displays = x11_max_displays + x11_display_offset; ++ + for (display_number = x11_display_offset; +- display_number < MAX_DISPLAYS; ++ display_number < x11_max_displays; + display_number++) { +- port = 6000 + display_number; ++ port = X11_PORT_MIN + display_number; ++ if (port < X11_PORT_MIN) /* overflow */ ++ break; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; +@@ -4295,7 +4301,7 @@ x11_create_display_inet(int x11_display_ + if (num_socks > 0) + break; + } +- if (display_number >= MAX_DISPLAYS) { ++ if (display_number >= x11_max_displays || port < X11_PORT_MIN ) { + error("Failed to allocate internet-domain X11 display socket."); + return -1; + } +@@ -4441,7 +4447,7 @@ x11_connect_display(void) + memset(&hints, 0, sizeof(hints)); + hints.ai_family = ssh->chanctxt->IPv4or6; + hints.ai_socktype = SOCK_STREAM; +- snprintf(strport, sizeof strport, "%u", 6000 + display_number); ++ snprintf(strport, sizeof strport, "%u", X11_PORT_MIN + display_number); + if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host. (%s)", buf, + ssh_gai_strerror(gaierr)); +@@ -4457,7 +4463,7 @@ x11_connect_display(void) + /* Connect it to the display. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug2("connect %.100s port %u: %.100s", buf, +- 6000 + display_number, strerror(errno)); ++ X11_PORT_MIN + display_number, strerror(errno)); + close(sock); + continue; + } +@@ -4466,8 +4472,8 @@ x11_connect_display(void) + } + freeaddrinfo(aitop); + if (!ai) { +- error("connect %.100s port %u: %.100s", buf, +- 6000 + display_number, strerror(errno)); ++ error("connect %.100s port %u: %.100s", buf, ++ X11_PORT_MIN + display_number, strerror(errno)); + return -1; + } + set_nodelay(sock); +diff -up openssh-7.4p1/channels.h.x11max openssh-7.4p1/channels.h +--- openssh-7.4p1/channels.h.x11max 2016-12-19 05:59:41.000000000 +0100 ++++ openssh-7.4p1/channels.h 2016-12-23 15:46:32.139506636 +0100 +@@ -293,7 +293,7 @@ int permitopen_port(const char *); + + void channel_set_x11_refuse_time(struct ssh *, u_int); + int x11_connect_display(struct ssh *); +-int x11_create_display_inet(struct ssh *, int, int, int, u_int *, int **); ++int x11_create_display_inet(struct ssh *, int, int, int, int, u_int *, int **); + void x11_request_forwarding_with_spoofing(struct ssh *, int, + const char *, const char *, const char *, int); + +diff -up openssh-7.4p1/servconf.c.x11max openssh-7.4p1/servconf.c +--- openssh-7.4p1/servconf.c.x11max 2016-12-23 15:46:32.133506635 +0100 ++++ openssh-7.4p1/servconf.c 2016-12-23 15:47:27.320519121 +0100 +@@ -95,6 +95,7 @@ initialize_server_options(ServerOptions + options->print_lastlog = -1; + options->x11_forwarding = -1; + options->x11_display_offset = -1; ++ options->x11_max_displays = -1; + options->x11_use_localhost = -1; + options->permit_tty = -1; + options->permit_user_rc = -1; +@@ -243,6 +244,8 @@ fill_default_server_options(ServerOption + options->x11_forwarding = 0; + if (options->x11_display_offset == -1) + options->x11_display_offset = 10; ++ if (options->x11_max_displays == -1) ++ options->x11_max_displays = DEFAULT_MAX_DISPLAYS; + if (options->x11_use_localhost == -1) + options->x11_use_localhost = 1; + if (options->xauth_location == NULL) +@@ -419,7 +422,7 @@ typedef enum { + sPasswordAuthentication, sKbdInteractiveAuthentication, + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, +- sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, ++ sX11Forwarding, sX11DisplayOffset, sX11MaxDisplays, sX11UseLocalhost, + sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, + sPermitUserEnvironment, sAllowTcpForwarding, sCompression, + sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, +@@ -540,6 +543,7 @@ static struct { + { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, + { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, + { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, ++ { "x11maxdisplays", sX11MaxDisplays, SSHCFG_ALL }, + { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, + { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, + { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, +@@ -1316,6 +1320,10 @@ process_server_config_line(ServerOptions + *intptr = value; + break; + ++ case sX11MaxDisplays: ++ intptr = &options->x11_max_displays; ++ goto parse_int; ++ + case sX11UseLocalhost: + intptr = &options->x11_use_localhost; + goto parse_flag; +@@ -2063,6 +2071,7 @@ copy_set_server_options(ServerOptions *d + M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink); + M_CP_INTOPT(x11_display_offset); + M_CP_INTOPT(x11_forwarding); ++ M_CP_INTOPT(x11_max_displays); + M_CP_INTOPT(x11_use_localhost); + M_CP_INTOPT(permit_tty); + M_CP_INTOPT(permit_user_rc); +@@ -2315,6 +2324,7 @@ dump_config(ServerOptions *o) + #endif + dump_cfg_int(sLoginGraceTime, o->login_grace_time); + dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); ++ dump_cfg_int(sX11MaxDisplays, o->x11_max_displays); + dump_cfg_int(sMaxAuthTries, o->max_authtries); + dump_cfg_int(sMaxSessions, o->max_sessions); + dump_cfg_int(sClientAliveInterval, o->client_alive_interval); +diff -up openssh-7.4p1/servconf.h.x11max openssh-7.4p1/servconf.h +--- openssh-7.4p1/servconf.h.x11max 2016-12-23 15:46:32.133506635 +0100 ++++ openssh-7.4p1/servconf.h 2016-12-23 15:46:32.140506636 +0100 +@@ -55,6 +55,7 @@ + + #define DEFAULT_AUTH_FAIL_MAX 6 /* Default for MaxAuthTries */ + #define DEFAULT_SESSIONS_MAX 10 /* Default for MaxSessions */ ++#define DEFAULT_MAX_DISPLAYS 1000 /* Maximum number of fake X11 displays to try. */ + + /* Magic name for internal sftp-server */ + #define INTERNAL_SFTP_NAME "internal-sftp" +@@ -85,6 +86,7 @@ typedef struct { + int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ + int x11_display_offset; /* What DISPLAY number to start + * searching at */ ++ int x11_max_displays; /* Number of displays to search */ + int x11_use_localhost; /* If true, use localhost for fake X11 server. */ + char *xauth_location; /* Location of xauth program */ + int permit_tty; /* If false, deny pty allocation */ +diff -up openssh-7.4p1/session.c.x11max openssh-7.4p1/session.c +--- openssh-7.4p1/session.c.x11max 2016-12-23 15:46:32.136506636 +0100 ++++ openssh-7.4p1/session.c 2016-12-23 15:46:32.141506636 +0100 +@@ -2518,8 +2518,9 @@ session_setup_x11fwd(Session *s) + return 0; + } + if (x11_create_display_inet(ssh, options.x11_display_offset, +- options.x11_use_localhost, s->single_connection, +- &s->display_number, &s->x11_chanids) == -1) { ++ options.x11_use_localhost, options.x11_max_displays, ++ s->single_connection, &s->display_number, ++ &s->x11_chanids) == -1) { + debug("x11_create_display_inet failed."); + return 0; + } +diff -up openssh-7.4p1/sshd_config.5.x11max openssh-7.4p1/sshd_config.5 +--- openssh-7.4p1/sshd_config.5.x11max 2016-12-23 15:46:32.134506635 +0100 ++++ openssh-7.4p1/sshd_config.5 2016-12-23 15:46:32.141506636 +0100 +@@ -1133,6 +1133,7 @@ Available keywords are + .Cm StreamLocalBindUnlink , + .Cm TrustedUserCAKeys , + .Cm X11DisplayOffset , ++.Cm X11MaxDisplays , + .Cm X11Forwarding + and + .Cm X11UseLocalHost . +@@ -1566,6 +1567,12 @@ Specifies the first display number avail + X11 forwarding. + This prevents sshd from interfering with real X11 servers. + The default is 10. ++.It Cm X11MaxDisplays ++Specifies the maximum number of displays available for ++.Xr sshd 8 Ns 's ++X11 forwarding. ++This prevents sshd from exhausting local ports. ++The default is 1000. + .It Cm X11Forwarding + Specifies whether X11 forwarding is permitted. + The argument must be diff --git a/SOURCES/openssh-7.4p1-systemd.patch b/SOURCES/openssh-7.4p1-systemd.patch new file mode 100644 index 0000000..4f9e58a --- /dev/null +++ b/SOURCES/openssh-7.4p1-systemd.patch @@ -0,0 +1,98 @@ +commit 0e22b79bfde45a7cf7a2e51a68ec11c4285f3b31 +Author: Jakub Jelen +Date: Mon Nov 21 15:04:06 2016 +0100 + + systemd stuff + +diff --git a/configure.ac b/configure.ac +index 2ffc369..162ce92 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -4265,6 +4265,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 +@@ -5097,6 +5121,7 @@ echo " libedit support: $LIBEDIT_MSG" + 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" +diff --git a/contrib/sshd.service b/contrib/sshd.service +new file mode 100644 +index 0000000..e0d4923 +--- /dev/null ++++ b/contrib/sshd.service +@@ -0,0 +1,16 @@ ++[Unit] ++Description=OpenSSH server daemon ++Documentation=man:sshd(8) man:sshd_config(5) ++After=network.target ++ ++[Service] ++Type=notify ++ExecStart=/usr/sbin/sshd -D $OPTIONS ++ExecReload=/bin/kill -HUP $MAINPID ++KillMode=process ++Restart=on-failure ++RestartPreventExitStatus=255 ++ ++[Install] ++WantedBy=multi-user.target ++ +diff --git a/sshd.c b/sshd.c +index 816611c..b8b9d13 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -85,6 +85,10 @@ + #include + #endif + ++#ifdef HAVE_SYSTEMD ++#include ++#endif ++ + #include "xmalloc.h" + #include "ssh.h" + #include "ssh2.h" +@@ -1888,6 +1892,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/SOURCES/openssh-7.5p1-gssapi-kex-with-ec.patch b/SOURCES/openssh-7.5p1-gssapi-kex-with-ec.patch new file mode 100644 index 0000000..5f2763a --- /dev/null +++ b/SOURCES/openssh-7.5p1-gssapi-kex-with-ec.patch @@ -0,0 +1,1377 @@ +From 6ff8f667f792052fd47689c3e421fcd6ddca1cd0 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 25 Aug 2017 19:15:48 +0200 +Subject: [PATCH 1/3] GSSAPI Key exchange methods with DH and SHA2 + +--- + gss-genr.c | 10 ++++++++++ + kex.c | 2 ++ + kex.h | 2 ++ + kexgssc.c | 6 ++++++ + kexgsss.c | 6 ++++++ + monitor.c | 2 ++ + regress/kextype.sh | 4 +++- + regress/rekey.sh | 8 ++++++-- + ssh-gss.h | 2 ++ + ssh_config.5 | 4 +++- + sshconnect2.c | 2 ++ + sshd.c | 2 ++ + sshd_config.5 | 4 +++- + 13 files changed, 49 insertions(+), 5 deletions(-) + +diff --git a/gss-genr.c b/gss-genr.c +index dc63682d..c6eff3d7 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -183,6 +183,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; + break; ++ case KEX_GSS_GRP14_SHA256: ++ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; ++ break; ++ case KEX_GSS_GRP16_SHA512: ++ if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; ++ break; + case KEX_GSS_GEX_SHA1: + if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) + return GSS_C_NO_OID; +diff --git a/kex.c b/kex.c +index 63e028fa..e798fecb 100644 +--- a/kex.c ++++ b/kex.c +@@ -112,6 +112,8 @@ static const struct kexalg kexalgs[] = { + { 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 }, ++ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, + #endif + { NULL, -1, -1, -1}, + }; +diff --git a/kex.h b/kex.h +index 8a2b37c5..f27958ae 100644 +--- a/kex.h ++++ b/kex.h +@@ -102,6 +102,8 @@ enum kex_exchange { + #ifdef GSSAPI + KEX_GSS_GRP1_SHA1, + KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GRP14_SHA256, ++ KEX_GSS_GRP16_SHA512, + KEX_GSS_GEX_SHA1, + #endif + KEX_MAX +diff --git a/kexgssc.c b/kexgssc.c +index 132df8b5..ed23f06d 100644 +--- a/kexgssc.c ++++ b/kexgssc.c +@@ -88,8 +88,12 @@ kexgss_client(struct ssh *ssh) { + dh = dh_new_group1(); + break; + case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: + dh = dh_new_group14(); + break; ++ case KEX_GSS_GRP16_SHA512: ++ dh = dh_new_group16(); ++ break; + case KEX_GSS_GEX_SHA1: + debug("Doing group exchange\n"); + nbits = dh_estimate(ssh->kex->we_need * 8); +@@ -272,6 +276,8 @@ kexgss_client(struct ssh *ssh) { + switch (ssh->kex->kex_type) { + case KEX_GSS_GRP1_SHA1: + case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: + 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), +diff --git a/kexgsss.c b/kexgsss.c +index 82a715cc..b7da8823 100644 +--- a/kexgsss.c ++++ b/kexgsss.c +@@ -104,8 +104,12 @@ kexgss_server(struct ssh *ssh) + dh = dh_new_group1(); + break; + case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: + dh = dh_new_group14(); + break; ++ case KEX_GSS_GRP16_SHA512: ++ dh = dh_new_group16(); ++ break; + case KEX_GSS_GEX_SHA1: + debug("Doing group exchange"); + packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); +@@ -223,6 +227,8 @@ kexgss_server(struct ssh *ssh) + switch (ssh->kex->kex_type) { + case KEX_GSS_GRP1_SHA1: + case KEX_GSS_GRP14_SHA1: ++ case KEX_GSS_GRP14_SHA256: ++ case KEX_GSS_GRP16_SHA512: + 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), +diff --git a/monitor.c b/monitor.c +index 17046936..d6bc7ac7 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1648,6 +1648,8 @@ monitor_apply_keystate(struct monitor *pmonitor) + if (options.gss_keyex) { + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; + } + #endif +diff --git a/regress/kextype.sh b/regress/kextype.sh +index 780362ca..45f4f16d 100644 +--- a/regress/kextype.sh ++++ b/regress/kextype.sh +@@ -14,7 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy + + 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 ++ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ ++ $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ ++ $k = "gss-group16-sha512-" ]; then + continue + fi + verbose "kex $k" +diff --git a/regress/rekey.sh b/regress/rekey.sh +index 9fbe9b38..a2921bef 100644 +--- a/regress/rekey.sh ++++ b/regress/rekey.sh +@@ -38,7 +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 ++ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ ++ $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ ++ $i = "gss-group16-sha512-" ]; then + continue + fi + opts="$opts KexAlgorithms=$i" +@@ -59,7 +61,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 ++ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ ++ $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ ++ $kex = "gss-group16-sha512-" ]; then + continue + fi + verbose "client rekey $c $kex" +diff --git a/ssh-gss.h b/ssh-gss.h +index 6b6adb2b..7bf8d75e 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -70,6 +70,8 @@ + #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_GRP14_SHA256_ID "gss-group14-sha256-" ++#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" + #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" + + #define GSS_KEX_DEFAULT_KEX \ +diff --git a/ssh_config.5 b/ssh_config.5 +index 6b24649e..3d6da510 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -760,7 +760,9 @@ key exchange. Possible values are + .Bd -literal -offset 3n + gss-gex-sha1-, + gss-group1-sha1-, +-gss-group14-sha1- ++gss-group14-sha1-, ++gss-group14-sha256-, ++gss-group16-sha512- + .Ed + .Pp + The default is +diff --git a/sshconnect2.c b/sshconnect2.c +index 8db98293..5d6b8be0 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -253,6 +253,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + if (options.gss_keyex) { + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; + } + #endif +diff --git a/sshd.c b/sshd.c +index 895df26f..e4c879a2 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -2244,6 +2244,8 @@ do_ssh2_kex(void) + if (options.gss_keyex) { + kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; + kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; ++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; + } + #endif +diff --git a/sshd_config.5 b/sshd_config.5 +index bf81f6af..0793418b 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -675,7 +675,9 @@ key exchange. Possible values are + .Bd -literal -offset 3n + gss-gex-sha1-, + gss-group1-sha1-, +-gss-group14-sha1- ++gss-group14-sha1-, ++gss-group14-sha256-, ++gss-group16-sha512- + .Ed + .Pp + The default is +-- +2.13.5 + + +From 7d56144903fc625c33da7fabf103f4f6bba4d43a Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 29 Aug 2017 15:32:14 +0200 +Subject: [PATCH 2/3] GSSAPI Key exchange using ECDH and SHA2 + +--- + gss-genr.c | 10 ++ + kex.c | 3 + + kex.h | 4 + + kexgssc.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++- + kexgsss.c | 333 +++++++++++++++++++++++++++++++++++++++++++++ + monitor.c | 5 +- + regress/kextype.sh | 1 + + regress/rekey.sh | 2 + + ssh-gss.h | 2 + + ssh_config.5 | 4 +- + sshconnect2.c | 2 + + sshd.c | 2 + + sshd_config.5 | 4 +- + 13 files changed, 754 insertions(+), 10 deletions(-) + +diff --git a/gss-genr.c b/gss-genr.c +index c6eff3d7..22040244 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -198,6 +198,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { + return GSS_C_NO_OID; + name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; + break; ++ case KEX_GSS_NISTP256_SHA256: ++ if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) ++ return GSS_C_NO_OID; ++ name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; ++ break; + default: + return GSS_C_NO_OID; + } +diff --git a/kex.c b/kex.c +index e798fecb..bdeeada9 100644 +--- a/kex.c ++++ b/kex.c +@@ -114,6 +114,9 @@ static const struct kexalg kexalgs[] = { + { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, + { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, ++ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, ++ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, ++ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + #endif + { NULL, -1, -1, -1}, + }; +diff --git a/kex.h b/kex.h +index f27958ae..7def8561 100644 +--- a/kex.h ++++ b/kex.h +@@ -105,6 +105,8 @@ enum kex_exchange { + KEX_GSS_GRP14_SHA256, + KEX_GSS_GRP16_SHA512, + KEX_GSS_GEX_SHA1, ++ KEX_GSS_NISTP256_SHA256, ++ KEX_GSS_C25519_SHA256, + #endif + KEX_MAX + }; +@@ -211,6 +213,8 @@ int kexecdh_server(struct ssh *); + int kexc25519_client(struct ssh *); + int kexc25519_server(struct ssh *); + #ifdef GSSAPI ++int kexecgss_client(struct ssh *); ++int kexecgss_server(struct ssh *); + int kexgss_client(struct ssh *); + int kexgss_server(struct ssh *); + #endif +diff --git a/kexgssc.c b/kexgssc.c +index ed23f06d..bdb3109a 100644 +--- a/kexgssc.c ++++ b/kexgssc.c +@@ -43,6 +43,7 @@ + #include "packet.h" + #include "dh.h" + #include "digest.h" ++#include "ssherr.h" + + #include "ssh-gss.h" + +@@ -52,7 +53,7 @@ kexgss_client(struct ssh *ssh) { + 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; ++ u_int klen, kout, slen = 0, packet_len; + DH *dh; + BIGNUM *dh_server_pub = NULL; + BIGNUM *shared_secret = NULL; +@@ -201,20 +202,20 @@ kexgss_client(struct ssh *ssh) { + 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; ++ recv_tok.value = packet_get_string(&packet_len); ++ recv_tok.length = packet_len; + 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; ++ msg_tok.value = packet_get_string(&packet_len); ++ msg_tok.length = packet_len; + + /* Is there a token included? */ + if (packet_get_char()) { + recv_tok.value= +- packet_get_string(&strlen); +- recv_tok.length = strlen; ++ packet_get_string(&packet_len); ++ recv_tok.length = packet_len; + /* If we're already complete - protocol error */ + if (maj_status == GSS_S_COMPLETE) + packet_disconnect("Protocol error: received token when complete"); +@@ -344,4 +345,382 @@ kexgss_client(struct ssh *ssh) { + return kex_send_newkeys(ssh); + } + ++int ++kexecgss_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 = 0, slen = 0, packet_len; ++ u_char *server_pub = NULL; ++ u_int server_pub_len = 0; ++ BIGNUM *shared_secret = NULL; ++ u_char *kbuf = NULL; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ char *lang; ++ int type = 0; ++ int first = 1; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ const EC_GROUP *group = NULL; ++ const EC_POINT *public_key; ++ struct sshbuf *Q_C = NULL; ++ struct kex *kex = ssh->kex; ++ EC_POINT *server_public = NULL; ++ struct sshbuf *c25519_shared_secret = NULL; ++ int r; ++ ++ /* Initialise our GSSAPI world */ ++ ssh_gssapi_build_ctx(&ctxt); ++ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) ++ == GSS_C_NO_OID) ++ fatal("Couldn't identify host exchange"); ++ ++ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) ++ fatal("Couldn't import hostname"); ++ ++ if (kex->gss_client && ++ ssh_gssapi_client_identity(ctxt, kex->gss_client)) ++ fatal("Couldn't acquire client credentials"); ++ ++ if ((Q_C = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if ((kex->ec_client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if (EC_KEY_generate_key(kex->ec_client_key) != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ group = EC_KEY_get0_group(kex->ec_client_key); ++ public_key = EC_KEY_get0_public_key(kex->ec_client_key); ++#ifdef DEBUG_KEXECDH ++ fputs("client private key:\n", stderr); ++ sshkey_dump_ec_key(kex->ec_client_key); ++#endif ++ ++ sshbuf_put_ec(Q_C, public_key, group); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ kexc25519_keygen(kex->c25519_client_key, kex->c25519_client_pubkey); ++#ifdef DEBUG_KEXECDH ++ dump_digest("client private key:", kex->c25519_client_key, ++ sizeof(kex->c25519_client_key)); ++#endif ++ ++ if ((r = sshbuf_put_string(Q_C, kex->c25519_client_pubkey, ++ sizeof(kex->c25519_client_pubkey))) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ /* Step 2 - call GSS_Init_sec_context() */ ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ 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) { ++ const u_char * ptr; ++ size_t len; ++ ++ packet_start(SSH2_MSG_KEXGSS_INIT); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ sshbuf_get_string_direct(Q_C, &ptr, &len); ++ packet_put_string(ptr, len); ++ 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(&packet_len); ++ recv_tok.length = packet_len; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ server_pub = packet_get_string(&server_pub_len); ++ msg_tok.value = packet_get_string(&packet_len); ++ msg_tok.length = packet_len; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&packet_len); ++ recv_tok.length = packet_len; ++ /* 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); ++ 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"); ++ ++ /* 7. C verifies that the key Q_S is valid */ ++ /* 8. C computes shared secret */ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if (server_pub_len != 65) ++ fatal("The received NIST-P256 key did not match" ++ "expected length (expected 65, got %d)", server_pub_len); ++ ++ if (server_pub[0] != POINT_CONVERSION_UNCOMPRESSED) ++ fatal("The received NIST-P256 key does not have first octet 0x04"); ++ ++ if ((server_public = EC_POINT_new(group)) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if (!EC_POINT_oct2point(group, server_public, server_pub, ++ server_pub_len, NULL)) ++ fatal("Can not decode received NIST-P256 client key"); ++#ifdef DEBUG_KEXECDH ++ fputs("server public key:\n", stderr); ++ sshkey_dump_ec_point(group, server_public); ++#endif ++ ++ if (sshkey_ec_validate_public(group, server_public) != 0) { ++ sshpkt_disconnect(ssh, "invalid client public key"); ++ r = SSH_ERR_MESSAGE_INCOMPLETE; ++ goto out; ++ } ++ ++ if (!EC_POINT_is_on_curve(group, server_public, NULL)) ++ fatal("Received NIST-P256 client key is not on curve"); ++ ++ /* Calculate shared_secret */ ++ klen = (EC_GROUP_get_degree(group) + 7) / 8; ++ if ((kbuf = malloc(klen)) == NULL || ++ (shared_secret = BN_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if (ECDH_compute_key(kbuf, klen, server_public, ++ kex->ec_client_key, NULL) != (int)klen || ++ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++#ifdef DEBUG_KEXECDH ++ dump_digest("shared secret", kbuf, klen); ++#endif ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if (server_pub_len != 32) ++ fatal("The received curve25519 key did not match" ++ "expected length (expected 32, got %d)", server_pub_len); ++ ++ if (server_pub[server_pub_len-1] & 0x80) ++ fatal("The received key has MSB of last octet set!"); ++#ifdef DEBUG_KEXECDH ++ dump_digest("server public key:", server_pub, CURVE25519_SIZE); ++#endif ++ ++ /* generate shared secret */ ++ if ((c25519_shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if ((r = kexc25519_shared_key(kex->c25519_client_key, ++ server_pub, c25519_shared_secret)) < 0) ++ goto out; ++ ++ /* if all octets of the shared secret are zero octets, ++ * is already checked in kexc25519_shared_key() */ ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ hashlen = sizeof(hash); ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ kex_ecdh_hash( ++ kex->hash_alg, ++ group, ++ kex->client_version_string, ++ kex->server_version_string, ++ sshbuf_ptr(kex->my), sshbuf_len(kex->my), ++ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ EC_KEY_get0_public_key(kex->ec_client_key), ++ server_public, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ kex_c25519_hash( ++ kex->hash_alg, ++ kex->client_version_string, kex->server_version_string, ++ sshbuf_ptr(kex->my), sshbuf_len(kex->my), ++ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ kex->c25519_client_pubkey, server_pub, ++ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, 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); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* Finally derive the keys and send them */ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) ++ goto out; ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) ++ goto out; ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ r = kex_send_newkeys(ssh); ++out: ++ free(serverhostkey); ++ explicit_bzero(hash, sizeof(hash)); ++ sshbuf_free(Q_C); ++ if (server_pub) ++ free(server_pub); ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if (kex->ec_client_key) { ++ EC_KEY_free(kex->ec_client_key); ++ kex->ec_client_key = NULL; ++ } ++ if (server_public) ++ EC_POINT_clear_free(server_public); ++ if (kbuf) { ++ explicit_bzero(kbuf, klen); ++ free(kbuf); ++ } ++ if (shared_secret) ++ BN_clear_free(shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); ++ sshbuf_free(c25519_shared_secret); ++ break; ++ } ++ return r; ++} + #endif /* GSSAPI */ +diff --git a/kexgsss.c b/kexgsss.c +index b7da8823..a7c42803 100644 +--- a/kexgsss.c ++++ b/kexgsss.c +@@ -46,6 +46,7 @@ + #include "servconf.h" + #include "ssh-gss.h" + #include "digest.h" ++#include "ssherr.h" + + extern ServerOptions options; + +@@ -303,4 +304,338 @@ kexgss_server(struct ssh *ssh) + ssh_gssapi_rekey_creds(); + return 0; + } ++ ++int ++kexecgss_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 = 0; ++ u_char *kbuf = NULL; ++ BIGNUM *shared_secret = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ size_t hashlen; ++ u_char *client_pub = NULL; ++ u_int client_pub_len = 0; ++ const EC_GROUP *group = NULL; ++ EC_POINT *client_public = NULL; ++ EC_KEY *server_key = NULL; ++ const EC_POINT *public_key; ++ u_char c25519_server_key[CURVE25519_SIZE]; ++ u_char c25519_server_pubkey[CURVE25519_SIZE]; ++ struct sshbuf *c25519_shared_secret = NULL; ++ struct sshbuf *Q_S; ++ struct kex *kex = ssh->kex; ++ int r; ++ ++ /* 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__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, 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"); ++ ++ if ((Q_S = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ /* 5. S generates an ephemeral key pair (do the allocations early) */ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if (EC_KEY_generate_key(server_key) != 1) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ group = EC_KEY_get0_group(server_key); ++ public_key = EC_KEY_get0_public_key(server_key); ++ ++ sshbuf_put_ec(Q_S, public_key, group); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ kexc25519_keygen(c25519_server_key, c25519_server_pubkey); ++#ifdef DEBUG_KEXECDH ++ dump_digest("server private key:", c25519_server_key, ++ sizeof(c25519_server_key)); ++#endif ++ if ((r = sshbuf_put_string(Q_S, c25519_server_pubkey, ++ sizeof(c25519_server_pubkey))) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ do { ++ debug("Wait SSH2_MSG_GSSAPI_INIT"); ++ type = packet_read(); ++ switch(type) { ++ case SSH2_MSG_KEXGSS_INIT: ++ if (client_pub != NULL) ++ fatal("Received KEXGSS_INIT after initialising"); ++ recv_tok.value = packet_get_string(&slen); ++ recv_tok.length = slen; ++ ++ client_pub = packet_get_string(&client_pub_len); ++ ++ /* 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 (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"); ++ ++ /* 3. S verifies that the (client) key is valid */ ++ /* calculate shared secret */ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if (client_pub_len != 65) ++ fatal("The received NIST-P256 key did not match" ++ "expected length (expected 65, got %d)", client_pub_len); ++ ++ if (client_pub[0] != POINT_CONVERSION_UNCOMPRESSED) ++ fatal("The received NIST-P256 key does not have first octet 0x04"); ++ ++ if ((client_public = EC_POINT_new(group)) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ ++ if (!EC_POINT_oct2point(group, client_public, client_pub, ++ client_pub_len, NULL)) ++ fatal("Can not decode received NIST-P256 client key"); ++ ++ if (sshkey_ec_validate_public(group, client_public) != 0) { ++ sshpkt_disconnect(ssh, "invalid client public key"); ++ r = SSH_ERR_MESSAGE_INCOMPLETE; ++ goto out; ++ } ++ ++ if (!EC_POINT_is_on_curve(group, client_public, NULL)) ++ fatal("Received NIST-P256 client key is not on curve"); ++ ++ /* Calculate shared_secret */ ++ klen = (EC_GROUP_get_degree(group) + 7) / 8; ++ if ((kbuf = malloc(klen)) == NULL || ++ (shared_secret = BN_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if (ECDH_compute_key(kbuf, klen, client_public, ++ server_key, NULL) != (int)klen || ++ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { ++ r = SSH_ERR_LIBCRYPTO_ERROR; ++ goto out; ++ } ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if (client_pub_len != 32) ++ fatal("The received curve25519 key did not match" ++ "expected length (expected 32, got %d)", client_pub_len); ++ ++ if (client_pub[client_pub_len-1] & 0x80) ++ fatal("The received key has MSB of last octet set!"); ++ ++ /* generate shared secret */ ++ if ((c25519_shared_secret = sshbuf_new()) == NULL) { ++ r = SSH_ERR_ALLOC_FAIL; ++ goto out; ++ } ++ if ((r = kexc25519_shared_key(c25519_server_key, ++ client_pub, c25519_shared_secret)) < 0) ++ goto out; ++ ++ /* if all octets of the shared secret are zero octets, ++ * is already checked in kexc25519_shared_key() */ ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ hashlen = sizeof(hash); ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ kex_ecdh_hash( ++ kex->hash_alg, ++ group, ++ kex->client_version_string, ++ kex->server_version_string, ++ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), ++ sshbuf_ptr(kex->my), sshbuf_len(kex->my), ++ NULL, 0, ++ client_public, ++ EC_KEY_get0_public_key(server_key), ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ kex_c25519_hash( ++ kex->hash_alg, ++ kex->client_version_string, kex->server_version_string, ++ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), ++ sshbuf_ptr(kex->my), sshbuf_len(kex->my), ++ NULL, 0, ++ client_pub, c25519_server_pubkey, ++ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, 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); ++ { ++ const u_char *ptr; ++ size_t len; ++ if ((r = sshbuf_get_string_direct(Q_S, &ptr, &len)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ packet_put_string(ptr, len); ++ } ++ 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); ++ ++ /* Finally derive the keys and send them */ ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) ++ goto out; ++ break; ++ case KEX_GSS_C25519_SHA256: ++ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) ++ goto out; ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ if ((r = kex_send_newkeys(ssh)) != 0) ++ goto out; ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++out: ++ explicit_bzero(hash, sizeof(hash)); ++ if (Q_S) ++ sshbuf_free(Q_S); ++ if (client_pub) ++ free(client_pub); ++ switch (kex->kex_type) { ++ case KEX_GSS_NISTP256_SHA256: ++ if (server_key) ++ EC_KEY_free(server_key); ++ if (kbuf) { ++ explicit_bzero(kbuf, klen); ++ free(kbuf); ++ } ++ if (shared_secret) ++ BN_clear_free(shared_secret); ++ break; ++ case KEX_GSS_C25519_SHA256: ++ explicit_bzero(c25519_server_key, sizeof(c25519_server_key)); ++ sshbuf_free(c25519_shared_secret); ++ break; ++ } ++ return r; ++} + #endif /* GSSAPI */ +diff --git a/monitor.c b/monitor.c +index d6bc7ac7..b11616c8 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1651,6 +1651,8 @@ monitor_apply_keystate(struct monitor *pmonitor) + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; + } + #endif + kex->load_host_public_key=&get_hostkey_public_by_type; +@@ -1867,7 +1869,8 @@ mm_answer_gss_sign(int socket, Buffer *m) + + 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) ++ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ ++ if (data.length != 20 && data.length != 32 && data.length != 64) + fatal("%s: data length incorrect: %d", __func__, + (int) data.length); + +diff --git a/regress/kextype.sh b/regress/kextype.sh +index 45f4f16d..d5b4a713 100644 +--- a/regress/kextype.sh ++++ b/regress/kextype.sh +@@ -15,6 +15,7 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy + 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-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ + $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ + $k = "gss-group16-sha512-" ]; then + continue +diff --git a/regress/rekey.sh b/regress/rekey.sh +index a2921bef..b118c6c8 100644 +--- a/regress/rekey.sh ++++ b/regress/rekey.sh +@@ -39,6 +39,7 @@ 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-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ + $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ + $i = "gss-group16-sha512-" ]; then + continue +@@ -62,6 +63,7 @@ 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-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ + $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ + $kex = "gss-group16-sha512-" ]; then + continue +diff --git a/ssh-gss.h b/ssh-gss.h +index 7bf8d75e..1f73721d 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -73,6 +73,8 @@ + #define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" + #define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" + #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" ++#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" ++#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" + + #define GSS_KEX_DEFAULT_KEX \ + KEX_GSS_GEX_SHA1_ID "," \ +diff --git a/ssh_config.5 b/ssh_config.5 +index 3d6da510..1dc29bf1 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -762,7 +762,9 @@ gss-gex-sha1-, + gss-group1-sha1-, + gss-group14-sha1-, + gss-group14-sha256-, +-gss-group16-sha512- ++gss-group16-sha512-, ++gss-nistp256-sha256-, ++gss-curve25519-sha256- + .Ed + .Pp + The default is +diff --git a/sshconnect2.c b/sshconnect2.c +index 5d6b8be0..280ae5a6 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -256,6 +256,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_client; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_client; + } + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; +diff --git a/sshd.c b/sshd.c +index e4c879a2..a35735d8 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -2247,6 +2247,8 @@ do_ssh2_kex(void) + kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; + kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; + kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; ++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; ++ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; + } + #endif + kex->server = 1; +diff --git a/sshd_config.5 b/sshd_config.5 +index 0793418b..888316bf 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -677,7 +677,9 @@ gss-gex-sha1-, + gss-group1-sha1-, + gss-group14-sha1-, + gss-group14-sha256-, +-gss-group16-sha512- ++gss-group16-sha512-, ++gss-nistp256-sha256-, ++gss-curve25519-sha256- + .Ed + .Pp + The default is +-- +2.13.5 + + +From 0431695660d5eb1dd1169d42a1624c75a92aa5d2 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 30 Aug 2017 15:30:51 +0200 +Subject: [PATCH 3/3] Simplify rough edges of GSSAPI Kex + +--- + gss-genr.c | 53 +++++++++++++++++------------------------------------ + regress/kextype.sh | 10 ++++------ + regress/rekey.sh | 20 ++++++++------------ + 3 files changed, 29 insertions(+), 54 deletions(-) + +diff --git a/gss-genr.c b/gss-genr.c +index 22040244..c671be31 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -171,47 +171,28 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, + 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_GRP14_SHA256: +- if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) +- return GSS_C_NO_OID; +- name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; +- break; +- case KEX_GSS_GRP16_SHA512: +- if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) +- return GSS_C_NO_OID; +- name += sizeof(KEX_GSS_GRP16_SHA512_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; +- case KEX_GSS_NISTP256_SHA256: +- if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) +- return GSS_C_NO_OID; +- name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; +- break; +- case KEX_GSS_C25519_SHA256: +- if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) +- return GSS_C_NO_OID; +- name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; ++ ++#define SKIP_KEX_NAME(type) \ ++ case type: \ ++ if (strlen(name) < sizeof(type##_ID)) \ ++ return GSS_C_NO_OID; \ ++ name += sizeof(type##_ID) - 1; \ + break; ++ ++ switch (kex_type) { ++ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) ++ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) ++ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) ++ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) + default: + return GSS_C_NO_OID; + } + ++#undef SKIP_KEX_NAME ++ + while (gss_enc2oid[i].encoded != NULL && + strcmp(name, gss_enc2oid[i].encoded) != 0) + i++; +diff --git a/regress/kextype.sh b/regress/kextype.sh +index d5b4a713..6b4af28a 100644 +--- a/regress/kextype.sh ++++ b/regress/kextype.sh +@@ -14,12 +14,10 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy + + 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-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ +- $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ +- $k = "gss-group16-sha512-" ]; then +- continue +- fi ++ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) ++ case $k in ++ gss-* ) continue ;; ++ esac + verbose "kex $k" + for i in $tries; do + ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true +diff --git a/regress/rekey.sh b/regress/rekey.sh +index b118c6c8..d6a8742f 100644 +--- a/regress/rekey.sh ++++ b/regress/rekey.sh +@@ -38,12 +38,10 @@ 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-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ +- $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ +- $i = "gss-group16-sha512-" ]; then +- continue +- fi ++ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) ++ case $i in ++ gss-* ) continue ;; ++ esac + opts="$opts KexAlgorithms=$i" + done + for i in `${SSH} -Q cipher`; do +@@ -62,12 +60,10 @@ 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-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ +- $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ +- $kex = "gss-group16-sha512-" ]; then +- continue +- fi ++ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) ++ case $kex in ++ gss-* ) continue ;; ++ esac + verbose "client rekey $c $kex" + ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c + done +-- +2.13.5 + diff --git a/SOURCES/openssh-7.5p1-sandbox.patch b/SOURCES/openssh-7.5p1-sandbox.patch new file mode 100644 index 0000000..b761962 --- /dev/null +++ b/SOURCES/openssh-7.5p1-sandbox.patch @@ -0,0 +1,108 @@ +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 +--- + sandbox-seccomp-filter.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index ca75cc7..6e7de31 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -166,6 +166,9 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_exit_group + SC_ALLOW(__NR_exit_group), + #endif ++#if defined(__NR_flock) && defined(__s390__) ++ SC_ALLOW(__NR_flock), ++#endif + #ifdef __NR_geteuid + SC_ALLOW(__NR_geteuid), + #endif +@@ -178,6 +181,9 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_gettimeofday + SC_ALLOW(__NR_gettimeofday), + #endif ++#if defined(__NR_ipc) && defined(__s390__) ++ SC_ALLOW(__NR_ipc), ++#endif + #ifdef __NR_getuid + SC_ALLOW(__NR_getuid), + #endif +-- +1.9.1 + +getuid and geteuid are needed when using an openssl engine that calls a +crypto card, e.g. ICA (libica). +Those syscalls are also needed by the distros for audit code. + +Signed-off-by: Eduardo Barretto +--- + sandbox-seccomp-filter.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index 6e7de31..e86aa2c 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -175,6 +175,18 @@ static const struct sock_filter preauth_insns[] = { + #ifdef __NR_getpid + SC_ALLOW(__NR_getpid), + #endif ++#ifdef __NR_getuid ++ SC_ALLOW(__NR_getuid), ++#endif ++#ifdef __NR_getuid32 ++ SC_ALLOW(__NR_getuid32), ++#endif ++#ifdef __NR_geteuid ++ SC_ALLOW(__NR_geteuid), ++#endif ++#ifdef __NR_geteuid32 ++ SC_ALLOW(__NR_geteuid32), ++#endif + #ifdef __NR_getrandom + SC_ALLOW(__NR_getrandom), + #endif +-- 1.9.1 + +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 +--- + sandbox-seccomp-filter.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/sandbox-seccomp-filter.c b/sandbox-seccomp-filter.c +index e86aa2c..98062f1 100644 +--- a/sandbox-seccomp-filter.c ++++ b/sandbox-seccomp-filter.c +@@ -250,6 +250,8 @@ static const struct sock_filter preauth_insns[] = { + 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) + /* +-- +1.9.1 +diff -up openssh-7.6p1/sandbox-seccomp-filter.c.sandbox openssh-7.6p1/sandbox-seccomp-filter.c +--- openssh-7.6p1/sandbox-seccomp-filter.c.sandbox 2017-12-12 13:59:30.563874059 +0100 ++++ openssh-7.6p1/sandbox-seccomp-filter.c 2017-12-12 13:59:14.842784083 +0100 +@@ -190,6 +190,9 @@ static const struct sock_filter preauth_ + #ifdef __NR_geteuid32 + SC_ALLOW(__NR_geteuid32), + #endif ++#ifdef __NR_gettid ++ SC_ALLOW(__NR_gettid), ++#endif + #ifdef __NR_getrandom + SC_ALLOW(__NR_getrandom), + #endif diff --git a/SOURCES/openssh-7.6p1-audit.patch b/SOURCES/openssh-7.6p1-audit.patch new file mode 100644 index 0000000..e184894 --- /dev/null +++ b/SOURCES/openssh-7.6p1-audit.patch @@ -0,0 +1,2172 @@ +diff -up openssh-7.6p1/audit-bsm.c.audit openssh-7.6p1/audit-bsm.c +--- openssh-7.6p1/audit-bsm.c.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/audit-bsm.c 2017-10-04 17:18:32.834505048 +0200 +@@ -373,10 +373,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 +@@ -391,6 +404,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) + { +@@ -452,4 +471,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 */ +diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c +--- openssh-7.6p1/audit.c.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/audit.c 2017-10-04 17:18:32.834505048 +0200 +@@ -34,6 +35,12 @@ + #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 +@@ -41,6 +47,7 @@ + * 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 +76,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 +113,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 +173,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 +218,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 */ +diff -up openssh-7.6p1/audit.h.audit openssh-7.6p1/audit.h +--- openssh-7.6p1/audit.h.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/audit.h 2017-10-04 17:18:32.834505048 +0200 +@@ -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 */ +diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +--- openssh-7.6p1/audit-linux.c.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/audit-linux.c 2017-10-04 17:18:32.835505053 +0200 +@@ -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 +177,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 +231,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 */ +diff -up openssh-7.6p1/auditstub.c.audit openssh-7.6p1/auditstub.c +--- openssh-7.6p1/auditstub.c.audit 2017-10-04 17:18:32.835505053 +0200 ++++ openssh-7.6p1/auditstub.c 2017-10-04 17:18:32.835505053 +0200 +@@ -0,0 +1,50 @@ ++/* $Id: auditstub.c,v 1.1 jfch Exp $ */ ++ ++/* ++ * Copyright 2010 Red Hat, Inc. All rights reserved. ++ * Use is subject to license terms. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ * ++ * Red Hat author: Jan F. Chadima ++ */ ++ ++#include ++ ++void ++audit_unsupported(int n) ++{ ++} ++ ++void ++audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++{ ++} ++ ++void ++audit_session_key_free(int ctos) ++{ ++} ++ ++void ++audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++{ ++} +diff -up openssh-7.6p1/auth2.c.audit openssh-7.6p1/auth2.c +--- openssh-7.6p1/auth2.c.audit 2017-10-04 17:18:32.746504598 +0200 ++++ openssh-7.6p1/auth2.c 2017-10-04 17:18:32.835505053 +0200 +@@ -255,9 +255,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) +diff -up openssh-7.6p1/auth2-hostbased.c.audit openssh-7.6p1/auth2-hostbased.c +--- openssh-7.6p1/auth2-hostbased.c.audit 2017-10-04 17:18:32.683504276 +0200 ++++ openssh-7.6p1/auth2-hostbased.c 2017-10-04 17:18:32.835505053 +0200 +@@ -152,7 +152,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; + +@@ -169,6 +169,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 *pkalg, u_int compat) ++{ ++ int rv; ++ ++ rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, 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, +diff -up openssh-7.6p1/auth2-pubkey.c.audit openssh-7.6p1/auth2-pubkey.c +--- openssh-7.6p1/auth2-pubkey.c.audit 2017-10-04 17:18:32.828505018 +0200 ++++ openssh-7.6p1/auth2-pubkey.c 2017-10-04 17:18:32.835505053 +0200 +@@ -206,7 +206,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) { +@@ -250,6 +250,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 *pkalg, u_int compat) ++{ ++ int rv; ++ ++ rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, 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) + { +diff -up openssh-7.6p1/auth.c.audit openssh-7.6p1/auth.c +--- openssh-7.6p1/auth.c.audit 2017-10-04 17:18:32.746504598 +0200 ++++ openssh-7.6p1/auth.c 2017-10-04 17:18:32.835505053 +0200 +@@ -360,7 +360,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 + } +@@ -599,9 +599,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)) +diff -up openssh-7.6p1/auth.h.audit openssh-7.6p1/auth.h +--- openssh-7.6p1/auth.h.audit 2017-10-04 17:18:32.768504711 +0200 ++++ openssh-7.6p1/auth.h 2017-10-04 17:18:32.836505059 +0200 +@@ -198,6 +198,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); +@@ -217,6 +218,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 *); +diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c +--- openssh-7.6p1/cipher.c.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/cipher.c 2017-10-04 17:18:32.836505059 +0200 +@@ -61,25 +61,6 @@ struct sshcipher_ctx { + 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) +-#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[] = { + #ifdef WITH_OPENSSL + #ifndef OPENSSL_NO_DES +@@ -409,7 +409,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)); +diff -up openssh-7.6p1/cipher.h.audit openssh-7.6p1/cipher.h +--- openssh-7.6p1/cipher.h.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/cipher.h 2017-10-04 17:18:32.836505059 +0200 +@@ -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; + + const struct sshcipher *cipher_by_name(const char *); +diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c +--- openssh-7.6p1/kex.c.audit 2017-10-04 17:18:32.822504987 +0200 ++++ openssh-7.6p1/kex.c 2017-10-04 17:18:32.836505059 +0200 +@@ -54,6 +54,7 @@ + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" ++#include "audit.h" + + #ifdef GSSAPI + #include "ssh-gss.h" +@@ -692,8 +693,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; +@@ -713,8 +718,12 @@ choose_mac(struct ssh *ssh, struct sshma + { + char *name = match_list(client, server, NULL); + +- if (name == NULL) ++ if (name == NULL) { ++#ifdef SSH_AUDIT_EVENTS ++ audit_unsupported(SSH_AUDIT_UNSUPPORTED_MAC); ++#endif + return SSH_ERR_NO_MAC_ALG_MATCH; ++ } + if (mac_setup(mac, name) < 0) { + free(name); + return SSH_ERR_INTERNAL_ERROR; +@@ -733,8 +742,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) { +@@ -904,6 +917,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; +@@ -1037,3 +1054,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)); ++} +diff -up openssh-7.6p1/kex.h.audit openssh-7.6p1/kex.h +--- openssh-7.6p1/kex.h.audit 2017-10-04 17:18:32.822504987 +0200 ++++ openssh-7.6p1/kex.h 2017-10-04 17:18:32.836505059 +0200 +@@ -219,6 +219,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 *); +diff -up openssh-7.6p1/mac.c.audit openssh-7.6p1/mac.c +--- openssh-7.6p1/mac.c.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/mac.c 2017-10-04 17:18:32.836505059 +0200 +@@ -242,6 +242,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 +diff -up openssh-7.6p1/mac.h.audit openssh-7.6p1/mac.h +--- openssh-7.6p1/mac.h.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/mac.h 2017-10-04 17:18:32.837505064 +0200 +@@ -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 */ +diff -up openssh-7.6p1/Makefile.in.audit openssh-7.6p1/Makefile.in +--- openssh-7.6p1/Makefile.in.audit 2017-10-04 17:18:32.749504614 +0200 ++++ openssh-7.6p1/Makefile.in 2017-10-04 17:18:32.837505064 +0200 +@@ -100,7 +100,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ +- platform-pledge.o platform-tracing.o platform-misc.o ++ platform-pledge.o platform-tracing.o platform-misc.o \ ++ auditstub.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect2.o mux.o +diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +--- openssh-7.6p1/monitor.c.audit 2017-10-04 17:18:32.824504997 +0200 ++++ openssh-7.6p1/monitor.c 2017-10-04 17:18:32.837505064 +0200 +@@ -102,6 +102,7 @@ + #include "compat.h" + #include "ssh2.h" + #include "authfd.h" ++#include "audit.h" + #include "match.h" + #include "ssherr.h" + +@@ -117,6 +118,8 @@ extern Buffer auth_debug; + 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; + +@@ -167,6 +170,11 @@ int mm_answer_gss_updatecreds(int, Buffe + #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 *); +@@ -222,6 +230,10 @@ 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}, +@@ -260,6 +272,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 + {0, 0, NULL} + }; +@@ -1396,8 +1413,10 @@ 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 || ++ if ((r = sshbuf_get_u32(m, &type)) != 0 || ++ (r = sshbuf_get_string(m, &blob, &bloblen)) != 0 || + (r = sshbuf_get_string(m, &signature, &signaturelen)) != 0 || + (r = sshbuf_get_string(m, &data, &datalen)) != 0 || + (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) +@@ -1405,6 +1424,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') { +@@ -1414,21 +1435,24 @@ mm_answer_keyverify(int sock, struct ssh + case MM_USERKEY: + valid_data = monitor_valid_userblob(data, datalen); + auth_method = "publickey"; ++ ret = user_key_verify(key, signature, signaturelen, data, ++ datalen, sigalg, active_state->compat); + 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); +@@ -1485,6 +1509,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); + } + +@@ -1588,6 +1618,8 @@ mm_answer_term(int sock, Buffer *req) + sshpam_cleanup(); + #endif + ++ destroy_sensitive_data(0); ++ + while (waitpid(pmonitor->m_pid, &status, 0) == -1) + if (errno != EINTR) + exit(1); +@@ -1630,12 +1662,47 @@ mm_answer_audit_command(int socket, Buff + { + 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); ++ 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); + } +@@ -1702,6 +1768,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) +@@ -1709,6 +1776,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) ++ ; ++ + } + + +@@ -1976,3 +2056,102 @@ mm_answer_gss_updatecreds(int socket, Bu + + #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 */ +diff -up openssh-7.6p1/monitor.h.audit openssh-7.6p1/monitor.h +--- openssh-7.6p1/monitor.h.audit 2017-10-04 17:18:32.781504777 +0200 ++++ openssh-7.6p1/monitor.h 2017-10-04 17:18:32.837505064 +0200 +@@ -69,7 +69,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 + + }; + +diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +--- openssh-7.6p1/monitor_wrap.c.audit 2017-10-04 17:18:32.750504619 +0200 ++++ openssh-7.6p1/monitor_wrap.c 2017-10-04 17:18:32.838505069 +0200 +@@ -463,7 +463,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; +@@ -478,7 +478,8 @@ mm_sshkey_verify(const struct sshkey *ke + + 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) +@@ -497,6 +498,20 @@ 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 *pkalg, u_int compat) ++{ ++ return mm_sshkey_verify(MM_HOSTKEY, key, sig, siglen, data, datalen, pkalg, 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 *pkalg, u_int compat) ++{ ++ return mm_sshkey_verify(MM_USERKEY, key, sig, siglen, data, datalen, pkalg, compat); ++} ++ + void + mm_send_keystate(struct monitor *monitor) + { +@@ -874,11 +889,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); + +@@ -885,6 +901,30 @@ 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); ++ 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 */ +@@ -1020,3 +1056,83 @@ 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 */ +diff -up openssh-7.6p1/monitor_wrap.h.audit openssh-7.6p1/monitor_wrap.h +--- openssh-7.6p1/monitor_wrap.h.audit 2017-10-04 17:18:32.750504619 +0200 ++++ openssh-7.6p1/monitor_wrap.h 2017-10-04 17:18:32.838505069 +0200 +@@ -53,7 +53,9 @@ int mm_key_allowed(enum mm_keytype, cons + 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; +diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c +--- openssh-7.6p1/packet.c.audit 2017-10-04 17:18:32.672504220 +0200 ++++ openssh-7.6p1/packet.c 2017-10-04 17:25:48.141741390 +0200 +@@ -67,6 +67,7 @@ + #include + + #include "xmalloc.h" ++#include "audit.h" + #include "crc32.h" + #include "compat.h" + #include "ssh2.h" +@@ -502,6 +503,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. +@@ -566,22 +574,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; +@@ -615,8 +616,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); +@@ -854,6 +863,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]); +@@ -2135,6 +2145,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) +diff -up openssh-7.6p1/packet.h.audit openssh-7.6p1/packet.h +--- openssh-7.6p1/packet.h.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/packet.h 2017-10-04 17:18:32.838505069 +0200 +@@ -217,4 +217,5 @@ extern struct ssh *active_state; + # undef EC_POINT + #endif + ++void packet_destroy_all(int, int); + #endif /* PACKET_H */ +diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c +--- openssh-7.6p1/session.c.audit 2017-10-04 17:18:32.812504936 +0200 ++++ openssh-7.6p1/session.c 2017-10-04 17:18:32.839505074 +0200 +@@ -138,7 +138,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 */ +@@ -605,6 +605,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, +@@ -724,15 +732,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); +@@ -1499,8 +1511,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) { +@@ -1714,6 +1729,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; + } +@@ -1796,6 +1814,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; +@@ -2307,6 +2338,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) + { +@@ -2320,6 +2377,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); +@@ -2528,6 +2589,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) + { +@@ -2585,7 +2655,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 */ +diff -up openssh-7.6p1/session.h.audit openssh-7.6p1/session.h +--- openssh-7.6p1/session.h.audit 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/session.h 2017-10-04 17:18:32.839505074 +0200 +@@ -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 *); +diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c +--- openssh-7.6p1/sshd.c.audit 2017-10-04 17:18:32.830505028 +0200 ++++ openssh-7.6p1/sshd.c 2017-10-04 17:18:32.839505074 +0200 +@@ -122,6 +122,7 @@ + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" ++#include "audit.h" + #include "ssh-sandbox.h" + #include "auth-options.h" + #include "version.h" +@@ -248,7 +249,7 @@ Buffer 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); + +@@ -265,6 +266,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) + { +@@ -475,18 +485,45 @@ 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]) { ++ 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; + } +@@ -499,16 +536,34 @@ demote_sensitive_data(void) + struct sshkey *tmp; + u_int i; + int r; ++#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]) { ++ 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", + sshkey_type(sensitive_data.host_keys[i]), + 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 */ + } +@@ -587,7 +642,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) { +@@ -1162,6 +1217,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); +@@ -2165,6 +2221,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); +@@ -2344,6 +2403,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); +@@ -2356,9 +2424,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); +diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c +--- openssh-7.6p1/sshkey.c.audit 2017-10-04 17:18:32.758504660 +0200 ++++ openssh-7.6p1/sshkey.c 2017-10-04 17:18:32.839505074 +0200 +@@ -295,6 +295,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) +diff -up openssh-7.6p1/sshkey.h.audit openssh-7.6p1/sshkey.h +--- openssh-7.6p1/sshkey.h.audit 2017-10-04 17:18:32.758504660 +0200 ++++ openssh-7.6p1/sshkey.h 2017-10-04 17:18:32.840505079 +0200 +@@ -133,6 +133,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/SOURCES/openssh-7.6p1-cleanup-selinux.patch b/SOURCES/openssh-7.6p1-cleanup-selinux.patch new file mode 100644 index 0000000..3b5001a --- /dev/null +++ b/SOURCES/openssh-7.6p1-cleanup-selinux.patch @@ -0,0 +1,253 @@ +diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c +--- openssh/auth2-pubkey.c.refactor 2017-09-27 13:10:19.556830609 +0200 ++++ openssh/auth2-pubkey.c 2017-09-27 13:10:19.677831274 +0200 +@@ -72,6 +72,9 @@ + extern ServerOptions options; + extern u_char *session_id2; + extern u_int session_id2_len; ++extern int inetd_flag; ++extern int rexeced_flag; ++extern Authctxt *the_authctxt; + + static char * + format_key(const struct sshkey *key) +@@ -432,7 +435,8 @@ match_principals_command(struct passwd * + + if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command, + ac, av, &f, +- SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) ++ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, ++ (inetd_flag && !rexeced_flag), the_authctxt)) == 0) + goto out; + + uid_swapped = 1; +@@ -762,7 +766,8 @@ user_key_command_allowed2(struct passwd + + if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command, + ac, av, &f, +- SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) ++ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD, ++ (inetd_flag && !rexeced_flag), the_authctxt)) == 0) + goto out; + + uid_swapped = 1; +diff -up openssh/auth.c.refactor openssh/auth.c +--- openssh/auth.c.refactor 2017-09-27 13:10:19.640831071 +0200 ++++ openssh/auth.c 2017-09-27 13:10:19.678831279 +0200 +@@ -1435,7 +1435,8 @@ argv_assemble(int argc, char **argv) + */ + pid_t + subprocess(const char *tag, struct passwd *pw, const char *command, +- int ac, char **av, FILE **child, u_int flags) ++ int ac, char **av, FILE **child, u_int flags, int inetd, ++ void *the_authctxt) + { + FILE *f = NULL; + struct stat st; +@@ -1551,7 +1552,7 @@ subprocess(const char *tag, struct passw + } + + #ifdef WITH_SELINUX +- if (sshd_selinux_setup_env_variables() < 0) { ++ if (sshd_selinux_setup_env_variables(inetd, the_authctxt) < 0) { + error ("failed to copy environment: %s", + strerror(errno)); + _exit(127); +diff -up openssh/auth.h.refactor openssh/auth.h +--- openssh/auth.h.refactor 2017-09-25 01:48:10.000000000 +0200 ++++ openssh/auth.h 2017-09-27 13:10:19.678831279 +0200 +@@ -144,7 +144,7 @@ int exited_cleanly(pid_t, const char *, + #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ + #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ + pid_t subprocess(const char *, struct passwd *, +- const char *, int, char **, FILE **, u_int flags); ++ const char *, int, char **, FILE **, u_int flags, int, void *); + + int sys_auth_passwd(struct ssh *, const char *); + +diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/port-linux.h +--- openssh/openbsd-compat/port-linux.h.refactor 2017-09-27 13:10:19.634831038 +0200 ++++ openssh/openbsd-compat/port-linux.h 2017-09-27 13:10:54.954025248 +0200 +@@ -26,8 +26,8 @@ void ssh_selinux_setfscreatecon(const ch + + int sshd_selinux_enabled(void); + void sshd_selinux_copy_context(void); +-void sshd_selinux_setup_exec_context(char *); +-int sshd_selinux_setup_env_variables(void); ++void sshd_selinux_setup_exec_context(char *, int, int(char *, const char *), void *, int); ++int sshd_selinux_setup_env_variables(int inetd, void *); + void sshd_selinux_change_privsep_preauth_context(void); + #endif + +diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compat/port-linux-sshd.c +--- openssh/openbsd-compat/port-linux-sshd.c.refactor 2017-09-27 13:10:19.634831038 +0200 ++++ openssh/openbsd-compat/port-linux-sshd.c 2017-09-27 13:12:06.811420371 +0200 +@@ -48,11 +48,6 @@ + #include + #endif + +-extern ServerOptions options; +-extern Authctxt *the_authctxt; +-extern int inetd_flag; +-extern int rexeced_flag; +- + /* Wrapper around is_selinux_enabled() to log its return value once only */ + int + sshd_selinux_enabled(void) +@@ -222,7 +217,8 @@ get_user_context(const char *sename, con + } + + static void +-ssh_selinux_get_role_level(char **role, const char **level) ++ssh_selinux_get_role_level(char **role, const char **level, ++ Authctxt *the_authctxt) + { + *role = NULL; + *level = NULL; +@@ -240,8 +236,8 @@ ssh_selinux_get_role_level(char **role, + + /* Return the default security context for the given username */ + static int +-sshd_selinux_getctxbyname(char *pwname, +- security_context_t *default_sc, security_context_t *user_sc) ++sshd_selinux_getctxbyname(char *pwname, security_context_t *default_sc, ++ security_context_t *user_sc, int inetd, Authctxt *the_authctxt) + { + char *sename, *lvl; + char *role; +@@ -249,7 +245,7 @@ sshd_selinux_getctxbyname(char *pwname, + int r = 0; + context_t con = NULL; + +- ssh_selinux_get_role_level(&role, &reqlvl); ++ ssh_selinux_get_role_level(&role, &reqlvl, the_authctxt); + + #ifdef HAVE_GETSEUSERBYNAME + if ((r=getseuserbyname(pwname, &sename, &lvl)) != 0) { +@@ -271,7 +267,7 @@ sshd_selinux_getctxbyname(char *pwname, + + if (r == 0) { + /* If launched from xinetd, we must use current level */ +- if (inetd_flag && !rexeced_flag) { ++ if (inetd) { + security_context_t sshdsc=NULL; + + if (getcon_raw(&sshdsc) < 0) +@@ -332,7 +328,8 @@ sshd_selinux_getctxbyname(char *pwname, + + /* Setup environment variables for pam_selinux */ + static int +-sshd_selinux_setup_variables(int(*set_it)(char *, const char *)) ++sshd_selinux_setup_variables(int(*set_it)(char *, const char *), int inetd, ++ Authctxt *the_authctxt) + { + const char *reqlvl; + char *role; +@@ -341,11 +338,11 @@ sshd_selinux_setup_variables(int(*set_it + + debug3("%s: setting execution context", __func__); + +- ssh_selinux_get_role_level(&role, &reqlvl); ++ ssh_selinux_get_role_level(&role, &reqlvl, the_authctxt); + + rv = set_it("SELINUX_ROLE_REQUESTED", role ? role : ""); + +- if (inetd_flag && !rexeced_flag) { ++ if (inetd) { + use_current = "1"; + } else { + use_current = ""; +@@ -361,9 +358,10 @@ sshd_selinux_setup_variables(int(*set_it + } + + static int +-sshd_selinux_setup_pam_variables(void) ++sshd_selinux_setup_pam_variables(int inetd, ++ int(pam_setenv)(char *, const char *), Authctxt *the_authctxt) + { +- return sshd_selinux_setup_variables(do_pam_putenv); ++ return sshd_selinux_setup_variables(pam_setenv, inetd, the_authctxt); + } + + static int +@@ -373,25 +371,28 @@ do_setenv(char *name, const char *value) + } + + int +-sshd_selinux_setup_env_variables(void) ++sshd_selinux_setup_env_variables(int inetd, void *the_authctxt) + { +- return sshd_selinux_setup_variables(do_setenv); ++ Authctxt *authctxt = (Authctxt *) the_authctxt; ++ return sshd_selinux_setup_variables(do_setenv, inetd, authctxt); + } + + /* Set the execution context to the default for the specified user */ + void +-sshd_selinux_setup_exec_context(char *pwname) ++sshd_selinux_setup_exec_context(char *pwname, int inetd, ++ int(pam_setenv)(char *, const char *), void *the_authctxt, int use_pam) + { + security_context_t user_ctx = NULL; + int r = 0; + security_context_t default_ctx = NULL; ++ Authctxt *authctxt = (Authctxt *) the_authctxt; + + if (!sshd_selinux_enabled()) + return; + +- if (options.use_pam) { ++ if (use_pam) { + /* do not compute context, just setup environment for pam_selinux */ +- if (sshd_selinux_setup_pam_variables()) { ++ if (sshd_selinux_setup_pam_variables(inetd, pam_setenv, authctxt)) { + switch (security_getenforce()) { + case -1: + fatal("%s: security_getenforce() failed", __func__); +@@ -409,7 +410,7 @@ sshd_selinux_setup_exec_context(char *pw + + debug3("%s: setting execution context", __func__); + +- r = sshd_selinux_getctxbyname(pwname, &default_ctx, &user_ctx); ++ r = sshd_selinux_getctxbyname(pwname, &default_ctx, &user_ctx, inetd, authctxt); + if (r >= 0) { + r = setexeccon(user_ctx); + if (r < 0) { +diff -up openssh/platform.c.refactor openssh/platform.c +--- openssh/platform.c.refactor 2017-09-27 13:10:19.574830708 +0200 ++++ openssh/platform.c 2017-09-27 13:11:45.475303050 +0200 +@@ -33,6 +33,9 @@ + + extern int use_privsep; + extern ServerOptions options; ++extern int inetd_flag; ++extern int rexeced_flag; ++extern Authctxt *the_authctxt; + + void + platform_pre_listen(void) +@@ -184,7 +187,9 @@ platform_setusercontext_post_groups(stru + } + #endif /* HAVE_SETPCRED */ + #ifdef WITH_SELINUX +- sshd_selinux_setup_exec_context(pw->pw_name); ++ sshd_selinux_setup_exec_context(pw->pw_name, ++ (inetd_flag && !rexeced_flag), do_pam_putenv, the_authctxt, ++ options.use_pam); + #endif + } + +diff -up openssh/sshd.c.refactor openssh/sshd.c +--- openssh/sshd.c.refactor 2017-09-27 13:10:19.674831257 +0200 ++++ openssh/sshd.c 2017-09-27 13:12:01.635391909 +0200 +@@ -2135,7 +2135,9 @@ main(int ac, char **av) + } + #endif + #ifdef WITH_SELINUX +- sshd_selinux_setup_exec_context(authctxt->pw->pw_name); ++ sshd_selinux_setup_exec_context(authctxt->pw->pw_name, ++ (inetd_flag && !rexeced_flag), do_pam_putenv, the_authctxt, ++ options.use_pam); + #endif + #ifdef USE_PAM + if (options.use_pam) { diff --git a/SOURCES/openssh-7.6p1-pkcs11-ecdsa.patch b/SOURCES/openssh-7.6p1-pkcs11-ecdsa.patch new file mode 100644 index 0000000..2e73d45 --- /dev/null +++ b/SOURCES/openssh-7.6p1-pkcs11-ecdsa.patch @@ -0,0 +1,801 @@ +diff -up openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-client.c +--- openssh-7.6p1/ssh-pkcs11-client.c.pkcs11-ecdsa 2018-02-16 13:25:59.426469253 +0100 ++++ openssh-7.6p1/ssh-pkcs11-client.c 2018-02-16 13:25:59.428469265 +0100 +@@ -31,6 +31,15 @@ + #include + + #include ++#ifdef OPENSSL_HAS_ECC ++#include ++#if ((defined(LIBRESSL_VERSION_NUMBER) && \ ++ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ ++ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ ++ (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++#define ENABLE_PKCS11_ECDSA 1 ++#endif ++#endif + + #include "pathnames.h" + #include "xmalloc.h" +@@ -139,9 +147,9 @@ pkcs11_rsa_private_encrypt(int flen, con + return (ret); + } + +-/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ ++/* redirect the RSA private key encrypt operation to the ssh-pkcs11-helper */ + static int +-wrap_key(RSA *rsa) ++wrap_rsa_key(RSA *rsa) + { + static RSA_METHOD helper_rsa; + +@@ -152,6 +160,88 @@ wrap_key(RSA *rsa) + return (0); + } + ++#ifdef ENABLE_PKCS11_ECDSA ++static ECDSA_SIG * ++pkcs11_ecdsa_private_sign(const unsigned char *from, int flen, ++ const BIGNUM *inv, const BIGNUM *rp, EC_KEY * ecdsa) ++{ ++ struct sshkey *key = NULL; ++ u_char *blob, *signature = NULL; ++ size_t blen, slen = 0; ++ struct sshbuf *msg = NULL; ++ ECDSA_SIG *ret = NULL; ++ BIGNUM *r = NULL, *s = NULL; ++ int rv; ++ ++ if ((key = sshkey_new(KEY_ECDSA)) == NULL) ++ fatal("%s: sshkey_new failed", __func__); ++ key->ecdsa = ecdsa; ++ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); ++ if (sshkey_to_blob(key, &blob, &blen) == 0) ++ goto out; ++ if ((msg = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((rv = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || ++ (rv = sshbuf_put_string(msg, blob, blen)) != 0 || ++ (rv = sshbuf_put_string(msg, from, flen)) != 0 || ++ (rv = sshbuf_put_u32(msg, 0)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); ++ free(blob); ++ send_msg(msg); ++ sshbuf_reset(msg); ++ ++ if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { ++ if ((rv = sshbuf_get_string(msg, &signature, &slen)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); ++ if (slen <= (size_t)ECDSA_size(ecdsa)) { ++ int nlen = slen / 2; ++ ret = ECDSA_SIG_new(); ++ r = BN_new(); ++ s = BN_new(); ++ BN_bin2bn(&signature[0], nlen, r); ++ BN_bin2bn(&signature[nlen], nlen, s); ++ ECDSA_SIG_set0(ret, r, s); ++ } ++ free(signature); ++ } ++out: ++ sshkey_free(key); ++ sshbuf_free(msg); ++ return (ret); ++} ++ ++/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */ ++static int ++wrap_ecdsa_key(EC_KEY *ecdsa) { ++#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++ static EC_KEY_METHOD *helper_ecdsa = NULL; ++ if (helper_ecdsa == NULL) { ++ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); ++ helper_ecdsa = EC_KEY_METHOD_new(def); ++ EC_KEY_METHOD_set_sign(helper_ecdsa, NULL, NULL, pkcs11_ecdsa_private_sign); ++ } ++ EC_KEY_set_method(ecdsa, helper_ecdsa); ++#else ++ static ECDSA_METHOD *helper_ecdsa = NULL; ++ if(helper_ecdsa == NULL) { ++ const ECDSA_METHOD *def = ECDSA_get_default_method(); ++# ifdef ECDSA_F_ECDSA_METHOD_NEW ++ helper_ecdsa = ECDSA_METHOD_new((ECDSA_METHOD *)def); ++ ECDSA_METHOD_set_name(helper_ecdsa, "ssh-pkcs11-helper-ecdsa"); ++ ECDSA_METHOD_set_sign(helper_ecdsa, pkcs11_ecdsa_private_sign); ++# else ++ helper_ecdsa = xcalloc(1, sizeof(*helper_ecdsa)); ++ memcpy(helper_ecdsa, def, sizeof(*helper_ecdsa)); ++ helper_ecdsa->name = "ssh-pkcs11-helper-ecdsa"; ++ helper_ecdsa->ecdsa_do_sign = pkcs11_ecdsa_private_sign; ++# endif ++ } ++ ECDSA_set_method(ecdsa, helper_ecdsa); ++#endif ++ return (0); ++} ++#endif ++ + static int + pkcs11_start_helper(void) + { +@@ -212,7 +281,15 @@ pkcs11_add_provider(char *name, char *pi + __func__, ssh_err(r)); + if ((r = sshkey_from_blob(blob, blen, &k)) != 0) + fatal("%s: bad key: %s", __func__, ssh_err(r)); +- wrap_key(k->rsa); ++ if(k->type == KEY_RSA) { ++ wrap_rsa_key(k->rsa); ++#ifdef ENABLE_PKCS11_ECDSA ++ } else if(k->type == KEY_ECDSA) { ++ wrap_ecdsa_key(k->ecdsa); ++#endif /* ENABLE_PKCS11_ECDSA */ ++ } else { ++ /* Unsupported type */ ++ } + (*keysp)[i] = k; + free(blob); + } +diff -up openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.c +--- openssh-7.6p1/ssh-pkcs11.c.pkcs11-ecdsa 2018-02-16 13:25:59.427469259 +0100 ++++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 13:44:51.270554797 +0100 +@@ -32,6 +32,16 @@ + #include "openbsd-compat/sys-queue.h" + + #include ++#include ++#ifdef OPENSSL_HAS_ECC ++#include ++#if ((defined(LIBRESSL_VERSION_NUMBER) && \ ++ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ ++ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ ++ (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++#define ENABLE_PKCS11_ECDSA 1 ++#endif ++#endif + + #define CRYPTOKI_COMPAT + #include "pkcs11.h" +@@ -67,6 +76,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_pro + struct pkcs11_key { + struct pkcs11_provider *provider; + CK_ULONG slotidx; ++ CK_ULONG key_type; + int (*orig_finish)(RSA *rsa); + RSA_METHOD rsa_method; + char *keyid; +@@ -75,6 +85,9 @@ struct pkcs11_key { + }; + + int pkcs11_interactive = 0; ++#ifdef ENABLE_PKCS11_ECDSA ++static int pkcs11_key_idx = -1; ++#endif /* ENABLE_PKCS11_ECDSA */ + + /* + * This can't be in the ssh-pkcs11-uri, becase we can not depend on +@@ -289,6 +302,40 @@ pkcs11_find(struct pkcs11_provider *p, C + return (ret); + } + ++int pkcs11_login(struct pkcs11_key *k11, CK_FUNCTION_LIST *f, struct pkcs11_slotinfo *si) { ++ char *pin = NULL, prompt[1024]; ++ CK_RV rv; ++ if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { ++ if (!pkcs11_interactive) { ++ error("need pin entry%s", (si->token.flags & ++ CKF_PROTECTED_AUTHENTICATION_PATH) ? ++ " on reader keypad" : ""); ++ return (-1); ++ } ++ if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) ++ verbose("Deferring PIN entry to reader keypad."); ++ else { ++ snprintf(prompt, sizeof(prompt), ++ "Enter PIN for '%s': ", si->token.label); ++ pin = read_passphrase(prompt, RP_ALLOW_EOF); ++ if (pin == NULL) ++ return (-1); /* bail out */ ++ } ++ rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, ++ (pin != NULL) ? strlen(pin) : 0); ++ if (pin != NULL) { ++ explicit_bzero(pin, strlen(pin)); ++ free(pin); ++ } ++ if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { ++ error("C_Login failed: %lu", rv); ++ return (-1); ++ } ++ si->logged_in = 1; ++ } ++ return 0; ++} ++ + /* openssl callback doing the actual signing operation */ + static int + pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, +@@ -310,7 +357,6 @@ pkcs11_rsa_private_encrypt(int flen, con + {CKA_ID, NULL, 0}, + {CKA_SIGN, NULL, sizeof(true_val) } + }; +- char *pin = NULL, prompt[1024]; + int rval = -1; + + key_filter[0].pValue = &private_key_class; +@@ -326,33 +372,8 @@ pkcs11_rsa_private_encrypt(int flen, con + } + f = k11->provider->module->function_list; + si = &k11->provider->module->slotinfo[k11->slotidx]; +- if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { +- if (!pkcs11_interactive) { +- error("need pin entry%s", (si->token.flags & +- CKF_PROTECTED_AUTHENTICATION_PATH) ? +- " on reader keypad" : ""); +- return (-1); +- } +- if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) +- verbose("Deferring PIN entry to reader keypad."); +- else { +- snprintf(prompt, sizeof(prompt), +- "Enter PIN for '%s': ", si->token.label); +- pin = read_passphrase(prompt, RP_ALLOW_EOF); +- if (pin == NULL) +- return (-1); /* bail out */ +- } +- rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, +- (pin != NULL) ? strlen(pin) : 0); +- if (pin != NULL) { +- explicit_bzero(pin, strlen(pin)); +- free(pin); +- } +- if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { +- error("C_Login failed: %lu", rv); +- return (-1); +- } +- si->logged_in = 1; ++ if(pkcs11_login(k11, f, si)) { ++ return (-1); + } + key_filter[1].pValue = k11->keyid; + key_filter[1].ulValueLen = k11->keyid_len; +@@ -390,6 +411,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider * + const RSA_METHOD *def = RSA_get_default_method(); + + k11 = xcalloc(1, sizeof(*k11)); ++ k11->key_type = CKK_RSA; + k11->provider = provider; + provider->refcount++; /* provider referenced by RSA key */ + k11->slotidx = slotidx; +@@ -415,6 +437,184 @@ pkcs11_rsa_wrap(struct pkcs11_provider * + return (0); + } + ++#ifdef ENABLE_PKCS11_ECDSA ++static ECDSA_SIG *pkcs11_ecdsa_sign(const unsigned char *dgst, int dgst_len, ++ const BIGNUM *inv, const BIGNUM *rp, ++ EC_KEY *ecdsa) { ++ struct pkcs11_key *k11; ++ struct pkcs11_slotinfo *si; ++ CK_FUNCTION_LIST *f; ++ CK_OBJECT_HANDLE obj; ++ CK_ULONG tlen = 0; ++ CK_RV rv; ++ CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; ++ CK_BBOOL true_val = CK_TRUE; ++ CK_MECHANISM mech = { ++ CKM_ECDSA, NULL_PTR, 0 ++ }; ++ CK_ATTRIBUTE key_filter[] = { ++ {CKA_CLASS, NULL, sizeof(private_key_class) }, ++ {CKA_ID, NULL, 0}, ++ {CKA_SIGN, NULL, sizeof(true_val) } ++ }; ++ ECDSA_SIG *rval = NULL; ++ key_filter[0].pValue = &private_key_class; ++ key_filter[2].pValue = &true_val; ++ ++ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++ if ((k11 = (struct pkcs11_key *)EC_KEY_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { ++ error("EC_KEY_get_ex_data failed for ecdsa %p", ecdsa); ++ #else ++ if ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { ++ error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa); ++ #endif ++ return NULL; ++ } ++ if (!k11->provider || !k11->provider->valid) { ++ error("no pkcs11 (valid) provider for ecdsa %p", ecdsa); ++ return NULL; ++ } ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; ++ if(pkcs11_login(k11, f, si)) { ++ return NULL; ++ } ++ key_filter[1].pValue = k11->keyid; ++ key_filter[1].ulValueLen = k11->keyid_len; ++ /* try to find object w/CKA_SIGN first, retry w/o */ ++ if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && ++ pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { ++ error("cannot find private key"); ++ } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { ++ error("C_SignInit failed: %lu", rv); ++ } else { ++ CK_BYTE_PTR buf = NULL; ++ BIGNUM *r = NULL, *s = NULL; ++ int nlen; ++ /* Make a call to C_Sign to find out the size of the signature */ ++ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen); ++ if (rv != CKR_OK) { ++ error("C_Sign failed: %lu", rv); ++ return NULL; ++ } ++ if ((buf = xmalloc(tlen)) == NULL) { ++ error("failure to allocate signature buffer"); ++ return NULL; ++ } ++ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen); ++ if (rv != CKR_OK) { ++ error("C_Sign failed: %lu", rv); ++ } ++ ++ if ((rval = ECDSA_SIG_new()) == NULL || ++ (r = BN_new()) == NULL || ++ (s = BN_new()) == NULL) { ++ error("failure to allocate ECDSA signature"); ++ } else { ++ /* ++ * ECDSA signature is 2 large integers of same size returned ++ * concatenated by PKCS#11, we separate them to create an ++ * ECDSA_SIG for OpenSSL. ++ */ ++ nlen = tlen / 2; ++ BN_bin2bn(&buf[0], nlen, r); ++ BN_bin2bn(&buf[nlen], nlen, s); ++ ECDSA_SIG_set0(rval, r, s); ++ } ++ free(buf); ++ } ++ return (rval); ++} ++ ++#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++static EC_KEY_METHOD *get_pkcs11_ecdsa_method(void) { ++ static EC_KEY_METHOD *pkcs11_ecdsa_method = NULL; ++ if(pkcs11_key_idx == -1) { ++ pkcs11_key_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); ++ } ++ if (pkcs11_ecdsa_method == NULL) { ++ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); ++ pkcs11_ecdsa_method = EC_KEY_METHOD_new(def); ++ EC_KEY_METHOD_set_sign(pkcs11_ecdsa_method, NULL, NULL, pkcs11_ecdsa_sign); ++ } ++#else ++static ECDSA_METHOD *get_pkcs11_ecdsa_method(void) { ++ static ECDSA_METHOD *pkcs11_ecdsa_method = NULL; ++ if(pkcs11_key_idx == -1) { ++ pkcs11_key_idx = ECDSA_get_ex_new_index(0, NULL, NULL, NULL, 0); ++ } ++ if(pkcs11_ecdsa_method == NULL) { ++ const ECDSA_METHOD *def = ECDSA_get_default_method(); ++ #ifdef ECDSA_F_ECDSA_METHOD_NEW ++ pkcs11_ecdsa_method = ECDSA_METHOD_new((ECDSA_METHOD *)def); ++ ECDSA_METHOD_set_name(pkcs11_ecdsa_method, "pkcs11"); ++ ECDSA_METHOD_set_sign(pkcs11_ecdsa_method, pkcs11_ecdsa_sign); ++ #else ++ pkcs11_ecdsa_method = xcalloc(1, sizeof(*pkcs11_ecdsa_method)); ++ memcpy(pkcs11_ecdsa_method, def, sizeof(*pkcs11_ecdsa_method)); ++ pkcs11_ecdsa_method->name = "pkcs11"; ++ pkcs11_ecdsa_method->ecdsa_do_sign = pkcs11_ecdsa_sign; ++ #endif ++ } ++#endif ++ return pkcs11_ecdsa_method; ++} ++ ++static int ++pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, ++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ecdsa) ++{ ++ struct pkcs11_key *k11; ++ k11 = xcalloc(1, sizeof(*k11)); ++ k11->key_type = CKK_EC; ++ k11->provider = provider; ++ provider->refcount++; /* provider referenced by ECDSA key */ ++ k11->slotidx = slotidx; ++ /* identify key object on smartcard */ ++ k11->keyid_len = keyid_attrib->ulValueLen; ++ if (k11->keyid_len > 0) { ++ k11->keyid = xmalloc(k11->keyid_len); ++ memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); ++ } ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } ++ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++ EC_KEY_set_method(ecdsa, get_pkcs11_ecdsa_method()); ++ EC_KEY_set_ex_data(ecdsa, pkcs11_key_idx, k11); ++ #else ++ ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method()); ++ ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11); ++ #endif ++ return (0); ++} ++#endif /* ENABLE_PKCS11_ECDSA */ ++ ++int pkcs11_del_key(struct sshkey *key) { ++#ifdef ENABLE_PKCS11_ECDSA ++ if(key->type == KEY_ECDSA) { ++ struct pkcs11_key *k11 = (struct pkcs11_key *) ++ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++ EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx); ++ #else ++ ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx); ++ #endif ++ if (k11 == NULL) { ++ error("EC_KEY_get_ex_data failed for ecdsa %p", key->ecdsa); ++ } else { ++ if (k11->provider) ++ pkcs11_provider_unref(k11->provider); ++ free(k11->keyid); ++ free(k11); ++ } ++ } ++#endif /* ENABLE_PKCS11_ECDSA */ ++ sshkey_free(key); ++ return (0); ++} ++ + /* remove trailing spaces */ + static void + rmspace(u_char *buf, size_t len) +@@ -482,11 +646,13 @@ static int + pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, + struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { +- size_t filter_size = 1; ++ size_t filter_size = 2; ++ CK_KEY_TYPE pubkey_type = CKK_RSA; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE pubkey_filter[] = { + { CKA_CLASS, NULL, sizeof(pubkey_class) }, ++ { CKA_KEY_TYPE, NULL, sizeof(pubkey_type) }, + { CKA_ID, NULL, 0 }, + { CKA_LABEL, NULL, 0 } + }; +@@ -507,29 +673,60 @@ pkcs11_fetch_keys(struct pkcs11_provider + { CKA_SUBJECT, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; ++#ifdef ENABLE_PKCS11_ECDSA ++ CK_KEY_TYPE ecdsa_type = CKK_EC; ++ CK_ATTRIBUTE ecdsa_filter[] = { ++ { CKA_CLASS, NULL, sizeof(pubkey_class) }, ++ { CKA_KEY_TYPE, NULL, sizeof(ecdsa_type) }, ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 } ++ }; ++ CK_ATTRIBUTE ecdsa_attribs[] = { ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 }, ++ { CKA_EC_PARAMS, NULL, 0 }, ++ { CKA_EC_POINT, NULL, 0 } ++ }; ++ ecdsa_filter[0].pValue = &pubkey_class; ++ ecdsa_filter[1].pValue = &ecdsa_type; ++#endif /* ENABLE_PKCS11_ECDSA */ + pubkey_filter[0].pValue = &pubkey_class; ++ pubkey_filter[1].pValue = &pubkey_type; + cert_filter[0].pValue = &cert_class; + + if (uri->id != NULL) { + pubkey_filter[filter_size].pValue = uri->id; + pubkey_filter[filter_size].ulValueLen = uri->id_len; +- cert_filter[filter_size].pValue = uri->id; +- cert_filter[filter_size].ulValueLen = uri->id_len; ++#ifdef ENABLE_PKCS11_ECDSA ++ ecdsa_filter[filter_size].pValue = uri->id; ++ ecdsa_filter[filter_size].ulValueLen = uri->id_len; ++#endif /* ENABLE_PKCS11_ECDSA */ ++ cert_filter[filter_size-1].pValue = uri->id; ++ cert_filter[filter_size-1].ulValueLen = uri->id_len; + filter_size++; + } + if (uri->object != NULL) { + pubkey_filter[filter_size].pValue = uri->object; + pubkey_filter[filter_size].ulValueLen = strlen(uri->object); + pubkey_filter[filter_size].type = CKA_LABEL; +- cert_filter[filter_size].pValue = uri->object; +- cert_filter[filter_size].ulValueLen = strlen(uri->object); +- cert_filter[filter_size].type = CKA_LABEL; ++#ifdef ENABLE_PKCS11_ECDSA ++ ecdsa_filter[filter_size].pValue = uri->object; ++ ecdsa_filter[filter_size].ulValueLen = strlen(uri->object); ++ ecdsa_filter[filter_size].type = CKA_LABEL; ++#endif /* ENABLE_PKCS11_ECDSA */ ++ cert_filter[filter_size-1].pValue = uri->object; ++ cert_filter[filter_size-1].ulValueLen = strlen(uri->object); ++ cert_filter[filter_size-1].type = CKA_LABEL; + filter_size++; + } + + if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, + pubkey_attribs, keysp, nkeys) < 0 || +- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, ++#ifdef ENABLE_PKCS11_ECDSA ++ pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size, ++ ecdsa_attribs, keysp, nkeys) < 0|| ++#endif /* ENABLE_PKCS11_ECDSA */ ++ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1, + cert_attribs, keysp, nkeys) < 0) + return (-1); + return (0); +@@ -553,6 +746,11 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + { + struct sshkey *key; + RSA *rsa; ++#ifdef ENABLE_PKCS11_ECDSA ++ EC_KEY *ecdsa; ++#else ++ void *ecdsa; ++#endif /* ENABLE_PKCS11_ECDSA */ + X509 *x509; + EVP_PKEY *evp = NULL; + int i; +@@ -608,6 +806,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + * or ID, label, subject and value for certificates. + */ + rsa = NULL; ++#ifdef ENABLE_PKCS11_ECDSA ++ ecdsa = NULL; ++#endif /* ENABLE_PKCS11_ECDSA */ + if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); +@@ -620,6 +821,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + rsa->e = BN_bin2bn(attribs[3].pValue, + attribs[3].ulValueLen, NULL); + } ++#ifdef ENABLE_PKCS11_ECDSA ++ } else if (attribs[2].type == CKA_EC_PARAMS ) { ++ if ((ecdsa = EC_KEY_new()) == NULL) { ++ error("EC_KEY_new failed"); ++ } else { ++ const unsigned char *ptr1 = attribs[2].pValue; ++ const unsigned char *ptr2 = attribs[3].pValue; ++ CK_ULONG len1 = attribs[2].ulValueLen; ++ CK_ULONG len2 = attribs[3].ulValueLen; ++ ASN1_OCTET_STRING *point = NULL; ++ ++ /* ++ * CKA_EC_PARAMS contains the curve parameters of the key ++ * either referenced as an OID or directly with all values. ++ * CKA_EC_POINT contains the point (public key) on the curve. ++ * The point is should be returned inside a DER-encoded ++ * ASN.1 OCTET STRING value (but some implementation). ++ */ ++ if ((point = d2i_ASN1_OCTET_STRING(NULL, &ptr2, len2))) { ++ /* Pointing to OCTET STRING content */ ++ ptr2 = point->data; ++ len2 = point->length; ++ } else { ++ /* No OCTET STRING */ ++ ptr2 = attribs[3].pValue; ++ } ++ ++ if((d2i_ECParameters(&ecdsa, &ptr1, len1) == NULL) || ++ (o2i_ECPublicKey(&ecdsa, &ptr2, len2) == NULL)) { ++ EC_KEY_free(ecdsa); ++ ecdsa = NULL; ++ error("EC public key parsing failed"); ++ } ++ ++ if(point) { ++ ASN1_OCTET_STRING_free(point); ++ } ++ } ++#endif /* ENABLE_PKCS11_ECDSA */ + } else { + cp = attribs[3].pValue; + if ((x509 = X509_new()) == NULL) { +@@ -639,13 +879,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + X509_free(x509); + EVP_PKEY_free(evp); + } +- if (rsa && rsa->n && rsa->e && +- pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { +- if ((key = sshkey_new(KEY_UNSPEC)) == NULL) +- fatal("sshkey_new failed"); +- key->rsa = rsa; +- key->type = KEY_RSA; +- key->flags |= SSHKEY_FLAG_EXT; ++ key = NULL; ++ if (rsa || ecdsa) { ++ if (rsa && rsa->n && rsa->e && ++ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { ++ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) ++ fatal("sshkey_new failed"); ++ key->rsa = rsa; ++ key->type = KEY_RSA; ++ key->flags |= SSHKEY_FLAG_EXT; ++#ifdef ENABLE_PKCS11_ECDSA ++ } else if(ecdsa && pkcs11_ecdsa_wrap(p, slotidx, &attribs[0], &attribs[1], ecdsa) == 0) { ++ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) ++ fatal("sshkey_new failed"); ++ key->ecdsa = ecdsa; ++ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); ++ key->type = KEY_ECDSA; ++ key->flags |= SSHKEY_FLAG_EXT; ++#endif /* ENABLE_PKCS11_ECDSA */ ++ } ++ } ++ ++ if(key) { + if (pkcs11_key_included(keysp, nkeys, key)) { + sshkey_free(key); + } else { +@@ -658,6 +913,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + } + } else if (rsa) { + RSA_free(rsa); ++#ifdef ENABLE_PKCS11_ECDSA ++ } else if (ecdsa) { ++ EC_KEY_free(ecdsa); ++#endif /* ENABLE_PKCS11_ECDSA */ + } + for (i = 0; i < nattribs; i++) + free(attribs[i].pValue); +diff -up openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11-helper.c +--- openssh-7.6p1/ssh-pkcs11-helper.c.pkcs11-ecdsa 2017-10-02 21:34:26.000000000 +0200 ++++ openssh-7.6p1/ssh-pkcs11-helper.c 2018-02-16 13:25:59.428469265 +0100 +@@ -24,6 +24,17 @@ + + #include "openbsd-compat/sys-queue.h" + ++#include ++#ifdef OPENSSL_HAS_ECC ++#include ++#if ((defined(LIBRESSL_VERSION_NUMBER) && \ ++ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ ++ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ ++ (OPENSSL_VERSION_NUMBER >= 0x00010100L) ++#define ENABLE_PKCS11_ECDSA 1 ++#endif ++#endif ++ + #include + #include + #include +@@ -80,7 +90,7 @@ del_keys_by_name(char *name) + if (!strcmp(ki->providername, name)) { + TAILQ_REMOVE(&pkcs11_keylist, ki, next); + free(ki->providername); +- sshkey_free(ki->key); ++ pkcs11_del_key(ki->key); + free(ki); + } + } +@@ -164,6 +174,20 @@ process_del(void) + sshbuf_free(msg); + } + ++#ifdef ENABLE_PKCS11_ECDSA ++static u_int EC_KEY_order_size(EC_KEY *key) ++{ ++ const EC_GROUP *group = EC_KEY_get0_group(key); ++ BIGNUM *order = BN_new(); ++ u_int nbytes = 0; ++ if ((group != NULL) && (order != NULL) && EC_GROUP_get_order(group, order, NULL)) { ++ nbytes = BN_num_bytes(order); ++ } ++ BN_clear_free(order); ++ return nbytes; ++} ++#endif /* ENABLE_PKCS11_ECDSA */ ++ + static void + process_sign(void) + { +@@ -180,14 +204,38 @@ process_sign(void) + else { + if ((found = lookup_key(key)) != NULL) { + #ifdef WITH_OPENSSL +- int ret; +- +- slen = RSA_size(key->rsa); +- signature = xmalloc(slen); +- if ((ret = RSA_private_encrypt(dlen, data, signature, +- found->rsa, RSA_PKCS1_PADDING)) != -1) { +- slen = ret; +- ok = 0; ++ if(found->type == KEY_RSA) { ++ int ret; ++ slen = RSA_size(key->rsa); ++ signature = xmalloc(slen); ++ if ((ret = RSA_private_encrypt(dlen, data, signature, ++ found->rsa, RSA_PKCS1_PADDING)) != -1) { ++ slen = ret; ++ ok = 0; ++ } ++#ifdef ENABLE_PKCS11_ECDSA ++ } else if(found->type == KEY_ECDSA) { ++ ECDSA_SIG *sig; ++ const BIGNUM *r = NULL, *s = NULL; ++ if ((sig = ECDSA_do_sign(data, dlen, found->ecdsa)) != NULL) { ++ /* PKCS11 2.3.1 recommends both r and s to have the order size for ++ backward compatiblity */ ++ ECDSA_SIG_get0(sig, &r, &s); ++ u_int o_len = EC_KEY_order_size(found->ecdsa); ++ u_int r_len = BN_num_bytes(r); ++ u_int s_len = BN_num_bytes(s); ++ if (o_len > 0 && r_len <= o_len && s_len <= o_len) { ++ signature = xcalloc(2, o_len); ++ BN_bn2bin(r, signature + o_len - r_len); ++ BN_bn2bin(s, signature + (2 * o_len) - s_len); ++ slen = 2 * o_len; ++ ok = 0; ++ } ++ ECDSA_SIG_free(sig); ++ } ++#endif /* ENABLE_PKCS11_ECDSA */ ++ } else { ++ /* Unsupported type */ + } + #endif /* WITH_OPENSSL */ + } +diff -up openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa openssh-7.6p1/ssh-pkcs11.h +--- openssh-7.6p1/ssh-pkcs11.h.pkcs11-ecdsa 2018-02-16 13:25:59.429469272 +0100 ++++ openssh-7.6p1/ssh-pkcs11.h 2018-02-16 13:45:29.623800048 +0100 +@@ -20,6 +20,7 @@ + int pkcs11_init(int); + void pkcs11_terminate(void); + int pkcs11_add_provider(char *, char *, struct sshkey ***); ++int pkcs11_del_key(struct sshkey *); + int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); + int pkcs11_del_provider(char *); + int pkcs11_uri_write(const struct sshkey *, FILE *); + + + +diff -up openssh-7.6p1/ssh-pkcs11.c.old openssh-7.6p1/ssh-pkcs11.c +--- openssh-7.6p1/ssh-pkcs11.c.old 2018-02-16 16:43:08.861520255 +0100 ++++ openssh-7.6p1/ssh-pkcs11.c 2018-02-16 16:56:35.312601451 +0100 +@@ -917,13 +917,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p + } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) + == NULL) { + error("d2i_X509 failed"); +- } else if ((evp = X509_get_pubkey(x509)) == NULL || +- evp->type != EVP_PKEY_RSA || +- evp->pkey.rsa == NULL) { +- debug("X509_get_pubkey failed or no rsa"); +- } else if ((rsa = RSAPublicKey_dup(evp->pkey.rsa)) +- == NULL) { +- error("RSAPublicKey_dup"); ++ } else if ((evp = X509_get_pubkey(x509)) == NULL) { ++ debug("X509_get_pubkey failed"); ++ } else { ++ switch (evp->type) { ++ case EVP_PKEY_RSA: ++ if (evp->pkey.rsa == NULL) ++ debug("Missing RSA key"); ++ else if ((rsa = RSAPublicKey_dup( ++ evp->pkey.rsa)) == NULL) ++ error("RSAPublicKey_dup failed"); ++ break; ++ case EVP_PKEY_EC: ++ if (evp->pkey.ecdsa == NULL) ++ debug("Missing ECDSA key"); ++ else if ((ecdsa = EC_KEY_dup( ++ evp->pkey.ecdsa)) == NULL) ++ error("EC_KEY_dup failed"); ++ break; ++ default: ++ debug("not a RSA or ECDSA key"); ++ break; ++ } + } + X509_free(x509); + EVP_PKEY_free(evp); diff --git a/SOURCES/openssh-7.6p1-pkcs11-uri.patch b/SOURCES/openssh-7.6p1-pkcs11-uri.patch new file mode 100644 index 0000000..adc00bf --- /dev/null +++ b/SOURCES/openssh-7.6p1-pkcs11-uri.patch @@ -0,0 +1,4771 @@ +diff --git a/Makefile.in b/Makefile.in +index ac959c1f..f8ed1781 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + kexgssc.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ +- ssh-pkcs11.o smult_curve25519_ref.o \ ++ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ +@@ -248,6 +248,8 @@ clean: regressclean + rm -f regress/unittests/match/test_match$(EXEEXT) + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8$(EXEEXT) ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) + (cd openbsd-compat && $(MAKE) clean) +@@ -276,6 +278,8 @@ distclean: regressclean + rm -f regress/unittests/match/test_match + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8 ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11 + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz + (cd openbsd-compat && $(MAKE) distclean) +@@ -437,6 +441,7 @@ regress-prep: + $(MKDIR_P) `pwd`/regress/unittests/kex + $(MKDIR_P) `pwd`/regress/unittests/match + $(MKDIR_P) `pwd`/regress/unittests/utf8 ++ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 + $(MKDIR_P) `pwd`/regress/misc/kexfuzz + [ -f `pwd`/regress/Makefile ] || \ + ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile +@@ -455,6 +460,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++regress/soft-pkcs11.so: $(srcdir)/regress/soft-pkcs11.c $(REGRESSLIBS) ++ $(CC) $(CFLAGS) $(CPPFLAGS) -fpic -c $(srcdir)/regress/soft-pkcs11.c \ ++ -o $(srcdir)/regress/soft-pkcs11.o ++ $(CC) -shared -o $@ $(srcdir)/regress/soft-pkcs11.o ++ + regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) + $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ + $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) +@@ -556,6 +566,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++UNITTESTS_TEST_PKCS11_OBJS=\ ++ regress/unittests/pkcs11/tests.o ++ ++regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ ++ ${UNITTESTS_TEST_PKCS11_OBJS} \ ++ regress/unittests/test_helper/libtest_helper.a libssh.a ++ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ ++ regress/unittests/test_helper/libtest_helper.a \ ++ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ++ + MISC_KEX_FUZZ_OBJS=\ + regress/misc/kexfuzz/kexfuzz.o + +@@ -566,6 +586,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a + regress-binaries: regress/modpipe$(EXEEXT) \ + regress/setuid-allowed$(EXEEXT) \ + regress/netcat$(EXEEXT) \ ++ regress/soft-pkcs11.so \ + regress/check-perm$(EXEEXT) \ + regress/mkdtemp$(EXEEXT) \ + regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ +@@ -575,6 +596,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ + regress/unittests/kex/test_kex$(EXEEXT) \ + regress/unittests/match/test_match$(EXEEXT) \ + regress/unittests/utf8/test_utf8$(EXEEXT) \ ++ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ + regress/misc/kexfuzz/kexfuzz$(EXEEXT) + + tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) +diff --git a/authfd.c b/authfd.c +index 1eff7ba9..35153f47 100644 +--- a/authfd.c ++++ b/authfd.c +@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl) + if (idl->comments != NULL) + free(idl->comments[i]); + } ++ free(idl->keys); ++ free(idl->comments); + free(idl); + } + +diff --git a/configure.ac b/configure.ac +index d7bcaf01..171a8597 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1895,12 +1895,14 @@ AC_LINK_IFELSE( + [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) + ]) + ++SCARD_MSG="yes" + disable_pkcs11= + AC_ARG_ENABLE([pkcs11], + [ --disable-pkcs11 disable PKCS#11 support code [no]], + [ + if test "x$enableval" = "xno" ; then + disable_pkcs11=1 ++ SCARD_MSG="no" + fi + ] + ) +@@ -1916,6 +1918,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then + ) + fi + ++# Check whether we have a p11-kit, we got default provider on command line ++DEFAULT_PKCS11_PROVIDER_MSG="no" ++AC_ARG_WITH([default-pkcs11-provider], ++ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], ++ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then ++ if test "x$withval" = "xyes" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "x$PKGCONFIG" != "xno"; then ++ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) ++ if "$PKGCONFIG" "p11-kit-1"; then ++ AC_MSG_RESULT([yes]) ++ use_pkgconfig_for_p11kit=yes ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ else ++ PKCS11_PATH="${withval}" ++ fi ++ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then ++ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` ++ fi ++ AC_CHECK_FILE("$PKCS11_PATH", ++ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) ++ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" ++ ], ++ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] ++ ) ++ else ++ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) ++ fi ] ++) ++ ++ + # IRIX has a const char return value for gai_strerror() + AC_CHECK_FUNCS([gai_strerror], [ + AC_DEFINE([HAVE_GAI_STRERROR]) +@@ -5226,6 +5262,7 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" + echo " Random number source: $RAND_MSG" + echo " Privsep sandbox style: $SANDBOX_STYLE" + echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" ++echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" + + echo "" + +diff --git a/regress/Makefile b/regress/Makefile +index d15898ad..9c15afa4 100644 +--- a/regress/Makefile ++++ b/regress/Makefile +@@ -108,9 +110,11 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ + known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ + modpipe netcat no_identity_config \ + pidfile putty.rsa2 ready regress.log \ +- remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \ ++ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ ++ rsa-agent-cert.pub rsa.pub \ + rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ +- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ ++ soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \ ++ pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ + scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ +@@ -225,6 +229,7 @@ unit: + V="" ; \ + test "x${USE_VALGRIND}" = "x" || \ + V=${.CURDIR}/valgrind-unit.sh ; \ ++ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ + $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ + $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ + -d ${.CURDIR}/unittests/sshkey/testdata ; \ +diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh +index db3018b8..4991d0bc 100644 +--- a/regress/agent-pkcs11.sh ++++ b/regress/agent-pkcs11.sh +@@ -4,10 +4,17 @@ + tid="pkcs11 agent test" + + TEST_SSH_PIN="" +-TEST_SSH_PKCS11=/usr/local/lib/soft-pkcs11.so.0.0 ++TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so + + test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" + ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ + # setup environment for soft-pkcs11 token + SOFTPKCS11RC=$OBJ/pkcs11.info + export SOFTPKCS11RC +@@ -23,7 +30,7 @@ notty() { + } + + trace "start agent" +-eval `${SSHAGENT} -s` > /dev/null ++eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null + r=$? + if [ $r -ne 0 ]; then + fail "could not start ssh-agent: exit code $r" +@@ -60,7 +67,7 @@ else + fi + + trace "remove pkcs11 keys" +- echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 ++ ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 + r=$? + if [ $r -ne 0 ]; then + fail "ssh-add -e failed: exit code $r" +diff --git a/regress/locl.h b/regress/locl.h +new file mode 100644 +index 00000000..afefe27d +--- /dev/null ++++ b/regress/locl.h +@@ -0,0 +1,79 @@ ++/* ++ * Copyright (c) 2004, Stockholms universitet ++ * (Stockholm University, Stockholm Sweden) ++ * 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. ++ * ++ * 3. Neither the name of the university nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. ++ */ ++ ++/* $Id: locl.h,v 1.5 2005/08/28 15:30:31 lha Exp $ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include "../libcrypto-compat.h" ++ ++#include ++#include ++#include ++#include ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++ ++#include "../pkcs11.h" ++ ++#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R) \ ++{ \ ++ unsigned char *p; \ ++ (BL) = i2d_##T((S), NULL); \ ++ if ((BL) <= 0) { \ ++ (R) = EINVAL; \ ++ } else { \ ++ (B) = malloc((BL)); \ ++ if ((B) == NULL) { \ ++ (R) = ENOMEM; \ ++ } else { \ ++ p = (B); \ ++ (R) = 0; \ ++ (BL) = i2d_##T((S), &p); \ ++ if ((BL) <= 0) { \ ++ free((B)); \ ++ (R) = EINVAL; \ ++ } \ ++ } \ ++ } \ ++} +diff --git a/regress/pkcs11.sh b/regress/pkcs11.sh +new file mode 100644 +index 00000000..cf98e379 +--- /dev/null ++++ b/regress/pkcs11.sh +@@ -0,0 +1,300 @@ ++# ++# Copyright (c) 2017 Red Hat ++# ++# Authors: Jakub Jelen ++# ++# 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. ++ ++tid="pkcs11 tests with soft token" ++ ++TEST_SSH_PIN="" ++TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so ++ ++test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" ++ ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ ++# setup environment for soft-pkcs11 token ++SOFTPKCS11RC=$OBJ/pkcs11.info ++rm -f $SOFTPKCS11RC ++export SOFTPKCS11RC ++# prevent ssh-agent from calling ssh-askpass ++SSH_ASKPASS=/usr/bin/true ++export SSH_ASKPASS ++unset DISPLAY ++ ++# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) ++notty() { ++ perl -e 'use POSIX; POSIX::setsid(); ++ if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" ++} ++ ++create_key() { ++ ID=$1 ++ LABEL=$2 ++ rm -f $OBJ/pkcs11-${ID}.key $OBJ/pkcs11-${ID}.crt ++ openssl genrsa -out $OBJ/pkcs11-${ID}.key 2048 > /dev/null 2>&1 ++ chmod 600 $OBJ/pkcs11-${ID}.key ++ openssl req -key $OBJ/pkcs11-${ID}.key -new -x509 \ ++ -out $OBJ/pkcs11-${ID}.crt -text -subj '/CN=pkcs11 test' >/dev/null ++ printf "${ID}\t${LABEL}\t$OBJ/pkcs11-${ID}.crt\t$OBJ/pkcs11-${ID}.key\n" \ ++ >> $SOFTPKCS11RC ++} ++ ++trace "Create a key pairs on soft token" ++ID1="02" ++ID2="04" ++create_key "$ID1" "SSH RSA Key" ++create_key "$ID2" "SSH RSA Key 2" ++ ++trace "List the keys in the ssh-keygen with PKCS#11 URIs" ++${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys ++if [ $? -ne 0 ]; then ++ fail "keygen fails to enumerate keys on PKCS#11 token" ++fi ++grep "pkcs11:" $OBJ/token_keys > /dev/null ++if [ $? -ne 0 ]; then ++ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" ++fi ++tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++ ++ ++trace "Simple connect with ssh (without PKCS#11 URI)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ ++ -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with pkcs11 failed (exit code $r)" ++fi ++ ++ ++trace "Connect with PKCS#11 URI" ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace "Connect with various filtering options in PKCS#11 URI" ++trace " (by object label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by object label, first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace " (by token label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by wrong token label, should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++ ++ ++ ++trace "Test PKCS#11 URI specification in configuration files" ++echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ ++ >> $OBJ/ssh_proxy ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++trace "Test PKCS#11 URI specification in configuration files with bogus spaces" ++echo "IdentityFile \" pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} \"" \ ++ >> $OBJ/ssh_proxy ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ ++ "(exit code $r)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++ ++trace "Combination of PKCS11Provider and PKCS11URI on commandline" ++trace " (first key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI and provider combination" \ ++ "failed (exit code $r)" ++fi ++ ++trace "Regress: Missing provider in PKCS11URI option" ++${SSH} -F $OBJ/ssh_proxy \ ++ -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 ++r=$? ++if [ $r -eq 139 ]; then ++ fail "ssh connect with missing provider_id from configuration option" \ ++ "crashed (exit code $r)" ++fi ++ ++ ++trace "SSH Agent can work with PKCS#11 URI" ++trace "start the agent" ++eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null ++ ++r=$? ++if [ $r -ne 0 ]; then ++ fail "could not start ssh-agent: exit code $r" ++else ++ trace "add whole provider to agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 list via agent (all keys)" ++ ${SSHADD} -l > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -l failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (all keys)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with whole provider (exit code $r)" ++ fi ++ ++ trace " remove pkcs11 keys (all keys)" ++ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with whole provider: exit code $r" ++ fi ++ ++ trace "add only first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (first key)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (exit code $r)" ++ fi ++ ++ trace " remove first pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with first key: exit code $r" ++ fi ++ ++ trace "add only second key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with second key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (second key should fail)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -eq 5 ]; then ++ fail "ssh connect passed without key (should fail)" ++ fi ++ ++ trace "add also the first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " remove second pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with second key: exit code $r" ++ fi ++ ++ trace " remove already-removed pkcs11 key should fail" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -eq 0 ]; then ++ fail "ssh-add -d passed with non-existing key (should fail)" ++ fi ++ ++ trace " pkcs11 connect via agent (the first key should be still usable)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (after removing second): exit code $r" ++ fi ++ ++ trace "kill agent" ++ ${SSHAGENT} -k > /dev/null ++fi ++ ++rm -rf $OBJ/.tokens $OBJ/token_keys +diff --git a/regress/soft-pkcs11.c b/regress/soft-pkcs11.c +new file mode 100644 +index 00000000..8b4981bd +--- /dev/null ++++ b/regress/soft-pkcs11.c +@@ -0,0 +1,2058 @@ ++/* ++ * Copyright (c) 2004-2006, Stockholms universitet ++ * (Stockholm University, Stockholm Sweden) ++ * 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. ++ * ++ * 3. Neither the name of the university nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 "locl.h" ++ ++/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */ ++ ++#define OBJECT_ID_MASK 0xfff ++#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) ++#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) ++ ++#if OPENSSL_VERSION_NUMBER < 0x10100000L ++ #define RSA_PKCS1_SSLeay RSA_PKCS1_OpenSSL ++#endif ++ ++struct st_attr { ++ CK_ATTRIBUTE attribute; ++ int secret; ++}; ++ ++struct st_object { ++ CK_OBJECT_HANDLE object_handle; ++ struct st_attr *attrs; ++ int num_attributes; ++ enum { ++ STO_T_CERTIFICATE, ++ STO_T_PRIVATE_KEY, ++ STO_T_PUBLIC_KEY ++ } type; ++ union { ++ X509 *cert; ++ EVP_PKEY *public_key; ++ struct { ++ const char *file; ++ EVP_PKEY *key; ++ X509 *cert; ++ } private_key; ++ } u; ++}; ++ ++static struct soft_token { ++ CK_VOID_PTR application; ++ CK_NOTIFY notify; ++ struct { ++ struct st_object **objs; ++ int num_objs; ++ } object; ++ struct { ++ int hardware_slot; ++ int app_error_fatal; ++ int login_done; ++ } flags; ++ int open_sessions; ++ struct session_state { ++ CK_SESSION_HANDLE session_handle; ++ ++ struct { ++ CK_ATTRIBUTE *attributes; ++ CK_ULONG num_attributes; ++ int next_object; ++ } find; ++ ++ int encrypt_object; ++ CK_MECHANISM_PTR encrypt_mechanism; ++ int decrypt_object; ++ CK_MECHANISM_PTR decrypt_mechanism; ++ int sign_object; ++ CK_MECHANISM_PTR sign_mechanism; ++ int verify_object; ++ CK_MECHANISM_PTR verify_mechanism; ++ int digest_object; ++ } state[10]; ++#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) ++ FILE *logfile; ++} soft_token; ++ ++static void ++application_error(const char *fmt, ...) ++{ ++ va_list ap; ++ va_start(ap, fmt); ++ vprintf(fmt, ap); ++ va_end(ap); ++ if (soft_token.flags.app_error_fatal) ++ abort(); ++} ++ ++static void ++st_logf(const char *fmt, ...) ++{ ++ va_list ap; ++ if (soft_token.logfile == NULL) ++ return; ++ va_start(ap, fmt); ++ vfprintf(soft_token.logfile, fmt, ap); ++ va_end(ap); ++ fflush(soft_token.logfile); ++} ++ ++static void ++snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) ++{ ++ int len; ++ va_list ap; ++ len = vsnprintf(str, size, fmt, ap); ++ va_end(ap); ++ if (len < 0 || len > (int) size) ++ return; ++ while(len < (int) size) ++ str[len++] = fillchar; ++} ++ ++#ifndef TEST_APP ++#define printf error_use_st_logf ++#endif ++ ++#define VERIFY_SESSION_HANDLE(s, state) \ ++{ \ ++ CK_RV ret; \ ++ ret = verify_session_handle(s, state); \ ++ if (ret != CKR_OK) { \ ++ /* return CKR_OK */; \ ++ } \ ++} ++ ++static CK_RV ++verify_session_handle(CK_SESSION_HANDLE hSession, ++ struct session_state **state) ++{ ++ size_t i; ++ ++ for (i = 0; i < MAX_NUM_SESSION; i++){ ++ if (soft_token.state[i].session_handle == hSession) ++ break; ++ } ++ if (i == MAX_NUM_SESSION) { ++ application_error("use of invalid handle: 0x%08lx\n", ++ (unsigned long)hSession); ++ return CKR_SESSION_HANDLE_INVALID; ++ } ++ if (state) ++ *state = &soft_token.state[i]; ++ return CKR_OK; ++} ++ ++static CK_RV ++object_handle_to_object(CK_OBJECT_HANDLE handle, ++ struct st_object **object) ++{ ++ int i = HANDLE_OBJECT_ID(handle); ++ ++ *object = NULL; ++ if (i >= soft_token.object.num_objs) ++ return CKR_ARGUMENTS_BAD; ++ if (soft_token.object.objs[i] == NULL) ++ return CKR_ARGUMENTS_BAD; ++ if (soft_token.object.objs[i]->object_handle != handle) ++ return CKR_ARGUMENTS_BAD; ++ *object = soft_token.object.objs[i]; ++ return CKR_OK; ++} ++ ++static int ++attributes_match(const struct st_object *obj, ++ const CK_ATTRIBUTE *attributes, ++ CK_ULONG num_attributes) ++{ ++ CK_ULONG i; ++ int j; ++ st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); ++ ++ for (i = 0; i < num_attributes; i++) { ++ int match = 0; ++ for (j = 0; j < obj->num_attributes; j++) { ++ if (attributes[i].type == obj->attrs[j].attribute.type && ++ attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && ++ memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, ++ attributes[i].ulValueLen) == 0) { ++ match = 1; ++ break; ++ } ++ } ++ if (match == 0) { ++ st_logf("type %d attribute have no match\n", attributes[i].type); ++ return 0; ++ } ++ } ++ st_logf("attribute matches\n"); ++ return 1; ++} ++ ++static void ++print_attributes(const CK_ATTRIBUTE *attributes, ++ CK_ULONG num_attributes) ++{ ++ CK_ULONG i; ++ ++ st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); ++ ++ for (i = 0; i < num_attributes; i++) { ++ st_logf(" type: "); ++ switch (attributes[i].type) { ++ case CKA_TOKEN: { ++ CK_BBOOL *ck_true; ++ if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { ++ application_error("token attribute wrong length\n"); ++ break; ++ } ++ ck_true = attributes[i].pValue; ++ st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); ++ break; ++ } ++ case CKA_CLASS: { ++ CK_OBJECT_CLASS *class; ++ if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { ++ application_error("class attribute wrong length\n"); ++ break; ++ } ++ class = attributes[i].pValue; ++ st_logf("class "); ++ switch (*class) { ++ case CKO_CERTIFICATE: ++ st_logf("certificate"); ++ break; ++ case CKO_PUBLIC_KEY: ++ st_logf("public key"); ++ break; ++ case CKO_PRIVATE_KEY: ++ st_logf("private key"); ++ break; ++ case CKO_SECRET_KEY: ++ st_logf("secret key"); ++ break; ++ case CKO_DOMAIN_PARAMETERS: ++ st_logf("domain parameters"); ++ break; ++ default: ++ st_logf("[class %lx]", (long unsigned)*class); ++ break; ++ } ++ break; ++ } ++ case CKA_PRIVATE: ++ st_logf("private"); ++ break; ++ case CKA_LABEL: ++ st_logf("label"); ++ break; ++ case CKA_APPLICATION: ++ st_logf("application"); ++ break; ++ case CKA_VALUE: ++ st_logf("value"); ++ break; ++ case CKA_ID: ++ st_logf("id"); ++ break; ++ default: ++ st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); ++ break; ++ } ++ st_logf("\n"); ++ } ++} ++ ++static struct st_object * ++add_st_object(void) ++{ ++ struct st_object *o, **objs; ++ int i; ++ ++ o = malloc(sizeof(*o)); ++ if (o == NULL) ++ return NULL; ++ memset(o, 0, sizeof(*o)); ++ o->attrs = NULL; ++ o->num_attributes = 0; ++ ++ for (i = 0; i < soft_token.object.num_objs; i++) { ++ if (soft_token.object.objs == NULL) { ++ soft_token.object.objs[i] = o; ++ break; ++ } ++ } ++ if (i == soft_token.object.num_objs) { ++ objs = realloc(soft_token.object.objs, ++ (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); ++ if (objs == NULL) { ++ free(o); ++ return NULL; ++ } ++ soft_token.object.objs = objs; ++ soft_token.object.objs[soft_token.object.num_objs++] = o; ++ } ++ soft_token.object.objs[i]->object_handle = ++ (random() & (~OBJECT_ID_MASK)) | i; ++ ++ return o; ++} ++ ++static CK_RV ++add_object_attribute(struct st_object *o, ++ int secret, ++ CK_ATTRIBUTE_TYPE type, ++ CK_VOID_PTR pValue, ++ CK_ULONG ulValueLen) ++{ ++ struct st_attr *a; ++ int i; ++ ++ i = o->num_attributes; ++ a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); ++ if (a == NULL) ++ return CKR_DEVICE_MEMORY; ++ o->attrs = a; ++ o->attrs[i].secret = secret; ++ o->attrs[i].attribute.type = type; ++ o->attrs[i].attribute.pValue = malloc(ulValueLen); ++ if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) ++ return CKR_DEVICE_MEMORY; ++ memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); ++ o->attrs[i].attribute.ulValueLen = ulValueLen; ++ o->num_attributes++; ++ ++ return CKR_OK; ++} ++ ++static CK_RV ++add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key) ++{ ++ switch (key_type) { ++ case CKK_RSA: { ++ CK_BYTE *modulus = NULL; ++ size_t modulus_len = 0; ++ CK_ULONG modulus_bits = 0; ++ CK_BYTE *exponent = NULL; ++ size_t exponent_len = 0; ++ RSA* rsa = NULL; ++ const BIGNUM *n = NULL, *e = NULL; ++ ++ rsa = EVP_PKEY_get0_RSA(key); ++ RSA_get0_key(rsa, &n, &e, NULL); ++ ++ modulus_bits = BN_num_bits(n); ++ ++ modulus_len = BN_num_bytes(n); ++ modulus = malloc(modulus_len); ++ BN_bn2bin(n, modulus); ++ ++ exponent_len = BN_num_bytes(e); ++ exponent = malloc(exponent_len); ++ BN_bn2bin(e, exponent); ++ ++ add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); ++ add_object_attribute(o, 0, CKA_MODULUS_BITS, ++ &modulus_bits, sizeof(modulus_bits)); ++ add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, ++ exponent, exponent_len); ++ ++ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); ++ ++ free(modulus); ++ free(exponent); ++ } ++ default: ++ /* XXX */ ++ break; ++ } ++ return CKR_OK; ++} ++ ++ ++static int ++pem_callback(char *buf, int num, int w, void *key) ++{ ++ return -1; ++} ++ ++ ++static CK_RV ++add_certificate(char *label, ++ const char *cert_file, ++ const char *private_key_file, ++ char *id, ++ int anchor) ++{ ++ struct st_object *o = NULL; ++ CK_BBOOL bool_true = CK_TRUE; ++ CK_BBOOL bool_false = CK_FALSE; ++ CK_OBJECT_CLASS c; ++ CK_CERTIFICATE_TYPE cert_type = CKC_X_509; ++ CK_KEY_TYPE key_type; ++ CK_MECHANISM_TYPE mech_type; ++ void *cert_data = NULL; ++ size_t cert_length; ++ void *subject_data = NULL; ++ size_t subject_length; ++ void *issuer_data = NULL; ++ size_t issuer_length; ++ void *serial_data = NULL; ++ size_t serial_length; ++ CK_RV ret = CKR_GENERAL_ERROR; ++ X509 *cert; ++ EVP_PKEY *public_key; ++ ++ size_t id_len = strlen(id); ++ ++ { ++ FILE *f; ++ ++ f = fopen(cert_file, "r"); ++ if (f == NULL) { ++ st_logf("failed to open file %s\n", cert_file); ++ return CKR_GENERAL_ERROR; ++ } ++ ++ cert = PEM_read_X509(f, NULL, NULL, NULL); ++ fclose(f); ++ if (cert == NULL) { ++ st_logf("failed reading PEM cert\n"); ++ return CKR_GENERAL_ERROR; ++ } ++ ++ OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret); ++ if (ret) ++ goto out; ++ ++ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length, ++ X509_get_issuer_name(cert), ret); ++ if (ret) ++ goto out; ++ ++ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length, ++ X509_get_subject_name(cert), ret); ++ if (ret) ++ goto out; ++ ++ OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length, ++ X509_get_serialNumber(cert), ret); ++ if (ret) ++ goto out; ++ ++ } ++ ++ st_logf("done parsing, adding to internal structure\n"); ++ ++ o = add_st_object(); ++ if (o == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ o->type = STO_T_CERTIFICATE; ++ o->u.cert = cert; ++ public_key = X509_get_pubkey(o->u.cert); ++ ++ switch (EVP_PKEY_base_id(public_key)) { ++ case EVP_PKEY_RSA: ++ key_type = CKK_RSA; ++ break; ++ case EVP_PKEY_DSA: ++ key_type = CKK_DSA; ++ break; ++ default: ++ /* XXX */ ++ break; ++ } ++ ++ c = CKO_CERTIFICATE; ++ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); ++ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); ++ ++ add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); ++ add_object_attribute(o, 0, CKA_ID, id, id_len); ++ ++ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); ++ add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length); ++ add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length); ++ add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length); ++ if (anchor) ++ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); ++ else ++ add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); ++ ++ st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); ++ ++ o = add_st_object(); ++ if (o == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ o->type = STO_T_PUBLIC_KEY; ++ o->u.public_key = public_key; ++ ++ c = CKO_PUBLIC_KEY; ++ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); ++ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); ++ ++ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); ++ add_object_attribute(o, 0, CKA_ID, id, id_len); ++ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ ++ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ ++ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); ++ mech_type = CKM_RSA_X_509; ++ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); ++ ++ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); ++ add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); ++ ++ add_pubkey_info(o, key_type, public_key); ++ ++ st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); ++ ++ if (private_key_file) { ++ CK_FLAGS flags; ++ FILE *f; ++ ++ o = add_st_object(); ++ if (o == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ o->type = STO_T_PRIVATE_KEY; ++ o->u.private_key.file = strdup(private_key_file); ++ o->u.private_key.key = NULL; ++ ++ o->u.private_key.cert = cert; ++ ++ c = CKO_PRIVATE_KEY; ++ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); ++ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); ++ ++ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); ++ add_object_attribute(o, 0, CKA_ID, id, id_len); ++ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ ++ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ ++ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); ++ mech_type = CKM_RSA_X_509; ++ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); ++ ++ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); ++ add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); ++ flags = 0; ++ add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); ++ ++ add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); ++ add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); ++ add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); ++ ++ add_pubkey_info(o, key_type, public_key); ++ ++ f = fopen(private_key_file, "r"); ++ if (f == NULL) { ++ st_logf("failed to open private key\n"); ++ return CKR_GENERAL_ERROR; ++ } ++ ++ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL); ++ fclose(f); ++ if (o->u.private_key.key == NULL) { ++ st_logf("failed to read private key a startup\n"); ++ /* don't bother with this failure for now, ++ fix it at C_Login time */; ++ } else { ++ /* XXX verify keytype */ ++ ++ if (key_type == CKK_RSA) { ++ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); ++ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); ++ } ++ ++ if (X509_check_private_key(cert, o->u.private_key.key) != 1) { ++ EVP_PKEY_free(o->u.private_key.key); ++ o->u.private_key.key = NULL; ++ st_logf("private key doesn't verify\n"); ++ } else { ++ st_logf("private key usable\n"); ++ soft_token.flags.login_done = 1; ++ } ++ } ++ } ++ ++ ret = CKR_OK; ++ out: ++ if (ret != CKR_OK) { ++ st_logf("something went wrong when adding cert!\n"); ++ ++ /* XXX wack o */; ++ } ++ free(cert_data); ++ free(serial_data); ++ free(issuer_data); ++ free(subject_data); ++ ++ return ret; ++} ++ ++static void ++find_object_final(struct session_state *state) ++{ ++ if (state->find.attributes) { ++ CK_ULONG i; ++ ++ for (i = 0; i < state->find.num_attributes; i++) { ++ if (state->find.attributes[i].pValue) ++ free(state->find.attributes[i].pValue); ++ } ++ free(state->find.attributes); ++ state->find.attributes = NULL; ++ state->find.num_attributes = 0; ++ state->find.next_object = -1; ++ } ++} ++ ++static void ++reset_crypto_state(struct session_state *state) ++{ ++ state->encrypt_object = -1; ++ if (state->encrypt_mechanism) ++ free(state->encrypt_mechanism); ++ state->encrypt_mechanism = NULL_PTR; ++ state->decrypt_object = -1; ++ if (state->decrypt_mechanism) ++ free(state->decrypt_mechanism); ++ state->decrypt_mechanism = NULL_PTR; ++ state->sign_object = -1; ++ if (state->sign_mechanism) ++ free(state->sign_mechanism); ++ state->sign_mechanism = NULL_PTR; ++ state->verify_object = -1; ++ if (state->verify_mechanism) ++ free(state->verify_mechanism); ++ state->verify_mechanism = NULL_PTR; ++ state->digest_object = -1; ++} ++ ++static void ++close_session(struct session_state *state) ++{ ++ if (state->find.attributes) { ++ application_error("application didn't do C_FindObjectsFinal\n"); ++ find_object_final(state); ++ } ++ ++ state->session_handle = CK_INVALID_HANDLE; ++ soft_token.application = NULL_PTR; ++ soft_token.notify = NULL_PTR; ++ reset_crypto_state(state); ++} ++ ++static const char * ++has_session(void) ++{ ++ return soft_token.open_sessions > 0 ? "yes" : "no"; ++} ++ ++static void ++read_conf_file(const char *fn) ++{ ++ char buf[1024], *cert, *key, *id, *label, *s, *p; ++ int anchor; ++ FILE *f; ++ ++ f = fopen(fn, "r"); ++ if (f == NULL) { ++ st_logf("can't open configuration file %s\n", fn); ++ return; ++ } ++ ++ while(fgets(buf, sizeof(buf), f) != NULL) { ++ buf[strcspn(buf, "\n")] = '\0'; ++ ++ anchor = 0; ++ ++ st_logf("line: %s\n", buf); ++ ++ p = buf; ++ while (isspace(*p)) ++ p++; ++ if (*p == '#') ++ continue; ++ while (isspace(*p)) ++ p++; ++ ++ s = NULL; ++ id = strtok_r(p, "\t", &s); ++ if (id == NULL) ++ continue; ++ label = strtok_r(NULL, "\t", &s); ++ if (label == NULL) ++ continue; ++ cert = strtok_r(NULL, "\t", &s); ++ if (cert == NULL) ++ continue; ++ key = strtok_r(NULL, "\t", &s); ++ ++ /* XXX */ ++ if (strcmp(id, "anchor") == 0) { ++ id = "\x00\x00"; ++ anchor = 1; ++ } ++ ++ st_logf("adding: %s\n", label); ++ ++ add_certificate(label, cert, key, id, anchor); ++ } ++} ++ ++static CK_RV ++func_not_supported(void) ++{ ++ st_logf("function not supported\n"); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_Initialize(CK_VOID_PTR a) ++{ ++ CK_C_INITIALIZE_ARGS_PTR args = a; ++ st_logf("Initialize\n"); ++ size_t i; ++ ++ OpenSSL_add_all_algorithms(); ++ ERR_load_crypto_strings(); ++ ++ srandom(getpid() ^ time(NULL)); ++ ++ for (i = 0; i < MAX_NUM_SESSION; i++) { ++ soft_token.state[i].session_handle = CK_INVALID_HANDLE; ++ soft_token.state[i].find.attributes = NULL; ++ soft_token.state[i].find.num_attributes = 0; ++ soft_token.state[i].find.next_object = -1; ++ reset_crypto_state(&soft_token.state[i]); ++ } ++ ++ soft_token.flags.hardware_slot = 1; ++ soft_token.flags.app_error_fatal = 0; ++ soft_token.flags.login_done = 0; ++ ++ soft_token.object.objs = NULL; ++ soft_token.object.num_objs = 0; ++ ++ soft_token.logfile = NULL; ++#if 1 ++// soft_token.logfile = stdout; ++#endif ++#if 0 ++ soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); ++#endif ++ ++ if (a != NULL_PTR) { ++ st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); ++ st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); ++ st_logf("\tLockMutext\t%p\n", args->LockMutex); ++ st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); ++ st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); ++ } ++ ++ { ++ char *fn = NULL, *home = NULL; ++ ++ if (getuid() == geteuid()) { ++ fn = getenv("SOFTPKCS11RC"); ++ if (fn) ++ fn = strdup(fn); ++ home = getenv("HOME"); ++ } ++ if (fn == NULL && home == NULL) { ++ struct passwd *pw = getpwuid(getuid()); ++ if(pw != NULL) ++ home = pw->pw_dir; ++ } ++ if (fn == NULL) { ++ if (home) ++ asprintf(&fn, "%s/.soft-token.rc", home); ++ else ++ fn = strdup("/etc/soft-token.rc"); ++ } ++ ++ read_conf_file(fn); ++ free(fn); ++ } ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_Finalize(CK_VOID_PTR args) ++{ ++ size_t i; ++ ++ st_logf("Finalize\n"); ++ ++ for (i = 0; i < MAX_NUM_SESSION; i++) { ++ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { ++ application_error("application finalized without " ++ "closing session\n"); ++ close_session(&soft_token.state[i]); ++ } ++ } ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetInfo(CK_INFO_PTR args) ++{ ++ st_logf("GetInfo\n"); ++ ++ memset(args, 17, sizeof(*args)); ++ args->cryptokiVersion.major = 2; ++ args->cryptokiVersion.minor = 10; ++ snprintf_fill((char *)args->manufacturerID, ++ sizeof(args->manufacturerID), ++ ' ', ++ "SoftToken"); ++ snprintf_fill((char *)args->libraryDescription, ++ sizeof(args->libraryDescription), ' ', ++ "SoftToken"); ++ args->libraryVersion.major = 1; ++ args->libraryVersion.minor = 8; ++ ++ return CKR_OK; ++} ++ ++extern CK_FUNCTION_LIST funcs; ++ ++CK_RV ++C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) ++{ ++ *ppFunctionList = &funcs; ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetSlotList(CK_BBOOL tokenPresent, ++ CK_SLOT_ID_PTR pSlotList, ++ CK_ULONG_PTR pulCount) ++{ ++ st_logf("GetSlotList: %s\n", ++ tokenPresent ? "tokenPresent" : "token not Present"); ++ if (pSlotList) ++ pSlotList[0] = 1; ++ *pulCount = 1; ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetSlotInfo(CK_SLOT_ID slotID, ++ CK_SLOT_INFO_PTR pInfo) ++{ ++ st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); ++ ++ memset(pInfo, 18, sizeof(*pInfo)); ++ ++ if (slotID != 1) ++ return CKR_ARGUMENTS_BAD; ++ ++ snprintf_fill((char *)pInfo->slotDescription, ++ sizeof(pInfo->slotDescription), ++ ' ', ++ "SoftToken (slot)"); ++ snprintf_fill((char *)pInfo->manufacturerID, ++ sizeof(pInfo->manufacturerID), ++ ' ', ++ "SoftToken (slot)"); ++ pInfo->flags = CKF_TOKEN_PRESENT; ++ if (soft_token.flags.hardware_slot) ++ pInfo->flags |= CKF_HW_SLOT; ++ pInfo->hardwareVersion.major = 1; ++ pInfo->hardwareVersion.minor = 0; ++ pInfo->firmwareVersion.major = 1; ++ pInfo->firmwareVersion.minor = 0; ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetTokenInfo(CK_SLOT_ID slotID, ++ CK_TOKEN_INFO_PTR pInfo) ++{ ++ st_logf("GetTokenInfo: %s\n", has_session()); ++ ++ memset(pInfo, 19, sizeof(*pInfo)); ++ ++ snprintf_fill((char *)pInfo->label, ++ sizeof(pInfo->label), ++ ' ', ++ "SoftToken (token)"); ++ snprintf_fill((char *)pInfo->manufacturerID, ++ sizeof(pInfo->manufacturerID), ++ ' ', ++ "SoftToken (token)"); ++ snprintf_fill((char *)pInfo->model, ++ sizeof(pInfo->model), ++ ' ', ++ "SoftToken (token)"); ++ snprintf_fill((char *)pInfo->serialNumber, ++ sizeof(pInfo->serialNumber), ++ ' ', ++ "4711"); ++ pInfo->flags = ++ CKF_TOKEN_INITIALIZED | ++ CKF_USER_PIN_INITIALIZED; ++ ++ if (soft_token.flags.login_done == 0) ++ pInfo->flags |= CKF_LOGIN_REQUIRED; ++ ++ /* CFK_RNG | ++ CKF_RESTORE_KEY_NOT_NEEDED | ++ */ ++ pInfo->ulMaxSessionCount = MAX_NUM_SESSION; ++ pInfo->ulSessionCount = soft_token.open_sessions; ++ pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; ++ pInfo->ulRwSessionCount = soft_token.open_sessions; ++ pInfo->ulMaxPinLen = 1024; ++ pInfo->ulMinPinLen = 0; ++ pInfo->ulTotalPublicMemory = 4711; ++ pInfo->ulFreePublicMemory = 4712; ++ pInfo->ulTotalPrivateMemory = 4713; ++ pInfo->ulFreePrivateMemory = 4714; ++ pInfo->hardwareVersion.major = 2; ++ pInfo->hardwareVersion.minor = 0; ++ pInfo->firmwareVersion.major = 2; ++ pInfo->firmwareVersion.minor = 0; ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetMechanismList(CK_SLOT_ID slotID, ++ CK_MECHANISM_TYPE_PTR pMechanismList, ++ CK_ULONG_PTR pulCount) ++{ ++ st_logf("GetMechanismList\n"); ++ ++ *pulCount = 2; ++ if (pMechanismList == NULL_PTR) ++ return CKR_OK; ++ pMechanismList[0] = CKM_RSA_X_509; ++ pMechanismList[1] = CKM_RSA_PKCS; ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetMechanismInfo(CK_SLOT_ID slotID, ++ CK_MECHANISM_TYPE type, ++ CK_MECHANISM_INFO_PTR pInfo) ++{ ++ st_logf("GetMechanismInfo: slot %d type: %d\n", ++ (int)slotID, (int)type); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_InitToken(CK_SLOT_ID slotID, ++ CK_UTF8CHAR_PTR pPin, ++ CK_ULONG ulPinLen, ++ CK_UTF8CHAR_PTR pLabel) ++{ ++ st_logf("InitToken: slot %d\n", (int)slotID); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_OpenSession(CK_SLOT_ID slotID, ++ CK_FLAGS flags, ++ CK_VOID_PTR pApplication, ++ CK_NOTIFY Notify, ++ CK_SESSION_HANDLE_PTR phSession) ++{ ++ size_t i; ++ ++ st_logf("OpenSession: slot: %d\n", (int)slotID); ++ ++ if (soft_token.open_sessions == MAX_NUM_SESSION) ++ return CKR_SESSION_COUNT; ++ ++ soft_token.application = pApplication; ++ soft_token.notify = Notify; ++ ++ for (i = 0; i < MAX_NUM_SESSION; i++) ++ if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) ++ break; ++ if (i == MAX_NUM_SESSION) ++ abort(); ++ ++ soft_token.open_sessions++; ++ ++ soft_token.state[i].session_handle = ++ (CK_SESSION_HANDLE)(random() & 0xfffff); ++ *phSession = soft_token.state[i].session_handle; ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_CloseSession(CK_SESSION_HANDLE hSession) ++{ ++ struct session_state *state; ++ st_logf("CloseSession\n"); ++ ++ if (verify_session_handle(hSession, &state) != CKR_OK) ++ application_error("closed session not open"); ++ else ++ close_session(state); ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_CloseAllSessions(CK_SLOT_ID slotID) ++{ ++ size_t i; ++ ++ st_logf("CloseAllSessions\n"); ++ ++ for (i = 0; i < MAX_NUM_SESSION; i++) ++ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) ++ close_session(&soft_token.state[i]); ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_GetSessionInfo(CK_SESSION_HANDLE hSession, ++ CK_SESSION_INFO_PTR pInfo) ++{ ++ st_logf("GetSessionInfo\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ ++ memset(pInfo, 20, sizeof(*pInfo)); ++ ++ pInfo->slotID = 1; ++ if (soft_token.flags.login_done) ++ pInfo->state = CKS_RO_USER_FUNCTIONS; ++ else ++ pInfo->state = CKS_RO_PUBLIC_SESSION; ++ pInfo->flags = CKF_SERIAL_SESSION; ++ pInfo->ulDeviceError = 0; ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_Login(CK_SESSION_HANDLE hSession, ++ CK_USER_TYPE userType, ++ CK_UTF8CHAR_PTR pPin, ++ CK_ULONG ulPinLen) ++{ ++ char *pin = NULL; ++ int i; ++ ++ st_logf("Login\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ ++ if (pPin != NULL_PTR) { ++ asprintf(&pin, "%.*s", (int)ulPinLen, pPin); ++ st_logf("type: %d password: %s\n", (int)userType, pin); ++ } ++ ++ for (i = 0; i < soft_token.object.num_objs; i++) { ++ struct st_object *o = soft_token.object.objs[i]; ++ FILE *f; ++ ++ if (o->type != STO_T_PRIVATE_KEY) ++ continue; ++ ++ if (o->u.private_key.key) ++ continue; ++ ++ f = fopen(o->u.private_key.file, "r"); ++ if (f == NULL) { ++ st_logf("can't open private file: %s\n", o->u.private_key.file); ++ continue; ++ } ++ ++ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin); ++ fclose(f); ++ if (o->u.private_key.key == NULL) { ++ st_logf("failed to read key: %s error: %s\n", ++ o->u.private_key.file, ++ ERR_error_string(ERR_get_error(), NULL)); ++ /* just ignore failure */; ++ continue; ++ } ++ ++ /* XXX check keytype */ ++ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); ++ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); ++ ++ if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) { ++ EVP_PKEY_free(o->u.private_key.key); ++ o->u.private_key.key = NULL; ++ st_logf("private key %s doesn't verify\n", o->u.private_key.file); ++ continue; ++ } ++ ++ soft_token.flags.login_done = 1; ++ } ++ free(pin); ++ ++ return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; ++} ++ ++CK_RV ++C_Logout(CK_SESSION_HANDLE hSession) ++{ ++ st_logf("Logout\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_GetObjectSize(CK_SESSION_HANDLE hSession, ++ CK_OBJECT_HANDLE hObject, ++ CK_ULONG_PTR pulSize) ++{ ++ st_logf("GetObjectSize\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_GetAttributeValue(CK_SESSION_HANDLE hSession, ++ CK_OBJECT_HANDLE hObject, ++ CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulCount) ++{ ++ struct session_state *state; ++ struct st_object *obj; ++ CK_ULONG i; ++ CK_RV ret; ++ int j; ++ ++ st_logf("GetAttributeValue: %lx\n", ++ (unsigned long)HANDLE_OBJECT_ID(hObject)); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { ++ st_logf("object not found: %lx\n", ++ (unsigned long)HANDLE_OBJECT_ID(hObject)); ++ return ret; ++ } ++ ++ ret = CKR_OK; ++ for (i = 0; i < ulCount; i++) { ++ st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); ++ for (j = 0; j < obj->num_attributes; j++) { ++ if (obj->attrs[j].secret) { ++ pTemplate[i].ulValueLen = (CK_ULONG)-1; ++ break; ++ } ++ if (pTemplate[i].type == obj->attrs[j].attribute.type) { ++ if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { ++ if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) ++ memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, ++ obj->attrs[j].attribute.ulValueLen); ++ } ++ pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; ++ break; ++ } ++ } ++ if (j == obj->num_attributes) { ++ st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); ++ pTemplate[i].ulValueLen = (CK_ULONG)-1; ++ ret = CKR_ATTRIBUTE_TYPE_INVALID; ++ } ++ ++ } ++ return ret; ++} ++ ++CK_RV ++C_FindObjectsInit(CK_SESSION_HANDLE hSession, ++ CK_ATTRIBUTE_PTR pTemplate, ++ CK_ULONG ulCount) ++{ ++ struct session_state *state; ++ ++ st_logf("FindObjectsInit\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->find.next_object != -1) { ++ application_error("application didn't do C_FindObjectsFinal\n"); ++ find_object_final(state); ++ } ++ if (ulCount) { ++ CK_ULONG i; ++ ++ print_attributes(pTemplate, ulCount); ++ ++ state->find.attributes = ++ calloc(1, ulCount * sizeof(state->find.attributes[0])); ++ if (state->find.attributes == NULL) ++ return CKR_DEVICE_MEMORY; ++ for (i = 0; i < ulCount; i++) { ++ state->find.attributes[i].pValue = ++ malloc(pTemplate[i].ulValueLen); ++ if (state->find.attributes[i].pValue == NULL) { ++ find_object_final(state); ++ return CKR_DEVICE_MEMORY; ++ } ++ memcpy(state->find.attributes[i].pValue, ++ pTemplate[i].pValue, pTemplate[i].ulValueLen); ++ state->find.attributes[i].type = pTemplate[i].type; ++ state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; ++ } ++ state->find.num_attributes = ulCount; ++ state->find.next_object = 0; ++ } else { ++ st_logf("find all objects\n"); ++ state->find.attributes = NULL; ++ state->find.num_attributes = 0; ++ state->find.next_object = 0; ++ } ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_FindObjects(CK_SESSION_HANDLE hSession, ++ CK_OBJECT_HANDLE_PTR phObject, ++ CK_ULONG ulMaxObjectCount, ++ CK_ULONG_PTR pulObjectCount) ++{ ++ struct session_state *state; ++ int i; ++ ++ st_logf("FindObjects\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->find.next_object == -1) { ++ application_error("application didn't do C_FindObjectsInit\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ if (ulMaxObjectCount == 0) { ++ application_error("application asked for 0 objects\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ *pulObjectCount = 0; ++ for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { ++ st_logf("FindObjects: %d\n", i); ++ state->find.next_object = i + 1; ++ if (attributes_match(soft_token.object.objs[i], ++ state->find.attributes, ++ state->find.num_attributes)) { ++ *phObject++ = soft_token.object.objs[i]->object_handle; ++ ulMaxObjectCount--; ++ (*pulObjectCount)++; ++ if (ulMaxObjectCount == 0) ++ break; ++ } ++ } ++ return CKR_OK; ++} ++ ++CK_RV ++C_FindObjectsFinal(CK_SESSION_HANDLE hSession) ++{ ++ struct session_state *state; ++ ++ st_logf("FindObjectsFinal\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ find_object_final(state); ++ return CKR_OK; ++} ++ ++static CK_RV ++commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, ++ const CK_MECHANISM_TYPE *mechs, int mechs_len, ++ const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, ++ struct st_object **o) ++{ ++ CK_RV ret; ++ int i; ++ ++ *o = NULL; ++ if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) ++ return ret; ++ ++ ret = attributes_match(*o, attr_match, attr_match_len); ++ if (!ret) { ++ application_error("called commonInit on key that doesn't " ++ "support required attr"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ for (i = 0; i < mechs_len; i++) ++ if (mechs[i] == pMechanism->mechanism) ++ break; ++ if (i == mechs_len) { ++ application_error("called mech (%08lx) not supported\n", ++ pMechanism->mechanism); ++ return CKR_ARGUMENTS_BAD; ++ } ++ return CKR_OK; ++} ++ ++ ++static CK_RV ++dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism) ++{ ++ CK_MECHANISM_PTR p; ++ ++ p = malloc(sizeof(*p)); ++ if (p == NULL) ++ return CKR_DEVICE_MEMORY; ++ ++ if (*dup) ++ free(*dup); ++ *dup = p; ++ memcpy(p, pMechanism, sizeof(*p)); ++ ++ return CKR_OK; ++} ++ ++ ++CK_RV ++C_EncryptInit(CK_SESSION_HANDLE hSession, ++ CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey) ++{ ++ struct session_state *state; ++ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; ++ CK_BBOOL bool_true = CK_TRUE; ++ CK_ATTRIBUTE attr[] = { ++ { CKA_ENCRYPT, &bool_true, sizeof(bool_true) } ++ }; ++ struct st_object *o; ++ CK_RV ret; ++ ++ st_logf("EncryptInit\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), ++ mechs, sizeof(mechs)/sizeof(mechs[0]), ++ pMechanism, hKey, &o); ++ if (ret) ++ return ret; ++ ++ ret = dup_mechanism(&state->encrypt_mechanism, pMechanism); ++ if (ret == CKR_OK) ++ state->encrypt_object = OBJECT_ID(o); ++ ++ return ret; ++} ++ ++CK_RV ++C_Encrypt(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pData, ++ CK_ULONG ulDataLen, ++ CK_BYTE_PTR pEncryptedData, ++ CK_ULONG_PTR pulEncryptedDataLen) ++{ ++ struct session_state *state; ++ struct st_object *o; ++ void *buffer = NULL; ++ CK_RV ret; ++ RSA *rsa; ++ int padding, len, buffer_len, padding_len; ++ ++ st_logf("Encrypt\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->encrypt_object == -1) ++ return CKR_ARGUMENTS_BAD; ++ ++ o = soft_token.object.objs[state->encrypt_object]; ++ ++ if (o->u.public_key == NULL) { ++ st_logf("public key NULL\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ rsa = EVP_PKEY_get0_RSA(o->u.public_key); ++ if (rsa == NULL) ++ return CKR_ARGUMENTS_BAD; ++ ++ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ ++ ++ buffer_len = RSA_size(rsa); ++ ++ buffer = malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ ++ ret = CKR_OK; ++ switch(state->encrypt_mechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ padding = RSA_PKCS1_PADDING; ++ padding_len = RSA_PKCS1_PADDING_SIZE; ++ break; ++ case CKM_RSA_X_509: ++ padding = RSA_NO_PADDING; ++ padding_len = 0; ++ break; ++ default: ++ ret = CKR_FUNCTION_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if (buffer_len + padding_len < (long) ulDataLen) { ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pulEncryptedDataLen == NULL) { ++ st_logf("pulEncryptedDataLen NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pData == NULL_PTR) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding); ++ if (len <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ if (len > buffer_len) ++ abort(); ++ ++ if (pEncryptedData != NULL_PTR) ++ memcpy(pEncryptedData, buffer, len); ++ *pulEncryptedDataLen = len; ++ ++ out: ++ if (buffer) { ++ memset(buffer, 0, buffer_len); ++ free(buffer); ++ } ++ return ret; ++} ++ ++CK_RV ++C_EncryptUpdate(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pPart, ++ CK_ULONG ulPartLen, ++ CK_BYTE_PTR pEncryptedPart, ++ CK_ULONG_PTR pulEncryptedPartLen) ++{ ++ st_logf("EncryptUpdate\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++ ++CK_RV ++C_EncryptFinal(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pLastEncryptedPart, ++ CK_ULONG_PTR pulLastEncryptedPartLen) ++{ ++ st_logf("EncryptFinal\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++ ++/* C_DecryptInit initializes a decryption operation. */ ++CK_RV ++C_DecryptInit(CK_SESSION_HANDLE hSession, ++ CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey) ++{ ++ struct session_state *state; ++ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; ++ CK_BBOOL bool_true = CK_TRUE; ++ CK_ATTRIBUTE attr[] = { ++ { CKA_DECRYPT, &bool_true, sizeof(bool_true) } ++ }; ++ struct st_object *o; ++ CK_RV ret; ++ ++ st_logf("DecryptInit\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), ++ mechs, sizeof(mechs)/sizeof(mechs[0]), ++ pMechanism, hKey, &o); ++ if (ret) ++ return ret; ++ ++ ret = dup_mechanism(&state->decrypt_mechanism, pMechanism); ++ if (ret == CKR_OK) ++ state->decrypt_object = OBJECT_ID(o); ++ ++ return CKR_OK; ++} ++ ++ ++CK_RV ++C_Decrypt(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pEncryptedData, ++ CK_ULONG ulEncryptedDataLen, ++ CK_BYTE_PTR pData, ++ CK_ULONG_PTR pulDataLen) ++{ ++ struct session_state *state; ++ struct st_object *o; ++ void *buffer = NULL; ++ CK_RV ret; ++ RSA *rsa; ++ int padding, len, buffer_len, padding_len; ++ ++ st_logf("Decrypt\n"); ++ ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->decrypt_object == -1) ++ return CKR_ARGUMENTS_BAD; ++ ++ o = soft_token.object.objs[state->decrypt_object]; ++ ++ if (o->u.private_key.key == NULL) { ++ st_logf("private key NULL\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); ++ if (rsa == NULL) ++ return CKR_ARGUMENTS_BAD; ++ ++ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ ++ ++ buffer_len = RSA_size(rsa); ++ ++ buffer = malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ ++ ret = CKR_OK; ++ switch(state->decrypt_mechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ padding = RSA_PKCS1_PADDING; ++ padding_len = RSA_PKCS1_PADDING_SIZE; ++ break; ++ case CKM_RSA_X_509: ++ padding = RSA_NO_PADDING; ++ padding_len = 0; ++ break; ++ default: ++ ret = CKR_FUNCTION_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if (buffer_len + padding_len < (long) ulEncryptedDataLen) { ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pulDataLen == NULL) { ++ st_logf("pulDataLen NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pEncryptedData == NULL_PTR) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer, ++ rsa, padding); ++ if (len <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ if (len > buffer_len) ++ abort(); ++ ++ if (pData != NULL_PTR) ++ memcpy(pData, buffer, len); ++ *pulDataLen = len; ++ ++ out: ++ if (buffer) { ++ memset(buffer, 0, buffer_len); ++ free(buffer); ++ } ++ return ret; ++} ++ ++ ++CK_RV ++C_DecryptUpdate(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pEncryptedPart, ++ CK_ULONG ulEncryptedPartLen, ++ CK_BYTE_PTR pPart, ++ CK_ULONG_PTR pulPartLen) ++ ++{ ++ st_logf("DecryptUpdate\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++ ++CK_RV ++C_DecryptFinal(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pLastPart, ++ CK_ULONG_PTR pulLastPartLen) ++{ ++ st_logf("DecryptFinal\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_DigestInit(CK_SESSION_HANDLE hSession, ++ CK_MECHANISM_PTR pMechanism) ++{ ++ st_logf("DigestInit\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_SignInit(CK_SESSION_HANDLE hSession, ++ CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey) ++{ ++ struct session_state *state; ++ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; ++ CK_BBOOL bool_true = CK_TRUE; ++ CK_ATTRIBUTE attr[] = { ++ { CKA_SIGN, &bool_true, sizeof(bool_true) } ++ }; ++ struct st_object *o; ++ CK_RV ret; ++ ++ st_logf("SignInit\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), ++ mechs, sizeof(mechs)/sizeof(mechs[0]), ++ pMechanism, hKey, &o); ++ if (ret) ++ return ret; ++ ++ ret = dup_mechanism(&state->sign_mechanism, pMechanism); ++ if (ret == CKR_OK) ++ state->sign_object = OBJECT_ID(o); ++ ++ return CKR_OK; ++} ++ ++CK_RV ++C_Sign(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pData, ++ CK_ULONG ulDataLen, ++ CK_BYTE_PTR pSignature, ++ CK_ULONG_PTR pulSignatureLen) ++{ ++ struct session_state *state; ++ struct st_object *o; ++ void *buffer = NULL; ++ CK_RV ret; ++ RSA *rsa; ++ int padding, len, buffer_len; ++ size_t padding_len; ++ ++ st_logf("Sign\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->sign_object == -1) ++ return CKR_ARGUMENTS_BAD; ++ ++ o = soft_token.object.objs[state->sign_object]; ++ ++ if (o->u.private_key.key == NULL) { ++ st_logf("private key NULL\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); ++ if (rsa == NULL) ++ return CKR_ARGUMENTS_BAD; ++ ++ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ ++ ++ buffer_len = RSA_size(rsa); ++ ++ buffer = malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ ++ switch(state->sign_mechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ padding = RSA_PKCS1_PADDING; ++ padding_len = RSA_PKCS1_PADDING_SIZE; ++ break; ++ case CKM_RSA_X_509: ++ padding = RSA_NO_PADDING; ++ padding_len = 0; ++ break; ++ default: ++ ret = CKR_FUNCTION_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if ((size_t) buffer_len < ulDataLen + padding_len) { ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pulSignatureLen == NULL) { ++ st_logf("signature len NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pData == NULL_PTR) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding); ++ st_logf("private encrypt done\n"); ++ if (len <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ if (len > buffer_len) ++ abort(); ++ ++ if (pSignature != NULL_PTR) ++ memcpy(pSignature, buffer, len); ++ *pulSignatureLen = len; ++ ++ ret = CKR_OK; ++ ++ out: ++ if (buffer) { ++ memset(buffer, 0, buffer_len); ++ free(buffer); ++ } ++ return ret; ++} ++ ++CK_RV ++C_SignUpdate(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pPart, ++ CK_ULONG ulPartLen) ++{ ++ st_logf("SignUpdate\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++ ++CK_RV ++C_SignFinal(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pSignature, ++ CK_ULONG_PTR pulSignatureLen) ++{ ++ st_logf("SignUpdate\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_VerifyInit(CK_SESSION_HANDLE hSession, ++ CK_MECHANISM_PTR pMechanism, ++ CK_OBJECT_HANDLE hKey) ++{ ++ struct session_state *state; ++ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; ++ CK_BBOOL bool_true = CK_TRUE; ++ CK_ATTRIBUTE attr[] = { ++ { CKA_VERIFY, &bool_true, sizeof(bool_true) } ++ }; ++ struct st_object *o; ++ CK_RV ret; ++ ++ st_logf("VerifyInit\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), ++ mechs, sizeof(mechs)/sizeof(mechs[0]), ++ pMechanism, hKey, &o); ++ if (ret) ++ return ret; ++ ++ ret = dup_mechanism(&state->verify_mechanism, pMechanism); ++ if (ret == CKR_OK) ++ state->verify_object = OBJECT_ID(o); ++ ++ return ret; ++} ++ ++CK_RV ++C_Verify(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pData, ++ CK_ULONG ulDataLen, ++ CK_BYTE_PTR pSignature, ++ CK_ULONG ulSignatureLen) ++{ ++ struct session_state *state; ++ struct st_object *o; ++ void *buffer = NULL; ++ CK_RV ret; ++ RSA *rsa; ++ int padding, len, buffer_len; ++ ++ st_logf("Verify\n"); ++ VERIFY_SESSION_HANDLE(hSession, &state); ++ ++ if (state->verify_object == -1) ++ return CKR_ARGUMENTS_BAD; ++ ++ o = soft_token.object.objs[state->verify_object]; ++ ++ if (o->u.public_key == NULL) { ++ st_logf("public key NULL\n"); ++ return CKR_ARGUMENTS_BAD; ++ } ++ ++ rsa = EVP_PKEY_get0_RSA(o->u.public_key); ++ if (rsa == NULL) ++ return CKR_ARGUMENTS_BAD; ++ ++ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ ++ ++ buffer_len = RSA_size(rsa); ++ ++ buffer = malloc(buffer_len); ++ if (buffer == NULL) { ++ ret = CKR_DEVICE_MEMORY; ++ goto out; ++ } ++ ++ ret = CKR_OK; ++ switch(state->verify_mechanism->mechanism) { ++ case CKM_RSA_PKCS: ++ padding = RSA_PKCS1_PADDING; ++ break; ++ case CKM_RSA_X_509: ++ padding = RSA_NO_PADDING; ++ break; ++ default: ++ ret = CKR_FUNCTION_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if (buffer_len < (long) ulDataLen) { ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pSignature == NULL) { ++ st_logf("signature NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ if (pData == NULL_PTR) { ++ st_logf("data NULL\n"); ++ ret = CKR_ARGUMENTS_BAD; ++ goto out; ++ } ++ ++ len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding); ++ st_logf("private encrypt done\n"); ++ if (len <= 0) { ++ ret = CKR_DEVICE_ERROR; ++ goto out; ++ } ++ if (len > buffer_len) ++ abort(); ++ ++ if ((size_t) len != ulSignatureLen) { ++ ret = CKR_GENERAL_ERROR; ++ goto out; ++ } ++ ++ if (memcmp(pSignature, buffer, len) != 0) { ++ ret = CKR_GENERAL_ERROR; ++ goto out; ++ } ++ ++ out: ++ if (buffer) { ++ memset(buffer, 0, buffer_len); ++ free(buffer); ++ } ++ return ret; ++} ++ ++ ++CK_RV ++C_VerifyUpdate(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pPart, ++ CK_ULONG ulPartLen) ++{ ++ st_logf("VerifyUpdate\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_VerifyFinal(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR pSignature, ++ CK_ULONG ulSignatureLen) ++{ ++ st_logf("VerifyFinal\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++CK_RV ++C_GenerateRandom(CK_SESSION_HANDLE hSession, ++ CK_BYTE_PTR RandomData, ++ CK_ULONG ulRandomLen) ++{ ++ st_logf("GenerateRandom\n"); ++ VERIFY_SESSION_HANDLE(hSession, NULL); ++ return CKR_FUNCTION_NOT_SUPPORTED; ++} ++ ++ ++CK_FUNCTION_LIST funcs = { ++ { 2, 11 }, ++ C_Initialize, ++ C_Finalize, ++ C_GetInfo, ++ C_GetFunctionList, ++ C_GetSlotList, ++ C_GetSlotInfo, ++ C_GetTokenInfo, ++ C_GetMechanismList, ++ C_GetMechanismInfo, ++ C_InitToken, ++ (void *)func_not_supported, /* C_InitPIN */ ++ (void *)func_not_supported, /* C_SetPIN */ ++ C_OpenSession, ++ C_CloseSession, ++ C_CloseAllSessions, ++ C_GetSessionInfo, ++ (void *)func_not_supported, /* C_GetOperationState */ ++ (void *)func_not_supported, /* C_SetOperationState */ ++ C_Login, ++ C_Logout, ++ (void *)func_not_supported, /* C_CreateObject */ ++ (void *)func_not_supported, /* C_CopyObject */ ++ (void *)func_not_supported, /* C_DestroyObject */ ++ (void *)func_not_supported, /* C_GetObjectSize */ ++ C_GetAttributeValue, ++ (void *)func_not_supported, /* C_SetAttributeValue */ ++ C_FindObjectsInit, ++ C_FindObjects, ++ C_FindObjectsFinal, ++ C_EncryptInit, ++ C_Encrypt, ++ C_EncryptUpdate, ++ C_EncryptFinal, ++ C_DecryptInit, ++ C_Decrypt, ++ C_DecryptUpdate, ++ C_DecryptFinal, ++ C_DigestInit, ++ (void *)func_not_supported, /* C_Digest */ ++ (void *)func_not_supported, /* C_DigestUpdate */ ++ (void *)func_not_supported, /* C_DigestKey */ ++ (void *)func_not_supported, /* C_DigestFinal */ ++ C_SignInit, ++ C_Sign, ++ C_SignUpdate, ++ C_SignFinal, ++ (void *)func_not_supported, /* C_SignRecoverInit */ ++ (void *)func_not_supported, /* C_SignRecover */ ++ C_VerifyInit, ++ C_Verify, ++ C_VerifyUpdate, ++ C_VerifyFinal, ++ (void *)func_not_supported, /* C_VerifyRecoverInit */ ++ (void *)func_not_supported, /* C_VerifyRecover */ ++ (void *)func_not_supported, /* C_DigestEncryptUpdate */ ++ (void *)func_not_supported, /* C_DecryptDigestUpdate */ ++ (void *)func_not_supported, /* C_SignEncryptUpdate */ ++ (void *)func_not_supported, /* C_DecryptVerifyUpdate */ ++ (void *)func_not_supported, /* C_GenerateKey */ ++ (void *)func_not_supported, /* C_GenerateKeyPair */ ++ (void *)func_not_supported, /* C_WrapKey */ ++ (void *)func_not_supported, /* C_UnwrapKey */ ++ (void *)func_not_supported, /* C_DeriveKey */ ++ (void *)func_not_supported, /* C_SeedRandom */ ++ C_GenerateRandom, ++ (void *)func_not_supported, /* C_GetFunctionStatus */ ++ (void *)func_not_supported, /* C_CancelFunction */ ++ (void *)func_not_supported /* C_WaitForSlotEvent */ ++}; +diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile +index e464b085..a0e5a37c 100644 +--- a/regress/unittests/Makefile ++++ b/regress/unittests/Makefile +@@ -2,6 +2,6 @@ + + REGRESS_FAIL_EARLY?= yes + SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion +-SUBDIR+=authopt ++SUBDIR+=pkcs11 authopt + + .include +diff --git a/regress/unittests/pkcs11/Makefile b/regress/unittests/pkcs11/Makefile +new file mode 100644 +index 00000000..481b13d0 +--- /dev/null ++++ b/regress/unittests/pkcs11/Makefile +@@ -0,0 +1,9 @@ ++ ++PROG=test_pkcs11 ++SRCS=tests.c ++REGRESS_TARGETS=run-regress-${PROG} ++ ++run-regress-${PROG}: ${PROG} ++ env ${TEST_ENV} ./${PROG} ++ ++.include +diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c +new file mode 100644 +index 00000000..e83aca54 +--- /dev/null ++++ b/regress/unittests/pkcs11/tests.c +@@ -0,0 +1,330 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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. ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include "../test_helper/test_helper.h" ++ ++#include "sshbuf.h" ++#include "ssh-pkcs11-uri.h" ++ ++#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL) ++ ++/* prototypes are not public -- specify them here internally for tests */ ++struct sshbuf *percent_encode(const char *, size_t, char *); ++int percent_decode(char *, char **); ++ ++void ++compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) ++{ ++ ASSERT_PTR_NE(a, NULL); ++ ASSERT_PTR_NE(b, NULL); ++ ASSERT_SIZE_T_EQ(a->id_len, b->id_len); ++ ASSERT_MEM_EQ(a->id, b->id, a->id_len); ++ if (b->object != NULL) ++ ASSERT_STRING_EQ(a->object, b->object); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->object, b->object); ++ if (b->module_path != NULL) ++ ASSERT_STRING_EQ(a->module_path, b->module_path); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->module_path, b->module_path); ++ if (b->token != NULL) ++ ASSERT_STRING_EQ(a->token, b->token); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->token, b->token); ++ if (b->manuf != NULL) ++ ASSERT_STRING_EQ(a->manuf, b->manuf); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->manuf, b->manuf); ++ if (b->lib_manuf != NULL) ++ ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); ++ else /* both should be null */ ++ ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); ++} ++ ++void ++check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) ++{ ++ char *buf = NULL, *str; ++ struct pkcs11_uri *pkcs11uri = NULL; ++ int rv; ++ ++ if (expect_rv == 0) ++ str = "Valid"; ++ else ++ str = "Invalid"; ++ asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); ++ TEST_START(buf); ++ free(buf); ++ pkcs11uri = pkcs11_uri_init(); ++ rv = pkcs11_uri_parse(uri, pkcs11uri); ++ ASSERT_INT_EQ(rv, expect_rv); ++ if (rv == 0) /* in case of failure result is undefined */ ++ compare_uri(pkcs11uri, expect); ++ pkcs11_uri_cleanup(pkcs11uri); ++ free(expect); ++ TEST_DONE(); ++} ++ ++void ++check_parse(char *uri, struct pkcs11_uri *expect) ++{ ++ check_parse_rv(uri, expect, 0); ++} ++ ++struct pkcs11_uri * ++compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, ++ char *manuf, char *module_path, char *object) ++{ ++ struct pkcs11_uri *uri = pkcs11_uri_init(); ++ if (id_len > 0) { ++ uri->id_len = id_len; ++ uri->id = id; ++ } ++ uri->module_path = module_path; ++ uri->token = token; ++ uri->lib_manuf = lib_manuf; ++ uri->manuf = manuf; ++ uri->object = object; ++ return uri; ++} ++ ++static void ++test_parse_valid(void) ++{ ++ /* path arguments */ ++ check_parse("pkcs11:id=%01", ++ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:id=%00%01", ++ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:token=SSH%20Keys", ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); ++ check_parse("pkcs11:library-manufacturer=OpenSC", ++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL)); ++ check_parse("pkcs11:manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); ++ check_parse("pkcs11:object=SIGN%20Key", ++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key")); ++ /* query arguments */ ++ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); ++ ++ /* combinations */ ++ /* ID SHOULD be percent encoded */ ++ check_parse("pkcs11:token=SSH%20Key;id=0", ++ compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL)); ++ check_parse( ++ "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, "CAC", ++ "/usr/lib64/p11-kit-proxy.so", NULL)); ++ check_parse( ++ "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, ++ "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key")); ++ ++ /* empty path component matches everything */ ++ check_parse("pkcs11:", EMPTY_URI); ++ ++ /* empty string is a valid to match against (and different from NULL) */ ++ check_parse("pkcs11:token=", ++ compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL)); ++ /* Percent character needs to be percent-encoded */ ++ check_parse("pkcs11:token=%25", ++ compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL)); ++} ++ ++static void ++test_parse_invalid(void) ++{ ++ /* Invalid percent encoding */ ++ check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); ++ /* Invalid percent encoding */ ++ check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); ++ /* Space MUST be percent encoded -- XXX not enforced yet */ ++ check_parse("pkcs11:token=SSH Keys", ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); ++ /* MUST NOT contain duplicate attributes of the same name */ ++ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); ++ /* Unrecognized attribute in path are ignored with log message */ ++ check_parse("pkcs11:key_name=SSH", EMPTY_URI); ++ /* Unrecognized attribute in query SHOULD be ignored */ ++ check_parse("pkcs11:?key_name=SSH", EMPTY_URI); ++} ++ ++void ++check_gen(char *expect, struct pkcs11_uri *uri) ++{ ++ char *buf = NULL, *uri_str; ++ ++ asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); ++ TEST_START(buf); ++ free(buf); ++ uri_str = pkcs11_uri_get(uri); ++ ASSERT_PTR_NE(uri_str, NULL); ++ ASSERT_STRING_EQ(uri_str, expect); ++ free(uri_str); ++ TEST_DONE(); ++} ++ ++static void ++test_generate_valid(void) ++{ ++ /* path arguments */ ++ check_gen("pkcs11:id=%01", ++ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:id=%00%01", ++ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ ++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); ++ /* library-manufacturer is not implmented now */ ++ /*check_gen("pkcs11:library-manufacturer=OpenSC", ++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL));*/ ++ check_gen("pkcs11:manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); ++ check_gen("pkcs11:object=RSA%20Key", ++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key")); ++ /* query arguments */ ++ check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); ++ ++ /* combinations */ ++ check_gen("pkcs11:id=%02;token=SSH%20Keys", ++ compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL)); ++ check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", ++ compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); ++ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", ++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key")); ++ ++ /* empty path component matches everything */ ++ check_gen("pkcs11:", EMPTY_URI); ++ ++} ++ ++void ++check_encode(char *source, size_t len, char *whitelist, char *expect) ++{ ++ char *buf = NULL; ++ struct sshbuf *b; ++ ++ asprintf(&buf, "percent_encode: expected %s", expect); ++ TEST_START(buf); ++ free(buf); ++ ++ b = percent_encode(source, len, whitelist); ++ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); ++ sshbuf_free(b); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_encode_multibyte(void) ++{ ++ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ ++ ++ /* multi-byte characters are "for free" */ ++ check_encode("$", 1, "", "%24"); ++ check_encode("¢", 2, "", "%C2%A2"); ++ check_encode("€", 3, "", "%E2%82%AC"); ++ check_encode("𐍈", 4, "", "%F0%90%8D%88"); ++ ++ /* CK_UTF8CHAR is unsigned char (1 byte) */ ++ /* labels SHOULD be normalized to NFC [UAX15] */ ++ ++} ++ ++static void ++test_percent_encode(void) ++{ ++ /* Without whitelist encodes everything (for CKA_ID) */ ++ check_encode("A*", 2, "", "%41%2A"); ++ check_encode("\x00", 1, "", "%00"); ++ check_encode("\x7F", 1, "", "%7F"); ++ check_encode("\x80", 1, "", "%80"); ++ check_encode("\xff", 1, "", "%FF"); ++ ++ /* Default whitelist encodes anything but safe letters */ ++ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, ++ "test%000alpha"); ++ check_encode(" ", 1, PKCS11_URI_WHITELIST, ++ "%20"); /* Space MUST be percent encoded */ ++ check_encode("/", 1, PKCS11_URI_WHITELIST, ++ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ ++ check_encode("?", 1, PKCS11_URI_WHITELIST, ++ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ ++ check_encode("#", 1, PKCS11_URI_WHITELIST, ++ "%23"); /* '#' MUST be always percent encoded */ ++ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, ++ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); ++ ++ /* Components in query can have '/' unencoded (useful for paths) */ ++ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", ++ "/path/to.file"); ++} ++ ++void ++check_decode(char *source, char *expect, int expect_len) ++{ ++ char *buf = NULL, *out = NULL; ++ int rv; ++ ++ asprintf(&buf, "percent_decode: %s", source); ++ TEST_START(buf); ++ free(buf); ++ ++ rv = percent_decode(source, &out); ++ ASSERT_INT_EQ(rv, expect_len); ++ if (rv >= 0) ++ ASSERT_MEM_EQ(out, expect, expect_len); ++ free(out); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_decode(void) ++{ ++ /* simple valid cases */ ++ check_decode("%00", "\x00", 1); ++ check_decode("%FF", "\xFF", 1); ++ ++ /* normal strings shold be kept intact */ ++ check_decode("strings are left", "strings are left", 16); ++ check_decode("10%25 of trees", "10% of trees", 12); ++ ++ /* make sure no more than 2 bytes are parsed */ ++ check_decode("%222", "\x22" "2", 2); ++ ++ /* invalid expects failure */ ++ check_decode("%0", "", -1); ++ check_decode("%Z", "", -1); ++ check_decode("%FG", "", -1); ++} ++ ++void ++tests(void) ++{ ++ test_percent_encode(); ++ test_percent_encode_multibyte(); ++ test_percent_decode(); ++ test_parse_valid(); ++ test_parse_invalid(); ++ test_generate_valid(); ++} +diff --git a/ssh-add.c b/ssh-add.c +index adcc4599..e4fd5623 100644 +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -64,6 +64,7 @@ + #include "misc.h" + #include "ssherr.h" + #include "digest.h" ++#include "ssh-pkcs11-uri.h" + + /* argv0 */ + extern char *__progname; +@@ -188,6 +189,24 @@ delete_all(int agent_fd) + return ret; + } + ++#ifdef ENABLE_PKCS11 ++static int update_card(int, int, const char *); ++ ++int ++update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri) ++{ ++ struct pkcs11_uri *uri; ++ ++ /* dry-run parse to make sure the URI is valid and to report errors */ ++ uri = pkcs11_uri_init(); ++ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ pkcs11_uri_cleanup(uri); ++ ++ return update_card(agent_fd, adding, pkcs11_uri); ++} ++#endif ++ + static int + add_file(int agent_fd, const char *filename, int key_only, int qflag) + { +@@ -480,6 +499,13 @@ lock_agent(int agent_fd, int lock) + static int + do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) + { ++#ifdef ENABLE_PKCS11 ++ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(file, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ return update_pkcs11_uri(agent_fd, !deleting, file); ++ } ++#endif + if (deleting) { + if (delete_file(agent_fd, file, key_only, qflag) == -1) + return -1; +diff --git a/ssh-agent.c b/ssh-agent.c +index 2a4578b0..f6c86240 100644 +--- a/ssh-agent.c ++++ b/ssh-agent.c +@@ -546,10 +546,72 @@ no_identities(SocketEntry *e) + } + + #ifdef ENABLE_PKCS11 ++static char * ++sanitize_pkcs11_provider(const char *provider) ++{ ++ struct pkcs11_uri *uri = NULL; ++ char *sane_uri, *module_path = NULL; /* default path */ ++ char canonical_provider[PATH_MAX]; ++ ++ if (provider == NULL) ++ return NULL; ++ ++ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ /* PKCS#11 URI */ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) { ++ error("Failed to init PCKS#11 URI"); ++ return NULL; ++ } ++ ++ if (pkcs11_uri_parse(provider, uri) != 0) { ++ error("Failed to parse PKCS#11 URI"); ++ return NULL; ++ } ++ /* validate also provider from URI */ ++ if (uri->module_path) ++ module_path = strdup(uri->module_path); ++ } else ++ module_path = strdup(provider); /* simple path */ ++ ++ if (module_path != NULL) { /* do not validate default NULL path in URI */ ++ if (realpath(module_path, canonical_provider) == NULL) { ++ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", ++ module_path, strerror(errno)); ++ free(module_path); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ free(module_path); ++ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { ++ verbose("refusing PKCS#11 provider \"%.100s\": " ++ "not whitelisted", canonical_provider); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ ++ /* copy verified and sanitized provider path back to the uri */ ++ if (uri) { ++ free(uri->module_path); ++ uri->module_path = xstrdup(canonical_provider); ++ } ++ } ++ ++ if (uri) { ++ sane_uri = pkcs11_uri_get(uri); ++ pkcs11_uri_cleanup(uri); ++ return sane_uri; ++ } else { ++ return xstrdup(canonical_provider); /* simple path */ ++ } ++} ++ + static void + process_add_smartcard_key(SocketEntry *e) + { +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; + int r, i, count = 0, success = 0, confirm = 0; + u_int seconds; + time_t death = 0; +@@ -585,28 +645,23 @@ process_add_smartcard_key(SocketEntry *e) + goto send; + } + } +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); +- goto send; +- } +- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { +- verbose("refusing PKCS#11 add of \"%.100s\": " +- "provider not whitelisted", canonical_provider); ++ ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } +- debug("%s: add %.100s", __func__, canonical_provider); ++ + if (lifetime && !death) + death = monotime() + lifetime; + +- count = pkcs11_add_provider(canonical_provider, pin, &keys); ++ debug("%s: add %.100s", __func__, sane_uri); ++ count = pkcs11_add_provider(sane_uri, pin, &keys); + for (i = 0; i < count; i++) { + k = keys[i]; + if (lookup_identity(k) == NULL) { + id = xcalloc(1, sizeof(Identity)); + id->key = k; +- id->provider = xstrdup(canonical_provider); +- id->comment = xstrdup(canonical_provider); /* XXX */ ++ id->provider = xstrdup(sane_uri); ++ id->comment = xstrdup(sane_uri); + id->death = death; + id->confirm = confirm; + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); +@@ -620,6 +675,7 @@ process_add_smartcard_key(SocketEntry *e) + send: + free(pin); + free(provider); ++ free(sane_uri); + free(keys); + send_status(e, success); + } +@@ -627,7 +683,7 @@ send: + static void + process_remove_smartcard_key(SocketEntry *e) + { +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; + int r, success = 0; + Identity *id, *nxt; + +@@ -638,30 +694,29 @@ process_remove_smartcard_key(SocketEntry *e) + } + free(pin); + +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } + +- debug("%s: remove %.100s", __func__, canonical_provider); ++ debug("%s: remove %.100s", __func__, sane_uri); + for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + /* Skip file--based keys */ + if (id->provider == NULL) + continue; +- if (!strcmp(canonical_provider, id->provider)) { ++ if (!strcmp(sane_uri, id->provider)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + } + } +- if (pkcs11_del_provider(canonical_provider) == 0) ++ if (pkcs11_del_provider(sane_uri) == 0) + success = 1; + else + error("%s: pkcs11_del_provider failed", __func__); + send: + free(provider); ++ free(sane_uri); + send_status(e, success); + } + #endif /* ENABLE_PKCS11 */ +diff --git a/ssh-keygen.c b/ssh-keygen.c +index d1ffc30c..00e38049 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -820,6 +820,7 @@ do_download(struct passwd *pw) + free(fp); + } else { + (void) sshkey_write(keys[i], stdout); /* XXX check */ ++ (void) pkcs11_uri_write(keys[i], stdout); + fprintf(stdout, "\n"); + } + sshkey_free(keys[i]); +diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c +index a023f5f4..882e8381 100644 +--- a/ssh-pkcs11-client.c ++++ b/ssh-pkcs11-client.c +@@ -117,6 +117,7 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + return (-1); + key.type = KEY_RSA; + key.rsa = rsa; ++ key.ecdsa_nid = 0; + if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { + error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); + return -1; +@@ -195,6 +196,8 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) + u_int nkeys, i; + struct sshbuf *msg; + ++ debug("%s: called, name = %s", __func__, name); ++ + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + +@@ -208,6 +211,7 @@ pkcs11_add_provider(char *name, char *pin, Key ***keysp) + if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); ++ debug("%s: nkeys = %u", __func__, nkeys); + for (i = 0; i < nkeys; i++) { + /* XXX clean up properly instead of fatal() */ + if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || +diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c +new file mode 100644 +index 00000000..da15c164 +--- /dev/null ++++ b/ssh-pkcs11-uri.c +@@ -0,0 +1,395 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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. ++ */ ++ ++#include "includes.h" ++ ++#ifdef ENABLE_PKCS11 ++ ++#include ++#include ++ ++#include "sshkey.h" ++#include "sshbuf.h" ++#include "log.h" ++ ++#define CRYPTOKI_COMPAT ++#include "pkcs11.h" ++ ++#include "ssh-pkcs11-uri.h" ++ ++#define PKCS11_URI_PATH_SEPARATOR ";" ++#define PKCS11_URI_QUERY_SEPARATOR "&" ++#define PKCS11_URI_VALUE_SEPARATOR "=" ++#define PKCS11_URI_ID "id" ++#define PKCS11_URI_TOKEN "token" ++#define PKCS11_URI_OBJECT "object" ++#define PKCS11_URI_LIB_MANUF "library-manufacturer" ++#define PKCS11_URI_MANUF "manufacturer" ++#define PKCS11_URI_MODULE_PATH "module-path" ++ ++/* Keyword tokens. */ ++typedef enum { ++ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, ++ pBadOption ++} pkcs11uriOpCodes; ++ ++/* Textual representation of the tokens. */ ++static struct { ++ const char *name; ++ pkcs11uriOpCodes opcode; ++} keywords[] = { ++ { PKCS11_URI_ID, pId }, ++ { PKCS11_URI_TOKEN, pToken }, ++ { PKCS11_URI_OBJECT, pObject }, ++ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, ++ { PKCS11_URI_MANUF, pManufacturer }, ++ { PKCS11_URI_MODULE_PATH, pModulePath }, ++ { NULL, pBadOption } ++}; ++ ++static pkcs11uriOpCodes ++parse_token(const char *cp) ++{ ++ u_int i; ++ ++ for (i = 0; keywords[i].name; i++) ++ if (strncasecmp(cp, keywords[i].name, ++ strlen(keywords[i].name)) == 0) ++ return keywords[i].opcode; ++ ++ return pBadOption; ++} ++ ++int ++percent_decode(char *data, char **outp) ++{ ++ char tmp[3]; ++ char *out, *tmp_end; ++ char *p = data; ++ long value; ++ size_t outlen = 0; ++ ++ out = malloc(strlen(data)+1); /* upper bound */ ++ if (out == NULL) ++ return -1; ++ while (*p != '\0') { ++ switch (*p) { ++ case '%': ++ p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[0] = *p++; ++ if (*p == '\0') ++ goto fail; ++ tmp[1] = *p++; ++ tmp[2] = '\0'; ++ tmp_end = NULL; ++ value = strtol(tmp, &tmp_end, 16); ++ if (tmp_end != tmp+2) ++ goto fail; ++ else ++ out[outlen++] = (char) value; ++ break; ++ default: ++ out[outlen++] = *p++; ++ break; ++ } ++ } ++ ++ /* zero terminate */ ++ out[outlen] = '\0'; ++ *outp = out; ++ return outlen; ++fail: ++ free(out); ++ return -1; ++} ++ ++struct sshbuf * ++percent_encode(const char *data, size_t length, const char *whitelist) ++{ ++ struct sshbuf *b = NULL; ++ char tmp[4], *cp; ++ size_t i; ++ ++ if ((b = sshbuf_new()) == NULL) ++ return NULL; ++ for (i = 0; i < length; i++) { ++ cp = strchr(whitelist, data[i]); ++ /* if c is specified as '\0' pointer to terminator is returned !! */ ++ if (cp != NULL && *cp != '\0') { ++ if (sshbuf_put(b, &data[i], 1) != 0) ++ goto err; ++ } else ++ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 ++ || sshbuf_put(b, tmp, 3) != 0) ++ goto err; ++ } ++ if (sshbuf_put(b, "\0", 1) == 0) ++ return b; ++err: ++ sshbuf_free(b); ++ return NULL; ++} ++ ++char * ++pkcs11_uri_append(char *part, const char *separator, const char *key, ++ struct sshbuf *value) ++{ ++ char *new_part; ++ size_t size = 0; ++ ++ if (value == NULL) ++ return NULL; ++ ++ size = asprintf(&new_part, ++ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", ++ (part != NULL ? part : ""), ++ (part != NULL ? separator : ""), ++ key, sshbuf_ptr(value)); ++ sshbuf_free(value); ++ free(part); ++ ++ if (size <= 0) ++ return NULL; ++ return new_part; ++} ++ ++char * ++pkcs11_uri_get(struct pkcs11_uri *uri) ++{ ++ size_t size = 0; ++ char *p = NULL, *path = NULL, *query = NULL; ++ ++ /* compose a percent-encoded ID */ ++ if (uri->id_len > 0) { ++ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_ID, key_id); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write object label */ ++ if (uri->object) { ++ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_OBJECT, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write token label */ ++ if (uri->token) { ++ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_TOKEN, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write manufacturer */ ++ if (uri->manuf) { ++ struct sshbuf *manuf = percent_encode(uri->manuf, ++ strlen(uri->manuf), PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_MANUF, manuf); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write module_path */ ++ if (uri->module_path) { ++ struct sshbuf *module = percent_encode(uri->module_path, ++ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); ++ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, ++ PKCS11_URI_MODULE_PATH, module); ++ if (query == NULL) ++ goto err; ++ } ++ ++ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", ++ path != NULL ? path : "", ++ query != NULL ? "?" : "", ++ query != NULL ? query : ""); ++err: ++ free(query); ++ free(path); ++ if (size <= 0) ++ return NULL; ++ return p; ++} ++ ++struct pkcs11_uri * ++pkcs11_uri_init() ++{ ++ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); ++ return d; ++} ++ ++void ++pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) ++{ ++ free(pkcs11->id); ++ free(pkcs11->module_path); ++ free(pkcs11->token); ++ free(pkcs11->object); ++ free(pkcs11->lib_manuf); ++ free(pkcs11->manuf); ++ free(pkcs11); ++} ++ ++int ++pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) ++{ ++ char *saveptr1, *saveptr2, *str1, *str2, *tok; ++ int rv = 0, len; ++ char *p = NULL; ++ ++ size_t scheme_len = strlen(PKCS11_URI_SCHEME); ++ if (strlen(uri) < scheme_len || /* empty URI matches everything */ ++ strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) { ++ error("%s: The '%s' does not look like PKCS#11 URI", ++ __func__, uri); ++ return -1; ++ } ++ ++ if (pkcs11 == NULL) { ++ error("%s: Bad arguments. The pkcs11 can't be null", __func__); ++ return -1; ++ } ++ ++ /* skip URI schema name */ ++ p = strdup(uri); ++ str1 = p; ++ ++ /* everything before ? */ ++ tok = strtok_r(str1, "?", &saveptr1); ++ if (tok == NULL) { ++ free(p); ++ error("%s: pk11-path expected, got EOF", __func__); ++ return -1; ++ } ++ ++ /* skip URI schema name: ++ * the scheme ensures that there is at least something before "?" ++ * allowing empty pk11-path. Resulting token at worst pointing to ++ * \0 byte */ ++ tok = tok + scheme_len; ++ ++ /* parse pk11-path */ ++ for (str2 = tok; ; str2 = NULL) { ++ char **charptr; ++ pkcs11uriOpCodes opcode; ++ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2); ++ if (tok == NULL) ++ break; ++ opcode = parse_token(tok); ++ ++ char *arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ ++ switch (opcode) { ++ case pId: ++ /* CKA_ID */ ++ if (pkcs11->id != NULL) { ++ verbose("%s: The id already set in the PKCS#11 URI", ++ __func__); ++ rv = -1; ++ } ++ len = percent_decode(arg, &pkcs11->id); ++ if (len <= 0) { ++ verbose("%s: Failed to percent-decode CKA_ID: %s", ++ __func__, arg); ++ rv = -1; ++ } else ++ pkcs11->id_len = len; ++ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", ++ __func__, arg); ++ break; ++ case pToken: ++ /* CK_TOKEN_INFO -> label */ ++ charptr = &pkcs11->token; ++ parse_string: ++ if (*charptr != NULL) { ++ verbose("%s: The %s already set in the PKCS#11 URI", ++ keywords[opcode].name, __func__); ++ rv = -1; ++ } ++ percent_decode(arg, charptr); ++ debug3("%s: Setting %s = %s from PKCS#11 URI", ++ __func__, keywords[opcode].name, *charptr); ++ break; ++ ++ case pObject: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->object; ++ goto parse_string; ++ ++ case pManufacturer: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->manuf; ++ goto parse_string; ++ ++ case pLibraryManufacturer: ++ /* CK_INFO -> manufacturerID */ ++ charptr = &pkcs11->lib_manuf; ++ goto parse_string; ++ ++ case pBadOption: ++ default: ++ /* Unrecognized attribute in the URI path SHOULD be error */ ++ verbose("%s: Unknown part of path in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++ ++ tok = strtok_r(NULL, "?", &saveptr1); ++ if (tok == NULL) { ++ free(p); ++ return rv; ++ } ++ /* parse pk11-query (optional) */ ++ for (str2 = tok; ; str2 = NULL) { ++ size_t key_len = strlen(PKCS11_URI_MODULE_PATH) + 1; ++ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2); ++ if (tok == NULL) ++ break; ++ if (strncasecmp(tok, PKCS11_URI_MODULE_PATH ++ PKCS11_URI_VALUE_SEPARATOR, key_len) == 0) { ++ /* module-path is PKCS11Provider */ ++ if (pkcs11->module_path != NULL) { ++ verbose("%s: Multiple module-path attributes are" ++ "not supported the PKCS#11 URI", __func__); ++ rv = -1; ++ } ++ percent_decode(tok + key_len, &pkcs11->module_path); ++ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", ++ __func__, pkcs11->module_path); ++ /* } else if ( pin-value ) { */ ++ } else { ++ /* Unrecognized attribute in the URI query SHOULD be ignored */ ++ verbose("%s: Unknown part of query in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++ free(p); ++ return rv; ++} ++ ++#endif /* ENABLE_PKCS11 */ +diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h +new file mode 100644 +index 00000000..609c4df1 +--- /dev/null ++++ b/ssh-pkcs11-uri.h +@@ -0,0 +1,41 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * 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. ++ */ ++ ++#define PKCS11_URI_SCHEME "pkcs11:" ++#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ ++ "0123456789_-.()" ++ ++struct pkcs11_uri { ++ /* path */ ++ char *id; ++ size_t id_len; ++ char *token; ++ char *object; ++ char *lib_manuf; ++ char *manuf; ++ /* query */ ++ char *module_path; ++}; ++ ++struct pkcs11_uri *pkcs11_uri_init(); ++void pkcs11_uri_cleanup(struct pkcs11_uri *); ++int pkcs11_uri_parse(const char *, struct pkcs11_uri *); ++struct pkcs11_uri *pkcs11_uri_init(); ++char * pkcs11_uri_get(struct pkcs11_uri *uri); ++ +diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c +index 88c9d6e2..a29b4451 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -48,8 +48,8 @@ struct pkcs11_slotinfo { + int logged_in; + }; + +-struct pkcs11_provider { +- char *name; ++struct pkcs11_module { ++ char *module_path; + void *handle; + CK_FUNCTION_LIST *function_list; + CK_INFO info; +@@ -58,6 +58,13 @@ struct pkcs11_provider { + struct pkcs11_slotinfo *slotinfo; + int valid; + int refcount; ++}; ++ ++struct pkcs11_provider { ++ char *name; ++ struct pkcs11_module *module; /* can be shared between various providers */ ++ int refcount; ++ int valid; + TAILQ_ENTRY(pkcs11_provider) next; + }; + +@@ -70,10 +77,46 @@ struct pkcs11_key { + RSA_METHOD rsa_method; + char *keyid; + int keyid_len; ++ char *label; + }; + + int pkcs11_interactive = 0; + ++/* ++ * This can't be in the ssh-pkcs11-uri, becase we can not depend on ++ * PKCS#11 structures in ssh-agent (using client-helper communication) ++ */ ++int ++pkcs11_uri_write(const struct sshkey *key, FILE *f) ++{ ++ char *p = NULL; ++ struct pkcs11_uri uri; ++ struct pkcs11_key *k11; ++ ++ /* sanity - is it a RSA key with associated app_data? */ ++ if (key->type != KEY_RSA || ++ (k11 = RSA_get_app_data(key->rsa)) == NULL) ++ return -1; ++ ++ /* omit type -- we are looking for private-public or private-certificate pairs */ ++ uri.id = k11->keyid; ++ uri.id_len = k11->keyid_len; ++ uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label; ++ uri.object = k11->label; ++ uri.module_path = k11->provider->module->module_path; ++ uri.lib_manuf = k11->provider->module->info.manufacturerID; ++ uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID; ++ ++ p = pkcs11_uri_get(&uri); ++ /* do not cleanup -- we do not allocate here, only reference */ ++ if (p == NULL) ++ return -1; ++ ++ fprintf(f, " %s", p); ++ free(p); ++ return 0; ++} ++ + int + pkcs11_init(int interactive) + { +@@ -89,26 +132,63 @@ pkcs11_init(int interactive) + * this is called when a provider gets unregistered. + */ + static void +-pkcs11_provider_finalize(struct pkcs11_provider *p) ++pkcs11_module_finalize(struct pkcs11_module *m) + { + CK_RV rv; + CK_ULONG i; + +- debug("pkcs11_provider_finalize: %p refcount %d valid %d", +- p, p->refcount, p->valid); +- if (!p->valid) ++ debug("%s: %p refcount %d valid %d", __func__, ++ m, m->refcount, m->valid); ++ if (!m->valid) + return; +- for (i = 0; i < p->nslots; i++) { +- if (p->slotinfo[i].session && +- (rv = p->function_list->C_CloseSession( +- p->slotinfo[i].session)) != CKR_OK) ++ for (i = 0; i < m->nslots; i++) { ++ if (m->slotinfo[i].session && ++ (rv = m->function_list->C_CloseSession( ++ m->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } +- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) ++ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); ++ m->valid = 0; ++ m->function_list = NULL; ++ dlclose(m->handle); ++} ++ ++/* ++ * remove a reference to the pkcs11 module. ++ * called when a provider is unregistered. ++ */ ++static void ++pkcs11_module_unref(struct pkcs11_module *m) ++{ ++ debug("%s: %p refcount %d", __func__, m, m->refcount); ++ if (--m->refcount <= 0) { ++ pkcs11_module_finalize(m); ++ if (m->valid) ++ error("%s: %p still valid", __func__, m); ++ free(m->slotlist); ++ free(m->slotinfo); ++ free(m->module_path); ++ free(m); ++ } ++} ++ ++/* ++ * finalize a provider shared libarary, it's no longer usable. ++ * however, there might still be keys referencing this provider, ++ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). ++ * this is called when a provider gets unregistered. ++ */ ++static void ++pkcs11_provider_finalize(struct pkcs11_provider *p) ++{ ++ debug("%s: %p refcount %d valid %d", __func__, ++ p, p->refcount, p->valid); ++ if (!p->valid) ++ return; ++ pkcs11_module_unref(p->module); ++ p->module = NULL; + p->valid = 0; +- p->function_list = NULL; +- dlclose(p->handle); + } + + /* +@@ -118,12 +198,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p) + static void + pkcs11_provider_unref(struct pkcs11_provider *p) + { +- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); ++ debug("%s: %p refcount %d", __func__, p, p->refcount); + if (--p->refcount <= 0) { +- if (p->valid) +- error("pkcs11_provider_unref: %p still valid", p); +- free(p->slotlist); +- free(p->slotinfo); ++ if (p->module) ++ pkcs11_module_unref(p->module); ++ free(p->name); + free(p); + } + } +@@ -141,6 +220,20 @@ pkcs11_terminate(void) + } + } + ++/* lookup provider by module path */ ++static struct pkcs11_module * ++pkcs11_provider_lookup_module(char *module_path) ++{ ++ struct pkcs11_provider *p; ++ ++ TAILQ_FOREACH(p, &pkcs11_providers, next) { ++ debug("check %p %s (%s)", p, p->name, p->module->module_path); ++ if (!strcmp(module_path, p->module->module_path)) ++ return (p->module); ++ } ++ return (NULL); ++} ++ + /* lookup provider by name */ + static struct pkcs11_provider * + pkcs11_provider_lookup(char *provider_id) +@@ -155,19 +248,52 @@ pkcs11_provider_lookup(char *provider_id) + return (NULL); + } + ++int pkcs11_del_provider_by_uri(struct pkcs11_uri *); ++ + /* unregister provider by name */ + int + pkcs11_del_provider(char *provider_id) ++{ ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ rv = pkcs11_del_provider_by_uri(uri); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} ++ ++/* unregister provider by PKCS#11 URI */ ++int ++pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) + { + struct pkcs11_provider *p; ++ int rv = -1; ++ char *provider_uri = pkcs11_uri_get(uri); + +- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { ++ debug3("%s(%s): called", __func__, provider_uri); ++ ++ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); +- return (0); ++ rv = 0; + } +- return (-1); ++ free(provider_uri); ++ return rv; + } + + /* openssl callback for freeing an RSA key */ +@@ -183,6 +309,7 @@ pkcs11_rsa_finish(RSA *rsa) + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); ++ free(k11->label); + free(k11); + } + return (rv); +@@ -199,8 +327,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_RV rv; + int ret = -1; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); +@@ -247,12 +375,13 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + error("RSA_get_app_data failed for rsa %p", rsa); + return (-1); + } +- if (!k11->provider || !k11->provider->valid) { ++ if (!k11->provider || !k11->provider->valid || !k11->provider->module ++ || !k11->provider->module->valid) { + error("no pkcs11 (valid) provider for rsa %p", rsa); + return (-1); + } +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { + if (!pkcs11_interactive) { + error("need pin entry%s", (si->token.flags & +@@ -311,7 +440,7 @@ pkcs11_rsa_private_decrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + /* redirect private key operations for rsa key to pkcs11 token */ + static int + pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, +- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) ++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) + { + struct pkcs11_key *k11; + const RSA_METHOD *def = RSA_get_default_method(); +@@ -326,6 +455,11 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + } ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } + k11->orig_finish = def->finish; + memcpy(&k11->rsa_method, def, sizeof(k11->rsa_method)); + k11->rsa_method.name = "pkcs11"; +@@ -372,16 +506,16 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + CK_SESSION_HANDLE session; + int login_required; + +- f = p->function_list; +- login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; ++ f = p->module->function_list; ++ login_required = p->module->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; + if (pin && login_required && !strlen(pin)) { + error("pin required"); + return (-1); + } +- if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| ++ if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION| + CKF_SERIAL_SESSION, NULL, NULL, &session)) + != CKR_OK) { +- error("C_OpenSession failed: %lu", rv); ++ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); + return (-1); + } + if (login_required && pin) { +@@ -393,9 +527,9 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + error("C_CloseSession failed: %lu", rv); + return (-1); + } +- p->slotinfo[slotidx].logged_in = 1; ++ p->module->slotinfo[slotidx].logged_in = 1; + } +- p->slotinfo[slotidx].session = session; ++ p->module->slotinfo[slotidx].session = session; + return (0); + } + +@@ -405,38 +539,62 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin) + * keysp points to an (possibly empty) array with *nkeys keys. + */ + static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, +- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) ++ CK_ATTRIBUTE [], size_t, CK_ATTRIBUTE [3], struct sshkey ***, int *) + __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); + + static int + pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { ++ size_t filter_size = 1; + CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE pubkey_filter[] = { +- { CKA_CLASS, NULL, sizeof(pubkey_class) } ++ { CKA_CLASS, NULL, sizeof(pubkey_class) }, ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE cert_filter[] = { +- { CKA_CLASS, NULL, sizeof(cert_class) } ++ { CKA_CLASS, NULL, sizeof(cert_class) }, ++ { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 } + }; + CK_ATTRIBUTE pubkey_attribs[] = { + { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 }, + { CKA_MODULUS, NULL, 0 }, + { CKA_PUBLIC_EXPONENT, NULL, 0 } + }; + CK_ATTRIBUTE cert_attribs[] = { + { CKA_ID, NULL, 0 }, ++ { CKA_LABEL, NULL, 0 }, + { CKA_SUBJECT, NULL, 0 }, + { CKA_VALUE, NULL, 0 } + }; + pubkey_filter[0].pValue = &pubkey_class; + cert_filter[0].pValue = &cert_class; + +- if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, +- keysp, nkeys) < 0 || +- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, +- keysp, nkeys) < 0) ++ if (uri->id != NULL) { ++ pubkey_filter[filter_size].pValue = uri->id; ++ pubkey_filter[filter_size].ulValueLen = uri->id_len; ++ cert_filter[filter_size].pValue = uri->id; ++ cert_filter[filter_size].ulValueLen = uri->id_len; ++ filter_size++; ++ } ++ if (uri->object != NULL) { ++ pubkey_filter[filter_size].pValue = uri->object; ++ pubkey_filter[filter_size].ulValueLen = strlen(uri->object); ++ pubkey_filter[filter_size].type = CKA_LABEL; ++ cert_filter[filter_size].pValue = uri->object; ++ cert_filter[filter_size].ulValueLen = strlen(uri->object); ++ cert_filter[filter_size].type = CKA_LABEL; ++ filter_size++; ++ } ++ ++ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, ++ pubkey_attribs, keysp, nkeys) < 0 || ++ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, ++ cert_attribs, keysp, nkeys) < 0) + return (-1); + return (0); + } +@@ -454,14 +612,15 @@ pkcs11_key_included(struct sshkey ***keysp, int *nkeys, struct sshkey *key) + + static int + pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, +- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], ++ CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], + struct sshkey ***keysp, int *nkeys) + { + struct sshkey *key; + RSA *rsa; + X509 *x509; +- EVP_PKEY *evp; ++ EVP_PKEY *evp = NULL; + int i; ++ int nattribs = 4; + const u_char *cp; + CK_RV rv; + CK_OBJECT_HANDLE obj; +@@ -470,16 +629,15 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_SESSION_HANDLE session; + CK_FUNCTION_LIST *f; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + /* setup a filter the looks for public keys */ +- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { ++ if ((rv = f->C_FindObjectsInit(session, filter, filter_size)) != CKR_OK) { + error("C_FindObjectsInit failed: %lu", rv); + return (-1); + } + while (1) { +- /* XXX 3 attributes in attribs[] */ +- for (i = 0; i < 3; i++) { ++ for (i = 0; i < nattribs; i++) { + attribs[i].pValue = NULL; + attribs[i].ulValueLen = 0; + } +@@ -487,22 +645,22 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + || nfound == 0) + break; + /* found a key, so figure out size of the attributes */ +- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) ++ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); + continue; + } + /* +- * Allow CKA_ID (always first attribute) to be empty, but +- * ensure that none of the others are zero length. ++ * Allow CKA_ID (always first attribute) and CKA_LABEL (second) ++ * to be empty, but ensure that none of the others are zero length. + * XXX assumes CKA_ID is always first. + */ +- if (attribs[1].ulValueLen == 0 || +- attribs[2].ulValueLen == 0) { ++ if (attribs[2].ulValueLen == 0 || ++ attribs[3].ulValueLen == 0) { + continue; + } + /* allocate buffers for attributes */ +- for (i = 0; i < 3; i++) { ++ for (i = 0; i < nattribs; i++) { + if (attribs[i].ulValueLen > 0) { + attribs[i].pValue = xmalloc( + attribs[i].ulValueLen); +@@ -510,27 +668,27 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + } + + /* +- * retrieve ID, modulus and public exponent of RSA key, +- * or ID, subject and value for certificates. ++ * retrieve ID, label, modulus and public exponent of RSA key, ++ * or ID, label, subject and value for certificates. + */ + rsa = NULL; +- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) ++ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) + != CKR_OK) { + error("C_GetAttributeValue failed: %lu", rv); +- } else if (attribs[1].type == CKA_MODULUS ) { ++ } else if (attribs[2].type == CKA_MODULUS ) { + if ((rsa = RSA_new()) == NULL) { + error("RSA_new failed"); + } else { +- rsa->n = BN_bin2bn(attribs[1].pValue, +- attribs[1].ulValueLen, NULL); +- rsa->e = BN_bin2bn(attribs[2].pValue, ++ rsa->n = BN_bin2bn(attribs[2].pValue, + attribs[2].ulValueLen, NULL); ++ rsa->e = BN_bin2bn(attribs[3].pValue, ++ attribs[3].ulValueLen, NULL); + } + } else { +- cp = attribs[2].pValue; ++ cp = attribs[3].pValue; + if ((x509 = X509_new()) == NULL) { + error("X509_new failed"); +- } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) ++ } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) + == NULL) { + error("d2i_X509 failed"); + } else if ((evp = X509_get_pubkey(x509)) == NULL || +@@ -546,9 +704,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + error("RSAPublicKey_dup"); + } + X509_free(x509); ++ EVP_PKEY_free(evp); + } + if (rsa && rsa->n && rsa->e && +- pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { ++ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { + if ((key = sshkey_new(KEY_UNSPEC)) == NULL) + fatal("sshkey_new failed"); + key->rsa = rsa; +@@ -569,7 +728,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + } else if (rsa) { + RSA_free(rsa); + } +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattribs; i++) + free(attribs[i].pValue); + } + if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) +@@ -581,126 +740,239 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, + int + pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) + { +- int nkeys, need_finalize = 0; +- struct pkcs11_provider *p = NULL; ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} ++ ++struct pkcs11_provider * ++pkcs11_provider_initialize(struct pkcs11_uri *uri) ++{ ++ int need_finalize = 0; + void *handle = NULL; + CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); + CK_RV rv; + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; ++ char *provider_module = NULL; ++ struct pkcs11_provider *p; ++ struct pkcs11_module *m; + +- *keyp = NULL; +- if (pkcs11_provider_lookup(provider_id) != NULL) { +- debug("%s: provider already registered: %s", +- __func__, provider_id); ++ /* if no provider specified, fallback to p11-kit */ ++ if (uri->module_path == NULL) { ++#ifdef PKCS11_DEFAULT_PROVIDER ++ provider_module = strdup(PKCS11_DEFAULT_PROVIDER); ++#else ++ error("%s: No module path provided", __func__); + goto fail; ++#endif ++ } else ++ provider_module = strdup(uri->module_path); ++ ++ p = xcalloc(1, sizeof(*p)); ++ p->name = pkcs11_uri_get(uri); ++ ++ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL ++ && m->valid) { ++ debug("%s: provider module already initialized: %s", ++ __func__, provider_module); ++ free(provider_module); ++ /* Skip the initialization of PKCS#11 module */ ++ m->refcount++; ++ p->module = m; ++ p->valid = 1; ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ ++ return p; ++ } else { ++ m = xcalloc(1, sizeof(*m)); ++ p->module = m; ++ m->refcount++; + } ++ + /* open shared pkcs11-libarary */ +- if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { +- error("dlopen %s failed: %s", provider_id, dlerror()); ++ if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) { ++ error("dlopen %s failed: %s", provider_module, dlerror()); + goto fail; + } + if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { + error("dlsym(C_GetFunctionList) failed: %s", dlerror()); + goto fail; + } +- p = xcalloc(1, sizeof(*p)); +- p->name = xstrdup(provider_id); +- p->handle = handle; ++ m->handle = handle; + /* setup the pkcs11 callbacks */ + if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { + error("C_GetFunctionList for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- p->function_list = f; ++ m->function_list = f; + if ((rv = f->C_Initialize(NULL)) != CKR_OK) { + error("C_Initialize for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } + need_finalize = 1; +- if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { ++ if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) { + error("C_GetInfo for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID)); +- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); ++ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); ++ if (uri->lib_manuf != NULL && ++ strcmp(uri->lib_manuf, m->info.manufacturerID)) { ++ debug("%s: Skipping provider %s not matching library_manufacturer", ++ __func__, m->info.manufacturerID); ++ goto fail; ++ } ++ rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription)); + debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", +- provider_id, +- p->info.manufacturerID, +- p->info.cryptokiVersion.major, +- p->info.cryptokiVersion.minor, +- p->info.libraryDescription, +- p->info.libraryVersion.major, +- p->info.libraryVersion.minor); +- if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { ++ provider_module, ++ m->info.manufacturerID, ++ m->info.cryptokiVersion.major, ++ m->info.cryptokiVersion.minor, ++ m->info.libraryDescription, ++ m->info.libraryVersion.major, ++ m->info.libraryVersion.minor); ++ ++ if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } +- if (p->nslots == 0) { ++ if (m->nslots == 0) { + debug("%s: provider %s returned no slots", __func__, +- provider_id); ++ provider_module); + goto fail; + } +- p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); +- if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) ++ m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID)); ++ if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots)) + != CKR_OK) { + error("C_GetSlotList for provider %s failed: %lu", +- provider_id, rv); ++ provider_module, rv); + goto fail; + } +- p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); ++ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo)); ++ m->valid = 1; + p->valid = 1; +- nkeys = 0; +- for (i = 0; i < p->nslots; i++) { +- token = &p->slotinfo[i].token; +- if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) ++ ++ for (i = 0; i < m->nslots; i++) { ++ token = &m->slotinfo[i].token; ++ if ((rv = f->C_GetTokenInfo(m->slotlist[i], token)) + != CKR_OK) { + error("C_GetTokenInfo for provider %s slot %lu " +- "failed: %lu", provider_id, (unsigned long)i, rv); ++ "failed: %lu", provider_module, (unsigned long)i, rv); + continue; + } + if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { +- debug2("%s: ignoring uninitialised token in " +- "provider %s slot %lu", __func__, +- provider_id, (unsigned long)i); + continue; + } + rmspace(token->label, sizeof(token->label)); + rmspace(token->manufacturerID, sizeof(token->manufacturerID)); + rmspace(token->model, sizeof(token->model)); + rmspace(token->serialNumber, sizeof(token->serialNumber)); ++ } ++ m->module_path = provider_module; ++ provider_module = NULL; ++ ++ /* insert unconditionally -- remove if there will be no keys later */ ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ ++ return p; ++ ++fail: ++ if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) ++ error("C_Finalize for provider %s failed: %lu", ++ provider_module, rv); ++ free(provider_module); ++ free(p); ++ if (handle) ++ dlclose(handle); ++ return NULL; ++} ++ ++int ++pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***keyp) ++{ ++ int nkeys; ++ struct pkcs11_provider *p = NULL; ++ CK_TOKEN_INFO *token; ++ CK_ULONG i; ++ char *provider_uri = pkcs11_uri_get(uri); ++ ++ debug("%s: called, provider_uri = %s", __func__, provider_uri); ++ ++ *keyp = NULL; ++ if ((p = pkcs11_provider_initialize(uri)) == NULL) { ++ debug("%s: failed to initialize provider: %s", ++ __func__, provider_uri); ++ goto fail; ++ } ++ ++ nkeys = 0; ++ for (i = 0; i < p->module->nslots; i++) { ++ token = &p->module->slotinfo[i].token; ++ if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { ++ debug2("%s: ignoring uninitialised token in " ++ "provider %s slot %lu", __func__, ++ provider_uri, (unsigned long)i); ++ continue; ++ } ++ if (uri->token != NULL && ++ strcmp(token->label, uri->token) != 0) { ++ debug2("%s: ignoring token not matching label (%s) " ++ "specified by PKCS#11 URI in slot %lu", __func__, ++ token->label, (unsigned long)i); ++ continue; ++ } ++ if (uri->manuf != NULL && ++ strcmp(token->manufacturerID, uri->manuf) != 0) { ++ debug2("%s: ignoring token not matching requrested " ++ "manufacturerID (%s) specified by PKCS#11 URI in " ++ "slot %lu", __func__, ++ token->manufacturerID, (unsigned long)i); ++ continue; ++ } + debug("provider %s slot %lu: label <%s> manufacturerID <%s> " + "model <%s> serial <%s> flags 0x%lx", +- provider_id, (unsigned long)i, ++ provider_uri, (unsigned long)i, + token->label, token->manufacturerID, token->model, + token->serialNumber, token->flags); +- /* open session, login with pin and retrieve public keys */ +- if (pkcs11_open_session(p, i, pin) == 0) +- pkcs11_fetch_keys(p, i, keyp, &nkeys); ++ /* open session if not yet opened, login with pin ++ * and retrieve public keys */ ++ if ((p->module->slotinfo[i].session != 0) || ++ pkcs11_open_session(p, i, pin) == 0) ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); + } + if (nkeys > 0) { +- TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); +- p->refcount++; /* add to provider list */ ++ free(provider_uri); + return (nkeys); + } +- debug("%s: provider %s returned no keys", __func__, provider_id); ++ debug("%s: provider %s returned no keys", __func__, provider_uri); + /* don't add the provider, since it does not have any keys */ + fail: +- if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) +- error("C_Finalize for provider %s failed: %lu", +- provider_id, rv); + if (p) { +- free(p->slotlist); +- free(p->slotinfo); +- free(p); ++ pkcs11_provider_unref(p); + } +- if (handle) +- dlclose(handle); ++ free(provider_uri); + return (-1); + } + +diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h +index 0ced74f2..c63a88f6 100644 +--- a/ssh-pkcs11.h ++++ b/ssh-pkcs11.h +@@ -14,10 +14,15 @@ + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ ++ ++#include "ssh-pkcs11-uri.h" ++ + int pkcs11_init(int); + void pkcs11_terminate(void); + int pkcs11_add_provider(char *, char *, struct sshkey ***); ++int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); + int pkcs11_del_provider(char *); ++int pkcs11_uri_write(const struct sshkey *, FILE *); + + #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) + #undef ENABLE_PKCS11 +diff --git a/ssh.c b/ssh.c +index d3619fe2..180eb2e0 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -769,6 +769,14 @@ main(int ac, char **av) + options.gss_deleg_creds = 1; + break; + case 'i': ++#ifdef ENABLE_PKCS11 ++ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(optarg, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ add_identity_file(&options, NULL, optarg, 1); ++ break; ++ } ++#endif + p = tilde_expand_filename(optarg, getuid()); + if (stat(p, &st) < 0) + fprintf(stderr, "Warning: Identity file %s " +@@ -1999,6 +2007,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) + options.escape_char : SSH_ESCAPECHAR_NONE, id); + } + ++#ifdef ENABLE_PKCS11 ++static void ++load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], ++ struct sshkey *identity_keys[], int *n_ids) ++{ ++ int nkeys, i; ++ struct sshkey **keys; ++ struct pkcs11_uri *uri; ++ ++ debug("identity file '%s' from pkcs#11", pkcs11_uri); ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); ++ ++ /* we need to merge URI and provider together */ ++ if (options.pkcs11_provider != NULL && uri->module_path == NULL) ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && ++ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { ++ for (i = 0; i < nkeys; i++) { ++ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { ++ sshkey_free(keys[i]); ++ continue; ++ } ++ identity_keys[*n_ids] = keys[i]; ++ identity_files[*n_ids] = pkcs11_uri_get(uri); ++ (*n_ids)++; ++ } ++ free(keys); ++ } ++ ++ pkcs11_uri_cleanup(uri); ++} ++#endif /* ENABLE_PKCS11 */ ++ + /* Loads all IdentityFile and CertificateFile keys */ + static void + load_public_identity_files(struct passwd *pw) +@@ -2011,10 +2058,6 @@ load_public_identity_files(struct passwd *pw) + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; + int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; +-#ifdef ENABLE_PKCS11 +- struct sshkey **keys; +- int nkeys; +-#endif /* PKCS11 */ + + n_ids = n_certs = 0; + memset(identity_files, 0, sizeof(identity_files)); +@@ -2023,32 +2066,46 @@ load_public_identity_files(struct passwd *pw) + sizeof(certificate_file_userprovided)); + + #ifdef ENABLE_PKCS11 +- if (options.pkcs11_provider != NULL && +- options.num_identity_files < SSH_MAX_IDENTITY_FILES && +- (pkcs11_init(!options.batch_mode) == 0) && +- (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, +- &keys)) > 0) { +- for (i = 0; i < nkeys; i++) { +- if (n_ids >= SSH_MAX_IDENTITY_FILES) { +- sshkey_free(keys[i]); +- continue; +- } +- identity_keys[n_ids] = keys[i]; +- identity_files[n_ids] = +- xstrdup(options.pkcs11_provider); /* XXX */ +- n_ids++; +- } +- free(keys); ++ /* handle fallback from PKCS11Provider option */ ++ pkcs11_init(!options.batch_mode); ++ ++ if (options.pkcs11_provider != NULL) { ++ struct pkcs11_uri *uri; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ /* Construct simple PKCS#11 URI to simplify access */ ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ /* Add it as any other IdentityFile */ ++ cp = pkcs11_uri_get(uri); ++ add_identity_file(&options, NULL, cp, 1); ++ free(cp); ++ ++ pkcs11_uri_cleanup(uri); + } + #endif /* ENABLE_PKCS11 */ + for (i = 0; i < options.num_identity_files; i++) { ++ char *name = options.identity_files[i]; + if (n_ids >= SSH_MAX_IDENTITY_FILES || +- strcasecmp(options.identity_files[i], "none") == 0) { ++ strcasecmp(name, "none") == 0) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + continue; + } +- cp = tilde_expand_filename(options.identity_files[i], getuid()); ++#ifdef ENABLE_PKCS11 ++ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(name, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ load_pkcs11_identity(name, identity_files, ++ identity_keys, &n_ids); ++ free(options.identity_files[i]); ++ continue; ++ } ++#endif /* ENABLE_PKCS11 */ ++ cp = tilde_expand_filename(name, getuid()); + filename = percent_expand(cp, "d", pw->pw_dir, + "u", pw->pw_name, "l", thishost, "h", host, + "r", options.user, (char *)NULL); +diff --git a/ssh_config.5 b/ssh_config.5 +index 71705cab..e0266609 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -919,6 +919,19 @@ may also be used in conjunction with + .Cm CertificateFile + in order to provide any certificate also needed for authentication with + the identity. ++.Pp ++The authentication identity can be also specified in a form of PKCS#11 URI ++starting with a string ++.Cm pkcs11: . ++There is supported a subset of the PKCS#11 URI as defined ++in RFC 7512 (implemented path arguments ++.Cm id , ++.Cm manufacturer , ++.Cm object , ++.Cm token ++and query argument ++.Cm module-path ++). The URI can not be in quotes. + .It Cm IgnoreUnknown + Specifies a pattern-list of unknown options to be ignored if they are + encountered in configuration parsing. diff --git a/SOURCES/openssh-7.7p1-fips.patch b/SOURCES/openssh-7.7p1-fips.patch new file mode 100644 index 0000000..8606ca5 --- /dev/null +++ b/SOURCES/openssh-7.7p1-fips.patch @@ -0,0 +1,709 @@ +diff -up openssh-7.7p1/cipher.c.fips openssh-7.7p1/cipher.c +--- openssh-7.7p1/cipher.c.fips 2018-08-08 10:08:40.814719906 +0200 ++++ openssh-7.7p1/cipher.c 2018-08-08 10:08:40.821719965 +0200 +@@ -39,6 +39,8 @@ + + #include + ++#include ++ + #include + #include + #include +@@ -90,6 +92,33 @@ static const struct sshcipher ciphers[] + { NULL, 0, 0, 0, 0, 0, NULL } + }; + ++static const struct sshcipher fips_ciphers[] = { ++#ifdef WITH_OPENSSL ++ { "3des-cbc", 8, 24, 0, 0, CFLAG_CBC, EVP_des_ede3_cbc }, ++ { "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 a comma-separated list of supported ciphers. */ +@@ -100,7 +129,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_mode() ? fips_ciphers : ciphers; c->name != NULL; c++) { + if ((c->flags & CFLAG_INTERNAL) != 0) + continue; + if (auth_only && c->auth_len == 0) +@@ -172,7 +201,7 @@ const struct sshcipher * + cipher_by_name(const char *name) + { + const struct sshcipher *c; +- for (c = ciphers; c->name != NULL; c++) ++ for (c = FIPS_mode() ? fips_ciphers : ciphers; c->name != NULL; c++) + if (strcmp(c->name, name) == 0) + return c; + return NULL; +diff -up openssh-7.7p1/cipher-ctr.c.fips openssh-7.7p1/cipher-ctr.c +--- openssh-7.7p1/cipher-ctr.c.fips 2018-08-08 10:08:40.709719021 +0200 ++++ openssh-7.7p1/cipher-ctr.c 2018-08-08 10:08:40.821719965 +0200 +@@ -179,7 +179,8 @@ evp_aes_128_ctr(void) + aes_ctr.do_cipher = ssh_aes_ctr; + #ifndef SSH_OLD_EVP + aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | +- EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; ++ EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV | ++ EVP_CIPH_FLAG_FIPS; + #endif + return (&aes_ctr); + } +diff -up openssh-7.7p1/clientloop.c.fips openssh-7.7p1/clientloop.c +--- openssh-7.7p1/clientloop.c.fips 2018-08-08 10:08:40.769719527 +0200 ++++ openssh-7.7p1/clientloop.c 2018-08-08 10:08:40.822719973 +0200 +@@ -1978,7 +1978,8 @@ key_accepted_by_hostkeyalgs(const struct + { + const char *ktype = sshkey_ssh_name(key); + const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? +- options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; ++ options.hostkeyalgorithms : (FIPS_mode() ? ++ KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG); + + if (key == NULL || key->type == KEY_UNSPEC) + return 0; +diff -up openssh-7.7p1/dh.h.fips openssh-7.7p1/dh.h +--- openssh-7.7p1/dh.h.fips 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/dh.h 2018-08-08 10:08:40.822719973 +0200 +@@ -51,6 +51,7 @@ u_int dh_estimate(int); + * Miniumum increased in light of DH precomputation attacks. + */ + #define DH_GRP_MIN 2048 ++#define DH_GRP_MIN_FIPS 2048 + #define DH_GRP_MAX 8192 + + /* +diff -up openssh-7.7p1/entropy.c.fips openssh-7.7p1/entropy.c +--- openssh-7.7p1/entropy.c.fips 2018-08-08 10:08:40.698718928 +0200 ++++ openssh-7.7p1/entropy.c 2018-08-08 10:08:40.822719973 +0200 +@@ -217,6 +217,9 @@ seed_rng(void) + fatal("OpenSSL version mismatch. Built against %lx, you " + "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); + ++ /* clean the PRNG status when exiting the program */ ++ atexit(RAND_cleanup); ++ + #ifndef OPENSSL_PRNG_ONLY + if (RAND_status() == 1) { + debug3("RNG is ready, skipping seeding"); +diff -up openssh-7.7p1/kex.c.fips openssh-7.7p1/kex.c +--- openssh-7.7p1/kex.c.fips 2018-08-08 10:08:40.815719915 +0200 ++++ openssh-7.7p1/kex.c 2018-08-08 10:11:24.109081924 +0200 +@@ -35,6 +35,7 @@ + #ifdef WITH_OPENSSL + #include + #include ++#include + #endif + + #include "ssh2.h" +@@ -122,6 +123,26 @@ static const struct kexalg kexalgs[] = { + { NULL, -1, -1, -1}, + }; + ++static const struct kexalg kexalgs_fips[] = { ++ { 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 }, ++#ifdef HAVE_EVP_SHA256 ++ { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, ++#endif ++#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 ++#endif ++ { NULL, -1, -1, -1}, ++}; ++ + char * + kex_alg_list(char sep) + { +@@ -129,7 +150,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_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); +@@ -149,7 +170,7 @@ kex_alg_by_name(const char *name) + { + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = (FIPS_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; + #ifdef GSSAPI +@@ -175,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) { +- error("Unsupported KEX algorithm \"%.100s\"", p); ++ if (FIPS_mode()) ++ error("\"%.100s\" is not allowed in FIPS mode", p); ++ else ++ error("Unsupported KEX algorithm \"%.100s\"", p); + free(s); + return 0; + } +diff -up openssh-7.7p1/kexgexc.c.fips openssh-7.7p1/kexgexc.c +--- openssh-7.7p1/kexgexc.c.fips 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/kexgexc.c 2018-08-08 10:08:40.822719973 +0200 +@@ -28,6 +28,7 @@ + + #ifdef WITH_OPENSSL + ++#include + #include + + #include +@@ -63,7 +64,7 @@ kexgex_client(struct ssh *ssh) + + nbits = dh_estimate(kex->dh_need * 8); + +- kex->min = DH_GRP_MIN; ++ kex->min = FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN; + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (datafellows & SSH_BUG_DHGEX_LARGE) +diff -up openssh-7.7p1/kexgexs.c.fips openssh-7.7p1/kexgexs.c +--- openssh-7.7p1/kexgexs.c.fips 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/kexgexs.c 2018-08-08 10:08:40.823719982 +0200 +@@ -82,9 +82,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_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, min); + max = MINIMUM(DH_GRP_MAX, max); +- nbits = MAXIMUM(DH_GRP_MIN, nbits); ++ nbits = MAXIMUM(FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, nbits); + nbits = MINIMUM(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || +diff -up openssh-7.7p1/mac.c.fips openssh-7.7p1/mac.c +--- openssh-7.7p1/mac.c.fips 2018-08-08 10:08:40.815719915 +0200 ++++ openssh-7.7p1/mac.c 2018-08-08 10:11:56.915352642 +0200 +@@ -27,6 +27,8 @@ + + #include + ++#include ++ + #include + #include + +@@ -54,7 +56,7 @@ struct macalg { + int etm; /* Encrypt-then-MAC */ + }; + +-static const struct macalg macs[] = { ++static const struct macalg all_macs[] = { + /* 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 +84,24 @@ static const struct macalg macs[] = { + { NULL, 0, 0, 0, 0, 0, 0 } + }; + ++static const struct macalg fips_macs[] = { ++ /* 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 a list of supported MACs separated by the specified char. */ + char * + mac_alg_list(char sep) +@@ -90,7 +110,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_mode() ? fips_macs : all_macs; m->name != NULL; m++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(m->name); +@@ -129,7 +149,7 @@ mac_setup(struct sshmac *mac, char *name + { + const struct macalg *m; + +- for (m = macs; m->name != NULL; m++) { ++ for (m = FIPS_mode() ? fips_macs : all_macs; m->name != NULL; m++) { + if (strcmp(name, m->name) != 0) + continue; + if (mac != NULL) +diff -up openssh-7.7p1/Makefile.in.fips openssh-7.7p1/Makefile.in +--- openssh-7.7p1/Makefile.in.fips 2018-08-08 10:08:40.815719915 +0200 ++++ openssh-7.7p1/Makefile.in 2018-08-08 10:08:40.823719982 +0200 +@@ -179,25 +179,25 @@ libssh.a: $(LIBSSH_OBJS) + $(RANLIB) $@ + + ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) +- $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) ++ $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(SSHLIBS) $(LIBS) $(GSSLIBS) + + sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) +- $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) ++ $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) + + scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o + $(LD) -o $@ scp.o progressmeter.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o +- $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + + ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o +- $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + + ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o +- $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + + ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o +- $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ++ $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) + + ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o + $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) +@@ -215,7 +215,7 @@ ssh-cavs$(EXEEXT): $(LIBCOMPAT) libssh.a + $(LD) -o $@ ssh-cavs.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + + ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o +- $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) ++ $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lfipscheck $(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) +diff -up openssh-7.7p1/myproposal.h.fips openssh-7.7p1/myproposal.h +--- openssh-7.7p1/myproposal.h.fips 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/myproposal.h 2018-08-08 10:08:40.823719982 +0200 +@@ -114,6 +114,14 @@ + "rsa-sha2-256," \ + "ssh-rsa" + ++#define KEX_FIPS_PK_ALG \ ++ HOSTKEY_ECDSA_CERT_METHODS \ ++ "ssh-rsa-cert-v01@openssh.com," \ ++ HOSTKEY_ECDSA_METHODS \ ++ "rsa-sha2-512," \ ++ "rsa-sha2-256," \ ++ "ssh-rsa" ++ + /* the actual algorithms */ + + #define KEX_SERVER_ENCRYPT \ +@@ -137,6 +145,38 @@ + + #define KEX_CLIENT_MAC KEX_SERVER_MAC + ++#define KEX_FIPS_ENCRYPT \ ++ "aes128-ctr,aes192-ctr,aes256-ctr," \ ++ "aes128-cbc,3des-cbc," \ ++ "aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se" \ ++ AESGCM_CIPHER_MODES ++#ifdef HAVE_EVP_SHA256 ++# define KEX_DEFAULT_KEX_FIPS \ ++ KEX_ECDH_METHODS \ ++ KEX_SHA2_METHODS \ ++ "diffie-hellman-group14-sha256" ++# define KEX_FIPS_MAC \ ++ "hmac-sha1," \ ++ "hmac-sha2-256," \ ++ "hmac-sha2-512," \ ++ "hmac-sha1-etm@openssh.com," \ ++ "hmac-sha2-256-etm@openssh.com," \ ++ "hmac-sha2-512-etm@openssh.com" ++#else ++# ifdef OPENSSL_HAS_NISTP521 ++# define KEX_DEFAULT_KEX_FIPS \ ++ "ecdh-sha2-nistp256," \ ++ "ecdh-sha2-nistp384," \ ++ "ecdh-sha2-nistp521" ++# else ++# define KEX_DEFAULT_KEX_FIPS \ ++ "ecdh-sha2-nistp256," \ ++ "ecdh-sha2-nistp384" ++# endif ++#define KEX_FIPS_MAC \ ++ "hmac-sha1" ++#endif ++ + #else /* WITH_OPENSSL */ + + #define KEX_SERVER_KEX \ +diff -up openssh-7.7p1/readconf.c.fips openssh-7.7p1/readconf.c +--- openssh-7.7p1/readconf.c.fips 2018-08-08 10:08:40.769719527 +0200 ++++ openssh-7.7p1/readconf.c 2018-08-08 10:08:40.824719990 +0200 +@@ -2081,17 +2081,18 @@ fill_default_options(Options * options) + all_mac = mac_alg_list(','); + all_kex = kex_alg_list(','); + all_key = sshkey_alg_list(0, 0, 1, ','); +-#define ASSEMBLE(what, defaults, all) \ ++#define ASSEMBLE(what, defaults, fips_defaults, all) \ + do { \ + if ((r = kex_assemble_names(&options->what, \ +- defaults, all)) != 0) \ ++ (FIPS_mode() ? fips_defaults : defaults), \ ++ all)) != 0) \ + fatal("%s: %s: %s", __func__, #what, ssh_err(r)); \ + } while (0) +- ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, all_cipher); +- ASSEMBLE(macs, KEX_SERVER_MAC, all_mac); +- ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); +- ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); +- ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); ++ ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); ++ ASSEMBLE(macs, KEX_SERVER_MAC, KEX_FIPS_MAC, all_mac); ++ ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); ++ ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); ++ ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + #undef ASSEMBLE + free(all_cipher); + free(all_mac); +diff -up openssh-7.7p1/sandbox-seccomp-filter.c.fips openssh-7.7p1/sandbox-seccomp-filter.c +--- openssh-7.7p1/sandbox-seccomp-filter.c.fips 2018-08-08 10:08:40.794719737 +0200 ++++ openssh-7.7p1/sandbox-seccomp-filter.c 2018-08-08 10:08:40.824719990 +0200 +@@ -137,6 +137,9 @@ static const struct sock_filter preauth_ + #ifdef __NR_open + SC_DENY(__NR_open, EACCES), + #endif ++#ifdef __NR_socket ++ SC_DENY(__NR_socket, EACCES), ++#endif + #ifdef __NR_openat + SC_DENY(__NR_openat, EACCES), + #endif +diff -up openssh-7.7p1/servconf.c.fips openssh-7.7p1/servconf.c +--- openssh-7.7p1/servconf.c.fips 2018-08-08 10:08:40.778719603 +0200 ++++ openssh-7.7p1/servconf.c 2018-08-08 10:08:40.824719990 +0200 +@@ -196,17 +196,18 @@ option_clear_or_none(const char *o) + all_mac = mac_alg_list(','); + all_kex = kex_alg_list(','); + all_key = sshkey_alg_list(0, 0, 1, ','); +-#define ASSEMBLE(what, defaults, all) \ ++#define ASSEMBLE(what, defaults, fips_defaults, all) \ + do { \ +- if ((r = kex_assemble_names(&o->what, defaults, all)) != 0) \ ++ if ((r = kex_assemble_names(&o->what, (FIPS_mode() \ ++ ? fips_defaults : defaults), all)) != 0) \ + fatal("%s: %s: %s", __func__, #what, ssh_err(r)); \ + } while (0) +- ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, all_cipher); +- ASSEMBLE(macs, KEX_SERVER_MAC, all_mac); +- ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); +- ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, all_key); +- ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); +- ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); ++ ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); ++ ASSEMBLE(macs, KEX_SERVER_MAC, KEX_FIPS_MAC, all_mac); ++ ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); ++ ASSEMBLE(hostkeyalgorithms, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); ++ ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); ++ ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + #undef ASSEMBLE + free(all_cipher); + free(all_mac); +diff -up openssh-7.7p1/ssh.c.fips openssh-7.7p1/ssh.c +--- openssh-7.7p1/ssh.c.fips 2018-08-08 10:08:40.811719881 +0200 ++++ openssh-7.7p1/ssh.c 2018-08-08 10:08:40.825719999 +0200 +@@ -76,6 +76,8 @@ + #include + #include + #endif ++#include ++#include + #include "openbsd-compat/openssl-compat.h" + #include "openbsd-compat/sys-queue.h" + +@@ -579,6 +581,14 @@ main(int ac, char **av) + sanitise_stdfd(); + + __progname = ssh_get_progname(av[0]); ++ SSLeay_add_all_algorithms(); ++ if (access("/etc/system-fips", F_OK) == 0) ++ if (! FIPSCHECK_verify(NULL, NULL)){ ++ if (FIPS_mode()) ++ fatal("FIPS integrity verification test failed."); ++ else ++ logit("FIPS integrity verification test failed."); ++ } + + #ifndef HAVE_SETPROCTITLE + /* Prepare for later setproctitle emulation */ +@@ -1045,7 +1055,6 @@ main(int ac, char **av) + host_arg = xstrdup(host); + + #ifdef WITH_OPENSSL +- OpenSSL_add_all_algorithms(); + ERR_load_crypto_strings(); + #endif + +@@ -1268,6 +1277,10 @@ main(int ac, char **av) + + seed_rng(); + ++ if (FIPS_mode()) { ++ logit("FIPS mode initialized"); ++ } ++ + if (options.user == NULL) + options.user = xstrdup(pw->pw_name); + +diff -up openssh-7.7p1/sshconnect2.c.fips openssh-7.7p1/sshconnect2.c +--- openssh-7.7p1/sshconnect2.c.fips 2018-08-08 10:08:40.786719670 +0200 ++++ openssh-7.7p1/sshconnect2.c 2018-08-08 10:08:40.825719999 +0200 +@@ -44,6 +44,8 @@ + #include + #endif + ++#include ++ + #include "openbsd-compat/sys-queue.h" + + #include "xmalloc.h" +@@ -235,7 +237,8 @@ order_hostkeyalgs(char *host, struct soc + for (i = 0; i < options.num_system_hostfiles; i++) + load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); + +- oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); ++ oavail = avail = xstrdup((FIPS_mode() ++ ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG)); + maxlen = strlen(avail) + 1; + first = xmalloc(maxlen); + last = xmalloc(maxlen); +@@ -290,23 +293,28 @@ ssh_kex2(char *host, struct sockaddr *ho + + #ifdef GSSAPI + 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_server_identity) +- gss_host = options.gss_server_identity; +- else 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.gss_kex_algorithms); +- if (gss) { +- debug("Offering GSSAPI proposal: %s", gss); +- xasprintf(&options.kex_algorithms, +- "%s,%s", gss, orig); ++ if (FIPS_mode()) { ++ logit("Disabling GSSAPIKeyExchange. Not usable in FIPS mode"); ++ options.gss_keyex = 0; ++ } else { ++ /* Add the GSSAPI mechanisms currently supported on this ++ * client to the key exchange algorithm proposal */ ++ orig = options.kex_algorithms; ++ ++ if (options.gss_server_identity) ++ gss_host = options.gss_server_identity; ++ else 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.gss_kex_algorithms); ++ if (gss) { ++ debug("Offering GSSAPI proposal: %s", gss); ++ xasprintf(&options.kex_algorithms, ++ "%s,%s", gss, orig); ++ } + } + } + #endif +@@ -322,14 +330,16 @@ ssh_kex2(char *host, struct sockaddr *ho + if (options.hostkeyalgorithms != NULL) { + all_key = sshkey_alg_list(0, 0, 1, ','); + if (kex_assemble_names(&options.hostkeyalgorithms, +- KEX_DEFAULT_PK_ALG, all_key) != 0) ++ (FIPS_mode() ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG), ++ all_key) != 0) + fatal("%s: kex_assemble_namelist", __func__); + free(all_key); + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal(options.hostkeyalgorithms); + } else { + /* Enforce default */ +- options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); ++ options.hostkeyalgorithms = xstrdup((FIPS_mode() ++ ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG)); + /* Prefer algorithms that we already have keys for */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal( +diff -up openssh-7.7p1/sshd.c.fips openssh-7.7p1/sshd.c +--- openssh-7.7p1/sshd.c.fips 2018-08-08 10:08:40.818719940 +0200 ++++ openssh-7.7p1/sshd.c 2018-08-08 10:08:40.826720007 +0200 +@@ -66,6 +66,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -77,6 +78,8 @@ + #include + #include + #include ++#include ++#include + #include "openbsd-compat/openssl-compat.h" + #endif + +@@ -1534,6 +1537,18 @@ main(int ac, char **av) + #endif + __progname = ssh_get_progname(av[0]); + ++ SSLeay_add_all_algorithms(); ++ if (access("/etc/system-fips", F_OK) == 0) ++ if (! FIPSCHECK_verify(NULL, NULL)) { ++ openlog(__progname, LOG_PID, LOG_AUTHPRIV); ++ if (FIPS_mode()) { ++ syslog(LOG_CRIT, "FIPS integrity verification test failed."); ++ cleanup_exit(255); ++ } ++ else ++ syslog(LOG_INFO, "FIPS integrity verification test failed."); ++ closelog(); ++ } + /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ + saved_argc = ac; + rexec_argc = ac; +@@ -1675,7 +1690,7 @@ main(int ac, char **av) + else + closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); + +-#ifdef WITH_OPENSSL ++#if 0 /* FIPS */ + OpenSSL_add_all_algorithms(); + #endif + +@@ -1979,6 +1994,10 @@ main(int ac, char **av) + /* Reinitialize the log (because of the fork above). */ + log_init(__progname, options.log_level, options.log_facility, log_stderr); + ++ if (FIPS_mode()) { ++ logit("FIPS mode initialized"); ++ } ++ + /* Chdir to the root directory so that the current disk can be + unmounted if desired. */ + if (chdir("/") == -1) +@@ -2359,10 +2378,14 @@ do_ssh2_kex(void) + if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) + orig = NULL; + +- if (options.gss_keyex) +- gss = ssh_gssapi_server_mechanisms(); +- else +- gss = NULL; ++ if (options.gss_keyex) { ++ if (FIPS_mode()) { ++ logit("Disabling GSSAPIKeyExchange. Not usable in FIPS mode"); ++ options.gss_keyex = 0; ++ } else { ++ gss = ssh_gssapi_server_mechanisms(); ++ } ++ } + + if (gss && orig) + xasprintf(&newstr, "%s,%s", gss, orig); +diff -up openssh-7.7p1/sshkey.c.fips openssh-7.7p1/sshkey.c +--- openssh-7.7p1/sshkey.c.fips 2018-08-08 10:08:40.818719940 +0200 ++++ openssh-7.7p1/sshkey.c 2018-08-08 10:08:40.826720007 +0200 +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #endif + + #include "crypto_api.h" +@@ -57,6 +58,7 @@ + #include "sshkey.h" + #include "sshkey-xmss.h" + #include "match.h" ++#include "log.h" + + #include "xmss_fast.h" + +@@ -1526,6 +1528,8 @@ rsa_generate_private_key(u_int bits, RSA + } + if (!BN_set_word(f4, RSA_F4) || + !RSA_generate_key_ex(private, bits, f4, NULL)) { ++ if (FIPS_mode()) ++ logit("%s: the key length might be unsupported by FIPS mode approved key generation method", __func__); + ret = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +diff -up openssh-7.7p1/ssh-keygen.c.fips openssh-7.7p1/ssh-keygen.c +--- openssh-7.7p1/ssh-keygen.c.fips 2018-08-08 10:08:40.801719797 +0200 ++++ openssh-7.7p1/ssh-keygen.c 2018-08-08 10:08:40.827720016 +0200 +@@ -229,6 +229,12 @@ type_bits_valid(int type, const char *na + OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; + if (*bitsp > maxbits) + fatal("key bits exceeds maximum %d", maxbits); ++ if (FIPS_mode()) { ++ if (type == KEY_DSA) ++ fatal("DSA keys are not allowed in FIPS mode"); ++ if (type == KEY_ED25519) ++ fatal("ED25519 keys are not allowed in FIPS mode"); ++ } + switch (type) { + case KEY_DSA: + if (*bitsp != 1024) diff --git a/SOURCES/openssh-7.7p1-gssapi-new-unique.patch b/SOURCES/openssh-7.7p1-gssapi-new-unique.patch new file mode 100644 index 0000000..5e9c145 --- /dev/null +++ b/SOURCES/openssh-7.7p1-gssapi-new-unique.patch @@ -0,0 +1,648 @@ +diff --git a/auth-krb5.c b/auth-krb5.c +index a5a81ed2..63f877f2 100644 +--- a/auth-krb5.c ++++ b/auth-krb5.c +@@ -51,6 +51,7 @@ + #include + #include + #include ++#include + + extern ServerOptions options; + +@@ -77,7 +78,7 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + #endif + krb5_error_code problem; + krb5_ccache ccache = NULL; +- int len; ++ char *ticket_name = NULL; + char *client, *platform_client; + const char *errmsg; + +@@ -163,7 +164,8 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + goto out; + } + +- problem = ssh_krb5_cc_gen(authctxt->krb5_ctx, &authctxt->krb5_fwd_ccache); ++ problem = ssh_krb5_cc_new_unique(authctxt->krb5_ctx, ++ &authctxt->krb5_fwd_ccache, &authctxt->krb5_set_env); + if (problem) + goto out; + +@@ -172,21 +174,20 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + if (problem) + goto out; + +- problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, ++ problem = krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, + &creds); + if (problem) + goto out; + #endif + +- authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); ++ problem = krb5_cc_get_full_name(authctxt->krb5_ctx, ++ authctxt->krb5_fwd_ccache, &ticket_name); + +- len = strlen(authctxt->krb5_ticket_file) + 6; +- authctxt->krb5_ccname = xmalloc(len); +- snprintf(authctxt->krb5_ccname, len, "FILE:%s", +- authctxt->krb5_ticket_file); ++ authctxt->krb5_ccname = xstrdup(ticket_name); ++ krb5_free_string(authctxt->krb5_ctx, ticket_name); + + #ifdef USE_PAM +- if (options.use_pam) ++ if (options.use_pam && authctxt->krb5_set_env) + do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); + #endif + +@@ -222,11 +223,54 @@ auth_krb5_password(Authctxt *authctxt, const char *password) + void + krb5_cleanup_proc(Authctxt *authctxt) + { ++ struct stat krb5_ccname_stat; ++ char krb5_ccname[128], *krb5_ccname_dir_start, *krb5_ccname_dir_end; ++ + debug("krb5_cleanup_proc called"); + if (authctxt->krb5_fwd_ccache) { +- krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); ++ krb5_context ctx = authctxt->krb5_ctx; ++ krb5_cccol_cursor cursor; ++ krb5_ccache ccache; ++ int ret; ++ ++ krb5_cc_destroy(ctx, authctxt->krb5_fwd_ccache); + authctxt->krb5_fwd_ccache = NULL; ++ ++ ret = krb5_cccol_cursor_new(ctx, &cursor); ++ if (ret) ++ goto out; ++ ++ ret = krb5_cccol_cursor_next(ctx, cursor, &ccache); ++ if (ret == 0 && ccache != NULL) { ++ /* There is at least one other ccache in collection ++ * we can switch to */ ++ krb5_cc_switch(ctx, ccache); ++ } else if (authctxt->krb5_ccname != NULL) { ++ /* Clean up the collection too */ ++ strncpy(krb5_ccname, authctxt->krb5_ccname, sizeof(krb5_ccname) - 10); ++ krb5_ccname_dir_start = strchr(krb5_ccname, ':') + 1; ++ *krb5_ccname_dir_start++ = '\0'; ++ if (strcmp(krb5_ccname, "DIR") == 0) { ++ ++ strcat(krb5_ccname_dir_start, "/primary"); ++ ++ if (stat(krb5_ccname_dir_start, &krb5_ccname_stat) == 0) { ++ if (unlink(krb5_ccname_dir_start) == 0) { ++ krb5_ccname_dir_end = strrchr(krb5_ccname_dir_start, '/'); ++ *krb5_ccname_dir_end = '\0'; ++ if (rmdir(krb5_ccname_dir_start) == -1) ++ debug("cache dir '%s' remove failed: %s", ++ krb5_ccname_dir_start, strerror(errno)); ++ } ++ else ++ debug("cache primary file '%s', remove failed: %s", ++ krb5_ccname_dir_start, strerror(errno)); ++ } ++ } ++ } ++ krb5_cccol_cursor_free(ctx, &cursor); + } ++out: + if (authctxt->krb5_user) { + krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); + authctxt->krb5_user = NULL; +@@ -237,36 +287,186 @@ krb5_cleanup_proc(Authctxt *authctxt) + } + } + +-#ifndef HEIMDAL +-krb5_error_code +-ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { +- int tmpfd, ret, oerrno; +- char ccname[40]; +- mode_t old_umask; + +- ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); +- if (ret < 0 || (size_t)ret >= sizeof(ccname)) +- return ENOMEM; +- +- old_umask = umask(0177); +- tmpfd = mkstemp(ccname + strlen("FILE:")); +- oerrno = errno; +- umask(old_umask); +- if (tmpfd == -1) { +- logit("mkstemp(): %.100s", strerror(oerrno)); +- return oerrno; ++#if !defined(HEIMDAL) ++int ++ssh_asprintf_append(char **dsc, const char *fmt, ...) { ++ char *src, *old; ++ va_list ap; ++ int i; ++ ++ va_start(ap, fmt); ++ i = vasprintf(&src, fmt, ap); ++ va_end(ap); ++ ++ if (i == -1 || src == NULL) ++ return -1; ++ ++ old = *dsc; ++ ++ i = asprintf(dsc, "%s%s", *dsc, src); ++ if (i == -1 || src == NULL) { ++ free(src); ++ return -1; ++ } ++ ++ free(old); ++ free(src); ++ ++ return i; ++} ++ ++int ++ssh_krb5_expand_template(char **result, const char *template) { ++ char *p_n, *p_o, *r, *tmp_template; ++ ++ debug3("%s: called, template = %s", __func__, template); ++ if (template == NULL) ++ return -1; ++ ++ tmp_template = p_n = p_o = xstrdup(template); ++ r = xstrdup(""); ++ ++ while ((p_n = strstr(p_o, "%{")) != NULL) { ++ ++ *p_n++ = '\0'; ++ if (ssh_asprintf_append(&r, "%s", p_o) == -1) ++ goto cleanup; ++ ++ if (strncmp(p_n, "{uid}", 5) == 0 || strncmp(p_n, "{euid}", 6) == 0 || ++ strncmp(p_n, "{USERID}", 8) == 0) { ++ p_o = strchr(p_n, '}') + 1; ++ if (ssh_asprintf_append(&r, "%d", geteuid()) == -1) ++ goto cleanup; ++ continue; ++ } ++ else if (strncmp(p_n, "{TEMP}", 6) == 0) { ++ p_o = strchr(p_n, '}') + 1; ++ if (ssh_asprintf_append(&r, "/tmp") == -1) ++ goto cleanup; ++ continue; ++ } else { ++ p_o = strchr(p_n, '}') + 1; ++ p_o = '\0'; ++ debug("%s: unsupported token %s in %s", __func__, p_n, template); ++ /* unknown token, fallback to the default */ ++ goto cleanup; ++ } + } + +- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { ++ if (ssh_asprintf_append(&r, "%s", p_o) == -1) ++ goto cleanup; ++ ++ *result = r; ++ free(tmp_template); ++ return 0; ++ ++cleanup: ++ free(r); ++ free(tmp_template); ++ return -1; ++} ++ ++krb5_error_code ++ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { ++ profile_t p; ++ int ret = 0; ++ char *value = NULL; ++ ++ debug3("%s: called", __func__); ++ ret = krb5_get_profile(ctx, &p); ++ if (ret) ++ return ret; ++ ++ ret = profile_get_string(p, "libdefaults", "default_ccache_name", NULL, NULL, &value); ++ if (ret || !value) ++ return ret; ++ ++ ret = ssh_krb5_expand_template(ccname, value); ++ ++ debug3("%s: returning with ccname = %s", __func__, *ccname); ++ return ret; ++} ++ ++krb5_error_code ++ssh_krb5_cc_new_unique(krb5_context ctx, krb5_ccache *ccache, int *need_environment) { ++ int tmpfd, ret, oerrno, type_len; ++ char *ccname = NULL; ++ mode_t old_umask; ++ char *type = NULL, *colon = NULL; ++ ++ debug3("%s: called", __func__); ++ if (need_environment) ++ *need_environment = 0; ++ ret = ssh_krb5_get_cctemplate(ctx, &ccname); ++ if (ret || !ccname || options.kerberos_unique_ccache) { ++ /* Otherwise, go with the old method */ ++ if (ccname) ++ free(ccname); ++ ccname = NULL; ++ ++ ret = asprintf(&ccname, ++ "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ if (ret < 0) ++ return ENOMEM; ++ ++ old_umask = umask(0177); ++ tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; +- logit("fchmod(): %.100s", strerror(oerrno)); ++ umask(old_umask); ++ if (tmpfd == -1) { ++ logit("mkstemp(): %.100s", strerror(oerrno)); ++ return oerrno; ++ } ++ ++ if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { ++ oerrno = errno; ++ logit("fchmod(): %.100s", strerror(oerrno)); ++ close(tmpfd); ++ return oerrno; ++ } ++ /* make sure the KRB5CCNAME is set for non-standard location */ ++ if (need_environment) ++ *need_environment = 1; + close(tmpfd); +- return oerrno; + } +- close(tmpfd); + +- return (krb5_cc_resolve(ctx, ccname, ccache)); ++ debug3("%s: setting default ccname to %s", __func__, ccname); ++ /* set the default with already expanded user IDs */ ++ ret = krb5_cc_set_default_name(ctx, ccname); ++ if (ret) ++ return ret; ++ ++ if ((colon = strstr(ccname, ":")) != NULL) { ++ type_len = colon - ccname; ++ type = malloc((type_len + 1) * sizeof(char)); ++ if (type == NULL) ++ return ENOMEM; ++ strncpy(type, ccname, type_len); ++ type[type_len] = 0; ++ } else { ++ type = strdup(ccname); ++ } ++ ++ /* If we have a credential cache from krb5.conf, we need to switch ++ * a primary cache for this collection, if it supports that (non-FILE) ++ */ ++ if (krb5_cc_support_switch(ctx, type)) { ++ debug3("%s: calling cc_new_unique(%s)", __func__, ccname); ++ ret = krb5_cc_new_unique(ctx, type, NULL, ccache); ++ if (ret) ++ return ret; ++ ++ debug3("%s: calling cc_switch()", __func__); ++ return krb5_cc_switch(ctx, *ccache); ++ } else { ++ /* Otherwise, we can not create a unique ccname here (either ++ * it is already unique from above or the type does not support ++ * collections ++ */ ++ debug3("%s: calling cc_resolve(%s)", __func__, ccname); ++ return (krb5_cc_resolve(ctx, ccname, ccache)); ++ } + } + #endif /* !HEIMDAL */ + #endif /* KRB5 */ +diff --git a/auth.h b/auth.h +index 29491df9..fdab5040 100644 +--- a/auth.h ++++ b/auth.h +@@ -82,6 +82,7 @@ struct Authctxt { + krb5_principal krb5_user; + char *krb5_ticket_file; + char *krb5_ccname; ++ int krb5_set_env; + #endif + struct sshbuf *loginmsg; + +@@ -243,6 +244,6 @@ int sys_auth_passwd(struct ssh *, const char *); + + #if defined(KRB5) && !defined(HEIMDAL) + #include +-krb5_error_code ssh_krb5_cc_gen(krb5_context, krb5_ccache *); ++krb5_error_code ssh_krb5_cc_new_unique(krb5_context, krb5_ccache *, int *); + #endif + #endif +diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c +index 795992d9..0623a107 100644 +--- a/gss-serv-krb5.c ++++ b/gss-serv-krb5.c +@@ -114,7 +114,7 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) + /* This writes out any forwarded credentials from the structure populated + * during userauth. Called after we have setuid to the user */ + +-static void ++static int + ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + { + krb5_ccache ccache; +@@ -121,16 +121,17 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; +- const char *new_ccname, *new_cctype; ++ int len; + const char *errmsg; ++ int set_env = 0; + + if (client->creds == NULL) { + debug("No credentials stored"); +- return; ++ return 0; + } + + if (ssh_gssapi_krb5_init() == 0) +- return; ++ return 0; + + #ifdef HEIMDAL + # ifdef HAVE_KRB5_CC_NEW_UNIQUE +@@ -144,14 +145,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_get_err_text(krb_context, problem)); + # endif + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + #else +- if ((problem = ssh_krb5_cc_gen(krb_context, &ccache))) { ++ if ((problem = ssh_krb5_cc_new_unique(krb_context, &ccache, &set_env)) != 0) { + errmsg = krb5_get_error_message(krb_context, problem); +- logit("ssh_krb5_cc_gen(): %.100s", errmsg); ++ logit("ssh_krb5_cc_new_unique(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + #endif /* #ifdef HEIMDAL */ + +@@ -160,7 +161,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + errmsg = krb5_get_error_message(krb_context, problem); + logit("krb5_parse_name(): %.100s", errmsg); + krb5_free_error_message(krb_context, errmsg); +- return; ++ return 0; + } + + if ((problem = krb5_cc_initialize(krb_context, ccache, princ))) { +@@ -169,7 +170,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + krb5_free_error_message(krb_context, errmsg); + krb5_free_principal(krb_context, princ); + krb5_cc_destroy(krb_context, ccache); +- return; ++ return 0; + } + + krb5_free_principal(krb_context, princ); +@@ -178,37 +179,27 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + client->creds, ccache))) { + logit("gss_krb5_copy_ccache() failed"); + krb5_cc_destroy(krb_context, ccache); +- return; ++ return 0; + } + +- new_cctype = krb5_cc_get_type(krb_context, ccache); +- new_ccname = krb5_cc_get_name(krb_context, ccache); +- +- client->store.envvar = "KRB5CCNAME"; +-#ifdef USE_CCAPI +- xasprintf(&client->store.envval, "API:%s", new_ccname); +-#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'; +- } +-#endif ++ if (set_env) { ++ const char *filename = krb5_cc_get_name(krb_context, ccache); ++ client->store.envvar = "KRB5CCNAME"; ++ len = strlen(filename) + 6; ++ client->store.envval = xmalloc(len); ++ snprintf(client->store.envval, len, "FILE:%s", filename); ++ } + + #ifdef USE_PAM +- if (options.use_pam) ++ if (options.use_pam && set_env) + do_pam_putenv(client->store.envvar, client->store.envval); + #endif + + krb5_cc_close(krb_context, ccache); + + client->store.data = krb_context; + +- return; ++ return set_env; + } + + int +diff --git a/gss-serv.c b/gss-serv.c +index 6cae720e..16e55cbc 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -320,13 +320,15 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) + } + + /* As user */ +-void ++int + ssh_gssapi_storecreds(void) + { + if (gssapi_client.mech && gssapi_client.mech->storecreds) { +- (*gssapi_client.mech->storecreds)(&gssapi_client); ++ return (*gssapi_client.mech->storecreds)(&gssapi_client); + } else + debug("ssh_gssapi_storecreds: Not a GSSAPI mechanism"); ++ ++ return 0; + } + + /* This allows GSSAPI methods to do things to the childs environment based +diff --git a/servconf.c b/servconf.c +index cb578658..a6e01df2 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -122,6 +122,7 @@ initialize_server_options(ServerOptions *options) + options->kerberos_or_local_passwd = -1; + options->kerberos_ticket_cleanup = -1; + options->kerberos_get_afs_token = -1; ++ options->kerberos_unique_ccache = -1; + options->gss_authentication=-1; + options->gss_keyex = -1; + options->gss_cleanup_creds = -1; +@@ -315,6 +316,8 @@ fill_default_server_options(ServerOptions *options) + options->kerberos_ticket_cleanup = 1; + if (options->kerberos_get_afs_token == -1) + options->kerberos_get_afs_token = 0; ++ if (options->kerberos_unique_ccache == -1) ++ options->kerberos_unique_ccache = 0; + if (options->gss_authentication == -1) + options->gss_authentication = 0; + if (options->gss_keyex == -1) +@@ -447,7 +450,8 @@ typedef enum { + sPermitRootLogin, sLogFacility, sLogLevel, + sRhostsRSAAuthentication, sRSAAuthentication, + sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, +- sKerberosGetAFSToken, sChallengeResponseAuthentication, ++ sKerberosGetAFSToken, sKerberosUniqueCCache, ++ sChallengeResponseAuthentication, + sPasswordAuthentication, sKbdInteractiveAuthentication, + sListenAddress, sAddressFamily, + sPrintMotd, sPrintLastLog, sIgnoreRhosts, +@@ -526,11 +530,13 @@ static struct { + #else + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + #endif ++ { "kerberosuniqueccache", sKerberosUniqueCCache, SSHCFG_GLOBAL }, + #else + { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, + { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, ++ { "kerberosuniqueccache", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, + { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, +@@ -1437,6 +1443,10 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->kerberos_get_afs_token; + goto parse_flag; + ++ case sKerberosUniqueCCache: ++ intptr = &options->kerberos_unique_ccache; ++ goto parse_flag; ++ + case sGssAuthentication: + intptr = &options->gss_authentication; + goto parse_flag; +@@ -2507,6 +2517,7 @@ dump_config(ServerOptions *o) + # ifdef USE_AFS + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); + # endif ++ dump_cfg_fmtint(sKerberosUniqueCCache, o->kerberos_unique_ccache); + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); +diff --git a/servconf.h b/servconf.h +index db8362c6..4fa42d64 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -123,6 +123,8 @@ typedef struct { + * file on logout. */ + int kerberos_get_afs_token; /* If true, try to get AFS token if + * authenticated with Kerberos. */ ++ int kerberos_unique_ccache; /* If true, the acquired ticket will ++ * be stored in per-session ccache */ + 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 */ +diff --git a/session.c b/session.c +index 85df6a27..480a5ead 100644 +--- a/session.c ++++ b/session.c +@@ -1033,7 +1033,8 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) + /* Allow any GSSAPI methods that we've used to alter + * the childs environment as they see fit + */ +- ssh_gssapi_do_child(&env, &envsize); ++ if (s->authctxt->krb5_set_env) ++ ssh_gssapi_do_child(&env, &envsize); + #endif + + /* Set basic environment. */ +@@ -1105,7 +1106,7 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) + } + #endif + #ifdef KRB5 +- if (s->authctxt->krb5_ccname) ++ if (s->authctxt->krb5_ccname && s->authctxt->krb5_set_env) + child_set_env(&env, &envsize, "KRB5CCNAME", + s->authctxt->krb5_ccname); + #endif +diff --git a/ssh-gss.h b/ssh-gss.h +index 6593e422..245178af 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -62,7 +62,6 @@ + #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" + + typedef struct { +- char *filename; + char *envvar; + char *envval; + struct passwd *owner; +@@ -83,7 +82,7 @@ typedef struct ssh_gssapi_mech_struct { + int (*dochild) (ssh_gssapi_client *); + int (*userok) (ssh_gssapi_client *, char *); + int (*localname) (ssh_gssapi_client *, char **); +- void (*storecreds) (ssh_gssapi_client *); ++ int (*storecreds) (ssh_gssapi_client *); + int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); + } ssh_gssapi_mech; + +@@ -127,7 +126,7 @@ int ssh_gssapi_userok(char *name); + 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); ++int ssh_gssapi_storecreds(void); + const char *ssh_gssapi_displayname(void); + + char *ssh_gssapi_server_mechanisms(void); +diff --git a/sshd.c b/sshd.c +index edbe815c..89514e8a 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -2162,7 +2162,7 @@ main(int ac, char **av) + #ifdef GSSAPI + if (options.gss_authentication) { + temporarily_use_uid(authctxt->pw); +- ssh_gssapi_storecreds(); ++ authctxt->krb5_set_env = ssh_gssapi_storecreds(); + restore_uid(); + } + #endif +diff --git a/sshd_config.5 b/sshd_config.5 +index c0683d4a..2349f477 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -860,6 +860,14 @@ Specifies whether to automatically destroy the user's ticket cache + file on logout. + The default is + .Cm yes . ++.It Cm KerberosUniqueCCache ++Specifies whether to store the acquired tickets in the per-session credential ++cache under /tmp/ or whether to use per-user credential cache as configured in ++.Pa /etc/krb5.conf . ++The default value ++.Cm no ++can lead to overwriting previous tickets by subseqent connections to the same ++user account. + .It Cm KexAlgorithms + Specifies the available KEX (Key Exchange) algorithms. + Multiple algorithms must be comma-separated. diff --git a/SOURCES/openssh-7.7p1-redhat.patch b/SOURCES/openssh-7.7p1-redhat.patch new file mode 100644 index 0000000..933796a --- /dev/null +++ b/SOURCES/openssh-7.7p1-redhat.patch @@ -0,0 +1,164 @@ +diff -up openssh-7.7p1/ssh_config.redhat openssh-7.7p1/ssh_config +--- openssh-7.7p1/ssh_config.redhat 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/ssh_config 2018-07-03 10:44:06.522245125 +0200 +@@ -44,3 +44,7 @@ + # VisualHostKey no + # ProxyCommand ssh -q -W %h:%p gateway.example.com + # RekeyLimit 1G 1h ++# ++# To modify the system-wide ssh configuration, create a *.conf file under ++# /etc/ssh/ssh_config.d/ which will be automatically included below ++Include /etc/ssh/ssh_config.d/*.conf +diff -up openssh-7.7p1/ssh_config_redhat.redhat openssh-7.7p1/ssh_config_redhat +--- openssh-7.7p1/ssh_config_redhat.redhat 2018-07-03 10:44:06.522245125 +0200 ++++ openssh-7.7p1/ssh_config_redhat 2018-07-03 10:44:06.522245125 +0200 +@@ -0,0 +1,20 @@ ++# Follow system-wide Crypto Policy, if defined: ++Include /etc/crypto-policies/back-ends/openssh.config ++ ++# Uncomment this if you want to use .local domain ++# Host *.local ++# CheckHostIP no ++ ++Host * ++ GSSAPIAuthentication yes ++ ++# If this option is set to yes then remote X11 clients will have full access ++# to the original X11 display. As virtually no X11 client supports the untrusted ++# mode correctly we set this to yes. ++ ForwardX11Trusted yes ++ ++# Send locale-related environment variables ++ 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 LANGUAGE ++ SendEnv XMODIFIERS +diff -up openssh-7.7p1/sshd_config.0.redhat openssh-7.7p1/sshd_config.0 +--- openssh-7.7p1/sshd_config.0.redhat 2018-04-02 07:39:27.000000000 +0200 ++++ openssh-7.7p1/sshd_config.0 2018-07-03 10:44:06.523245133 +0200 +@@ -872,9 +872,9 @@ DESCRIPTION + + SyslogFacility + Gives the facility code that is used when logging messages from +- sshd(8). The possible values are: DAEMON, USER, AUTH, LOCAL0, +- LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The +- default is AUTH. ++ sshd(8). The possible values are: DAEMON, USER, AUTH, AUTHPRIV, ++ LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. ++ The default is AUTH. + + TCPKeepAlive + Specifies whether the system should send TCP keepalive messages +diff -up openssh-7.7p1/sshd_config.5.redhat openssh-7.7p1/sshd_config.5 +--- openssh-7.7p1/sshd_config.5.redhat 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/sshd_config.5 2018-07-03 10:44:06.523245133 +0200 +@@ -1461,7 +1461,7 @@ By default no subsystems are defined. + .It Cm SyslogFacility + Gives the facility code that is used when logging messages from + .Xr sshd 8 . +-The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, ++The possible values are: DAEMON, USER, AUTH, AUTHPRIV, LOCAL0, LOCAL1, LOCAL2, + LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. + The default is AUTH. + .It Cm TCPKeepAlive +diff -up openssh-7.7p1/sshd_config.redhat openssh-7.7p1/sshd_config +--- openssh-7.7p1/sshd_config.redhat 2018-04-02 07:38:28.000000000 +0200 ++++ openssh-7.7p1/sshd_config 2018-07-03 10:45:16.950782466 +0200 +@@ -10,20 +10,34 @@ + # possible, but leave them commented. Uncommented options override the + # default value. + ++# If you want to change the port on a SELinux system, you have to tell ++# SELinux about this change. ++# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER ++# + #Port 22 + #AddressFamily any + #ListenAddress 0.0.0.0 + #ListenAddress :: + +-#HostKey /etc/ssh/ssh_host_rsa_key +-#HostKey /etc/ssh/ssh_host_ecdsa_key +-#HostKey /etc/ssh/ssh_host_ed25519_key ++HostKey /etc/ssh/ssh_host_rsa_key ++HostKey /etc/ssh/ssh_host_ecdsa_key ++HostKey /etc/ssh/ssh_host_ed25519_key + + # Ciphers and keying + #RekeyLimit default none + ++# System-wide Crypto policy: ++# This system is following system-wide crypto policy. The changes to ++# Ciphers, MACs, KexAlgoritms and GSSAPIKexAlgorithsm will not have any ++# effect here. They will be overridden by command-line options passed on ++# the server start up. ++# To opt out, uncomment a line with redefinition of CRYPTO_POLICY= ++# variable in /etc/sysconfig/sshd to overwrite the policy. ++# For more information, see manual page for update-crypto-policies(8). ++ + # Logging + #SyslogFacility AUTH ++SyslogFacility AUTHPRIV + #LogLevel INFO + + # Authentication: +@@ -56,9 +70,11 @@ AuthorizedKeysFile .ssh/authorized_keys + # To disable tunneled clear text passwords, change to no here! + #PasswordAuthentication yes + #PermitEmptyPasswords no ++PasswordAuthentication yes + + # Change to no to disable s/key passwords + #ChallengeResponseAuthentication yes ++ChallengeResponseAuthentication no + + # Kerberos options + #KerberosAuthentication no +@@ -67,8 +83,8 @@ AuthorizedKeysFile .ssh/authorized_keys + #KerberosGetAFSToken no + + # GSSAPI options +-#GSSAPIAuthentication no +-#GSSAPICleanupCredentials yes ++GSSAPIAuthentication yes ++GSSAPICleanupCredentials no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +@@ -79,16 +95,20 @@ AuthorizedKeysFile .ssh/authorized_keys + # 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 no ++X11Forwarding yes + #X11DisplayOffset 10 + #X11UseLocalhost yes + #PermitTTY yes +-#PrintMotd yes ++ ++# It is recommended to use pam_motd in /etc/pam.d/sshd instead of PrintMotd, ++# as it is more configurable and versatile than the built-in version. ++PrintMotd no ++ + #PrintLastLog yes + #TCPKeepAlive yes + #PermitUserEnvironment no +@@ -106,6 +126,12 @@ AuthorizedKeysFile .ssh/authorized_keys + # no default banner path + #Banner none + ++# Accept locale-related environment variables ++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 LANGUAGE ++AcceptEnv XMODIFIERS ++ + # override default of no subsystems + Subsystem sftp /usr/libexec/sftp-server + diff --git a/SOURCES/openssh-7.8p1-UsePAM-warning.patch b/SOURCES/openssh-7.8p1-UsePAM-warning.patch new file mode 100644 index 0000000..da39361 --- /dev/null +++ b/SOURCES/openssh-7.8p1-UsePAM-warning.patch @@ -0,0 +1,26 @@ +diff --git a/sshd.c b/sshd.c +--- a/sshd.c ++++ b/sshd.c +@@ -1701,6 +1701,10 @@ main(int ac, char **av) + parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, + cfg, NULL); + ++ /* 'UsePAM no' is not supported in Fedora */ ++ if (! options.use_pam) ++ logit("WARNING: 'UsePAM no' is not supported in Fedora and may cause several problems."); ++ + seed_rng(); + + /* Fill in default values for those options not explicitly set. */ +diff --git a/sshd_config b/sshd_config +--- a/sshd_config ++++ b/sshd_config +@@ -101,6 +101,8 @@ GSSAPICleanupCredentials no + # 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'. ++# WARNING: 'UsePAM no' is not supported in Fedora and may cause several ++# problems. + UsePAM yes + + #AllowAgentForwarding yes diff --git a/SOURCES/openssh-7.8p1-gsskex.patch b/SOURCES/openssh-7.8p1-gsskex.patch new file mode 100644 index 0000000..f806fd5 --- /dev/null +++ b/SOURCES/openssh-7.8p1-gsskex.patch @@ -0,0 +1,2907 @@ +diff -up openssh/auth2.c.gsskex openssh/auth2.c +--- openssh/auth2.c.gsskex 2018-08-22 11:47:33.260216045 +0200 ++++ openssh/auth2.c 2018-08-22 11:47:33.307216424 +0200 +@@ -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, +diff -up openssh/auth2-gss.c.gsskex openssh/auth2-gss.c +--- openssh/auth2-gss.c.gsskex 2018-08-22 11:47:33.260216045 +0200 ++++ openssh/auth2-gss.c 2018-08-22 13:00:48.722680124 +0200 +@@ -31,6 +31,7 @@ + #include + + #include ++#include + + #include "xmalloc.h" + #include "sshkey.h" +@@ -54,6 +55,44 @@ static int input_gssapi_mic(int type, u_ + static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); + 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 +296,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) +@@ -313,7 +350,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"); + +@@ -335,6 +373,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, +diff -up openssh/auth.c.gsskex openssh/auth.c +--- openssh/auth.c.gsskex 2018-08-22 11:47:33.274216158 +0200 ++++ openssh/auth.c 2018-08-22 11:47:33.308216432 +0200 +@@ -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; +diff -up openssh/clientloop.c.gsskex openssh/clientloop.c +--- openssh/clientloop.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/clientloop.c 2018-08-22 11:47:33.309216441 +0200 +@@ -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(NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(readset); + +diff -up openssh/configure.ac.gsskex openssh/configure.ac +--- openssh/configure.ac.gsskex 2018-08-22 11:47:33.296216335 +0200 ++++ openssh/configure.ac 2018-08-22 11:47:33.309216441 +0200 +@@ -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]) +diff -up openssh/gss-genr.c.gsskex openssh/gss-genr.c +--- openssh/gss-genr.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/gss-genr.c 2018-08-22 13:18:47.444383602 +0200 +@@ -35,18 +35,177 @@ + #include + #include + #include ++#include + + #include "xmalloc.h" + #include "ssherr.h" + #include "sshbuf.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "sshkey.h" ++#include "kex.h" + + #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) { ++ 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); ++ ++ if (oidpos != 0) ++ if ((r = sshbuf_put_u8(buf, ',')) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0 || ++ (r = sshbuf_put_u8(buf, ',')) != 0 || ++ (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0 || ++ (r = sshbuf_put_u8(buf, ',')) != 0 || ++ (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 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++; ++ } ++ } ++ 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 +373,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 +403,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 +446,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,11 +475,16 @@ 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 && +@@ -287,6 +494,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx + 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, + NULL); +@@ -296,10 +507,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 */ +diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c +--- openssh/gss-serv.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/gss-serv.c 2018-08-22 11:47:33.310216448 +0200 +@@ -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_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, 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; ++ } ++ ++ 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_buffer_desc ename; ++ 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,89 @@ 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.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 */ +diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c +--- openssh/gss-serv-krb5.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/gss-serv-krb5.c 2018-08-22 11:47:33.311216457 +0200 +@@ -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,23 @@ 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); ++#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'; ++ } ++#endif + + #ifdef USE_PAM + if (options.use_pam) +@@ -193,9 +205,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 +282,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 */ +diff -up openssh/kex.c.gsskex openssh/kex.c +--- openssh/kex.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/kex.c 2018-08-22 11:47:33.311216457 +0200 +@@ -54,6 +54,10 @@ + #include "sshbuf.h" + #include "digest.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 *); +@@ -103,6 +107,11 @@ static const struct kexalg kexalgs[] = { + { 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}, + }; + +@@ -136,6 +145,12 @@ kex_alg_by_name(const char *name) + for (k = 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; + } +diff -up openssh/kexgssc.c.gsskex openssh/kexgssc.c +--- openssh/kexgssc.c.gsskex 2018-08-22 11:47:33.311216457 +0200 ++++ openssh/kexgssc.c 2018-08-22 11:47:33.311216457 +0200 +@@ -0,0 +1,338 @@ ++/* ++ * 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" ++ ++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; ++ char *lang; ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = 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); ++ 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); ++ ++ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); ++ BN_clear_free(shared_secret); ++ return kex_send_newkeys(ssh); ++} ++ ++#endif /* GSSAPI */ +diff -up openssh/kexgsss.c.gsskex openssh/kexgsss.c +--- openssh/kexgsss.c.gsskex 2018-08-22 11:47:33.311216457 +0200 ++++ openssh/kexgsss.c 2018-08-22 11:47:33.311216457 +0200 +@@ -0,0 +1,297 @@ ++/* ++ * 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 "ssh-gss.h" ++#include "digest.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(DH_GRP_MIN, cmin); ++ max = MIN(DH_GRP_MAX, cmax); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < 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); ++ ++ 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 */ +diff -up openssh/kex.h.gsskex openssh/kex.h +--- openssh/kex.h.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/kex.h 2018-08-22 11:47:33.311216457 +0200 +@@ -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, +diff -up openssh/Makefile.in.gsskex openssh/Makefile.in +--- openssh/Makefile.in.gsskex 2018-08-22 11:47:33.312216465 +0200 ++++ openssh/Makefile.in 2018-08-22 13:19:54.955928277 +0200 +@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + readpass.o ttymodes.o xmalloc.o addrmatch.o \ + atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ ++ kexgssc.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ + ssh-pkcs11.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ +@@ -121,7 +122,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw + auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ + auth2-none.o auth2-passwd.o auth2-pubkey.o \ + monitor.o monitor_wrap.o auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ +diff -up openssh/monitor.c.gsskex openssh/monitor.c +--- openssh/monitor.c.gsskex 2018-08-22 11:47:33.263216069 +0200 ++++ openssh/monitor.c 2018-08-22 13:22:19.589095240 +0200 +@@ -146,6 +146,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 +@@ -219,11 +221,18 @@ 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} + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#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 + #ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + #endif +@@ -293,6 +302,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) { +@@ -405,6 +418,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); +@@ -1695,6 +1712,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; +@@ -1785,7 +1809,7 @@ mm_answer_gss_setup_ctx(int sock, struct + u_char *p; + int r; + +- if (!options.gss_authentication) ++ if (!options.gss_authentication && !options.gss_keyex) + fatal("%s: GSSAPI authentication not enabled", __func__); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) +@@ -1818,7 +1842,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) +@@ -1839,6 +1863,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); + } +@@ -1850,7 +1875,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 || +@@ -1880,10 +1905,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) +@@ -1900,5 +1926,74 @@ mm_answer_gss_userok(int sock, struct ss + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++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 */ + +diff -up openssh/monitor.h.gsskex openssh/monitor.h +--- openssh/monitor.h.gsskex 2018-08-22 11:47:33.263216069 +0200 ++++ openssh/monitor.h 2018-08-22 11:47:33.313216473 +0200 +@@ -58,6 +58,8 @@ enum monitor_reqtype { + #ifdef WITH_SELINUX + MONITOR_REQ_AUTHROLE = 80, + #endif ++ MONITOR_REQ_GSSSIGN = 82, MONITOR_ANS_GSSSIGN = 83, ++ MONITOR_REQ_GSSUPCREDS = 84, MONITOR_ANS_GSSUPCREDS = 85, + + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, +diff -up openssh/monitor_wrap.c.gsskex openssh/monitor_wrap.c +--- openssh/monitor_wrap.c.gsskex 2018-08-22 11:47:33.313216473 +0200 ++++ openssh/monitor_wrap.c 2018-08-22 13:27:38.665669643 +0200 +@@ -1004,7 +1004,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; +@@ -1023,4 +1023,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 */ +diff -up openssh/monitor_wrap.h.gsskex openssh/monitor_wrap.h +--- openssh/monitor_wrap.h.gsskex 2018-08-22 11:47:33.263216069 +0200 ++++ openssh/monitor_wrap.h 2018-08-22 11:47:33.313216473 +0200 +@@ -63,8 +63,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 +diff -up openssh/readconf.c.gsskex openssh/readconf.c +--- openssh/readconf.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/readconf.c 2018-08-22 13:28:17.487982869 +0200 +@@ -161,6 +161,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, +@@ -201,10 +203,19 @@ 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 }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + #ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, +@@ -973,10 +984,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; +@@ -1817,7 +1848,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; +@@ -1962,8 +1998,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) +@@ -2603,7 +2645,12 @@ dump_client_config(Options *o, const cha + dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); + #ifdef GSSAPI + dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); ++ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns); ++ dump_cfg_string(oGssClientIdentity, o->gss_client_identity); ++ dump_cfg_string(oGssServerIdentity, o->gss_client_identity); ++ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); + #endif /* GSSAPI */ + dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); + dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); +diff -up openssh-7.9p1/readconf.h.gsskex openssh-7.9p1/readconf.h +--- openssh-7.9p1/readconf.h.gsskex 2018-10-17 02:01:20.000000000 +0200 ++++ openssh-7.9p1/readconf.h 2018-11-14 09:14:06.277379857 +0100 +@@ -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. */ +diff -up openssh/regress/cert-hostkey.sh.gsskex openssh/regress/cert-hostkey.sh +--- openssh/regress/cert-hostkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/regress/cert-hostkey.sh 2018-08-22 11:47:33.314216481 +0200 +@@ -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" +diff -up openssh/regress/cert-userkey.sh.gsskex openssh/regress/cert-userkey.sh +--- openssh/regress/cert-userkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/regress/cert-userkey.sh 2018-08-22 11:47:33.314216481 +0200 +@@ -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 +diff -up openssh/regress/kextype.sh.gsskex openssh/regress/kextype.sh +--- openssh/regress/kextype.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/regress/kextype.sh 2018-08-22 11:47:33.315216489 +0200 +@@ -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 +diff -up openssh/regress/rekey.sh.gsskex openssh/regress/rekey.sh +--- openssh/regress/rekey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/regress/rekey.sh 2018-08-22 11:47:33.315216489 +0200 +@@ -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 +diff -up openssh/servconf.c.gsskex openssh/servconf.c +--- openssh/servconf.c.gsskex 2018-08-22 11:47:33.296216335 +0200 ++++ openssh/servconf.c 2018-08-22 13:28:41.905179879 +0200 +@@ -124,8 +124,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; +@@ -334,10 +336,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) +@@ -484,7 +490,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, +- sAcceptEnv, sSetEnv, sPermitTunnel, ++ sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, +@@ -559,11 +565,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 }, +@@ -1463,6 +1475,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; +@@ -1471,6 +1487,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; +@@ -2560,6 +2580,9 @@ dump_config(ServerOptions *o) + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); ++ 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, +diff -up openssh/servconf.h.gsskex openssh/servconf.h +--- openssh/servconf.h.gsskex 2018-08-22 11:47:33.296216335 +0200 ++++ openssh/servconf.h 2018-08-22 11:47:33.316216497 +0200 +@@ -124,8 +124,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 */ +diff -up openssh/ssh_config.5.gsskex openssh/ssh_config.5 +--- openssh/ssh_config.5.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/ssh_config.5 2018-08-22 11:47:33.316216497 +0200 +@@ -718,10 +718,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 +diff -up openssh/ssh_config.gsskex openssh/ssh_config +--- openssh/ssh_config.gsskex 2018-08-22 11:47:33.289216279 +0200 ++++ openssh/ssh_config 2018-08-22 11:47:33.316216497 +0200 +@@ -24,6 +24,8 @@ + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any +diff -up openssh/sshconnect2.c.gsskex openssh/sshconnect2.c +--- openssh/sshconnect2.c.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/sshconnect2.c 2018-08-22 13:33:01.674275795 +0200 +@@ -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,36 @@ 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 ++ 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_server_identity) ++ gss_host = options.gss_server_identity; ++ else 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); ++ 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 +339,16 @@ 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); ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits(options.rekey_limit, + options.rekey_interval); +@@ -214,16 +369,46 @@ ssh_kex2(char *host, struct sockaddr *ho + kex->kex[KEX_ECDH_SHA2] = kexecdh_client; + # endif + #endif ++#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->kex[KEX_C25519_SHA256] = kexc25519_client; + 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 */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(options.kex_algorithms); ++#ifdef GSSAPI ++ /* repair myproposal after it was crumpled by the */ ++ /* ext-info removal above */ ++ if (gss) { ++ orig = myproposal[PROPOSAL_KEX_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], ++ "%s,%s", gss, orig); ++ free(gss); ++ } ++#endif + if ((r = kex_prop2buf(kex->my, myproposal)) != 0) + fatal("kex_prop2buf: %s", ssh_err(r)); + +@@ -314,6 +499,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 +505,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 +837,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 +1098,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 +diff -up openssh/sshd.c.gsskex openssh/sshd.c +--- openssh/sshd.c.gsskex 2018-08-22 11:47:33.299216360 +0200 ++++ openssh/sshd.c 2018-08-22 13:34:28.455975954 +0200 +@@ -537,8 +537,7 @@ privsep_preauth_child(void) + + #ifdef GSSAPI + /* Cache supported mechanism OIDs for later use */ +- if (options.gss_authentication) +- ssh_gssapi_prepare_supported_oids(); ++ ssh_gssapi_prepare_supported_oids(); + #endif + + reseed_prngs(); +@@ -887,8 +887,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); + } + +@@ -1841,7 +1842,8 @@ main(int ac, char **av) + free(fp); + } + accumulate_host_timing_secret(cfg, NULL); +- if (!sensitive_data.have_ssh2_key) { ++ /* The GSSAPI key exchange can run without a host key */ ++ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } +@@ -2321,6 +2323,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)); +@@ -2338,6 +2382,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; +diff -up openssh/sshd_config.5.gsskex openssh/sshd_config.5 +--- openssh/sshd_config.5.gsskex 2018-08-22 11:47:33.297216344 +0200 ++++ openssh/sshd_config.5 2018-08-22 13:35:05.531275099 +0200 +@@ -642,6 +642,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. +@@ -656,6 +661,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. +diff -up openssh/sshd_config.gsskex openssh/sshd_config +--- openssh/sshd_config.gsskex 2018-08-22 11:47:33.299216360 +0200 ++++ openssh/sshd_config 2018-08-22 11:47:33.318216513 +0200 +@@ -85,6 +85,8 @@ ChallengeResponseAuthentication no + # GSSAPI options + GSSAPIAuthentication yes + GSSAPICleanupCredentials no ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +diff -up openssh/ssh-gss.h.gsskex openssh/ssh-gss.h +--- openssh/ssh-gss.h.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/ssh-gss.h 2018-08-22 13:36:44.773075793 +0200 +@@ -1,6 +1,6 @@ + /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * 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 +@@ -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,33 @@ 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 *); ++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 */ +diff -up openssh/sshkey.c.gsskex openssh/sshkey.c +--- openssh/sshkey.c.gsskex 2018-08-22 11:47:33.319216521 +0200 ++++ openssh/sshkey.c 2018-08-22 13:37:18.979351804 +0200 +@@ -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 } + }; + +diff -up openssh/sshkey.h.gsskex openssh/sshkey.h +--- openssh/sshkey.h.gsskex 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/sshkey.h 2018-08-22 11:47:33.320216529 +0200 +@@ -63,6 +63,7 @@ enum sshkey_types { + KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/SOURCES/openssh-7.8p1-ip-port-config-parser.patch b/SOURCES/openssh-7.8p1-ip-port-config-parser.patch new file mode 100644 index 0000000..6ca0cf5 --- /dev/null +++ b/SOURCES/openssh-7.8p1-ip-port-config-parser.patch @@ -0,0 +1,72 @@ +diff -up openssh/misc.c.config openssh/misc.c +--- openssh/misc.c.config 2018-08-22 13:58:54.922807799 +0200 ++++ openssh/misc.c 2018-08-22 13:58:55.000808428 +0200 +@@ -485,7 +485,7 @@ put_host_port(const char *host, u_short + * The delimiter char, if present, is stored in delim. + * If this is the last field, *cp is set to NULL. + */ +-static char * ++char * + hpdelim2(char **cp, char *delim) + { + char *s, *old; +diff -up openssh/misc.h.config openssh/misc.h +--- openssh/misc.h.config 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/misc.h 2018-08-22 13:58:55.001808436 +0200 +@@ -54,6 +54,7 @@ int set_rdomain(int, const char *); + int a2port(const char *); + int a2tun(const char *, int *); + char *put_host_port(const char *, u_short); ++char *hpdelim2(char **, char *); + char *hpdelim(char **); + char *cleanhostname(char *); + char *colon(char *); +diff -up openssh/servconf.c.config openssh/servconf.c +--- openssh/servconf.c.config 2018-08-22 13:58:54.989808340 +0200 ++++ openssh/servconf.c 2018-08-22 14:18:49.235443937 +0200 +@@ -886,7 +886,7 @@ process_permitopen_list(struct ssh *ssh, + { + u_int i; + int port; +- char *host, *arg, *oarg; ++ char *host, *arg, *oarg, ch; + int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; + const char *what = lookup_opcode_name(opcode); + +@@ -904,8 +904,8 @@ process_permitopen_list(struct ssh *ssh, + /* Otherwise treat it as a list of permitted host:port */ + for (i = 0; i < num_opens; i++) { + oarg = arg = xstrdup(opens[i]); +- host = hpdelim(&arg); +- if (host == NULL) ++ host = hpdelim2(&arg, &ch); ++ if (host == NULL || ch == '/') + fatal("%s: missing host in %s", __func__, what); + host = cleanhostname(host); + if (arg == NULL || ((port = permitopen_port(arg)) < 0)) +@@ -1323,8 +1323,10 @@ process_server_config_line(ServerOptions + port = 0; + p = arg; + } else { +- p = hpdelim(&arg); +- if (p == NULL) ++ char ch; ++ arg2 = NULL; ++ p = hpdelim2(&arg, &ch); ++ if (p == NULL || ch == '/') + fatal("%s line %d: bad address:port usage", + filename, linenum); + p = cleanhostname(p); +@@ -1965,9 +1967,10 @@ process_server_config_line(ServerOptions + */ + xasprintf(&arg2, "*:%s", arg); + } else { ++ char ch; + arg2 = xstrdup(arg); +- p = hpdelim(&arg); +- if (p == NULL) { ++ p = hpdelim2(&arg, &ch); ++ if (p == NULL || ch == '/') { + fatal("%s line %d: missing host in %s", + filename, linenum, + lookup_opcode_name(opcode)); diff --git a/SOURCES/openssh-7.8p1-role-mls.patch b/SOURCES/openssh-7.8p1-role-mls.patch new file mode 100644 index 0000000..4c58d71 --- /dev/null +++ b/SOURCES/openssh-7.8p1-role-mls.patch @@ -0,0 +1,885 @@ +diff -up openssh/auth2.c.role-mls openssh/auth2.c +--- openssh/auth2.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth2.c 2018-08-22 11:14:56.815430916 +0200 +@@ -256,6 +256,9 @@ input_userauth_request(int type, u_int32 + Authctxt *authctxt = ssh->authctxt; + Authmethod *m = NULL; + char *user, *service, *method, *style = NULL; ++#ifdef WITH_SELINUX ++ char *role = NULL; ++#endif + int authenticated = 0; + double tstart = monotime_double(); + +@@ -268,6 +271,11 @@ input_userauth_request(int type, u_int32 + debug("userauth-request for user %s service %s method %s", user, service, method); + debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); + ++#ifdef WITH_SELINUX ++ if ((role = strchr(user, '/')) != NULL) ++ *role++ = 0; ++#endif ++ + if ((style = strchr(user, ':')) != NULL) + *style++ = 0; + +@@ -296,8 +304,15 @@ input_userauth_request(int type, u_int32 + use_privsep ? " [net]" : ""); + authctxt->service = xstrdup(service); + authctxt->style = style ? xstrdup(style) : NULL; +- if (use_privsep) ++#ifdef WITH_SELINUX ++ authctxt->role = role ? xstrdup(role) : NULL; ++#endif ++ if (use_privsep) { + mm_inform_authserv(service, style); ++#ifdef WITH_SELINUX ++ mm_inform_authrole(role); ++#endif ++ } + userauth_banner(); + if (auth2_setup_methods_lists(authctxt) != 0) + packet_disconnect("no authentication methods enabled"); +diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c +--- openssh/auth2-gss.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth2-gss.c 2018-08-22 11:15:42.459799171 +0200 +@@ -281,6 +281,7 @@ input_gssapi_mic(int type, u_int32_t ple + Authctxt *authctxt = ssh->authctxt; + Gssctxt *gssctxt; + int r, authenticated = 0; ++ char *micuser; + struct sshbuf *b; + gss_buffer_desc mic, gssbuf; + const char *displayname; +@@ -298,7 +299,13 @@ input_gssapi_mic(int type, u_int32_t ple + fatal("%s: sshbuf_new failed", __func__); + mic.value = p; + mic.length = len; +- ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, ++#ifdef WITH_SELINUX ++ if (authctxt->role && (strlen(authctxt->role) > 0)) ++ xasprintf(&micuser, "%s/%s", authctxt->user, authctxt->role); ++ else ++#endif ++ micuser = authctxt->user; ++ ssh_gssapi_buildmic(b, micuser, authctxt->service, + "gssapi-with-mic"); + + if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) +@@ -311,6 +318,8 @@ input_gssapi_mic(int type, u_int32_t ple + logit("GSSAPI MIC check failed"); + + sshbuf_free(b); ++ if (micuser != authctxt->user) ++ free(micuser); + free(mic.value); + + if ((!use_privsep || mm_is_monitor()) && +diff -up openssh/auth2-hostbased.c.role-mls openssh/auth2-hostbased.c +--- openssh/auth2-hostbased.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth2-hostbased.c 2018-08-22 11:14:56.816430924 +0200 +@@ -123,7 +123,16 @@ userauth_hostbased(struct ssh *ssh) + /* reconstruct packet */ + if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 || + (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || ++#ifdef WITH_SELINUX ++ (authctxt->role ++ ? ( (r = sshbuf_put_u32(b, strlen(authctxt->user)+strlen(authctxt->role)+1)) != 0 || ++ (r = sshbuf_put(b, authctxt->user, strlen(authctxt->user))) != 0 || ++ (r = sshbuf_put_u8(b, '/') != 0) || ++ (r = sshbuf_put(b, authctxt->role, strlen(authctxt->role))) != 0) ++ : (r = sshbuf_put_cstring(b, authctxt->user)) != 0) || ++#else + (r = sshbuf_put_cstring(b, authctxt->user)) != 0 || ++#endif + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || + (r = sshbuf_put_cstring(b, "hostbased")) != 0 || + (r = sshbuf_put_string(b, pkalg, alen)) != 0 || +diff -up openssh/auth2-pubkey.c.role-mls openssh/auth2-pubkey.c +--- openssh/auth2-pubkey.c.role-mls 2018-08-22 11:14:56.816430924 +0200 ++++ openssh/auth2-pubkey.c 2018-08-22 11:17:07.331483958 +0200 +@@ -169,9 +169,16 @@ userauth_pubkey(struct ssh *ssh) + goto done; + } + /* reconstruct packet */ +- xasprintf(&userstyle, "%s%s%s", authctxt->user, ++ xasprintf(&userstyle, "%s%s%s%s%s", authctxt->user, + authctxt->style ? ":" : "", +- authctxt->style ? authctxt->style : ""); ++ authctxt->style ? authctxt->style : "", ++#ifdef WITH_SELINUX ++ authctxt->role ? "/" : "", ++ authctxt->role ? authctxt->role : "" ++#else ++ "", "" ++#endif ++ ); + if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || + (r = sshbuf_put_cstring(b, userstyle)) != 0 || + (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || +diff -up openssh/auth.h.role-mls openssh/auth.h +--- openssh/auth.h.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth.h 2018-08-22 11:14:56.816430924 +0200 +@@ -65,6 +65,9 @@ struct Authctxt { + char *service; + struct passwd *pw; /* set if 'valid' */ + char *style; ++#ifdef WITH_SELINUX ++ char *role; ++#endif + + /* Method lists for multiple authentication */ + char **auth_methods; /* modified from server config */ +diff -up openssh/auth-pam.c.role-mls openssh/auth-pam.c +--- openssh/auth-pam.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth-pam.c 2018-08-22 11:14:56.816430924 +0200 +@@ -1172,7 +1172,7 @@ is_pam_session_open(void) + * during the ssh authentication process. + */ + int +-do_pam_putenv(char *name, char *value) ++do_pam_putenv(char *name, const char *value) + { + int ret = 1; + #ifdef HAVE_PAM_PUTENV +diff -up openssh/auth-pam.h.role-mls openssh/auth-pam.h +--- openssh/auth-pam.h.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/auth-pam.h 2018-08-22 11:14:56.817430932 +0200 +@@ -33,7 +33,7 @@ u_int do_pam_account(void); + void do_pam_session(struct ssh *); + void do_pam_setcred(int ); + void do_pam_chauthtok(void); +-int do_pam_putenv(char *, char *); ++int do_pam_putenv(char *, const char *); + char ** fetch_pam_environment(void); + char ** fetch_pam_child_environment(void); + void free_pam_environment(char **); +diff -up openssh/configure.ac.role-mls openssh/configure.ac +--- openssh/configure.ac.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/configure.ac 2018-08-22 11:14:56.820430957 +0200 +@@ -4241,10 +4241,7 @@ AC_ARG_WITH([selinux], + LIBS="$LIBS -lselinux" + ], + AC_MSG_ERROR([SELinux support requires libselinux library])) +- SSHLIBS="$SSHLIBS $LIBSELINUX" +- SSHDLIBS="$SSHDLIBS $LIBSELINUX" + AC_CHECK_FUNCS([getseuserbyname get_default_context_with_level]) +- LIBS="$save_LIBS" + fi ] + ) + AC_SUBST([SSHLIBS]) +diff -up openssh/misc.c.role-mls openssh/misc.c +--- openssh/misc.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/misc.c 2018-08-22 11:14:56.817430932 +0200 +@@ -542,6 +542,7 @@ char * + colon(char *cp) + { + int flag = 0; ++ int start = 1; + + if (*cp == ':') /* Leading colon is part of file name. */ + return NULL; +@@ -557,6 +558,13 @@ colon(char *cp) + return (cp); + if (*cp == '/') + return NULL; ++ if (start) { ++ /* Slash on beginning or after dots only denotes file name. */ ++ if (*cp == '/') ++ return (0); ++ if (*cp != '.') ++ start = 0; ++ } + } + return NULL; + } +diff -up openssh/monitor.c.role-mls openssh/monitor.c +--- openssh/monitor.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/monitor.c 2018-08-22 11:19:56.006844867 +0200 +@@ -115,6 +115,9 @@ int mm_answer_sign(int, struct sshbuf *) + int mm_answer_pwnamallow(int, struct sshbuf *); + int mm_answer_auth2_read_banner(int, struct sshbuf *); + int mm_answer_authserv(int, struct sshbuf *); ++#ifdef WITH_SELINUX ++int mm_answer_authrole(int, struct sshbuf *); ++#endif + int mm_answer_authpassword(int, struct sshbuf *); + int mm_answer_bsdauthquery(int, struct sshbuf *); + int mm_answer_bsdauthrespond(int, struct sshbuf *); +@@ -189,6 +192,9 @@ struct mon_table mon_dispatch_proto20[] + {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, + {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, + {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, ++#ifdef WITH_SELINUX ++ {MONITOR_REQ_AUTHROLE, MON_ONCE, mm_answer_authrole}, ++#endif + {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, + {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, + #ifdef USE_PAM +@@ -796,6 +802,9 @@ mm_answer_pwnamallow(int sock, struct ss + + /* Allow service/style information on the auth context */ + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); ++#ifdef WITH_SELINUX ++ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHROLE, 1); ++#endif + monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); + + #ifdef USE_PAM +@@ -842,6 +851,26 @@ mm_answer_authserv(int sock, struct sshb + return (0); + } + ++#ifdef WITH_SELINUX ++int ++mm_answer_authrole(int sock, struct sshbuf *m) ++{ ++ int r; ++ monitor_permit_authentications(1); ++ ++ if ((r = sshbuf_get_cstring(m, &authctxt->role, NULL)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ debug3("%s: role=%s", __func__, authctxt->role); ++ ++ if (strlen(authctxt->role) == 0) { ++ free(authctxt->role); ++ authctxt->role = NULL; ++ } ++ ++ return (0); ++} ++#endif ++ + int + mm_answer_authpassword(int sock, struct sshbuf *m) + { +@@ -1218,7 +1247,7 @@ monitor_valid_userblob(u_char *data, u_i + { + struct sshbuf *b; + const u_char *p; +- char *userstyle, *cp; ++ char *userstyle, *s, *cp; + size_t len; + u_char type; + int r, fail = 0; +@@ -1251,6 +1280,8 @@ monitor_valid_userblob(u_char *data, u_i + fail++; + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ if ((s = strchr(cp, '/')) != NULL) ++ *s = '\0'; + xasprintf(&userstyle, "%s%s%s", authctxt->user, + authctxt->style ? ":" : "", + authctxt->style ? authctxt->style : ""); +@@ -1286,7 +1317,7 @@ monitor_valid_hostbasedblob(u_char *data + { + struct sshbuf *b; + const u_char *p; +- char *cp, *userstyle; ++ char *cp, *s, *userstyle; + size_t len; + int r, fail = 0; + u_char type; +@@ -1308,6 +1339,8 @@ monitor_valid_hostbasedblob(u_char *data + fail++; + if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ if ((s = strchr(p, '/')) != NULL) ++ *s = '\0'; + xasprintf(&userstyle, "%s%s%s", authctxt->user, + authctxt->style ? ":" : "", + authctxt->style ? authctxt->style : ""); +diff -up openssh/monitor.h.role-mls openssh/monitor.h +--- openssh/monitor.h.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/monitor.h 2018-08-22 11:14:56.818430941 +0200 +@@ -55,6 +55,10 @@ enum monitor_reqtype { + MONITOR_REQ_GSSCHECKMIC = 48, MONITOR_ANS_GSSCHECKMIC = 49, + MONITOR_REQ_TERM = 50, + ++#ifdef WITH_SELINUX ++ MONITOR_REQ_AUTHROLE = 80, ++#endif ++ + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, + MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105, +diff -up openssh/monitor_wrap.c.role-mls openssh/monitor_wrap.c +--- openssh/monitor_wrap.c.role-mls 2018-08-22 11:14:56.818430941 +0200 ++++ openssh/monitor_wrap.c 2018-08-22 11:21:47.938747968 +0200 +@@ -390,6 +390,27 @@ mm_inform_authserv(char *service, char * + sshbuf_free(m); + } + ++/* Inform the privileged process about role */ ++ ++#ifdef WITH_SELINUX ++void ++mm_inform_authrole(char *role) ++{ ++ int r; ++ struct sshbuf *m; ++ ++ debug3("%s entering", __func__); ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_cstring(m, role ? role : "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHROLE, m); ++ ++ sshbuf_free(m); ++} ++#endif ++ + /* Do the password authentication */ + int + mm_auth_password(struct ssh *ssh, char *password) +diff -up openssh/monitor_wrap.h.role-mls openssh/monitor_wrap.h +--- openssh/monitor_wrap.h.role-mls 2018-08-22 11:14:56.818430941 +0200 ++++ openssh/monitor_wrap.h 2018-08-22 11:22:10.439929513 +0200 +@@ -44,6 +44,9 @@ DH *mm_choose_dh(int, int, int); + int mm_sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t, + const char *, u_int compat); + void mm_inform_authserv(char *, char *); ++#ifdef WITH_SELINUX ++void mm_inform_authrole(char *); ++#endif + struct passwd *mm_getpwnamallow(const char *); + char *mm_auth2_read_banner(void); + int mm_auth_password(struct ssh *, char *); +diff -up openssh/openbsd-compat/Makefile.in.role-mls openssh/openbsd-compat/Makefile.in +--- openssh/openbsd-compat/Makefile.in.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/openbsd-compat/Makefile.in 2018-08-22 11:14:56.819430949 +0200 +@@ -92,7 +92,8 @@ PORTS= port-aix.o \ + port-linux.o \ + port-solaris.o \ + port-net.o \ +- port-uw.o ++ port-uw.o \ ++ port-linux-sshd.o + + .c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< +diff -up openssh/openbsd-compat/port-linux.c.role-mls openssh/openbsd-compat/port-linux.c +--- openssh/openbsd-compat/port-linux.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/openbsd-compat/port-linux.c 2018-08-22 11:14:56.819430949 +0200 +@@ -100,37 +100,6 @@ ssh_selinux_getctxbyname(char *pwname) + return sc; + } + +-/* Set the execution context to the default for the specified user */ +-void +-ssh_selinux_setup_exec_context(char *pwname) +-{ +- security_context_t user_ctx = NULL; +- +- if (!ssh_selinux_enabled()) +- return; +- +- debug3("%s: setting execution context", __func__); +- +- user_ctx = ssh_selinux_getctxbyname(pwname); +- if (setexeccon(user_ctx) != 0) { +- switch (security_getenforce()) { +- case -1: +- fatal("%s: security_getenforce() failed", __func__); +- case 0: +- error("%s: Failed to set SELinux execution " +- "context for %s", __func__, pwname); +- break; +- default: +- fatal("%s: Failed to set SELinux execution context " +- "for %s (in enforcing mode)", __func__, pwname); +- } +- } +- if (user_ctx != NULL) +- freecon(user_ctx); +- +- debug3("%s: done", __func__); +-} +- + /* Set the TTY context for the specified user */ + void + ssh_selinux_setup_pty(char *pwname, const char *tty) +@@ -145,7 +114,11 @@ ssh_selinux_setup_pty(char *pwname, cons + + debug3("%s: setting TTY context on %s", __func__, tty); + +- user_ctx = ssh_selinux_getctxbyname(pwname); ++ if (getexeccon(&user_ctx) != 0) { ++ error("%s: getexeccon: %s", __func__, strerror(errno)); ++ goto out; ++ } ++ + + /* XXX: should these calls fatal() upon failure in enforcing mode? */ + +diff -up openssh/openbsd-compat/port-linux.h.role-mls openssh/openbsd-compat/port-linux.h +--- openssh/openbsd-compat/port-linux.h.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/openbsd-compat/port-linux.h 2018-08-22 11:14:56.819430949 +0200 +@@ -20,9 +20,10 @@ + #ifdef WITH_SELINUX + int ssh_selinux_enabled(void); + void ssh_selinux_setup_pty(char *, const char *); +-void ssh_selinux_setup_exec_context(char *); + void ssh_selinux_change_context(const char *); + void ssh_selinux_setfscreatecon(const char *); ++ ++void sshd_selinux_setup_exec_context(char *); + #endif + + #ifdef LINUX_OOM_ADJUST +diff -up openssh/openbsd-compat/port-linux-sshd.c.role-mls openssh/openbsd-compat/port-linux-sshd.c +--- openssh/openbsd-compat/port-linux-sshd.c.role-mls 2018-08-22 11:14:56.819430949 +0200 ++++ openssh/openbsd-compat/port-linux-sshd.c 2018-08-22 11:14:56.819430949 +0200 +@@ -0,0 +1,425 @@ ++/* ++ * Copyright (c) 2005 Daniel Walsh ++ * Copyright (c) 2014 Petr Lautrbach ++ * ++ * 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 - just SELinux support for sshd at present ++ */ ++ ++#include "includes.h" ++ ++#if defined(WITH_SELINUX) || defined(LINUX_OOM_ADJUST) ++#include ++#include ++#include ++#include ++#include ++ ++#include "log.h" ++#include "xmalloc.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "port-linux.h" ++#include "sshkey.h" ++#include "hostfile.h" ++#include "auth.h" ++ ++#ifdef WITH_SELINUX ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_LINUX_AUDIT ++#include ++#include ++#endif ++ ++extern ServerOptions options; ++extern Authctxt *the_authctxt; ++extern int inetd_flag; ++extern int rexeced_flag; ++ ++/* Send audit message */ ++static int ++sshd_selinux_send_audit_message(int success, security_context_t default_context, ++ security_context_t selected_context) ++{ ++ int rc=0; ++#ifdef HAVE_LINUX_AUDIT ++ char *msg = NULL; ++ int audit_fd = audit_open(); ++ security_context_t default_raw=NULL; ++ security_context_t selected_raw=NULL; ++ rc = -1; ++ if (audit_fd < 0) { ++ if (errno == EINVAL || errno == EPROTONOSUPPORT || ++ errno == EAFNOSUPPORT) ++ return 0; /* No audit support in kernel */ ++ error("Error connecting to audit system."); ++ return rc; ++ } ++ if (selinux_trans_to_raw_context(default_context, &default_raw) < 0) { ++ error("Error translating default context."); ++ default_raw = NULL; ++ } ++ if (selinux_trans_to_raw_context(selected_context, &selected_raw) < 0) { ++ error("Error translating selected context."); ++ selected_raw = NULL; ++ } ++ if (asprintf(&msg, "sshd: default-context=%s selected-context=%s", ++ default_raw ? default_raw : (default_context ? default_context: "?"), ++ selected_context ? selected_raw : (selected_context ? selected_context :"?")) < 0) { ++ error("Error allocating memory."); ++ goto out; ++ } ++ if (audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE, ++ msg, NULL, NULL, NULL, success) <= 0) { ++ error("Error sending audit message."); ++ goto out; ++ } ++ rc = 0; ++ out: ++ free(msg); ++ freecon(default_raw); ++ freecon(selected_raw); ++ close(audit_fd); ++#endif ++ return rc; ++} ++ ++static int ++mls_range_allowed(security_context_t src, security_context_t dst) ++{ ++ struct av_decision avd; ++ int retval; ++ access_vector_t bit; ++ security_class_t class; ++ ++ debug("%s: src:%s dst:%s", __func__, src, dst); ++ class = string_to_security_class("context"); ++ if (!class) { ++ error("string_to_security_class failed to translate security class context"); ++ return 1; ++ } ++ bit = string_to_av_perm(class, "contains"); ++ if (!bit) { ++ error("string_to_av_perm failed to translate av perm contains"); ++ return 1; ++ } ++ retval = security_compute_av(src, dst, class, bit, &avd); ++ if (retval || ((bit & avd.allowed) != bit)) ++ return 0; ++ ++ return 1; ++} ++ ++static int ++get_user_context(const char *sename, const char *role, const char *lvl, ++ security_context_t *sc) { ++#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL ++ if (lvl == NULL || lvl[0] == '\0' || get_default_context_with_level(sename, lvl, NULL, sc) != 0) { ++ /* User may have requested a level completely outside of his ++ allowed range. We get a context just for auditing as the ++ range check below will certainly fail for default context. */ ++#endif ++ if (get_default_context(sename, NULL, sc) != 0) { ++ *sc = NULL; ++ return -1; ++ } ++#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL ++ } ++#endif ++ if (role != NULL && role[0]) { ++ context_t con; ++ char *type=NULL; ++ if (get_default_type(role, &type) != 0) { ++ error("get_default_type: failed to get default type for '%s'", ++ role); ++ goto out; ++ } ++ con = context_new(*sc); ++ if (!con) { ++ goto out; ++ } ++ context_role_set(con, role); ++ context_type_set(con, type); ++ freecon(*sc); ++ *sc = strdup(context_str(con)); ++ context_free(con); ++ if (!*sc) ++ return -1; ++ } ++#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL ++ if (lvl != NULL && lvl[0]) { ++ /* verify that the requested range is obtained */ ++ context_t con; ++ security_context_t obtained_raw; ++ security_context_t requested_raw; ++ con = context_new(*sc); ++ if (!con) { ++ goto out; ++ } ++ context_range_set(con, lvl); ++ if (selinux_trans_to_raw_context(*sc, &obtained_raw) < 0) { ++ context_free(con); ++ goto out; ++ } ++ if (selinux_trans_to_raw_context(context_str(con), &requested_raw) < 0) { ++ freecon(obtained_raw); ++ context_free(con); ++ goto out; ++ } ++ ++ debug("get_user_context: obtained context '%s' requested context '%s'", ++ obtained_raw, requested_raw); ++ if (strcmp(obtained_raw, requested_raw)) { ++ /* set the context to the real requested one but fail */ ++ freecon(requested_raw); ++ freecon(obtained_raw); ++ freecon(*sc); ++ *sc = strdup(context_str(con)); ++ context_free(con); ++ return -1; ++ } ++ freecon(requested_raw); ++ freecon(obtained_raw); ++ context_free(con); ++ } ++#endif ++ return 0; ++ out: ++ freecon(*sc); ++ *sc = NULL; ++ return -1; ++} ++ ++static void ++ssh_selinux_get_role_level(char **role, const char **level) ++{ ++ *role = NULL; ++ *level = NULL; ++ if (the_authctxt) { ++ if (the_authctxt->role != NULL) { ++ char *slash; ++ *role = xstrdup(the_authctxt->role); ++ if ((slash = strchr(*role, '/')) != NULL) { ++ *slash = '\0'; ++ *level = slash + 1; ++ } ++ } ++ } ++} ++ ++/* Return the default security context for the given username */ ++static int ++sshd_selinux_getctxbyname(char *pwname, ++ security_context_t *default_sc, security_context_t *user_sc) ++{ ++ char *sename, *lvl; ++ char *role; ++ const char *reqlvl; ++ int r = 0; ++ context_t con = NULL; ++ ++ ssh_selinux_get_role_level(&role, &reqlvl); ++ ++#ifdef HAVE_GETSEUSERBYNAME ++ if ((r=getseuserbyname(pwname, &sename, &lvl)) != 0) { ++ sename = NULL; ++ lvl = NULL; ++ } ++#else ++ sename = pwname; ++ lvl = ""; ++#endif ++ ++ if (r == 0) { ++#ifdef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL ++ r = get_default_context_with_level(sename, lvl, NULL, default_sc); ++#else ++ r = get_default_context(sename, NULL, default_sc); ++#endif ++ } ++ ++ if (r == 0) { ++ /* If launched from xinetd, we must use current level */ ++ if (inetd_flag && !rexeced_flag) { ++ security_context_t sshdsc=NULL; ++ ++ if (getcon_raw(&sshdsc) < 0) ++ fatal("failed to allocate security context"); ++ ++ if ((con=context_new(sshdsc)) == NULL) ++ fatal("failed to allocate selinux context"); ++ reqlvl = context_range_get(con); ++ freecon(sshdsc); ++ if (reqlvl !=NULL && lvl != NULL && strcmp(reqlvl, lvl) == 0) ++ /* we actually don't change level */ ++ reqlvl = ""; ++ ++ debug("%s: current connection level '%s'", __func__, reqlvl); ++ ++ } ++ ++ if ((reqlvl != NULL && reqlvl[0]) || (role != NULL && role[0])) { ++ r = get_user_context(sename, role, reqlvl, user_sc); ++ ++ if (r == 0 && reqlvl != NULL && reqlvl[0]) { ++ security_context_t default_level_sc = *default_sc; ++ if (role != NULL && role[0]) { ++ if (get_user_context(sename, role, lvl, &default_level_sc) < 0) ++ default_level_sc = *default_sc; ++ } ++ /* verify that the requested range is contained in the user range */ ++ if (mls_range_allowed(default_level_sc, *user_sc)) { ++ logit("permit MLS level %s (user range %s)", reqlvl, lvl); ++ } else { ++ r = -1; ++ error("deny MLS level %s (user range %s)", reqlvl, lvl); ++ } ++ if (default_level_sc != *default_sc) ++ freecon(default_level_sc); ++ } ++ } else { ++ *user_sc = *default_sc; ++ } ++ } ++ if (r != 0) { ++ error("%s: Failed to get default SELinux security " ++ "context for %s", __func__, pwname); ++ } ++ ++#ifdef HAVE_GETSEUSERBYNAME ++ free(sename); ++ free(lvl); ++#endif ++ ++ if (role != NULL) ++ free(role); ++ if (con) ++ context_free(con); ++ ++ return (r); ++} ++ ++/* Setup environment variables for pam_selinux */ ++static int ++sshd_selinux_setup_pam_variables(void) ++{ ++ const char *reqlvl; ++ char *role; ++ char *use_current; ++ int rv; ++ ++ debug3("%s: setting execution context", __func__); ++ ++ ssh_selinux_get_role_level(&role, &reqlvl); ++ ++ rv = do_pam_putenv("SELINUX_ROLE_REQUESTED", role ? role : ""); ++ ++ if (inetd_flag && !rexeced_flag) { ++ use_current = "1"; ++ } else { ++ use_current = ""; ++ rv = rv || do_pam_putenv("SELINUX_LEVEL_REQUESTED", reqlvl ? reqlvl: ""); ++ } ++ ++ rv = rv || do_pam_putenv("SELINUX_USE_CURRENT_RANGE", use_current); ++ ++ if (role != NULL) ++ free(role); ++ ++ return rv; ++} ++ ++/* Set the execution context to the default for the specified user */ ++void ++sshd_selinux_setup_exec_context(char *pwname) ++{ ++ security_context_t user_ctx = NULL; ++ int r = 0; ++ security_context_t default_ctx = NULL; ++ ++ if (!ssh_selinux_enabled()) ++ return; ++ ++ if (options.use_pam) { ++ /* do not compute context, just setup environment for pam_selinux */ ++ if (sshd_selinux_setup_pam_variables()) { ++ switch (security_getenforce()) { ++ case -1: ++ fatal("%s: security_getenforce() failed", __func__); ++ case 0: ++ error("%s: SELinux PAM variable setup failure. Continuing in permissive mode.", ++ __func__); ++ break; ++ default: ++ fatal("%s: SELinux PAM variable setup failure. Aborting connection.", ++ __func__); ++ } ++ } ++ return; ++ } ++ ++ debug3("%s: setting execution context", __func__); ++ ++ r = sshd_selinux_getctxbyname(pwname, &default_ctx, &user_ctx); ++ if (r >= 0) { ++ r = setexeccon(user_ctx); ++ if (r < 0) { ++ error("%s: Failed to set SELinux execution context %s for %s", ++ __func__, user_ctx, pwname); ++ } ++#ifdef HAVE_SETKEYCREATECON ++ else if (setkeycreatecon(user_ctx) < 0) { ++ error("%s: Failed to set SELinux keyring creation context %s for %s", ++ __func__, user_ctx, pwname); ++ } ++#endif ++ } ++ if (user_ctx == NULL) { ++ user_ctx = default_ctx; ++ } ++ if (r < 0 || user_ctx != default_ctx) { ++ /* audit just the case when user changed a role or there was ++ a failure */ ++ sshd_selinux_send_audit_message(r >= 0, default_ctx, user_ctx); ++ } ++ if (r < 0) { ++ switch (security_getenforce()) { ++ case -1: ++ fatal("%s: security_getenforce() failed", __func__); ++ case 0: ++ error("%s: SELinux failure. Continuing in permissive mode.", ++ __func__); ++ break; ++ default: ++ fatal("%s: SELinux failure. Aborting connection.", ++ __func__); ++ } ++ } ++ if (user_ctx != NULL && user_ctx != default_ctx) ++ freecon(user_ctx); ++ if (default_ctx != NULL) ++ freecon(default_ctx); ++ ++ debug3("%s: done", __func__); ++} ++ ++#endif ++#endif ++ +diff -up openssh/platform.c.role-mls openssh/platform.c +--- openssh/platform.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/platform.c 2018-08-22 11:14:56.819430949 +0200 +@@ -183,7 +183,7 @@ platform_setusercontext_post_groups(stru + } + #endif /* HAVE_SETPCRED */ + #ifdef WITH_SELINUX +- ssh_selinux_setup_exec_context(pw->pw_name); ++ sshd_selinux_setup_exec_context(pw->pw_name); + #endif + } + +diff -up openssh/sshd.c.role-mls openssh/sshd.c +--- openssh/sshd.c.role-mls 2018-08-20 07:57:29.000000000 +0200 ++++ openssh/sshd.c 2018-08-22 11:14:56.820430957 +0200 +@@ -2186,6 +2186,9 @@ main(int ac, char **av) + restore_uid(); + } + #endif ++#ifdef WITH_SELINUX ++ sshd_selinux_setup_exec_context(authctxt->pw->pw_name); ++#endif + #ifdef USE_PAM + if (options.use_pam) { + do_pam_setcred(1); diff --git a/SOURCES/openssh-7.8p1-scp-ipv6.patch b/SOURCES/openssh-7.8p1-scp-ipv6.patch new file mode 100644 index 0000000..8ae0948 --- /dev/null +++ b/SOURCES/openssh-7.8p1-scp-ipv6.patch @@ -0,0 +1,16 @@ +diff --git a/scp.c b/scp.c +index 60682c68..9344806e 100644 +--- a/scp.c ++++ b/scp.c +@@ -714,7 +714,9 @@ toremote(int argc, char **argv) + addargs(&alist, "%s", host); + addargs(&alist, "%s", cmd); + addargs(&alist, "%s", src); +- addargs(&alist, "%s%s%s:%s", ++ addargs(&alist, ++ /* IPv6 address needs to be enclosed with sqare brackets */ ++ strchr(host, ':') != NULL ? "%s%s[%s]:%s" : "%s%s%s:%s", + tuser ? tuser : "", tuser ? "@" : "", + thost, targ); + if (do_local_cmd(&alist) != 0) + diff --git a/SOURCES/openssh-7.8p1.tar.gz.asc b/SOURCES/openssh-7.8p1.tar.gz.asc new file mode 100644 index 0000000..91665f5 --- /dev/null +++ b/SOURCES/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/SOURCES/openssh-7.9p1-disable-sha1.patch b/SOURCES/openssh-7.9p1-disable-sha1.patch new file mode 100644 index 0000000..c157b3c --- /dev/null +++ b/SOURCES/openssh-7.9p1-disable-sha1.patch @@ -0,0 +1,62 @@ +diff --git a/monitor.c b/monitor.c +index 12b33e7..a1c3c97 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -875,6 +875,34 @@ mm_answer_bsdauthrespond(int sock, struct sshbuf *m) + } + #endif + ++/* ++ * Check that the key type appears in the supplied pattern list, ignoring ++ * mismastches in the signature algorithm. (Signature algorithm checks are ++ * performed in the unprivileged authentication code). ++ * Returns 1 on success, 0 otherwise. ++ */ ++static int ++key_base_type_match(const struct sshkey *key, const char *list) ++{ ++ char *s, *l, *ol = xstrdup(list); ++ int found = 0; ++ ++ l = ol; ++ for ((s = strsep(&l, ",")); s && *s != '\0'; (s = strsep(&l, ","))) { ++ if (sshkey_type_from_name(s) == key->type) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) { ++ debug("key type %s does not appear in list %s", ++ sshkey_ssh_name(key), list); ++ } ++ ++ free(ol); ++ return found; ++} ++ + int + mm_answer_keyallowed(int sock, struct sshbuf *m) + { +@@ -909,8 +937,8 @@ mm_answer_keyallowed(int sock, struct sshbuf *m) + break; + if (auth2_key_already_used(authctxt, key)) + break; +- if (match_pattern_list(sshkey_ssh_name(key), +- options.pubkey_key_types, 0) != 1) ++ if (!key_base_type_match(key, ++ options.pubkey_key_types)) + break; + allowed = user_key_allowed(ssh, authctxt->pw, key, + pubkey_auth_attempt, &opts); +@@ -921,8 +949,8 @@ mm_answer_keyallowed(int sock, struct sshbuf *m) + break; + if (auth2_key_already_used(authctxt, key)) + break; +- if (match_pattern_list(sshkey_ssh_name(key), +- options.hostbased_key_types, 0) != 1) ++ if (!key_base_type_match(key, ++ options.hostbased_key_types)) + break; + allowed = hostbased_key_allowed(authctxt->pw, + cuser, chost, key); + diff --git a/SOURCES/openssh-7.9p1-gsskex-method.patch b/SOURCES/openssh-7.9p1-gsskex-method.patch new file mode 100644 index 0000000..b06620d --- /dev/null +++ b/SOURCES/openssh-7.9p1-gsskex-method.patch @@ -0,0 +1,150 @@ +From bc74944ce7a2eabd228d47051f277ce108914c96 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Tue, 16 Oct 2018 16:44:40 +0200 +Subject: [PATCH] Unbreak authentication using gssapi-keyex (#1625366) + +--- + auth2-gss.c | 6 +++--- + gss-serv.c | 4 +++- + monitor.c | 13 ++++++++++--- + monitor_wrap.c | 4 +++- + monitor_wrap.h | 2 +- + ssh-gss.h | 2 +- + 6 files changed, 21 insertions(+), 10 deletions(-) + +diff --git a/auth2-gss.c b/auth2-gss.c +index 3f2ad21d..a61ac089 100644 +--- a/auth2-gss.c ++++ b/auth2-gss.c +@@ -84,7 +84,7 @@ userauth_gsskeyex(Authctxt *authctxt) + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, + &gssbuf, &mic)))) + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, +- authctxt->pw)); ++ authctxt->pw, 1)); + + sshbuf_free(b); + free(mic.value); +@@ -299,7 +299,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) + fatal("%s: %s", __func__, ssh_err(r)); + + authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, +- authctxt->pw)); ++ authctxt->pw, 1)); + + if ((!use_privsep || mm_is_monitor()) && + (displayname = ssh_gssapi_displayname()) != NULL) +@@ -347,7 +347,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) + + if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) + authenticated = +- PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); ++ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw, 0)); + else + logit("GSSAPI MIC check failed"); + +diff --git a/gss-serv.c b/gss-serv.c +index 786ac95c..87de2baa 100644 +--- a/gss-serv.c ++++ b/gss-serv.c +@@ -493,10 +493,12 @@ verify_authentication_indicators(Gssctxt *gssctxt) + + /* Privileged */ + int +-ssh_gssapi_userok(char *user, struct passwd *pw) ++ssh_gssapi_userok(char *user, struct passwd *pw, int kex) + { + OM_uint32 lmin; + ++ (void) kex; /* used in privilege separation */ ++ + if (gssapi_client.exportedname.length == 0 || + gssapi_client.exportedname.value == NULL) { + debug("No suitable client data"); +diff --git a/monitor.c b/monitor.c +index 9bbe8cc4..7b1903af 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -1877,14 +1877,17 @@ mm_answer_gss_checkmic(int sock, struct sshbuf *m) + int + mm_answer_gss_userok(int sock, struct sshbuf *m) + { +- int r, authenticated; ++ int r, authenticated, kex; + const char *displayname; + + if (!options.gss_authentication && !options.gss_keyex) + fatal("%s: GSSAPI authentication not enabled", __func__); + ++ if ((r = sshbuf_get_u32(m, &kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ + authenticated = authctxt->valid && +- ssh_gssapi_userok(authctxt->user, authctxt->pw); ++ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) +@@ -1893,7 +1896,11 @@ mm_answer_gss_userok(int sock, struct sshbuf *m) + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + +- auth_method = "gssapi-with-mic"; ++ if (kex) { ++ auth_method = "gssapi-keyex"; ++ } else { ++ auth_method = "gssapi-with-mic"; ++ } + + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); +diff --git a/monitor_wrap.c b/monitor_wrap.c +index fb52a530..508d926d 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -984,13 +984,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) + } + + int +-mm_ssh_gssapi_userok(char *user, struct passwd *pw) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex) + { + struct sshbuf *m; + int r, authenticated = 0; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); + mm_request_receive_expect(pmonitor->m_recvfd, +diff --git a/monitor_wrap.h b/monitor_wrap.h +index 494760dd..5eba5ecc 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -60,7 +60,7 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, + 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, struct passwd *); ++int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex); + 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 *); +diff --git a/ssh-gss.h b/ssh-gss.h +index 39b6ce69..98262837 100644 +--- a/ssh-gss.h ++++ b/ssh-gss.h +@@ -162,7 +162,7 @@ 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, struct passwd *); ++int ssh_gssapi_userok(char *name, struct passwd *, int kex); + 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); +-- +2.17.2 + diff --git a/SOURCES/pam_ssh_agent-rmheaders b/SOURCES/pam_ssh_agent-rmheaders new file mode 100644 index 0000000..06d899d --- /dev/null +++ b/SOURCES/pam_ssh_agent-rmheaders @@ -0,0 +1,37 @@ +authfd.c +authfd.h +atomicio.c +atomicio.h +bufaux.c +bufbn.c +buffer.h +buffer.c +cleanup.c +cipher.h +compat.h +defines.h +entropy.c +entropy.h +fatal.c +includes.h +kex.h +key.c +key.h +log.c +log.h +match.h +misc.c +misc.h +pathnames.h +platform.h +rsa.h +ssh-dss.c +ssh-rsa.c +ssh.h +ssh2.h +uidswap.c +uidswap.h +uuencode.c +uuencode.h +xmalloc.c +xmalloc.h diff --git a/SOURCES/pam_ssh_agent_auth-0.10.2-compat.patch b/SOURCES/pam_ssh_agent_auth-0.10.2-compat.patch new file mode 100644 index 0000000..6352bfa --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.10.2-compat.patch @@ -0,0 +1,939 @@ +diff -up openssh/pam_ssh_agent_auth-0.10.3/get_command_line.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/get_command_line.c +--- openssh/pam_ssh_agent_auth-0.10.3/get_command_line.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/get_command_line.c 2018-08-24 10:22:56.281930322 +0200 +@@ -27,6 +27,7 @@ + * or implied, of Jamie Beverly. + */ + ++#include + #include + #include + #include +@@ -65,8 +66,8 @@ proc_pid_cmdline(char *** inargv) + case EOF: + case '\0': + if (len > 0) { +- argv = pamsshagentauth_xrealloc(argv, count + 1, sizeof(*argv)); +- argv[count] = pamsshagentauth_xcalloc(len + 1, sizeof(*argv[count])); ++ argv = xreallocarray(argv, count + 1, sizeof(*argv)); ++ argv[count] = xcalloc(len + 1, sizeof(*argv[count])); + strncpy(argv[count++], argbuf, len); + memset(argbuf, '\0', MAX_LEN_PER_CMDLINE_ARG + 1); + len = 0; +@@ -105,9 +106,9 @@ pamsshagentauth_free_command_line(char * + { + size_t i; + for (i = 0; i < n_args; i++) +- pamsshagentauth_xfree(argv[i]); ++ free(argv[i]); + +- pamsshagentauth_xfree(argv); ++ free(argv); + return; + } + +diff -up openssh/pam_ssh_agent_auth-0.10.3/identity.h.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/identity.h +--- openssh/pam_ssh_agent_auth-0.10.3/identity.h.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/identity.h 2018-08-24 10:18:05.009393312 +0200 +@@ -30,8 +30,8 @@ + #include "openbsd-compat/sys-queue.h" + #include "xmalloc.h" + #include "log.h" +-#include "buffer.h" +-#include "key.h" ++#include "sshbuf.h" ++#include "sshkey.h" + #include "authfd.h" + #include + +@@ -41,7 +41,7 @@ typedef struct idlist Idlist; + struct identity { + TAILQ_ENTRY(identity) next; + AuthenticationConnection *ac; /* set if agent supports key */ +- Key *key; /* public/private key */ ++ struct sshkey *key; /* public/private key */ + char *filename; /* comment for agent-only keys */ + int tried; + int isprivate; /* key points to the private key */ +diff -up openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c +--- openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-compat 2018-08-24 10:18:05.007393297 +0200 ++++ openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c 2018-08-24 10:18:32.937612513 +0200 +@@ -36,8 +36,8 @@ + #include "openbsd-compat/sys-queue.h" + #include "xmalloc.h" + #include "log.h" +-#include "buffer.h" +-#include "key.h" ++#include "sshbuf.h" ++#include "sshkey.h" + #include "authfd.h" + #include + #include +@@ -58,6 +58,8 @@ + #include "get_command_line.h" + extern char **environ; + ++#define PAM_SSH_AGENT_AUTH_REQUESTv1 101 ++ + /* + * Added by Jamie Beverly, ensure socket fd points to a socket owned by the user + * A cursory check is done, but to avoid race conditions, it is necessary +@@ -77,7 +79,7 @@ log_action(char ** action, size_t count) + if (count == 0) + return NULL; + +- buf = pamsshagentauth_xcalloc((count * MAX_LEN_PER_CMDLINE_ARG) + (count * 3), sizeof(*buf)); ++ buf = xcalloc((count * MAX_LEN_PER_CMDLINE_ARG) + (count * 3), sizeof(*buf)); + for (i = 0; i < count; i++) { + strcat(buf, (i > 0) ? " '" : "'"); + strncat(buf, action[i], MAX_LEN_PER_CMDLINE_ARG); +@@ -87,21 +89,25 @@ log_action(char ** action, size_t count) + } + + void +-agent_action(Buffer *buf, char ** action, size_t count) ++agent_action(struct sshbuf **buf, char ** action, size_t count) + { + size_t i; +- pamsshagentauth_buffer_init(buf); ++ int r; + +- pamsshagentauth_buffer_put_int(buf, count); ++ if ((*buf = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(*buf, count)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + for (i = 0; i < count; i++) { +- pamsshagentauth_buffer_put_cstring(buf, action[i]); ++ if ((r = sshbuf_put_cstring(*buf, action[i])) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + } + } + + +-void +-pamsshagentauth_session_id2_gen(Buffer * session_id2, const char * user, ++static void ++pamsshagentauth_session_id2_gen(struct sshbuf ** session_id2, const char * user, + const char * ruser, const char * servicename) + { + u_char *cookie = NULL; +@@ -114,22 +116,23 @@ pamsshagentauth_session_id2_gen(Buffer * + char ** reported_argv = NULL; + size_t count = 0; + char * action_logbuf = NULL; +- Buffer action_agentbuf; ++ struct sshbuf *action_agentbuf = NULL; + uint8_t free_logbuf = 0; + char * retc; + int32_t reti; ++ int r; + +- rnd = pamsshagentauth_arc4random(); ++ rnd = arc4random(); + cookie_len = ((uint8_t) rnd); + while (cookie_len < 16) { + cookie_len += 16; /* Add 16 bytes to the size to ensure that while the length is random, the length is always reasonable; ticket #18 */ + } + +- cookie = pamsshagentauth_xcalloc(1,cookie_len); ++ cookie = xcalloc(1, cookie_len); + + for (i = 0; i < cookie_len; i++) { + if (i % 4 == 0) { +- rnd = pamsshagentauth_arc4random(); ++ rnd = arc4random(); + } + cookie[i] = (u_char) rnd; + rnd >>= 8; +@@ -139,12 +141,13 @@ pamsshagentauth_session_id2_gen(Buffer * + if (count > 0) { + free_logbuf = 1; + action_logbuf = log_action(reported_argv, count); +- agent_action(&action_agentbuf, reported_argv, count); ++ agent_action(&action_agentbuf, reported_argv, count); + pamsshagentauth_free_command_line(reported_argv, count); + } + else { + action_logbuf = "unknown on this platform"; +- pamsshagentauth_buffer_init(&action_agentbuf); /* stays empty, means unavailable */ ++ if ((action_agentbuf = sshbuf_new()) == NULL) /* stays empty, means unavailable */ ++ fatal("%s: sshbuf_new failed", __func__); + } + + /* +@@ -161,35 +163,39 @@ pamsshagentauth_session_id2_gen(Buffer * + retc = getcwd(pwd, sizeof(pwd) - 1); + time(&ts); + +- pamsshagentauth_buffer_init(session_id2); ++ if ((*session_id2 = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); + +- pamsshagentauth_buffer_put_int(session_id2, PAM_SSH_AGENT_AUTH_REQUESTv1); +- /* pamsshagentauth_debug3("cookie: %s", pamsshagentauth_tohex(cookie, cookie_len)); */ +- pamsshagentauth_buffer_put_string(session_id2, cookie, cookie_len); +- /* pamsshagentauth_debug3("user: %s", user); */ +- pamsshagentauth_buffer_put_cstring(session_id2, user); +- /* pamsshagentauth_debug3("ruser: %s", ruser); */ +- pamsshagentauth_buffer_put_cstring(session_id2, ruser); +- /* pamsshagentauth_debug3("servicename: %s", servicename); */ +- pamsshagentauth_buffer_put_cstring(session_id2, servicename); +- /* pamsshagentauth_debug3("pwd: %s", pwd); */ +- if(retc) +- pamsshagentauth_buffer_put_cstring(session_id2, pwd); +- else +- pamsshagentauth_buffer_put_cstring(session_id2, ""); +- /* pamsshagentauth_debug3("action: %s", action_logbuf); */ +- pamsshagentauth_buffer_put_string(session_id2, action_agentbuf.buf + action_agentbuf.offset, action_agentbuf.end - action_agentbuf.offset); ++ if ((r = sshbuf_put_u32(*session_id2, PAM_SSH_AGENT_AUTH_REQUESTv1)) != 0 || ++ (r = sshbuf_put_string(*session_id2, cookie, cookie_len)) != 0 || ++ (r = sshbuf_put_cstring(*session_id2, user)) != 0 || ++ (r = sshbuf_put_cstring(*session_id2, ruser)) != 0 || ++ (r = sshbuf_put_cstring(*session_id2, servicename)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ if (retc) { ++ if ((r = sshbuf_put_cstring(*session_id2, pwd)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ } else { ++ if ((r = sshbuf_put_cstring(*session_id2, "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ } ++ if ((r = sshbuf_put_stringb(*session_id2, action_agentbuf)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (free_logbuf) { +- pamsshagentauth_xfree(action_logbuf); +- pamsshagentauth_buffer_free(&action_agentbuf); ++ free(action_logbuf); ++ sshbuf_free(action_agentbuf); + } +- /* pamsshagentauth_debug3("hostname: %s", hostname); */ +- if(reti >= 0) +- pamsshagentauth_buffer_put_cstring(session_id2, hostname); +- else +- pamsshagentauth_buffer_put_cstring(session_id2, ""); +- /* pamsshagentauth_debug3("ts: %ld", ts); */ +- pamsshagentauth_buffer_put_int64(session_id2, (uint64_t) ts); ++ /* debug3("hostname: %s", hostname); */ ++ if (reti >= 0) { ++ if ((r = sshbuf_put_cstring(*session_id2, hostname)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ } else { ++ if ((r = sshbuf_put_cstring(*session_id2, "")) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ } ++ /* debug3("ts: %ld", ts); */ ++ if ((r = sshbuf_put_u64(*session_id2, (uint64_t) ts)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + free(cookie); + return; +@@ -278,7 +280,8 @@ ssh_get_authentication_connection_for_ui + + auth = xmalloc(sizeof(*auth)); + auth->fd = sock; +- buffer_init(&auth->identities); ++ if ((auth->identities = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); + auth->howmany = 0; + + return auth; +@@ -287,43 +289,42 @@ ssh_get_authentication_connection_for_ui + int + pamsshagentauth_find_authorized_keys(const char * user, const char * ruser, const char * servicename) + { +- Buffer session_id2 = { 0 }; ++ struct sshbuf *session_id2 = NULL; + Identity *id; +- Key *key; ++ struct sshkey *key; + AuthenticationConnection *ac; + char *comment; + uint8_t retval = 0; + uid_t uid = getpwnam(ruser)->pw_uid; + + OpenSSL_add_all_digests(); +- pamsshagentauth_session_id2_gen(&session_id2, user, ruser, servicename); ++ pamsshagentauth_session_id2_gen(&session_id2, user, ruser, servicename); + + if ((ac = ssh_get_authentication_connection_for_uid(uid))) { +- pamsshagentauth_verbose("Contacted ssh-agent of user %s (%u)", ruser, uid); ++ verbose("Contacted ssh-agent of user %s (%u)", ruser, uid); + for (key = ssh_get_first_identity(ac, &comment, 2); key != NULL; key = ssh_get_next_identity(ac, &comment, 2)) + { + if(key != NULL) { +- id = pamsshagentauth_xcalloc(1, sizeof(*id)); ++ id = xcalloc(1, sizeof(*id)); + id->key = key; + id->filename = comment; + id->ac = ac; +- if(userauth_pubkey_from_id(ruser, id, &session_id2)) { ++ if(userauth_pubkey_from_id(ruser, id, session_id2)) { + retval = 1; + } +- pamsshagentauth_xfree(id->filename); +- pamsshagentauth_key_free(id->key); +- pamsshagentauth_xfree(id); ++ free(id->filename); ++ key_free(id->key); ++ free(id); + if(retval == 1) + break; + } + } +- pamsshagentauth_buffer_free(&session_id2); ++ sshbuf_free(session_id2); + ssh_close_authentication_connection(ac); + } + else { +- pamsshagentauth_verbose("No ssh-agent could be contacted"); ++ verbose("No ssh-agent could be contacted"); + } +- /* pamsshagentauth_xfree(session_id2); */ + EVP_cleanup(); + return retval; + } +diff -up openssh/pam_ssh_agent_auth-0.10.3/pam_ssh_agent_auth.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/pam_ssh_agent_auth.c +--- openssh/pam_ssh_agent_auth-0.10.3/pam_ssh_agent_auth.c.psaa-compat 2018-08-24 10:18:05.008393305 +0200 ++++ openssh/pam_ssh_agent_auth-0.10.3/pam_ssh_agent_auth.c 2018-08-24 10:18:05.009393312 +0200 +@@ -104,7 +104,7 @@ pam_sm_authenticate(pam_handle_t * pamh, + * a patch 8-) + */ + #if ! HAVE___PROGNAME || HAVE_BUNDLE +- __progname = pamsshagentauth_xstrdup(servicename); ++ __progname = xstrdup(servicename); + #endif + + for(i = argc, argv_ptr = (char **) argv; i > 0; ++argv_ptr, i--) { +@@ -130,11 +130,11 @@ pam_sm_authenticate(pam_handle_t * pamh, + #endif + } + +- pamsshagentauth_log_init(__progname, log_lvl, facility, getenv("PAM_SSH_AGENT_AUTH_DEBUG") ? 1 : 0); ++ log_init(__progname, log_lvl, facility, getenv("PAM_SSH_AGENT_AUTH_DEBUG") ? 1 : 0); + pam_get_item(pamh, PAM_USER, (void *) &user); + pam_get_item(pamh, PAM_RUSER, (void *) &ruser_ptr); + +- pamsshagentauth_verbose("Beginning pam_ssh_agent_auth for user %s", user); ++ verbose("Beginning pam_ssh_agent_auth for user %s", user); + + if(ruser_ptr) { + strncpy(ruser, ruser_ptr, sizeof(ruser) - 1); +@@ -149,12 +149,12 @@ pam_sm_authenticate(pam_handle_t * pamh, + #ifdef ENABLE_SUDO_HACK + if( (strlen(sudo_service_name) > 0) && strncasecmp(servicename, sudo_service_name, sizeof(sudo_service_name) - 1) == 0 && getenv("SUDO_USER") ) { + strncpy(ruser, getenv("SUDO_USER"), sizeof(ruser) - 1 ); +- pamsshagentauth_verbose( "Using environment variable SUDO_USER (%s)", ruser ); ++ verbose( "Using environment variable SUDO_USER (%s)", ruser ); + } else + #endif + { + if( ! getpwuid(getuid()) ) { +- pamsshagentauth_verbose("Unable to getpwuid(getuid())"); ++ verbose("Unable to getpwuid(getuid())"); + goto cleanexit; + } + strncpy(ruser, getpwuid(getuid())->pw_name, sizeof(ruser) - 1); +@@ -163,11 +163,11 @@ pam_sm_authenticate(pam_handle_t * pamh, + + /* Might as well explicitely confirm the user exists here */ + if(! getpwnam(ruser) ) { +- pamsshagentauth_verbose("getpwnam(%s) failed, bailing out", ruser); ++ verbose("getpwnam(%s) failed, bailing out", ruser); + goto cleanexit; + } + if( ! getpwnam(user) ) { +- pamsshagentauth_verbose("getpwnam(%s) failed, bailing out", user); ++ verbose("getpwnam(%s) failed, bailing out", user); + goto cleanexit; + } + +@@ -177,8 +177,8 @@ pam_sm_authenticate(pam_handle_t * pamh, + */ + parse_authorized_key_file(user, authorized_keys_file_input); + } else { +- pamsshagentauth_verbose("Using default file=/etc/security/authorized_keys"); +- authorized_keys_file = pamsshagentauth_xstrdup("/etc/security/authorized_keys"); ++ verbose("Using default file=/etc/security/authorized_keys"); ++ authorized_keys_file = xstrdup("/etc/security/authorized_keys"); + } + + /* +@@ -187,19 +187,19 @@ pam_sm_authenticate(pam_handle_t * pamh, + */ + + if(user && strlen(ruser) > 0) { +- pamsshagentauth_verbose("Attempting authentication: `%s' as `%s' using %s", ruser, user, authorized_keys_file); ++ verbose("Attempting authentication: `%s' as `%s' using %s", ruser, user, authorized_keys_file); + + /* + * this pw_uid is used to validate the SSH_AUTH_SOCK, and so must be the uid of the ruser invoking the program, not the target-user + */ + if(pamsshagentauth_find_authorized_keys(user, ruser, servicename)) { /* getpwnam(ruser)->pw_uid)) { */ +- pamsshagentauth_logit("Authenticated: `%s' as `%s' using %s", ruser, user, authorized_keys_file); ++ logit("Authenticated: `%s' as `%s' using %s", ruser, user, authorized_keys_file); + retval = PAM_SUCCESS; + } else { +- pamsshagentauth_logit("Failed Authentication: `%s' as `%s' using %s", ruser, user, authorized_keys_file); ++ logit("Failed Authentication: `%s' as `%s' using %s", ruser, user, authorized_keys_file); + } + } else { +- pamsshagentauth_logit("No %s specified, cannot continue with this form of authentication", (user) ? "ruser" : "user" ); ++ logit("No %s specified, cannot continue with this form of authentication", (user) ? "ruser" : "user" ); + } + + cleanexit: +diff -up openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.c +--- openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.c 2018-08-24 10:18:05.009393312 +0200 +@@ -66,8 +66,8 @@ + #include "xmalloc.h" + #include "match.h" + #include "log.h" +-#include "buffer.h" +-#include "key.h" ++#include "sshbuf.h" ++#include "sshkey.h" + #include "misc.h" + + #include "xmalloc.h" +@@ -77,7 +77,6 @@ + #include "pathnames.h" + #include "secure_filename.h" + +-#include "identity.h" + #include "pam_user_key_allowed2.h" + + extern char *authorized_keys_file; +@@ -117,12 +116,12 @@ parse_authorized_key_file(const char *us + } else { + slash_ptr = strchr(auth_keys_file_buf, '/'); + if(!slash_ptr) +- pamsshagentauth_fatal ++ fatal + ("cannot expand tilde in path without a `/'"); + + owner_uname_len = slash_ptr - auth_keys_file_buf - 1; + if(owner_uname_len > (sizeof(owner_uname) - 1)) +- pamsshagentauth_fatal("Username too long"); ++ fatal("Username too long"); + + strncat(owner_uname, auth_keys_file_buf + 1, owner_uname_len); + if(!authorized_keys_file_allowed_owner_uid) +@@ -130,11 +129,11 @@ parse_authorized_key_file(const char *us + getpwnam(owner_uname)->pw_uid; + } + authorized_keys_file = +- pamsshagentauth_tilde_expand_filename(auth_keys_file_buf, ++ tilde_expand_filename(auth_keys_file_buf, + authorized_keys_file_allowed_owner_uid); + strncpy(auth_keys_file_buf, authorized_keys_file, + sizeof(auth_keys_file_buf) - 1); +- pamsshagentauth_xfree(authorized_keys_file) /* when we ++ free(authorized_keys_file) /* when we + percent_expand + later, we'd step + on this, so free +@@ -150,13 +149,13 @@ parse_authorized_key_file(const char *us + strncat(hostname, fqdn, strcspn(fqdn, ".")); + #endif + authorized_keys_file = +- pamsshagentauth_percent_expand(auth_keys_file_buf, "h", ++ percent_expand(auth_keys_file_buf, "h", + getpwnam(user)->pw_dir, "H", hostname, + "f", fqdn, "u", user, NULL); + } + + int +-pam_user_key_allowed(const char *ruser, Key * key) ++pam_user_key_allowed(const char *ruser, struct sshkey * key) + { + return + pamsshagentauth_user_key_allowed2(getpwuid(authorized_keys_file_allowed_owner_uid), +diff -up openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.h.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.h +--- openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.h.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/pam_user_authorized_keys.h 2018-08-24 10:18:05.010393320 +0200 +@@ -32,7 +32,7 @@ + #define _PAM_USER_KEY_ALLOWED_H + + #include "identity.h" +-int pam_user_key_allowed(const char *, Key *); ++int pam_user_key_allowed(const char *, struct sshkey *); + void parse_authorized_key_file(const char *, const char *); + + #endif +diff -up openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.c +--- openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.c 2018-08-24 10:18:05.010393320 +0200 +@@ -45,44 +45,46 @@ + #include "xmalloc.h" + #include "ssh.h" + #include "ssh2.h" +-#include "buffer.h" ++#include "sshbuf.h" + #include "log.h" + #include "compat.h" +-#include "key.h" ++#include "digest.h" ++#include "sshkey.h" + #include "pathnames.h" + #include "misc.h" + #include "secure_filename.h" + #include "uidswap.h" +- +-#include "identity.h" ++#include + + /* return 1 if user allows given key */ + /* Modified slightly from original found in auth2-pubkey.c */ + static int +-pamsshagentauth_check_authkeys_file(FILE * f, char *file, Key * key) ++pamsshagentauth_check_authkeys_file(FILE * f, char *file, struct sshkey * key) + { +- char line[SSH_MAX_PUBKEY_BYTES]; ++ char *line = NULL; + int found_key = 0; + u_long linenum = 0; +- Key *found; ++ struct sshkey *found; + char *fp; ++ size_t linesize = 0; + + found_key = 0; +- found = pamsshagentauth_key_new(key->type); ++ found = sshkey_new(key->type); + +- while(read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { ++ while ((getline(&line, &linesize, f)) != -1) { + char *cp = NULL; /* *key_options = NULL; */ + ++ linenum++; + /* Skip leading whitespace, empty and comment lines. */ + for(cp = line; *cp == ' ' || *cp == '\t'; cp++); + if(!*cp || *cp == '\n' || *cp == '#') + continue; + +- if(pamsshagentauth_key_read(found, &cp) != 1) { ++ if (sshkey_read(found, &cp) != 0) { + /* no key? check if there are options for this key */ + int quoted = 0; + +- pamsshagentauth_verbose("user_key_allowed: check options: '%s'", cp); ++ verbose("user_key_allowed: check options: '%s'", cp); + /* key_options = cp; */ + for(; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { + if(*cp == '\\' && cp[1] == '"') +@@ -92,26 +94,27 @@ pamsshagentauth_check_authkeys_file(FILE + } + /* Skip remaining whitespace. */ + for(; *cp == ' ' || *cp == '\t'; cp++); +- if(pamsshagentauth_key_read(found, &cp) != 1) { +- pamsshagentauth_verbose("user_key_allowed: advance: '%s'", cp); ++ if(sshkey_read(found, &cp) != 0) { ++ verbose("user_key_allowed: advance: '%s'", cp); + /* still no key? advance to next line */ + continue; + } + } +- if(pamsshagentauth_key_equal(found, key)) { ++ if(sshkey_equal(found, key)) { + found_key = 1; +- pamsshagentauth_logit("matching key found: file/command %s, line %lu", file, ++ logit("matching key found: file/command %s, line %lu", file, + linenum); +- fp = pamsshagentauth_key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); +- pamsshagentauth_logit("Found matching %s key: %s", +- pamsshagentauth_key_type(found), fp); +- pamsshagentauth_xfree(fp); ++ fp = sshkey_fingerprint(found, SSH_DIGEST_SHA256, SSH_FP_BASE64); ++ logit("Found matching %s key: %s", ++ sshkey_type(found), fp); ++ free(fp); + break; + } + } +- pamsshagentauth_key_free(found); ++ free(line); ++ sshkey_free(found); + if(!found_key) +- pamsshagentauth_verbose("key not found"); ++ verbose("key not found"); + return found_key; + } + +@@ -120,19 +123,19 @@ pamsshagentauth_check_authkeys_file(FILE + * returns 1 if the key is allowed or 0 otherwise. + */ + int +-pamsshagentauth_user_key_allowed2(struct passwd *pw, Key * key, char *file) ++pamsshagentauth_user_key_allowed2(struct passwd *pw, struct sshkey * key, char *file) + { + FILE *f; + int found_key = 0; + struct stat st; +- char buf[SSH_MAX_PUBKEY_BYTES]; ++ char buf[256]; + + /* Temporarily use the user's uid. */ +- pamsshagentauth_verbose("trying public key file %s", file); ++ verbose("trying public key file %s", file); + + /* Fail not so quietly if file does not exist */ + if(stat(file, &st) < 0) { +- pamsshagentauth_verbose("File not found: %s", file); ++ verbose("File not found: %s", file); + return 0; + } + +@@ -144,7 +147,7 @@ pamsshagentauth_user_key_allowed2(struct + + if(pamsshagentauth_secure_filename(f, file, pw, buf, sizeof(buf)) != 0) { + fclose(f); +- pamsshagentauth_logit("Authentication refused: %s", buf); ++ logit("Authentication refused: %s", buf); + return 0; + } + +@@ -160,7 +163,7 @@ pamsshagentauth_user_key_allowed2(struct + int + pamsshagentauth_user_key_command_allowed2(char *authorized_keys_command, + char *authorized_keys_command_user, +- struct passwd *user_pw, Key * key) ++ struct passwd *user_pw, struct sshkey * key) + { + FILE *f; + int ok, found_key = 0; +@@ -187,44 +190,44 @@ pamsshagentauth_user_key_command_allowed + else { + pw = getpwnam(authorized_keys_command_user); + if(pw == NULL) { +- pamsshagentauth_logerror("authorized_keys_command_user \"%s\" not found: %s", ++ error("authorized_keys_command_user \"%s\" not found: %s", + authorized_keys_command_user, strerror(errno)); + return 0; + } + } + +- pamsshagentauth_temporarily_use_uid(pw); ++ temporarily_use_uid(pw); + + if(stat(authorized_keys_command, &st) < 0) { +- pamsshagentauth_logerror ++ error + ("Could not stat AuthorizedKeysCommand \"%s\": %s", + authorized_keys_command, strerror(errno)); + goto out; + } + if(pamsshagentauth_auth_secure_path + (authorized_keys_command, &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { +- pamsshagentauth_logerror("Unsafe AuthorizedKeysCommand: %s", errmsg); ++ error("Unsafe AuthorizedKeysCommand: %s", errmsg); + goto out; + } + + /* open the pipe and read the keys */ + if(pipe(p) != 0) { +- pamsshagentauth_logerror("%s: pipe: %s", __func__, strerror(errno)); ++ error("%s: pipe: %s", __func__, strerror(errno)); + goto out; + } + +- pamsshagentauth_debug("Running AuthorizedKeysCommand: \"%s\" as \"%s\" with argument: \"%s\"", ++ debug("Running AuthorizedKeysCommand: \"%s\" as \"%s\" with argument: \"%s\"", + authorized_keys_command, pw->pw_name, username); + + /* + * Don't want to call this in the child, where it can fatal() and + * run cleanup_exit() code. + */ +- pamsshagentauth_restore_uid(); ++ restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ +- pamsshagentauth_logerror("%s: fork: %s", __func__, strerror(errno)); ++ error("%s: fork: %s", __func__, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; +@@ -234,13 +237,13 @@ pamsshagentauth_user_key_command_allowed + + /* do this before the setresuid so thta they can be logged */ + if((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { +- pamsshagentauth_logerror("%s: open %s: %s", __func__, _PATH_DEVNULL, ++ error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if(dup2(devnull, STDIN_FILENO) == -1 || dup2(p[1], STDOUT_FILENO) == -1 + || dup2(devnull, STDERR_FILENO) == -1) { +- pamsshagentauth_logerror("%s: dup2: %s", __func__, strerror(errno)); ++ error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } + #if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID) +@@ -248,7 +251,7 @@ pamsshagentauth_user_key_command_allowed + #else + if (setgid(pw->pw_gid) != 0 || setegid(pw->pw_gid) != 0) { + #endif +- pamsshagentauth_logerror("setresgid %u: %s", (u_int) pw->pw_gid, ++ error("setresgid %u: %s", (u_int) pw->pw_gid, + strerror(errno)); + _exit(1); + } +@@ -258,7 +261,7 @@ pamsshagentauth_user_key_command_allowed + #else + if (setuid(pw->pw_uid) != 0 || seteuid(pw->pw_uid) != 0) { + #endif +- pamsshagentauth_logerror("setresuid %u: %s", (u_int) pw->pw_uid, ++ error("setresuid %u: %s", (u_int) pw->pw_uid, + strerror(errno)); + _exit(1); + } +@@ -270,18 +273,18 @@ pamsshagentauth_user_key_command_allowed + + /* pretty sure this will barf because we are now suid, but since we + should't reach this anyway, I'll leave it here */ +- pamsshagentauth_logerror("AuthorizedKeysCommand %s exec failed: %s", ++ error("AuthorizedKeysCommand %s exec failed: %s", + authorized_keys_command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + +- pamsshagentauth_temporarily_use_uid(pw); ++ temporarily_use_uid(pw); + + close(p[1]); + if((f = fdopen(p[0], "r")) == NULL) { +- pamsshagentauth_logerror("%s: fdopen: %s", __func__, strerror(errno)); ++ error("%s: fdopen: %s", __func__, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + while(waitpid(pid, NULL, 0) == -1 && errno == EINTR); +@@ -292,22 +295,22 @@ pamsshagentauth_user_key_command_allowed + + while(waitpid(pid, &status, 0) == -1) { + if(errno != EINTR) { +- pamsshagentauth_logerror("%s: waitpid: %s", __func__, ++ error("%s: waitpid: %s", __func__, + strerror(errno)); + goto out; + } + } + if(WIFSIGNALED(status)) { +- pamsshagentauth_logerror("AuthorizedKeysCommand %s exited on signal %d", ++ error("AuthorizedKeysCommand %s exited on signal %d", + authorized_keys_command, WTERMSIG(status)); + goto out; + } else if(WEXITSTATUS(status) != 0) { +- pamsshagentauth_logerror("AuthorizedKeysCommand %s returned status %d", ++ error("AuthorizedKeysCommand %s returned status %d", + authorized_keys_command, WEXITSTATUS(status)); + goto out; + } + found_key = ok; + out: +- pamsshagentauth_restore_uid(); ++ restore_uid(); + return found_key; + } +diff -up openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.h.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.h +--- openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.h.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/pam_user_key_allowed2.h 2018-08-24 10:18:05.010393320 +0200 +@@ -32,7 +32,7 @@ + #define _PAM_USER_KEY_ALLOWED_H + + #include "identity.h" +-int pamsshagentauth_user_key_allowed2(struct passwd *, Key *, char *); +-int pamsshagentauth_user_key_command_allowed2(char *, char *, struct passwd *, Key *); ++int pamsshagentauth_user_key_allowed2(struct passwd *, struct sshkey *, char *); ++int pamsshagentauth_user_key_command_allowed2(char *, char *, struct passwd *, struct sshkey *); + + #endif +diff -up openssh/pam_ssh_agent_auth-0.10.3/secure_filename.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/secure_filename.c +--- openssh/pam_ssh_agent_auth-0.10.3/secure_filename.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/secure_filename.c 2018-08-24 10:18:05.010393320 +0200 +@@ -53,8 +53,8 @@ + #include "xmalloc.h" + #include "match.h" + #include "log.h" +-#include "buffer.h" +-#include "key.h" ++#include "sshbuf.h" ++#include "sshkey.h" + #include "misc.h" + + +@@ -80,7 +80,7 @@ pamsshagentauth_auth_secure_path(const c + int comparehome = 0; + struct stat st; + +- pamsshagentauth_verbose("auth_secure_filename: checking for uid: %u", uid); ++ verbose("auth_secure_filename: checking for uid: %u", uid); + + if (realpath(name, buf) == NULL) { + snprintf(err, errlen, "realpath %s failed: %s", name, +@@ -115,9 +115,9 @@ pamsshagentauth_auth_secure_path(const c + snprintf(err, errlen, "dirname() failed"); + return -1; + } +- pamsshagentauth_strlcpy(buf, cp, sizeof(buf)); ++ strlcpy(buf, cp, sizeof(buf)); + +- pamsshagentauth_verbose("secure_filename: checking '%s'", buf); ++ verbose("secure_filename: checking '%s'", buf); + if (stat(buf, &st) < 0 || + (st.st_uid != 0 && st.st_uid != uid) || + (st.st_mode & 022) != 0) { +@@ -128,7 +128,7 @@ pamsshagentauth_auth_secure_path(const c + + /* If are passed the homedir then we can stop */ + if (comparehome && strcmp(homedir, buf) == 0) { +- pamsshagentauth_verbose("secure_filename: terminating check at '%s'", ++ verbose("secure_filename: terminating check at '%s'", + buf); + break; + } +diff -up openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c +--- openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c 2018-08-24 10:22:13.202657025 +0200 +@@ -37,10 +37,11 @@ + #include "xmalloc.h" + #include "ssh.h" + #include "ssh2.h" +-#include "buffer.h" ++#include "sshbuf.h" + #include "log.h" + #include "compat.h" +-#include "key.h" ++#include "sshkey.h" ++#include "ssherr.h" + #include "pathnames.h" + #include "misc.h" + #include "secure_filename.h" +@@ -48,54 +48,59 @@ + #include "identity.h" + #include "pam_user_authorized_keys.h" + ++#define SSH2_MSG_USERAUTH_TRUST_REQUEST 54 ++ + /* extern u_char *session_id2; + extern uint8_t session_id_len; + */ + + int +-userauth_pubkey_from_id(const char *ruser, Identity * id, Buffer * session_id2) ++userauth_pubkey_from_id(const char *ruser, Identity * id, struct sshbuf * session_id2) + { +- Buffer b = { 0 }; ++ struct sshbuf *b = NULL; + char *pkalg = NULL; + u_char *pkblob = NULL, *sig = NULL; +- u_int blen = 0, slen = 0; ++ size_t blen = 0, slen = 0; +- int authenticated = 0; ++ int r, authenticated = 0; + +- pkalg = (char *) key_ssh_name(id->key); ++ pkalg = (char *) sshkey_ssh_name(id->key); + + /* first test if this key is even allowed */ + if(! pam_user_key_allowed(ruser, id->key)) +- goto user_auth_clean_exit; ++ goto user_auth_clean_exit_without_buffer; + +- if(pamsshagentauth_key_to_blob(id->key, &pkblob, &blen) == 0) +- goto user_auth_clean_exit; ++ if(sshkey_to_blob(id->key, &pkblob, &blen) != 0) ++ goto user_auth_clean_exit_without_buffer; + + /* construct packet to sign and test */ +- pamsshagentauth_buffer_init(&b); ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); + +- pamsshagentauth_buffer_put_string(&b, session_id2->buf + session_id2->offset, session_id2->end - session_id2->offset); +- pamsshagentauth_buffer_put_char(&b, SSH2_MSG_USERAUTH_TRUST_REQUEST); +- pamsshagentauth_buffer_put_cstring(&b, ruser); +- pamsshagentauth_buffer_put_cstring(&b, "pam_ssh_agent_auth"); +- pamsshagentauth_buffer_put_cstring(&b, "publickey"); +- pamsshagentauth_buffer_put_char(&b, 1); +- pamsshagentauth_buffer_put_cstring(&b, pkalg); +- pamsshagentauth_buffer_put_string(&b, pkblob, blen); ++ if ((r = sshbuf_put_string(b, sshbuf_ptr(session_id2), sshbuf_len(session_id2))) != 0 || ++ (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_TRUST_REQUEST)) != 0 || ++ (r = sshbuf_put_cstring(b, ruser)) != 0 || ++ (r = sshbuf_put_cstring(b, "pam_ssh_agent_auth")) != 0 || ++ (r = sshbuf_put_cstring(b, "publickey")) != 0 || ++ (r = sshbuf_put_u8(b, 1)) != 0 || ++ (r = sshbuf_put_cstring(b, pkalg)) != 0 || ++ (r = sshbuf_put_string(b, pkblob, blen)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + +- if(ssh_agent_sign(id->ac, id->key, &sig, &slen, pamsshagentauth_buffer_ptr(&b), pamsshagentauth_buffer_len(&b)) != 0) ++ if (ssh_agent_sign(id->ac, id->key, &sig, &slen, sshbuf_ptr(b), sshbuf_len(b)) != 0) + goto user_auth_clean_exit; + + /* test for correct signature */ +- if(pamsshagentauth_key_verify(id->key, sig, slen, pamsshagentauth_buffer_ptr(&b), pamsshagentauth_buffer_len(&b)) == 1) ++ if (sshkey_verify(id->key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), NULL, 0) == 0) + authenticated = 1; + + user_auth_clean_exit: + /* if(&b != NULL) */ +- pamsshagentauth_buffer_free(&b); ++ sshbuf_free(b); ++ user_auth_clean_exit_without_buffer: + if(sig != NULL) +- pamsshagentauth_xfree(sig); ++ free(sig); + if(pkblob != NULL) +- pamsshagentauth_xfree(pkblob); ++ free(pkblob); + CRYPTO_cleanup_all_ex_data(); + return authenticated; + } +diff -up openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.h.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.h +--- openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.h.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.h 2018-08-24 10:18:05.010393320 +0200 +@@ -31,7 +31,7 @@ + #ifndef _USERAUTH_PUBKEY_FROM_ID_H + #define _USERAUTH_PUBKEY_FROM_ID_H + +-#include +-int userauth_pubkey_from_id(const char *, Identity *, Buffer *); ++#include "identity.h" ++int userauth_pubkey_from_id(const char *, Identity *, struct sshbuf *); + + #endif +diff -up openssh/pam_ssh_agent_auth-0.10.3/uuencode.c.psaa-compat openssh/pam_ssh_agent_auth-0.10.3/uuencode.c +--- openssh/pam_ssh_agent_auth-0.10.3/uuencode.c.psaa-compat 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/uuencode.c 2018-08-24 10:18:05.010393320 +0200 +@@ -56,7 +56,7 @@ pamsshagentauth_uudecode(const char *src + /* and remove trailing whitespace because __b64_pton needs this */ + *p = '\0'; + len = pamsshagentauth___b64_pton(encoded, target, targsize); +- pamsshagentauth_xfree(encoded); ++ xfree(encoded); + return len; + } + +@@ -70,7 +70,7 @@ pamsshagentauth_dump_base64(FILE *fp, co + fprintf(fp, "dump_base64: len > 65536\n"); + return; + } +- buf = pamsshagentauth_xmalloc(2*len); ++ buf = malloc(2*len); + n = pamsshagentauth_uuencode(data, len, buf, 2*len); + for (i = 0; i < n; i++) { + fprintf(fp, "%c", buf[i]); +@@ -79,5 +79,5 @@ pamsshagentauth_dump_base64(FILE *fp, co + } + if (i % 70 != 69) + fprintf(fp, "\n"); +- pamsshagentauth_xfree(buf); ++ free(buf); + } diff --git a/SOURCES/pam_ssh_agent_auth-0.10.2-dereference.patch b/SOURCES/pam_ssh_agent_auth-0.10.2-dereference.patch new file mode 100644 index 0000000..bf49c37 --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.10.2-dereference.patch @@ -0,0 +1,20 @@ +diff --git a/pam_ssh_agent_auth-0.10.2/pam_user_authorized_keys.c b/pam_ssh_agent_auth-0.10.2/pam_user_authorized_keys.c +--- a/pam_ssh_agent_auth-0.10.2/pam_user_authorized_keys.c ++++ b/pam_ssh_agent_auth-0.10.2/pam_user_authorized_keys.c +@@ -158,11 +158,12 @@ parse_authorized_key_file(const char *user, + int + pam_user_key_allowed(const char *ruser, struct sshkey * key) + { ++ struct passwd *pw; + return +- pamsshagentauth_user_key_allowed2(getpwuid(authorized_keys_file_allowed_owner_uid), +- key, authorized_keys_file) +- || pamsshagentauth_user_key_allowed2(getpwuid(0), key, +- authorized_keys_file) ++ ( (pw = getpwuid(authorized_keys_file_allowed_owner_uid)) && ++ pamsshagentauth_user_key_allowed2(pw, key, authorized_keys_file)) ++ || ((pw = getpwuid(0)) && ++ pamsshagentauth_user_key_allowed2(pw, key, authorized_keys_file)) + || pamsshagentauth_user_key_command_allowed2(authorized_keys_command, + authorized_keys_command_user, + getpwnam(ruser), key); diff --git a/SOURCES/pam_ssh_agent_auth-0.10.3-seteuid.patch b/SOURCES/pam_ssh_agent_auth-0.10.3-seteuid.patch new file mode 100644 index 0000000..be1f8e5 --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.10.3-seteuid.patch @@ -0,0 +1,37 @@ +diff -up openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-seteuid openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c +--- openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-seteuid 2017-02-07 15:41:53.172334151 +0100 ++++ openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c 2017-02-07 15:41:53.174334149 +0100 +@@ -238,17 +238,26 @@ ssh_get_authentication_socket_for_uid(ui + } + + errno = 0; +- seteuid(uid); /* To ensure a race condition is not used to circumvent the stat +- above, we will temporarily drop UID to the caller */ +- if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { ++ /* To ensure a race condition is not used to circumvent the stat ++ above, we will temporarily drop UID to the caller */ ++ if (seteuid(uid) == -1) { + close(sock); +- if(errno == EACCES) +- fatal("MAJOR SECURITY WARNING: uid %lu made a deliberate and malicious attempt to open an agent socket owned by another user", (unsigned long) uid); ++ error("seteuid(%lu) failed with error: %s", ++ (unsigned long) uid, strerror(errno)); + return -1; + } ++ if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { ++ close(sock); ++ sock = -1; ++ if(errno == EACCES) ++ fatal("MAJOR SECURITY WARNING: uid %lu made a deliberate and malicious attempt to open an agent socket owned by another user", (unsigned long) uid); ++ } + +- seteuid(0); /* we now continue the regularly scheduled programming */ +- ++ /* we now continue the regularly scheduled programming */ ++ if (0 != seteuid(0)) { ++ fatal("setuid(0) failed with error: %s", strerror(errno)); ++ return -1; ++ } + return sock; + } + diff --git a/SOURCES/pam_ssh_agent_auth-0.9.2-visibility.patch b/SOURCES/pam_ssh_agent_auth-0.9.2-visibility.patch new file mode 100644 index 0000000..aea068d --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.9.2-visibility.patch @@ -0,0 +1,21 @@ +diff -up openssh-7.1p2/pam_ssh_agent_auth-0.10.2/pam_ssh_agent_auth.c.psaa-visibility openssh-7.1p2/pam_ssh_agent_auth-0.10.2/pam_ssh_agent_auth.c +--- openssh-7.1p2/pam_ssh_agent_auth-0.10.2/pam_ssh_agent_auth.c.psaa-visibility 2014-03-31 19:35:17.000000000 +0200 ++++ openssh-7.1p2/pam_ssh_agent_auth-0.10.2/pam_ssh_agent_auth.c 2016-01-22 15:22:40.984469774 +0100 +@@ -72,7 +72,7 @@ char *__progname; + extern char *__progname; + #endif + +-PAM_EXTERN int ++PAM_EXTERN int __attribute__ ((visibility ("default"))) + pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) + { + char **argv_ptr; +@@ -214,7 +214,7 @@ cleanexit: + } + + +-PAM_EXTERN int ++PAM_EXTERN int __attribute__ ((visibility ("default"))) + pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) + { + UNUSED(pamh); diff --git a/SOURCES/pam_ssh_agent_auth-0.9.3-agent_structure.patch b/SOURCES/pam_ssh_agent_auth-0.9.3-agent_structure.patch new file mode 100644 index 0000000..1f2c02c --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.9.3-agent_structure.patch @@ -0,0 +1,96 @@ +diff -up openssh/pam_ssh_agent_auth-0.10.3/identity.h.psaa-agent openssh/pam_ssh_agent_auth-0.10.3/identity.h +--- openssh/pam_ssh_agent_auth-0.10.3/identity.h.psaa-agent 2016-11-13 04:24:32.000000000 +0100 ++++ openssh/pam_ssh_agent_auth-0.10.3/identity.h 2017-09-27 14:25:49.421739027 +0200 +@@ -38,6 +38,12 @@ + typedef struct identity Identity; + typedef struct idlist Idlist; + ++typedef struct { ++ int fd; ++ struct sshbuf *identities; ++ int howmany; ++} AuthenticationConnection; ++ + struct identity { + TAILQ_ENTRY(identity) next; + AuthenticationConnection *ac; /* set if agent supports key */ +diff -up openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-agent openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c +--- openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-agent 2017-09-27 14:25:49.420739021 +0200 ++++ openssh/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c 2017-09-27 14:25:49.421739027 +0200 +@@ -39,6 +39,7 @@ + #include "sshbuf.h" + #include "sshkey.h" + #include "authfd.h" ++#include "ssherr.h" + #include + #include + #include "ssh2.h" +@@ -291,36 +292,43 @@ pamsshagentauth_find_authorized_keys(con + { + struct sshbuf *session_id2 = NULL; + Identity *id; +- struct sshkey *key; + AuthenticationConnection *ac; +- char *comment; + uint8_t retval = 0; + uid_t uid = getpwnam(ruser)->pw_uid; ++ struct ssh_identitylist *idlist; ++ int r; ++ unsigned int i; + + OpenSSL_add_all_digests(); + pamsshagentauth_session_id2_gen(&session_id2, user, ruser, servicename); + + if ((ac = ssh_get_authentication_connection_for_uid(uid))) { + verbose("Contacted ssh-agent of user %s (%u)", ruser, uid); +- for (key = ssh_get_first_identity(ac, &comment, 2); key != NULL; key = ssh_get_next_identity(ac, &comment, 2)) +- { +- if(key != NULL) { ++ if ((r = ssh_fetch_identitylist(ac->fd, &idlist)) != 0) { ++ if (r != SSH_ERR_AGENT_NO_IDENTITIES) ++ fprintf(stderr, "error fetching identities for " ++ "protocol %d: %s\n", 2, ssh_err(r)); ++ } else { ++ for (i = 0; i < idlist->nkeys; i++) ++ { ++ if (idlist->keys[i] != NULL) { + id = xcalloc(1, sizeof(*id)); +- id->key = key; +- id->filename = comment; ++ id->key = idlist->keys[i]; ++ id->filename = idlist->comments[i]; + id->ac = ac; + if(userauth_pubkey_from_id(ruser, id, session_id2)) { + retval = 1; + } +- free(id->filename); +- key_free(id->key); + free(id); + if(retval == 1) + break; +- } +- } ++ } ++ } +- sshbuf_free(session_id2); +- ssh_close_authentication_connection(ac); ++ sshbuf_free(session_id2); ++ ssh_free_identitylist(idlist); ++ } ++ ssh_close_authentication_socket(ac->fd); ++ free(ac); + } + else { + verbose("No ssh-agent could be contacted"); +diff -up openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c.psaa-agent openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c +--- openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c.psaa-agent 2017-09-27 14:25:49.420739021 +0200 ++++ openssh/pam_ssh_agent_auth-0.10.3/userauth_pubkey_from_id.c 2017-09-27 14:25:49.422739032 +0200 +@@ -84,7 +85,7 @@ userauth_pubkey_from_id(const char *ruse + (r = sshbuf_put_string(b, pkblob, blen)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + +- if (ssh_agent_sign(id->ac, id->key, &sig, &slen, sshbuf_ptr(b), sshbuf_len(b)) != 0) ++ if (ssh_agent_sign(id->ac->fd, id->key, &sig, &slen, sshbuf_ptr(b), sshbuf_len(b), NULL, 0) != 0) + goto user_auth_clean_exit; + + /* test for correct signature */ diff --git a/SOURCES/pam_ssh_agent_auth-0.9.3-build.patch b/SOURCES/pam_ssh_agent_auth-0.9.3-build.patch new file mode 100644 index 0000000..f269b97 --- /dev/null +++ b/SOURCES/pam_ssh_agent_auth-0.9.3-build.patch @@ -0,0 +1,196 @@ +diff -up openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-build openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c +--- openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c.psaa-build 2016-11-13 04:24:32.000000000 +0100 ++++ openssh-7.4p1/pam_ssh_agent_auth-0.10.3/iterate_ssh_agent_keys.c 2017-02-07 14:29:41.626116675 +0100 +@@ -43,12 +43,31 @@ + #include + #include "ssh2.h" + #include "misc.h" ++#include "ssh.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + + #include "userauth_pubkey_from_id.h" + #include "identity.h" + #include "get_command_line.h" + extern char **environ; + ++/* ++ * Added by Jamie Beverly, ensure socket fd points to a socket owned by the user ++ * A cursory check is done, but to avoid race conditions, it is necessary ++ * to drop effective UID when connecting to the socket. ++ * ++ * If the cause of error is EACCES, because we verified we would not have that ++ * problem initially, we can safely assume that somebody is attempting to find a ++ * race condition; so a more "direct" log message is generated. ++ */ ++ + static char * + log_action(char ** action, size_t count) + { +@@ -85,7 +104,7 @@ void + pamsshagentauth_session_id2_gen(Buffer * session_id2, const char * user, + const char * ruser, const char * servicename) + { +- char *cookie = NULL; ++ u_char *cookie = NULL; + uint8_t i = 0; + uint32_t rnd = 0; + uint8_t cookie_len; +@@ -112,7 +131,7 @@ pamsshagentauth_session_id2_gen(Buffer * + if (i % 4 == 0) { + rnd = pamsshagentauth_arc4random(); + } +- cookie[i] = (char) rnd; ++ cookie[i] = (u_char) rnd; + rnd >>= 8; + } + +@@ -177,6 +196,86 @@ pamsshagentauth_session_id2_gen(Buffer * + } + + int ++ssh_get_authentication_socket_for_uid(uid_t uid) ++{ ++ const char *authsocket; ++ int sock; ++ struct sockaddr_un sunaddr; ++ struct stat sock_st; ++ ++ authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); ++ if (!authsocket) ++ return -1; ++ ++ /* Advisory only; seteuid ensures no race condition; but will only log if we see EACCES */ ++ if( stat(authsocket,&sock_st) == 0) { ++ if(uid != 0 && sock_st.st_uid != uid) { ++ fatal("uid %lu attempted to open an agent socket owned by uid %lu", (unsigned long) uid, (unsigned long) sock_st.st_uid); ++ return -1; ++ } ++ } ++ ++ /* ++ * Ensures that the EACCES tested for below can _only_ happen if somebody ++ * is attempting to race the stat above to bypass authentication. ++ */ ++ if( (sock_st.st_mode & S_IWUSR) != S_IWUSR || (sock_st.st_mode & S_IRUSR) != S_IRUSR) { ++ error("ssh-agent socket has incorrect permissions for owner"); ++ return -1; ++ } ++ ++ sunaddr.sun_family = AF_UNIX; ++ strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); ++ ++ sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (sock < 0) ++ return -1; ++ ++ /* close on exec */ ++ if (fcntl(sock, F_SETFD, 1) == -1) { ++ close(sock); ++ return -1; ++ } ++ ++ errno = 0; ++ seteuid(uid); /* To ensure a race condition is not used to circumvent the stat ++ above, we will temporarily drop UID to the caller */ ++ if (connect(sock, (struct sockaddr *)&sunaddr, sizeof sunaddr) < 0) { ++ close(sock); ++ if(errno == EACCES) ++ fatal("MAJOR SECURITY WARNING: uid %lu made a deliberate and malicious attempt to open an agent socket owned by another user", (unsigned long) uid); ++ return -1; ++ } ++ ++ seteuid(0); /* we now continue the regularly scheduled programming */ ++ ++ return sock; ++} ++ ++AuthenticationConnection * ++ssh_get_authentication_connection_for_uid(uid_t uid) ++{ ++ AuthenticationConnection *auth; ++ int sock; ++ ++ sock = ssh_get_authentication_socket_for_uid(uid); ++ ++ /* ++ * Fail if we couldn't obtain a connection. This happens if we ++ * exited due to a timeout. ++ */ ++ if (sock < 0) ++ return NULL; ++ ++ auth = xmalloc(sizeof(*auth)); ++ auth->fd = sock; ++ buffer_init(&auth->identities); ++ auth->howmany = 0; ++ ++ return auth; ++} ++ ++int + pamsshagentauth_find_authorized_keys(const char * user, const char * ruser, const char * servicename) + { + Buffer session_id2 = { 0 }; +@@ -190,7 +289,7 @@ pamsshagentauth_find_authorized_keys(con + OpenSSL_add_all_digests(); + pamsshagentauth_session_id2_gen(&session_id2, user, ruser, servicename); + +- if ((ac = ssh_get_authentication_connection(uid))) { ++ if ((ac = ssh_get_authentication_connection_for_uid(uid))) { + pamsshagentauth_verbose("Contacted ssh-agent of user %s (%u)", ruser, uid); + for (key = ssh_get_first_identity(ac, &comment, 2); key != NULL; key = ssh_get_next_identity(ac, &comment, 2)) + { +diff -up openssh-7.4p1/pam_ssh_agent_auth-0.10.3/Makefile.in.psaa-build openssh-7.4p1/pam_ssh_agent_auth-0.10.3/Makefile.in +--- openssh-7.4p1/pam_ssh_agent_auth-0.10.3/Makefile.in.psaa-build 2016-11-13 04:24:32.000000000 +0100 ++++ openssh-7.4p1/pam_ssh_agent_auth-0.10.3/Makefile.in 2017-02-07 14:40:14.407566921 +0100 +@@ -52,7 +52,7 @@ PATHS= + CC=@CC@ + LD=@LD@ + CFLAGS=@CFLAGS@ +-CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ ++CPPFLAGS=-I.. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ + LIBS=@LIBS@ + AR=@AR@ + AWK=@AWK@ +@@ -61,7 +61,7 @@ INSTALL=@INSTALL@ + PERL=@PERL@ + SED=@SED@ + ENT=@ENT@ +-LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ ++LDFLAGS=-L.. -L../openbsd-compat/ @LDFLAGS@ + LDFLAGS_SHARED = @LDFLAGS_SHARED@ + EXEEXT=@EXEEXT@ + +@@ -74,7 +74,7 @@ SSHOBJS=xmalloc.o atomicio.o authfd.o bu + + ED25519OBJS=ed25519-donna/ed25519.o + +-PAM_SSH_AGENT_AUTH_OBJS=pam_user_key_allowed2.o iterate_ssh_agent_keys.o userauth_pubkey_from_id.o pam_user_authorized_keys.o get_command_line.o ++PAM_SSH_AGENT_AUTH_OBJS=pam_user_key_allowed2.o iterate_ssh_agent_keys.o userauth_pubkey_from_id.o pam_user_authorized_keys.o get_command_line.o secure_filename.o + + + MANPAGES_IN = pam_ssh_agent_auth.pod +@@ -94,13 +94,13 @@ $(PAM_MODULES): Makefile.in config.h + .c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ + +-LIBCOMPAT=openbsd-compat/libopenbsd-compat.a ++LIBCOMPAT=../openbsd-compat/libopenbsd-compat.a + $(LIBCOMPAT): always + (cd openbsd-compat && $(MAKE)) + always: + +-pam_ssh_agent_auth.so: $(LIBCOMPAT) $(SSHOBJS) $(ED25519OBJS) $(PAM_SSH_AGENT_AUTH_OBJS) pam_ssh_agent_auth.o +- $(LD) $(LDFLAGS_SHARED) -o $@ $(SSHOBJS) $(ED25519OBJS) $(PAM_SSH_AGENT_AUTH_OBJS) $(LDFLAGS) -lopenbsd-compat pam_ssh_agent_auth.o $(LIBS) -lpam ++pam_ssh_agent_auth.so: $(PAM_SSH_AGENT_AUTH_OBJS) pam_ssh_agent_auth.o ../uidswap.o ++ $(LD) $(LDFLAGS_SHARED) -o $@ $(PAM_SSH_AGENT_AUTH_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat pam_ssh_agent_auth.o ../uidswap.o $(LIBS) -lpam + + $(MANPAGES): $(MANPAGES_IN) + pod2man --section=8 --release=v0.10.3 --name=pam_ssh_agent_auth --official --center "PAM" pam_ssh_agent_auth.pod > pam_ssh_agent_auth.8 diff --git a/SOURCES/ssh-keycat.pam b/SOURCES/ssh-keycat.pam new file mode 100644 index 0000000..d7a3f67 --- /dev/null +++ b/SOURCES/ssh-keycat.pam @@ -0,0 +1,8 @@ +#%PAM-1.0 +# pam_selinux.so close should be the first session rule +session required pam_selinux.so close +session required pam_loginuid.so +# pam_selinux.so open should only be followed by sessions to be executed in the user context +session required pam_selinux.so open env_params +session required pam_namespace.so + diff --git a/SOURCES/sshd-keygen b/SOURCES/sshd-keygen new file mode 100644 index 0000000..141814c --- /dev/null +++ b/SOURCES/sshd-keygen @@ -0,0 +1,40 @@ +#!/bin/bash + +# Create the host keys for the OpenSSH server. +KEYTYPE=$1 +case $KEYTYPE in + "dsa") ;& # disabled in FIPS + "ed25519") + FIPS=/proc/sys/crypto/fips_enabled + if [[ -r "$FIPS" && $(cat $FIPS) == "1" ]]; then + exit 0 + fi ;; + "rsa") ;; # always ok + "ecdsa") ;; + *) # wrong argument + exit 12 ;; +esac +KEY=/etc/ssh/ssh_host_${KEYTYPE}_key + +KEYGEN=/usr/bin/ssh-keygen +if [[ ! -x $KEYGEN ]]; then + exit 13 +fi + +# remove old keys +rm -f $KEY{,.pub} + +# create new keys +if ! $KEYGEN -q -t $KEYTYPE -f $KEY -C '' -N '' >&/dev/null; then + exit 1 +fi + +# sanitize permissions +/usr/bin/chgrp ssh_keys $KEY +/usr/bin/chmod 640 $KEY +/usr/bin/chmod 644 $KEY.pub +if [[ -x /usr/sbin/restorecon ]]; then + /usr/sbin/restorecon $KEY{,.pub} +fi + +exit 0 diff --git a/SOURCES/sshd-keygen.target b/SOURCES/sshd-keygen.target new file mode 100644 index 0000000..9efb4e2 --- /dev/null +++ b/SOURCES/sshd-keygen.target @@ -0,0 +1,5 @@ +[Unit] +Wants=sshd-keygen@rsa.service +Wants=sshd-keygen@ecdsa.service +Wants=sshd-keygen@ed25519.service +PartOf=sshd.service diff --git a/SOURCES/sshd-keygen@.service b/SOURCES/sshd-keygen@.service new file mode 100644 index 0000000..f27f729 --- /dev/null +++ b/SOURCES/sshd-keygen@.service @@ -0,0 +1,11 @@ +[Unit] +Description=OpenSSH %i Server Key Generation +ConditionFileNotEmpty=|!/etc/ssh/ssh_host_%i_key + +[Service] +Type=oneshot +EnvironmentFile=-/etc/sysconfig/sshd +ExecStart=/usr/libexec/openssh/sshd-keygen %i + +[Install] +WantedBy=sshd-keygen.target diff --git a/SOURCES/sshd.pam b/SOURCES/sshd.pam new file mode 100644 index 0000000..780f62e --- /dev/null +++ b/SOURCES/sshd.pam @@ -0,0 +1,17 @@ +#%PAM-1.0 +auth substack password-auth +auth include postlogin +account required pam_sepermit.so +account required pam_nologin.so +account include password-auth +password include password-auth +# pam_selinux.so close should be the first session rule +session required pam_selinux.so close +session required pam_loginuid.so +# pam_selinux.so open should only be followed by sessions to be executed in the user context +session required pam_selinux.so open env_params +session required pam_namespace.so +session optional pam_keyinit.so force revoke +session optional pam_motd.so +session include password-auth +session include postlogin diff --git a/SOURCES/sshd.service b/SOURCES/sshd.service new file mode 100644 index 0000000..f9a12a8 --- /dev/null +++ b/SOURCES/sshd.service @@ -0,0 +1,18 @@ +[Unit] +Description=OpenSSH server daemon +Documentation=man:sshd(8) man:sshd_config(5) +After=network.target sshd-keygen.target +Wants=sshd-keygen.target + +[Service] +Type=notify +EnvironmentFile=-/etc/crypto-policies/back-ends/opensshserver.config +EnvironmentFile=-/etc/sysconfig/sshd +ExecStart=/usr/sbin/sshd -D $OPTIONS $CRYPTO_POLICY +ExecReload=/bin/kill -HUP $MAINPID +KillMode=process +Restart=on-failure +RestartSec=42s + +[Install] +WantedBy=multi-user.target diff --git a/SOURCES/sshd.socket b/SOURCES/sshd.socket new file mode 100644 index 0000000..caa50c4 --- /dev/null +++ b/SOURCES/sshd.socket @@ -0,0 +1,11 @@ +[Unit] +Description=OpenSSH Server Socket +Documentation=man:sshd(8) man:sshd_config(5) +Conflicts=sshd.service + +[Socket] +ListenStream=22 +Accept=yes + +[Install] +WantedBy=sockets.target diff --git a/SOURCES/sshd.sysconfig b/SOURCES/sshd.sysconfig new file mode 100644 index 0000000..6c2bff9 --- /dev/null +++ b/SOURCES/sshd.sysconfig @@ -0,0 +1,11 @@ +# Configuration file for the sshd service. + +# The server keys are automatically generated if they are missing. +# To change the automatic creation, adjust sshd.service options for +# example using systemctl enable sshd-keygen@dsa.service to allow creation +# of DSA key or systemctl mask sshd-keygen@rsa.service to disable RSA key +# creation. + +# System-wide crypto policy: +# To opt-out, uncomment the following line +# CRYPTO_POLICY= diff --git a/SOURCES/sshd.tmpfiles b/SOURCES/sshd.tmpfiles new file mode 100644 index 0000000..c35a2b8 --- /dev/null +++ b/SOURCES/sshd.tmpfiles @@ -0,0 +1 @@ +d /var/empty/sshd 711 root root - diff --git a/SOURCES/sshd@.service b/SOURCES/sshd@.service new file mode 100644 index 0000000..9b8e012 --- /dev/null +++ b/SOURCES/sshd@.service @@ -0,0 +1,11 @@ +[Unit] +Description=OpenSSH per-connection server daemon +Documentation=man:sshd(8) man:sshd_config(5) +Wants=sshd-keygen.target +After=sshd-keygen.target + +[Service] +EnvironmentFile=-/etc/crypto-policies/back-ends/opensshserver.config +EnvironmentFile=-/etc/sysconfig/sshd +ExecStart=-/usr/sbin/sshd -i $OPTIONS $CRYPTO_POLICY +StandardInput=socket diff --git a/SPECS/openssh.spec b/SPECS/openssh.spec new file mode 100644 index 0000000..0f39971 --- /dev/null +++ b/SPECS/openssh.spec @@ -0,0 +1,2817 @@ +# Do we want SELinux & Audit +%if 0%{?!noselinux:1} +%global WITH_SELINUX 1 +%else +%global WITH_SELINUX 0 +%endif + +%global _hardened_build 1 + +# OpenSSH privilege separation requires a user & group ID +%global sshd_uid 74 +%global sshd_gid 74 + +# Do we want to disable building of gnome-askpass? (1=yes 0=no) +%global no_gnome_askpass 0 + +# Do we want to link against a static libcrypto? (1=yes 0=no) +%global static_libcrypto 0 + +# Use GTK2 instead of GNOME in gnome-ssh-askpass +%global gtk2 1 + +# Build position-independent executables (requires toolchain support)? +%global pie 1 + +# Do we want kerberos5 support (1=yes 0=no) +%global kerberos5 1 + +# Do we want libedit support +%global libedit 1 + +# Do we want LDAP support +%global ldap 1 + +# Whether to build pam_ssh_agent_auth +%if 0%{?!nopam:1} +%global pam_ssh_agent 1 +%else +%global pam_ssh_agent 0 +%endif + +# Reserve options to override askpass settings with: +# rpm -ba|--rebuild --define 'skip_xxx 1' +%{?skip_gnome_askpass:%global no_gnome_askpass 1} + +# Add option to build without GTK2 for older platforms with only GTK+. +# Red Hat Linux <= 7.2 and Red Hat Advanced Server 2.1 are examples. +# rpm -ba|--rebuild --define 'no_gtk2 1' +%{?no_gtk2:%global gtk2 0} + +# Options for static OpenSSL link: +# rpm -ba|--rebuild --define "static_openssl 1" +%{?static_openssl:%global static_libcrypto 1} + +# Is this a build for the rescue CD (without PAM, with MD5)? (1=yes 0=no) +%global rescue 0 +%{?build_rescue:%global rescue 1} +%{?build_rescue:%global rescue_rel rescue} + +# Turn off some stuff for resuce builds +%if %{rescue} +%global kerberos5 0 +%global libedit 0 +%global pam_ssh_agent 0 +%endif + +# Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 +%global openssh_ver 7.8p1 +%global openssh_rel 4 +%global pam_ssh_agent_ver 0.10.3 +%global pam_ssh_agent_rel 5 + +Summary: An open source implementation of SSH protocol version 2 +Name: openssh +Version: %{openssh_ver} +Release: %{openssh_rel}%{?dist}%{?rescue_rel} +URL: http://www.openssh.com/portable.html +#URL1: http://pamsshagentauth.sourceforge.net +Source0: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz +Source1: ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc +Source2: sshd.pam +Source3: DJM-GPG-KEY.gpg +Source4: http://prdownloads.sourceforge.net/pamsshagentauth/pam_ssh_agent_auth/pam_ssh_agent_auth-%{pam_ssh_agent_ver}.tar.bz2 +Source5: pam_ssh_agent-rmheaders +Source6: ssh-keycat.pam +Source7: sshd.sysconfig +Source9: sshd@.service +Source10: sshd.socket +Source11: sshd.service +Source12: sshd-keygen@.service +Source13: sshd-keygen +Source14: sshd.tmpfiles +Source15: sshd-keygen.target + +# Internal debug +Patch0: openssh-5.9p1-wIm.patch + +#https://bugzilla.mindrot.org/show_bug.cgi?id=2581 +Patch100: openssh-6.7p1-coverity.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1894 +#https://bugzilla.redhat.com/show_bug.cgi?id=735889 +#Patch102: openssh-5.8p1-getaddrinfo.patch +# OpenSSL 1.1.0 compatibility +Patch104: openssh-7.3p1-openssl-1.1.0.patch + +#https://bugzilla.mindrot.org/show_bug.cgi?id=1402 +# https://bugzilla.redhat.com/show_bug.cgi?id=1171248 +# record pfs= field in CRYPTO_SESSION audit event +Patch200: openssh-7.6p1-audit.patch +# Audit race condition in forked child (#1310684) +Patch201: openssh-7.1p2-audit-race-condition.patch + +# --- pam_ssh-agent --- +# make it build reusing the openssh sources +Patch300: pam_ssh_agent_auth-0.9.3-build.patch +# check return value of seteuid() +# https://sourceforge.net/p/pamsshagentauth/bugs/23/ +Patch301: pam_ssh_agent_auth-0.10.3-seteuid.patch +# explicitly make pam callbacks visible +Patch302: pam_ssh_agent_auth-0.9.2-visibility.patch +# update to current version of agent structure +Patch305: pam_ssh_agent_auth-0.9.3-agent_structure.patch +# remove prefixes to be able to build against current openssh library +Patch306: pam_ssh_agent_auth-0.10.2-compat.patch +# Fix NULL dereference from getpwuid() return value +# https://sourceforge.net/p/pamsshagentauth/bugs/22/ +Patch307: pam_ssh_agent_auth-0.10.2-dereference.patch + +#https://bugzilla.mindrot.org/show_bug.cgi?id=1641 (WONTFIX) +Patch400: openssh-7.8p1-role-mls.patch +#https://bugzilla.redhat.com/show_bug.cgi?id=781634 +Patch404: openssh-6.6p1-privsep-selinux.patch + +#?-- unwanted child :( +Patch501: openssh-6.7p1-ldap.patch +#? +Patch502: openssh-6.6p1-keycat.patch + +#https://bugzilla.mindrot.org/show_bug.cgi?id=1644 +Patch601: openssh-6.6p1-allow-ip-opts.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1893 (WONTFIX) +Patch604: openssh-6.6p1-keyperm.patch +#(drop?) https://bugzilla.mindrot.org/show_bug.cgi?id=1925 +Patch606: openssh-5.9p1-ipv6man.patch +#? +Patch607: openssh-5.8p2-sigpipe.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1789 +Patch609: openssh-7.2p2-x11.patch + +#? +Patch700: openssh-7.7p1-fips.patch +#? +Patch702: openssh-5.1p1-askpass-progress.patch +#https://bugzilla.redhat.com/show_bug.cgi?id=198332 +Patch703: openssh-4.3p2-askpass-grab-info.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1635 (WONTFIX) +Patch707: openssh-7.7p1-redhat.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1640 (WONTFIX) +Patch709: openssh-6.2p1-vendor.patch +# warn users for unsupported UsePAM=no (#757545) +Patch711: openssh-7.8p1-UsePAM-warning.patch +# make aes-ctr ciphers use EVP engines such as AES-NI from OpenSSL +Patch712: openssh-6.3p1-ctr-evp-fast.patch +# add cavs test binary for the aes-ctr +Patch713: openssh-6.6p1-ctr-cavstest.patch +# add SSH KDF CAVS test driver +Patch714: openssh-6.7p1-kdf-cavs.patch + + +#http://www.sxw.org.uk/computing/patches/openssh.html +#changed cache storage type - #848228 +Patch800: openssh-7.8p1-gsskex.patch +#http://www.mail-archive.com/kerberos@mit.edu/msg17591.html +Patch801: openssh-6.6p1-force_krb.patch +# add new option GSSAPIEnablek5users and disable using ~/.k5users by default (#1169843) +# CVE-2014-9278 +Patch802: openssh-6.6p1-GSSAPIEnablek5users.patch +# Documentation about GSSAPI +# from https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=765655 +Patch803: openssh-7.1p1-gssapi-documentation.patch +# Improve ccache handling in openssh (#991186, #1199363, #1566494) +# https://bugzilla.mindrot.org/show_bug.cgi?id=2775 +Patch804: openssh-7.7p1-gssapi-new-unique.patch +# Respect k5login_directory option in krk5.conf (#1328243) +Patch805: openssh-7.2p2-k5login_directory.patch +# Support SHA2 in GSS key exchanges from draft-ssorce-gss-keyex-sha2-02 +Patch807: openssh-7.5p1-gssapi-kex-with-ec.patch +# Do not break when using AuthenticationMethods with gssapi-keyex auth method (#1625366) +Patch808: openssh-7.9p1-gsskex-method.patch + +Patch900: openssh-6.1p1-gssapi-canohost.patch +#https://bugzilla.mindrot.org/show_bug.cgi?id=1780 +Patch901: openssh-6.6p1-kuserok.patch +# Use tty allocation for a remote scp (#985650) +Patch906: openssh-6.4p1-fromto-remote.patch +# privsep_preauth: use SELinux context from selinux-policy (#1008580) +Patch916: openssh-6.6.1p1-selinux-contexts.patch +# log via monitor in chroots without /dev/log (#2681) +Patch918: openssh-6.6.1p1-log-in-chroot.patch +# scp file into non-existing directory (#1142223) +Patch919: openssh-6.6.1p1-scp-non-existing-directory.patch +# Config parser shouldn't accept ip/port syntax (#1130733) +Patch920: openssh-7.8p1-ip-port-config-parser.patch +# apply upstream patch and make sshd -T more consistent (#1187521) +Patch922: openssh-6.8p1-sshdT-output.patch +# Add sftp option to force mode of created files (#1191055) +Patch926: openssh-6.7p1-sftp-force-permission.patch +# Restore compatible default (#89216) +Patch929: openssh-6.9p1-permit-root-login.patch +# Add GSSAPIKexAlgorithms option for server and client application +Patch932: openssh-7.0p1-gssKexAlgorithms.patch +# make s390 use /dev/ crypto devices -- ignore closefrom +Patch939: openssh-7.2p2-s390-closefrom.patch +# Move MAX_DISPLAYS to a configuration option (#1341302) +Patch944: openssh-7.3p1-x11-max-displays.patch +# Help systemd to track the running service +Patch948: openssh-7.4p1-systemd.patch +# Pass inetd flags for SELinux down to openbsd compat level +Patch949: openssh-7.6p1-cleanup-selinux.patch +# Sandbox adjustments for s390 and audit +Patch950: openssh-7.5p1-sandbox.patch +# PKCS#11 URIs (upstream #2817, 2nd iteration) +Patch951: openssh-7.6p1-pkcs11-uri.patch +# PKCS#11 ECDSA keys (upstream #2474, 8th iteration) +Patch952: openssh-7.6p1-pkcs11-ecdsa.patch +# Unbreak scp between two IPv6 hosts (#1620333) +Patch953: openssh-7.8p1-scp-ipv6.patch +# Allow to disable RSA signatures with SHA-1 in server +# https://bugzilla.mindrot.org/show_bug.cgi?id=2746 +Patch954: openssh-7.9p1-disable-sha1.patch + +License: BSD +Group: Applications/Internet +Requires: /sbin/nologin +Obsoletes: openssh-clients-fips, openssh-server-fips +Obsoletes: openssh-server-sysvinit + +%if ! %{no_gnome_askpass} +%if %{gtk2} +BuildRequires: gtk2-devel +BuildRequires: libX11-devel +%else +BuildRequires: gnome-libs-devel +%endif +%endif + +%if %{ldap} +BuildRequires: openldap-devel +%endif +BuildRequires: autoconf, automake, perl-interpreter, perl-generators, zlib-devel +BuildRequires: audit-libs-devel >= 2.0.5 +BuildRequires: util-linux, groff +BuildRequires: pam-devel +BuildRequires: fipscheck-devel >= 1.3.0 +BuildRequires: openssl-devel >= 0.9.8j +BuildRequires: perl-podlators +BuildRequires: systemd-devel +BuildRequires: gcc +BuildRequires: p11-kit-devel +Recommends: p11-kit + +%if %{kerberos5} +BuildRequires: krb5-devel +%endif + +%if %{libedit} +BuildRequires: libedit-devel ncurses-devel +%endif + +%if %{WITH_SELINUX} +Requires: libselinux >= 2.3-5 +BuildRequires: libselinux-devel >= 2.3-5 +Requires: audit-libs >= 1.0.8 +BuildRequires: audit-libs >= 1.0.8 +%endif + +BuildRequires: xauth +# for tarball signature verification +BuildRequires: gnupg2 + +%package clients +Summary: An open source SSH client applications +Group: Applications/Internet +Requires: openssh = %{version}-%{release} +Requires: fipscheck-lib%{_isa} >= 1.3.0 +Requires: crypto-policies >= 20180306-1 + +%package server +Summary: An open source SSH server daemon +Group: System Environment/Daemons +Requires: openssh = %{version}-%{release} +Requires(pre): /usr/sbin/useradd +Requires: pam >= 1.0.1-3 +Requires: fipscheck-lib%{_isa} >= 1.3.0 +Requires: crypto-policies >= 20180306-1 +%{?systemd_requires} + +%if %{ldap} +%package ldap +Summary: A LDAP support for open source SSH server daemon +Requires: openssh = %{version}-%{release} +Group: System Environment/Daemons +%endif + +%package keycat +Summary: A mls keycat backend for openssh +Requires: openssh = %{version}-%{release} +Group: System Environment/Daemons + +%package askpass +Summary: A passphrase dialog for OpenSSH and X +Group: Applications/Internet +Requires: openssh = %{version}-%{release} +Obsoletes: openssh-askpass-gnome +Provides: openssh-askpass-gnome + +%package cavs +Summary: CAVS tests for FIPS validation +Group: Applications/Internet +Requires: openssh = %{version}-%{release} + +%package -n pam_ssh_agent_auth +Summary: PAM module for authentication with ssh-agent +Group: System Environment/Base +Version: %{pam_ssh_agent_ver} +Release: %{pam_ssh_agent_rel}.%{openssh_rel}%{?dist}%{?rescue_rel} +License: BSD + +%description +SSH (Secure SHell) is a program for logging into and executing +commands on a remote machine. SSH is intended to replace rlogin and +rsh, and to provide secure encrypted communications between two +untrusted hosts over an insecure network. X11 connections and +arbitrary TCP/IP ports can also be forwarded over the secure channel. + +OpenSSH is OpenBSD's version of the last free version of SSH, bringing +it up to date in terms of security and features. + +This package includes the core files necessary for both the OpenSSH +client and server. To make this package useful, you should also +install openssh-clients, openssh-server, or both. + +%description clients +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package includes +the clients necessary to make encrypted connections to SSH servers. + +%description server +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package contains +the secure shell daemon (sshd). The sshd daemon allows SSH clients to +securely connect to your SSH server. + +%if %{ldap} +%description ldap +OpenSSH LDAP backend is a way how to distribute the authorized tokens +among the servers in the network. +%endif + +%description keycat +OpenSSH mls keycat is backend for using the authorized keys in the +openssh in the mls mode. + +%description askpass +OpenSSH is a free version of SSH (Secure SHell), a program for logging +into and executing commands on a remote machine. This package contains +an X11 passphrase dialog for OpenSSH. + +%description cavs +This package contains test binaries and scripts to make FIPS validation +easier. Now contains CTR and KDF CAVS test driver. + +%description -n pam_ssh_agent_auth +This package contains a PAM module which can be used to authenticate +users using ssh keys stored in a ssh-agent. Through the use of the +forwarding of ssh-agent connection it also allows to authenticate with +remote ssh-agent instance. + +The module is most useful for su and sudo service stacks. + +%prep +gpgv2 --quiet --keyring %{SOURCE3} %{SOURCE1} %{SOURCE0} +%setup -q -a 4 +#Do not enable by default +%if 0 +%patch0 -p1 -b .wIm +%endif + +# investigate %%patch102 -p1 -b .getaddrinfo + +%if %{pam_ssh_agent} +pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver} +%patch300 -p2 -b .psaa-build +%patch301 -p2 -b .psaa-seteuid +%patch302 -p2 -b .psaa-visibility +%patch306 -p2 -b .psaa-compat +%patch305 -p2 -b .psaa-agent +%patch307 -p2 -b .psaa-deref +# Remove duplicate headers and library files +rm -f $(cat %{SOURCE5}) +popd +%endif + +%patch400 -p1 -b .role-mls +%patch404 -p1 -b .privsep-selinux + +%if %{ldap} +%patch501 -p1 -b .ldap +%endif +%patch502 -p1 -b .keycat + +%patch601 -p1 -b .ip-opts +%patch604 -p1 -b .keyperm +%patch606 -p1 -b .ipv6man +%patch607 -p1 -b .sigpipe +%patch609 -p1 -b .x11 +%patch702 -p1 -b .progress +%patch703 -p1 -b .grab-info +%patch707 -p1 -b .redhat +%patch709 -p1 -b .vendor +%patch711 -p1 -b .log-usepam-no +%patch712 -p1 -b .evp-ctr +%patch713 -p1 -b .ctr-cavs +%patch714 -p1 -b .kdf-cavs +# +%patch800 -p1 -b .gsskex +%patch801 -p1 -b .force_krb +%patch803 -p1 -b .gss-docs +%patch804 -p1 -b .ccache_name +%patch805 -p1 -b .k5login +# +%patch900 -p1 -b .canohost +%patch901 -p1 -b .kuserok +%patch906 -p1 -b .fromto-remote +%patch916 -p1 -b .contexts +%patch918 -p1 -b .log-in-chroot +%patch919 -p1 -b .scp +%patch920 -p1 -b .config +%patch802 -p1 -b .GSSAPIEnablek5users +%patch922 -p1 -b .sshdt +%patch926 -p1 -b .sftp-force-mode +%patch929 -p1 -b .root-login +%patch932 -p1 -b .gsskexalg +%patch939 -p1 -b .s390-dev +%patch944 -p1 -b .x11max +%patch948 -p1 -b .systemd +%patch807 -p1 -b .gsskex-ec +%patch949 -p1 -b .refactor +%patch950 -p1 -b .sandbox +%patch951 -p1 -b .pkcs11-uri +%patch952 -p1 -b .pkcs11-ecdsa +%patch953 -p1 -b .scp-ipv6 +%patch808 -p1 -b .gsskex-method +%patch954 -p1 -b .disable-sha1 + +%patch200 -p1 -b .audit +%patch201 -p1 -b .audit-race +%patch700 -p1 -b .fips + +%patch100 -p1 -b .coverity +%patch104 -p1 -b .openssl + +%if 0 +# Nothing here yet +%endif + +autoreconf +pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver} +autoreconf +popd + +%build +# the -fvisibility=hidden is needed for clean build of the pam_ssh_agent_auth +# and it makes the ssh build more clean and even optimized better +CFLAGS="$RPM_OPT_FLAGS -fvisibility=hidden"; export CFLAGS +%if %{rescue} +CFLAGS="$CFLAGS -Os" +%endif +%if %{pie} +%ifarch s390 s390x sparc sparcv9 sparc64 +CFLAGS="$CFLAGS -fPIC" +%else +CFLAGS="$CFLAGS -fpic" +%endif +SAVE_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -pie -z relro -z now" + +export CFLAGS +export LDFLAGS + +%endif +%if %{kerberos5} +if test -r /etc/profile.d/krb5-devel.sh ; then + source /etc/profile.d/krb5-devel.sh +fi +krb5_prefix=`krb5-config --prefix` +if test "$krb5_prefix" != "%{_prefix}" ; then + CPPFLAGS="$CPPFLAGS -I${krb5_prefix}/include -I${krb5_prefix}/include/gssapi"; export CPPFLAGS + CFLAGS="$CFLAGS -I${krb5_prefix}/include -I${krb5_prefix}/include/gssapi" + LDFLAGS="$LDFLAGS -L${krb5_prefix}/%{_lib}"; export LDFLAGS +else + krb5_prefix= + CPPFLAGS="-I%{_includedir}/gssapi"; export CPPFLAGS + CFLAGS="$CFLAGS -I%{_includedir}/gssapi" +fi +%endif + +%configure \ + --sysconfdir=%{_sysconfdir}/ssh \ + --libexecdir=%{_libexecdir}/openssh \ + --datadir=%{_datadir}/openssh \ + --with-default-path=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin \ + --with-superuser-path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \ + --with-privsep-path=%{_var}/empty/sshd \ + --enable-vendor-patchlevel="FC-%{openssh_ver}-%{openssh_rel}" \ + --disable-strip \ + --without-zlib-version-check \ + --with-ssl-engine \ + --with-ipaddr-display \ + --with-pie=no \ + --without-hardening `# The hardening flags are configured by system` \ + --with-systemd \ + --with-default-pkcs11-provider=yes \ +%if %{ldap} + --with-ldap \ +%endif +%if %{rescue} + --without-pam \ +%else + --with-pam \ +%endif +%if %{WITH_SELINUX} + --with-selinux --with-audit=linux \ + --with-sandbox=seccomp_filter \ +%endif +%if %{kerberos5} + --with-kerberos5${krb5_prefix:+=${krb5_prefix}} \ +%else + --without-kerberos5 \ +%endif +%if %{libedit} + --with-libedit +%else + --without-libedit +%endif + +%if %{static_libcrypto} +perl -pi -e "s|-lcrypto|%{_libdir}/libcrypto.a|g" Makefile +%endif + +make + +# Define a variable to toggle gnome1/gtk2 building. This is necessary +# because RPM doesn't handle nested %%if statements. +%if %{gtk2} + gtk2=yes +%else + gtk2=no +%endif + +%if ! %{no_gnome_askpass} +pushd contrib +if [ $gtk2 = yes ] ; then + CFLAGS="$CFLAGS %{?__global_ldflags}" \ + make gnome-ssh-askpass2 + mv gnome-ssh-askpass2 gnome-ssh-askpass +else + CFLAGS="$CFLAGS %{?__global_ldflags}" + make gnome-ssh-askpass1 + mv gnome-ssh-askpass1 gnome-ssh-askpass +fi +popd +%endif + +%if %{pam_ssh_agent} +pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver} +LDFLAGS="$SAVE_LDFLAGS" +%configure --with-selinux --libexecdir=/%{_libdir}/security --with-mantype=man +make +popd +%endif + +# Add generation of HMAC checksums of the final stripped binaries +%global __spec_install_post \ + %%{?__debug_package:%%{__debug_install_post}} \ + %%{__arch_install_post} \ + %%{__os_install_post} \ + fipshmac -d $RPM_BUILD_ROOT%{_libdir}/fipscheck $RPM_BUILD_ROOT%{_bindir}/ssh $RPM_BUILD_ROOT%{_sbindir}/sshd \ +%{nil} + +%check +#to run tests use "--with check" +%if %{?_with_check:1}%{!?_with_check:0} +make tests +%endif + +%install +rm -rf $RPM_BUILD_ROOT +mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/ssh +mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/ssh/ssh_config.d +mkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/openssh +mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshd +make install DESTDIR=$RPM_BUILD_ROOT +rm -f $RPM_BUILD_ROOT%{_sysconfdir}/ssh/ldap.conf + +install -d $RPM_BUILD_ROOT/etc/pam.d/ +install -d $RPM_BUILD_ROOT/etc/sysconfig/ +install -d $RPM_BUILD_ROOT%{_libexecdir}/openssh +install -d $RPM_BUILD_ROOT%{_libdir}/fipscheck +install -m644 %{SOURCE2} $RPM_BUILD_ROOT/etc/pam.d/sshd +install -m644 %{SOURCE6} $RPM_BUILD_ROOT/etc/pam.d/ssh-keycat +install -m644 %{SOURCE7} $RPM_BUILD_ROOT/etc/sysconfig/sshd +install -m644 ssh_config_redhat $RPM_BUILD_ROOT/etc/ssh/ssh_config.d/05-redhat.conf +install -d -m755 $RPM_BUILD_ROOT/%{_unitdir} +install -m644 %{SOURCE9} $RPM_BUILD_ROOT/%{_unitdir}/sshd@.service +install -m644 %{SOURCE10} $RPM_BUILD_ROOT/%{_unitdir}/sshd.socket +install -m644 %{SOURCE11} $RPM_BUILD_ROOT/%{_unitdir}/sshd.service +install -m644 %{SOURCE12} $RPM_BUILD_ROOT/%{_unitdir}/sshd-keygen@.service +install -m644 %{SOURCE15} $RPM_BUILD_ROOT/%{_unitdir}/sshd-keygen.target +install -m744 %{SOURCE13} $RPM_BUILD_ROOT/%{_libexecdir}/openssh/sshd-keygen +install -m755 contrib/ssh-copy-id $RPM_BUILD_ROOT%{_bindir}/ +install contrib/ssh-copy-id.1 $RPM_BUILD_ROOT%{_mandir}/man1/ +install -m644 -D %{SOURCE14} $RPM_BUILD_ROOT%{_tmpfilesdir}/%{name}.conf + +%if ! %{no_gnome_askpass} +install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass +%endif + +%if ! %{no_gnome_askpass} +ln -s gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/ssh-askpass +install -m 755 -d $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +install -m 755 contrib/redhat/gnome-ssh-askpass.csh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +install -m 755 contrib/redhat/gnome-ssh-askpass.sh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ +%endif + +%if %{no_gnome_askpass} +rm -f $RPM_BUILD_ROOT/etc/profile.d/gnome-ssh-askpass.* +%endif + +perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/* + +%if %{pam_ssh_agent} +pushd pam_ssh_agent_auth-%{pam_ssh_agent_ver} +make install DESTDIR=$RPM_BUILD_ROOT +popd +%endif +%pre +getent group ssh_keys >/dev/null || groupadd -r ssh_keys || : + +%pre server +getent group sshd >/dev/null || groupadd -g %{sshd_uid} -r sshd || : +getent passwd sshd >/dev/null || \ + useradd -c "Privilege-separated SSH" -u %{sshd_uid} -g sshd \ + -s /sbin/nologin -r -d /var/empty/sshd sshd 2> /dev/null || : + +%post server +%systemd_post sshd.service sshd.socket + +%preun server +%systemd_preun sshd.service sshd.socket + +%postun server +%systemd_postun_with_restart sshd.service + +%files +%license LICENCE +%doc CREDITS ChangeLog INSTALL OVERVIEW PROTOCOL* README README.platform README.privsep README.tun README.dns TODO +%attr(0755,root,root) %dir %{_sysconfdir}/ssh +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli +%if ! %{rescue} +%attr(0755,root,root) %{_bindir}/ssh-keygen +%attr(0644,root,root) %{_mandir}/man1/ssh-keygen.1* +%attr(0755,root,root) %dir %{_libexecdir}/openssh +%attr(2555,root,ssh_keys) %{_libexecdir}/openssh/ssh-keysign +%attr(0644,root,root) %{_mandir}/man8/ssh-keysign.8* +%endif + +%files clients +%attr(0755,root,root) %{_bindir}/ssh +%attr(0644,root,root) %{_libdir}/fipscheck/ssh.hmac +%attr(0644,root,root) %{_mandir}/man1/ssh.1* +%attr(0755,root,root) %{_bindir}/scp +%attr(0644,root,root) %{_mandir}/man1/scp.1* +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config +%dir %attr(0755,root,root) %{_sysconfdir}/ssh/ssh_config.d/ +%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config.d/05-redhat.conf +%attr(0644,root,root) %{_mandir}/man5/ssh_config.5* +%if ! %{rescue} +%attr(0755,root,root) %{_bindir}/ssh-agent +%attr(0755,root,root) %{_bindir}/ssh-add +%attr(0755,root,root) %{_bindir}/ssh-keyscan +%attr(0755,root,root) %{_bindir}/sftp +%attr(0755,root,root) %{_bindir}/ssh-copy-id +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-pkcs11-helper +%attr(0644,root,root) %{_mandir}/man1/ssh-agent.1* +%attr(0644,root,root) %{_mandir}/man1/ssh-add.1* +%attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1* +%attr(0644,root,root) %{_mandir}/man1/sftp.1* +%attr(0644,root,root) %{_mandir}/man1/ssh-copy-id.1* +%attr(0644,root,root) %{_mandir}/man8/ssh-pkcs11-helper.8* +%endif + +%if ! %{rescue} +%files server +%dir %attr(0711,root,root) %{_var}/empty/sshd +%attr(0755,root,root) %{_sbindir}/sshd +%attr(0644,root,root) %{_libdir}/fipscheck/sshd.hmac +%attr(0755,root,root) %{_libexecdir}/openssh/sftp-server +%attr(0755,root,root) %{_libexecdir}/openssh/sshd-keygen +%attr(0644,root,root) %{_mandir}/man5/sshd_config.5* +%attr(0644,root,root) %{_mandir}/man5/moduli.5* +%attr(0644,root,root) %{_mandir}/man8/sshd.8* +%attr(0644,root,root) %{_mandir}/man8/sftp-server.8* +%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config +%attr(0644,root,root) %config(noreplace) /etc/pam.d/sshd +%attr(0640,root,root) %config(noreplace) /etc/sysconfig/sshd +%attr(0644,root,root) %{_unitdir}/sshd.service +%attr(0644,root,root) %{_unitdir}/sshd@.service +%attr(0644,root,root) %{_unitdir}/sshd.socket +%attr(0644,root,root) %{_unitdir}/sshd-keygen@.service +%attr(0644,root,root) %{_unitdir}/sshd-keygen.target +%attr(0644,root,root) %{_tmpfilesdir}/openssh.conf +%endif + +%if %{ldap} +%files ldap +%doc HOWTO.ldap-keys openssh-lpk-openldap.schema openssh-lpk-sun.schema ldap.conf +%doc openssh-lpk-openldap.ldif openssh-lpk-sun.ldif +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-ldap-helper +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-ldap-wrapper +%attr(0644,root,root) %{_mandir}/man8/ssh-ldap-helper.8* +%attr(0644,root,root) %{_mandir}/man5/ssh-ldap.conf.5* +%endif + +%files keycat +%doc HOWTO.ssh-keycat +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-keycat +%attr(0644,root,root) %config(noreplace) /etc/pam.d/ssh-keycat + +%if ! %{no_gnome_askpass} +%files askpass +%attr(0644,root,root) %{_sysconfdir}/profile.d/gnome-ssh-askpass.* +%attr(0755,root,root) %{_libexecdir}/openssh/gnome-ssh-askpass +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-askpass +%endif + +%files cavs +%attr(0755,root,root) %{_libexecdir}/openssh/ctr-cavstest +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-cavs +%attr(0755,root,root) %{_libexecdir}/openssh/ssh-cavs_driver.pl + +%if %{pam_ssh_agent} +%files -n pam_ssh_agent_auth +%license pam_ssh_agent_auth-%{pam_ssh_agent_ver}/OPENSSH_LICENSE +%attr(0755,root,root) %{_libdir}/security/pam_ssh_agent_auth.so +%attr(0644,root,root) %{_mandir}/man8/pam_ssh_agent_auth.8* +%endif + +%changelog +* Mon Nov 26 2018 Jakub Jelen - 7.8p1-4 + 0.10.3-5 +- Unbreak PKCS#11 URI tests (#1648262) +- Allow to disable RSA signatures with SHA1 (#1648898) +- Dump missing GSS options from client configuration (#1649505) +- Minor fixes from Fedora related to GSSAPI and keberos +- Follow the system-wide PATH settings + +* Mon Sep 24 2018 Jakub Jelen - 7.8p1-3 + 0.10.3-5 +- Disable OpenSSH hardening flags and use the ones provided by system (#1630615) +- Ignore unknown parts of PKCS#11 URI (#1631478) +- Do not fail with GSSAPI enabled in match blocks (#1580017) +- Fix the segfaulting cavs test (#1629692) + +* Fri Aug 31 2018 Jakub Jelen - 7.8p1-2 + 0.10.3-5 +- New upstream release fixing CVE 2018-15473 +- Remove unused patches +- Remove reference to unused enviornment variable SSH_USE_STRONG_RNG +- Address coverity issues +- Unbreak scp between two IPv6 hosts (#1620333) +- Unbreak GSSAPI key exchange (#1624323) +- Unbreak rekeying with GSSAPI key exchange (#1624344) + +* Thu Aug 09 2018 Jakub Jelen - 7.7p1-6 + 0.10.3-4 +- Fix listing of kex algoritms in FIPS mode +- Allow aes-gcm cipher modes in FIPS mode +- Coverity fixes + +* Tue Jul 03 2018 Jakub Jelen - 7.7p1-5 + 0.10.3-4 +- Disable manual printing of motd by default (#1591381) + +* Wed Jun 27 2018 Jakub Jelen - 7.7p1-4 + 0.10.3-4 +- Better handling of kerberos tickets storage (#1566494) +- Add pam_motd to pam stack (#1591381) + +* Mon Apr 16 2018 Jakub Jelen - 7.7p1-3 + 0.10.3-4 +- Fix tun devices and other issues fixed after release upstream (#1567775) + +* Thu Apr 12 2018 Jakub Jelen - 7.7p1-2 + 0.10.3-4 +- Do not break quotes parsing in configuration file (#1566295) + +* Wed Apr 04 2018 Jakub Jelen - 7.7p1-1 + 0.10.3-4 +- New upstream release (#1563223) +- Add support for ECDSA keys in PKCS#11 (#1354510) +- Add support for PKCS#11 URIs + +* Tue Mar 06 2018 Jakub Jelen - 7.6p1-7 + 0.10.3-3 +- Require crypto-policies version and new path +- Remove bogus NSS linking + +* Thu Feb 08 2018 Fedora Release Engineering - 7.6p1-6.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Fri Jan 26 2018 Jakub Jelen - 7.6p1-6 + 0.10.3-3 +- Rebuild for gcc bug on i386 (#1536555) + +* Thu Jan 25 2018 Florian Weimer - 7.6p1-5.2 +- Rebuild to work around gcc bug leading to sshd miscompilation (#1538648) + +* Sat Jan 20 2018 Björn Esser - 7.6p1-5.1.1 +- Rebuilt for switch to libxcrypt + +* Wed Jan 17 2018 Jakub Jelen - 7.6p1-5 + 0.10.3-3 +- Drop support for TCP wrappers (#1530163) +- Do not pass hostnames to audit -- UseDNS is usually disabled (#1534577) + +* Thu Dec 14 2017 Jakub Jelen - 7.6p1-4 + 0.10.3-3 +- Whitelist gettid() syscall in seccomp filter (#1524392) + +* Mon Dec 11 2017 Jakub Jelen - 7.6p1-3 + 0.10.3-3 +- Do not segfault during audit cleanup (#1524233) +- Avoid gcc warnings about uninitialized variables + +* Wed Nov 22 2017 Jakub Jelen - 7.6p1-2 + 0.10.3-3 +- Do not build everything against libldap +- Do not segfault for ECC keys in PKCS#11 + +* Thu Oct 19 2017 Jakub Jelen - 7.6p1-1 + 0.10.3-3 +- New upstream release OpenSSH 7.6 +- Addressing review remarks for OpenSSL 1.1.0 patch +- Fix PermitOpen bug in OpenSSH 7.6 +- Drop support for ExposeAuthenticationMethods option + +* Mon Sep 11 2017 Jakub Jelen - 7.5p1-6 + 0.10.3-2 +- Do not export KRB5CCNAME if the default path is used (#1199363) +- Add enablement for openssl-ibmca and openssl-ibmpkcs11 (#1477636) +- Add new GSSAPI kex algorithms with SHA-2, but leave them disabled for now +- Enforce pam_sepermit for all logins in SSH (#1492313) +- Remove pam_reauthorize, since it is not needed by cockpit anymore (#1492313) + +* Mon Aug 14 2017 Jakub Jelen - 7.5p1-5 + 0.10.3-2 +- Another less-intrusive approach to crypto policy (#1479271) + +* Tue Aug 01 2017 Jakub Jelen - 7.5p1-4 + 0.10.3-2 +- Remove SSH-1 subpackage for Fedora 27 (#1474942) +- Follow system-wide crypto policy in server (#1479271) + +* Thu Jul 27 2017 Fedora Release Engineering - 7.5p1-3.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jun 30 2017 Jakub Jelen - 7.5p1-2 + 0.10.3-2 +- Sync downstream patches with RHEL (FIPS) +- Resolve potential issues with OpenSSL 1.1.0 patch + +* Wed Mar 22 2017 Jakub Jelen - 7.5p1-2 + 0.10.3-2 +- Fix various after-release typos including failed build in s390x (#1434341) +- Revert chroot magic with SELinux + +* Mon Mar 20 2017 Jakub Jelen - 7.5p1-1 + 0.10.3-2 +- New upstream release + +* Fri Mar 03 2017 Jakub Jelen - 7.4p1-4 + 0.10.3-1 +- Avoid sending the SD_NOTIFY messages from wrong processes (#1427526) +- Address reports by coverity + +* Mon Feb 20 2017 Jakub Jelen - 7.4p1-3 + 0.10.3-1 +- Properly report errors from included files (#1408558) +- New pam_ssh_agent_auth 0.10.3 release +- Switch to SD_NOTIFY to make systemd happy + +* Mon Feb 06 2017 Jakub Jelen - 7.4p1-2 + 0.10.2-5 +- Fix ssh-agent cert signing error (#1416584) +- Fix wrong path to crypto policies +- Attempt to resolve issue with systemd + +* Tue Jan 03 2017 Jakub Jelen - 7.4p1-1 + 0.10.2-5 +- New upstream release (#1406204) +- Cache supported OIDs for GSSAPI key exchange (#1395288) +- Fix typo causing heap corruption (use-after-free) (#1409433) +- Prevent hangs with long MOTD + +* Thu Dec 08 2016 Jakub Jelen - 7.3p1-7 + 0.10.2-4 +- Properly deserialize received RSA certificates in ssh-agent (#1402029) +- Move MAX_DISPLAYS to a configuration option + +* Wed Nov 16 2016 Jakub Jelen - 7.3p1-6 + 0.10.2-4 +- GSSAPI requires futex syscall in privsep child (#1395288) + +* Thu Oct 27 2016 Jakub Jelen - 7.3p1-5 + 0.10.2-4 +- Build against OpenSSL 1.1.0 with compat changes +- Recommend crypto-policies +- Fix chroot dropping capabilities (#1386755) + +* Thu Sep 29 2016 Jakub Jelen - 7.3p1-4 + 0.10.2-4 +- Fix NULL dereference (#1380297) +- Include client Crypto Policy (#1225752) + +* Mon Aug 15 2016 Jakub Jelen - 7.3p1-3 + 0.10.2-4 +- Proper content of included configuration file + +* Tue Aug 09 2016 Jakub Jelen - 7.3p1-2 + 0.10.2-4 +- Fix permissions on the include directory (#1365270) + +* Tue Aug 02 2016 Jakub Jelen - 7.3p1-1 + 0.10.2-4 +- New upstream release (#1362156) + +* Tue Jul 26 2016 Jakub Jelen - 7.2p2-11 + 0.10.2-3 +- Remove slogin and sshd-keygen (#1359762) +- Prevent guest_t from running sudo (#1357860) + +* Mon Jul 18 2016 Jakub Jelen - 7.2p2-10 + 0.10.2-3 +- CVE-2016-6210: User enumeration via covert timing channel (#1357443) +- Expose more information about authentication to PAM +- Make closefrom() ignore softlinks to the /dev/ devices on s390 + +* Fri Jul 01 2016 Jakub Jelen - 7.2p2-9 + 0.10.2-3 +- Fix wrong detection of UseLogin in server configuration (#1350347) + +* Fri Jun 24 2016 Jakub Jelen - 7.2p2-8 + 0.10.2-3 +- Enable seccomp filter for MIPS architectures +- UseLogin=yes is not supported in Fedora +- SFTP server forced permissions should restore umask +- pam_ssh_agent_auth: Fix conflict bewteen two getpwuid() calls (#1349551) + +* Mon Jun 06 2016 Jakub Jelen - 7.2p2-7 +- Fix regression in certificate-based authentication (#1333498) +- Check for real location of .k5login file (#1328243) +- Fix unchecked dereference in pam_ssh_agent_auth +- Clean up old patches +- Build with seccomp filter on ppc64(le) (#1195065) + +* Fri Apr 29 2016 Jakub Jelen - 7.2p2-6 + 0.10.2-3 +- Add legacy sshd-keygen for anaconda (#1331077) + +* Fri Apr 22 2016 Jakub Jelen - 7.2p2-5 + 0.10.2-3 +- CVE-2015-8325: ignore PAM environment vars when UseLogin=yes (#1328013) +- Fix typo in sysconfig/sshd (#1325535) + +* Fri Apr 15 2016 Jakub Jelen - 7.2p2-4 + 0.10.2-3 +- Revise socket activation and services dependencies (#1325535) +- Drop unused init script + +* Wed Apr 13 2016 Jakub Jelen 7.2p2-3 + 0.10.2-3 +- Make sshd-keygen comply with packaging guidelines (#1325535) +- Soft-deny socket() syscall in seccomp sandbox (#1324493) +- Remove *sha1 Kex in FIPS mode (#1324493) +- Remove *gcm ciphers in FIPS mode (#1324493) + +* Wed Apr 06 2016 Jakub Jelen 7.2p2-2 + 0.10.2-3 +- Fix GSSAPI Key Exchange according to RFC (#1323622) +- Remove init.d/functions dependency from sshd-keygen (#1317722) +- Do not use MD5 in pam_ssh_agent_auth in FIPS mode + +* Thu Mar 10 2016 Jakub Jelen 7.2p2-1 + 0.10.2-3 +- New upstream (security) release (#1316529) +- Clean up audit patch + +* Thu Mar 03 2016 Jakub Jelen 7.2p1-2 + 0.10.2-2 +- Restore slogin symlinks to preserve backward compatibility + +* Mon Feb 29 2016 Jakub Jelen 7.2p1-1 + 0.10.2-2 +- New upstream release (#1312870) + +* Wed Feb 24 2016 Jakub Jelen 7.1p2-4.1 + 0.10.2-1 +- Fix race condition in auditing events when using multiplexing (#1308295) +- Fix X11 forwarding CVE according to upstream +- Fix problem when running without privsep (#1303910) +- Remove hard glob limit in SFTP + +* Thu Feb 04 2016 Fedora Release Engineering - 7.1p2-3.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Sat Jan 30 2016 Jakub Jelen 7.1p2-3 + 0.10.2-1 +- Fix segfaults with pam_ssh_agent_auth (#1303036) +- Silently disable X11 forwarding on problems +- Systemd service should be forking to detect immediate failures + +* Mon Jan 25 2016 Jakub Jelen 7.1p2-2 + 0.10.2-1 +- Rebased to recent version of pam_ssh_agent_auth +- Upstream fix for CVE-2016-1908 +- Remove useless defattr + +* Thu Jan 14 2016 Jakub Jelen 7.1p2-1 + 0.9.2-9 +- New security upstream release for CVE-2016-0777 + +* Tue Jan 12 2016 Jakub Jelen 7.1p1-7 + 0.9.2-8 +- Change RPM define macros to global according to packaging guidelines +- Fix wrong handling of SSH_COPY_ID_LEGACY environment variable +- Update ssh-agent and ssh-keysign permissions (#1296724) +- Fix few problems with alternative builds without GSSAPI or openSSL +- Fix condition to run sshd-keygen + +* Fri Dec 18 2015 Jakub Jelen 7.1p1-6 + 0.9.2-8 +- Preserve IUTF8 tty mode flag over ssh connections (#1270248) +- Do not require sysconfig file to start service (#1279521) +- Update ssh-copy-id to upstream version +- GSSAPI Key Exchange documentation improvements +- Remove unused patches + +* Wed Nov 04 2015 Jakub Jelen 7.1p1-5 + 0.9.2-8 +- Do not set user context too many times for root logins (#1269072) + +* Thu Oct 22 2015 Jakub Jelen 7.1p1-4 + 0.9.2-8 +- Review SELinux user context handling after authentication (#1269072) +- Handle root logins the same way as other users (#1269072) +- Audit implicit mac, if mac is covered in cipher (#1271694) +- Increase size limit for remote glob over sftp + +* Fri Sep 25 2015 Jakub Jelen 7.1p1-3 + 0.9.2-8 +- Fix FIPS mode for DH kex (#1260253) +- Provide full RELRO and PIE form askpass helper (#1264036) +- Fix gssapi key exchange on server and client (#1261414) +- Allow gss-keyex root login when without-password is set (upstream #2456) +- Fix obsolete usage of SELinux constants (#1261496) + +* Wed Sep 09 2015 Jakub Jelen 7.1p1-2 + 0.9.2-8 +- Fix warnings reported by gcc related to keysign and keyAlgorithms + +* Sat Aug 22 2015 Jakub Jelen 7.1p1-1 + 0.9.2-8 +- New upstream release + +* Wed Aug 19 2015 Jakub Jelen 7.0p1-2 + 0.9.3-7 +- Fix problem with DSA keys using pam_ssh_agent_auth (#1251777) +- Add GSSAPIKexAlgorithms option for server and client application +- Possibility to validate legacy systems by more fingerprints (#1249626) + +* Wed Aug 12 2015 Jakub Jelen 7.0p1-1 + 0.9.3-7 +- New upstream release (#1252639) +- Fix pam_ssh_agent_auth package (#1251777) +- Security: Use-after-free bug related to PAM support (#1252853) +- Security: Privilege separation weakness related to PAM support (#1252854) +- Security: Incorrectly set TTYs to be world-writable (#1252862) + +* Tue Jul 28 2015 Jakub Jelen 6.9p1-4 + 0.9.3-6 +- Handle terminal control characters in scp progressmeter (#1247204) + +* Thu Jul 23 2015 Jakub Jelen 6.9p1-3 + 0.9.3-6 +- CVE-2015-5600: only query each keyboard-interactive device once (#1245971) + +* Wed Jul 15 2015 Jakub Jelen 6.9p1-2 + 0.9.3-6 +- Enable SECCOMP filter for s390* architecture (#1195065) +- Fix race condition when multiplexing connection (#1242682) + +* Wed Jul 01 2015 Jakub Jelen 6.9p1-1 + 0.9.3-6 +- New upstream release (#1238253) +- Increase limitation number of files which can be listed using glob in sftp +- Correctly revert "PermitRootLogin no" option from upstream sources (#89216) + +* Wed Jun 24 2015 Jakub Jelen 6.8p1-9 + 0.9.3-5 +- Allow socketcall(SYS_SHUTDOWN) for net_child on ix86 architecture + +* Thu Jun 18 2015 Fedora Release Engineering - 6.8p1-8.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Mon Jun 08 2015 Jakub Jelen 6.8p1-8 + 0.9.3-5 +- Return stat syscall to seccomp filter (#1228323) + +* Wed Jun 03 2015 Jakub Jelen 6.8p1-7 + 0.9.3-5 +- Handle pam_ssh_agent_auth memory, buffers and variable sizes (#1225106) + +* Thu May 28 2015 Jakub Jelen 6.8p1-6 + 0.9.3-5 +- Resolve problem with pam_ssh_agent_auth after rebase (#1225106) +- ssh-copy-id: tcsh doesnt work with multiline strings +- Fix upstream memory problems +- Add missing options in testmode output and manual pages +- Provide LDIF version of LPK schema +- Document required selinux boolean for working ssh-ldap-helper + +* Mon Apr 20 2015 Jakub Jelen 6.8p1-5 + 0.9.3-5 +- Fix segfault on daemon exit caused by API change (#1213423) + +* Thu Apr 02 2015 Jakub Jelen 6.8p1-4 + 0.9.3-5 +- Fix audit_end_command to restore ControlPersist function (#1203900) + +* Tue Mar 31 2015 Jakub Jelen 6.8p1-3 + 0.9.3-5 +- Fixed issue with GSSAPI key exchange (#1207719) +- Add pam_namespace to sshd pam stack (based on #1125110) +- Remove krb5-config workaround for #1203900 +- Fix handling SELinux context in MLS systems +- Regression: solve sshd segfaults if other instance already running + +* Thu Mar 26 2015 Jakub Jelen 6.8p1-2 + 0.9.3-5 +- Update audit and gss patches after rebase +- Fix reintroduced upstrem bug #1878 + +* Tue Mar 24 2015 Jakub Jelen 6.8p1-1 + 0.9.3-5 +- new upstream release openssh-6.8p1 (#1203245) +- Resolve segfault with auditing commands (#1203900) +- Workaround krb5-config bug (#1204646) + +* Thu Mar 12 2015 Jakub Jelen 6.7p1-11 + 0.9.3-4 +- Ability to specify LDAP filter in ldap.conf for ssh-ldap-helper +- Fix auditing when using combination of ForceCommand and PTY +- Add sftp option to force mode of created files (from rhel) +- Fix tmpfiles.d entries to be more consistent (#1196807) + +* Mon Mar 02 2015 Jakub Jelen 6.7p1-10 + 0.9.3-4 +- Add tmpfiles.d entries (#1196807) + +* Fri Feb 27 2015 Jakub Jelen 6.7p1-9 + 0.9.3-4 +- Adjust seccomp filter for primary architectures and solve aarch64 issue (#1197051) +- Solve issue with ssh-copy-id and keys without trailing newline (#1093168) + +* Tue Feb 24 2015 Jakub Jelen 6.7p1-8 + 0.9.3-4 +- Add AArch64 support for seccomp_filter sandbox (#1195065) + +* Mon Feb 23 2015 Jakub Jelen 6.7p1-7 + 0.9.3-4 +- Fix seccomp filter on architectures without getuid32 + +* Mon Feb 23 2015 Jakub Jelen 6.7p1-6 + 0.9.3-4 +- Update seccomp filter to work on i686 architectures (#1194401) +- Fix previous failing build (#1195065) + +* Sun Feb 22 2015 Peter Robinson 6.7p1-5 + 0.9.3-4 +- Only use seccomp for sandboxing on supported platforms + +* Fri Feb 20 2015 Jakub Jelen 6.7p1-4 + 0.9.3-4 +- Move cavs tests into subpackage -cavs (#1194320) + +* Wed Feb 18 2015 Jakub Jelen 6.7p1-3 + 0.9.3-4 +- update coverity patch +- make output of sshd -T more consistent (#1187521) +- enable seccomp for sandboxing instead of rlimit (#1062953) +- update hardening to compile on gcc5 +- Add SSH KDF CAVS test driver (#1193045) +- Fix ssh-copy-id on non-sh remote shells (#1045191) + +* Tue Jan 27 2015 Jakub Jelen 6.7p1-2 + 0.9.3-4 +- fixed audit patch after rebase + +* Tue Jan 20 2015 Petr Lautrbach 6.7p1-1 + 0.9.3-4 +- new upstream release openssh-6.7p1 + +* Thu Jan 15 2015 Jakub Jelen 6.6.1p1-11.1 + 0.9.3-3 +- error message if scp when directory doesn't exist (#1142223) +- parsing configuration file values (#1130733) +- documentation in service and socket files for systemd (#1181593) +- updated ldap patch (#981058) +- fixed vendor-patchlevel +- add new option GSSAPIEnablek5users and disable using ~/.k5users by default CVE-2014-9278 (#1170745) + +* Fri Dec 19 2014 Petr Lautrbach 6.6.1p1-10 + 0.9.3-3 +- log via monitor in chroots without /dev/log + +* Wed Dec 03 2014 Petr Lautrbach 6.6.1p1-9 + 0.9.3-3 +- the .local domain example should be in ssh_config, not in sshd_config +- use different values for DH for Cisco servers (#1026430) + +* Thu Nov 13 2014 Petr Lautrbach 6.6.1p1-8 + 0.9.3-3 +- fix gsskex patch to correctly handle MONITOR_REQ_GSSSIGN request (#1118005) + +* Fri Nov 07 2014 Petr Lautrbach 6.6.1p1-7 + 0.9.3-3 +- correct the calculation of bytes for authctxt->krb5_ccname (#1161073) + +* Tue Nov 04 2014 Petr Lautrbach 6.6.1p1-6 + 0.9.3-3 +- privsep_preauth: use SELinux context from selinux-policy (#1008580) +- change audit trail for unknown users (mindrot#2245) +- fix kuserok patch which checked for the existence of .k5login + unconditionally and hence prevented other mechanisms to be used properly +- revert the default of KerberosUseKuserok back to yes (#1153076) +- ignore SIGXFSZ in postauth monitor (mindrot#2263) +- sshd-keygen - don't generate DSA and ED25519 host keys in FIPS mode + +* Mon Sep 08 2014 Petr Lautrbach 6.6.1p1-5 + 0.9.3-3 +- set a client's address right after a connection is set (mindrot#2257) +- apply RFC3454 stringprep to banners when possible (mindrot#2058) +- don't consider a partial success as a failure (mindrot#2270) + +* Sun Aug 17 2014 Fedora Release Engineering - 6.6.1p1-4.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Fri Jul 18 2014 Tom Callaway 6.6.1p1-4 + 0.9.3-3 +- fix license handling (both) + +* Fri Jul 18 2014 Petr Lautrbach 6.6.1p1-3 + 0.9.3-2 +- standardise on NI_MAXHOST for gethostname() string lengths (#1051490) + +* Mon Jul 14 2014 Petr Lautrbach 6.6.1p1-2 + 0.9.3-2 +- add pam_reauthorize.so to sshd.pam (#1115977) +- spec file and patches clenup + +* Sat Jun 07 2014 Fedora Release Engineering - 6.6.1p1-1.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue Jun 03 2014 Petr Lautrbach 6.6.1p1-1 + 0.9.3-2 +- disable the curve25519 KEX when speaking to OpenSSH 6.5 or 6.6 +- add support for ED25519 keys to sshd-keygen and sshd.sysconfig +- drop openssh-server-sysvinit subpackage +- slightly change systemd units logic - use sshd-keygen.service (#1066615) + +* Tue Jun 03 2014 Petr Lautrbach 6.6p1-1 + 0.9.3-2 +- new upstream release openssh-6.6p1 + +* Thu May 15 2014 Petr Lautrbach 6.4p1-4 + 0.9.3-1 +- use SSH_COPY_ID_LEGACY variable to run ssh-copy-id in the legacy mode +- make /etc/ssh/moduli file public (#1043661) +- test existence of /etc/ssh/ssh_host_ecdsa_key in sshd-keygen.service +- don't clean up gssapi credentials by default (#1055016) +- ssh-agent - try CLOCK_BOOTTIME with fallback (#1091992) +- prevent a server from skipping SSHFP lookup - CVE-2014-2653 (#1081338) +- ignore environment variables with embedded '=' or '\0' characters - CVE-2014-2532 + (#1077843) + +* Wed Dec 11 2013 Petr Lautrbach 6.4p1-3 + 0.9.3-1 +- sshd-keygen - use correct permissions on ecdsa host key (#1023945) +- use only rsa and ecdsa host keys by default + +* Tue Nov 26 2013 Petr Lautrbach 6.4p1-2 + 0.9.3-1 +- fix fatal() cleanup in the audit patch (#1029074) +- fix parsing logic of ldap.conf file (#1033662) + +* Fri Nov 08 2013 Petr Lautrbach 6.4p1-1 + 0.9.3-1 +- new upstream release + +* Fri Nov 01 2013 Petr Lautrbach 6.3p1-5 + 0.9.3-7 +- adjust gss kex mechanism to the upstream changes (#1024004) +- don't use xfree in pam_ssh_agent_auth sources (#1024965) + +* Fri Oct 25 2013 Petr Lautrbach 6.3p1-4 + 0.9.3-6 +- rebuild with the openssl with the ECC support + +* Thu Oct 24 2013 Petr Lautrbach 6.3p1-3 + 0.9.3-6 +- don't use SSH_FP_MD5 for fingerprints in FIPS mode + +* Wed Oct 23 2013 Petr Lautrbach 6.3p1-2 + 0.9.3-6 +- use default_ccache_name from /etc/krb5.conf for a kerberos cache (#991186) +- increase the size of the Diffie-Hellman groups (#1010607) +- sshd-keygen to generate ECDSA keys (#1019222) + +* Tue Oct 15 2013 Petr Lautrbach 6.3p1-1.1 + 0.9.3-6 +- new upstream release (#1007769) + +* Tue Oct 08 2013 Petr Lautrbach 6.2p2-9 + 0.9.3-5 +- use dracut-fips package to determine if a FIPS module is installed +- revert -fips subpackages and hmac files suffixes + +* Wed Sep 25 2013 Petr Lautrbach 6.2p2-8 + 0.9.3-5 +- sshd-keygen: generate only RSA keys by default (#1010092) +- use dist tag in suffixes for hmac checksum files + +* Wed Sep 11 2013 Petr Lautrbach 6.2p2-7 + 0.9.3-5 +- use hmac_suffix for ssh{,d} hmac checksums +- bump the minimum value of SSH_USE_STRONG_RNG to 14 according to SP800-131A +- automatically restart sshd.service on-failure after 42s interval + +* Thu Aug 29 2013 Petr Lautrbach 6.2p2-6.1 + 0.9.3-5 +- add -fips subpackages that contains the FIPS module files + +* Wed Jul 31 2013 Petr Lautrbach 6.2p2-5 + 0.9.3-5 +- gssapi credentials need to be stored before a pam session opened (#987792) + +* Tue Jul 23 2013 Petr Lautrbach 6.2p2-4 + 0.9.3-5 +- don't show Success for EAI_SYSTEM (#985964) +- make sftp's libedit interface marginally multibyte aware (#841771) + +* Mon Jun 17 2013 Petr Lautrbach 6.2p2-3 + 0.9.3-5 +- move default gssapi cache to /run/user/ (#848228) + +* Tue May 21 2013 Petr Lautrbach 6.2p2-2 + 0.9.3-5 +- add socket activated sshd units to the package (#963268) +- fix the example in the HOWTO.ldap-keys + +* Mon May 20 2013 Petr Lautrbach 6.2p2-1 + 0.9.3-5 +- new upstream release (#963582) + +* Wed Apr 17 2013 Petr Lautrbach 6.2p1-4 + 0.9.3-4 +- don't use export in sysconfig file (#953111) + +* Tue Apr 16 2013 Petr Lautrbach 6.2p1-3 + 0.9.3-4 +- sshd.service: use KillMode=process (#890376) +- add latest config.{sub,guess} to support aarch64 (#926284) + +* Tue Apr 09 2013 Petr Lautrbach 6.2p1-2 + 0.9.3-4 +- keep track of which IndentityFile options were manually supplied and + which were default options, and don't warn if the latter are missing. + (mindrot#2084) + +* Tue Apr 09 2013 Petr Lautrbach 6.2p1-1 + 0.9.3-4 +- new upstream release (#924727) + +* Wed Mar 06 2013 Petr Lautrbach 6.1p1-7 + 0.9.3-3 +- use SELinux type sshd_net_t for [net] childs (#915085) + +* Thu Feb 14 2013 Petr Lautrbach 6.1p1-6 + 0.9.3-3 +- fix AuthorizedKeysCommand option + +* Fri Feb 08 2013 Petr Lautrbach 6.1p1-5 + 0.9.3-3 +- change default value of MaxStartups - CVE-2010-5107 (#908707) + +* Mon Dec 03 2012 Petr Lautrbach 6.1p1-4 + 0.9.3-3 +- fix segfault in openssh-5.8p2-force_krb.patch (#882541) + +* Mon Dec 03 2012 Petr Lautrbach 6.1p1-3 + 0.9.3-3 +- replace RequiredAuthentications2 with AuthenticationMethods based on upstream +- obsolete RequiredAuthentications[12] options +- fix openssh-6.1p1-privsep-selinux.patch + +* Fri Oct 26 2012 Petr Lautrbach 6.1p1-2 +- add SELinux comment to /etc/ssh/sshd_config about SELinux command to modify port (#861400) +- drop required chkconfig (#865498) +- drop openssh-5.9p1-sftp-chroot.patch (#830237) + +* Sat Sep 15 2012 Petr Lautrbach 6.1p1-1 + 0.9.3-3 +- new upstream release (#852651) +- use DIR: kerberos type cache (#848228) +- don't use chroot_user_t for chrooted users (#830237) +- replace scriptlets with systemd macros (#850249) +- don't use /bin and /sbin paths (#856590) + +* Mon Aug 06 2012 Petr Lautrbach 6.0p1-1 + 0.9.3-2 +- new upstream release + +* Mon Aug 06 2012 Petr Lautrbach 5.9p1-26 + 0.9.3-1 +- change SELinux context also for root user (#827109) + +* Fri Jul 27 2012 Petr Lautrbach 5.9p1-25 + 0.9.3-1 +- fix various issues in openssh-5.9p1-required-authentications.patch + +* Tue Jul 17 2012 Tomas Mraz 5.9p1-24 + 0.9.3-1 +- allow sha256 and sha512 hmacs in the FIPS mode + +* Fri Jun 22 2012 Tomas Mraz 5.9p1-23 + 0.9.3-1 +- fix segfault in su when pam_ssh_agent_auth is used and the ssh-agent + is not running, most probably not exploitable +- update pam_ssh_agent_auth to 0.9.3 upstream version + +* Fri Apr 06 2012 Petr Lautrbach 5.9p1-22 + 0.9.2-32 +- don't create RSA1 key in FIPS mode +- don't install sshd-keygen.service (#810419) + +* Fri Mar 30 2012 Petr Lautrbach 5.9p1-21 + 0.9.2-32 +- fix various issues in openssh-5.9p1-required-authentications.patch + +* Wed Mar 21 2012 Petr Lautrbach 5.9p1-20 + 0.9.2-32 +- Fix dependencies in systemd units, don't enable sshd-keygen.service (#805338) + +* Wed Feb 22 2012 Petr Lautrbach 5.9p1-19 + 0.9.2-32 +- Look for x11 forward sockets with AI_ADDRCONFIG flag getaddrinfo (#735889) + +* Mon Feb 06 2012 Petr Lautrbach 5.9p1-18 + 0.9.2-32 +- replace TwoFactorAuth with RequiredAuthentications[12] + https://bugzilla.mindrot.org/show_bug.cgi?id=983 + +* Tue Jan 31 2012 Petr Lautrbach 5.9p1-17 + 0.9.2-32 +- run privsep slave process as the users SELinux context (#781634) + +* Tue Dec 13 2011 Tomas Mraz 5.9p1-16 + 0.9.2-32 +- add CAVS test driver for the aes-ctr ciphers + +* Sun Dec 11 2011 Tomas Mraz 5.9p1-15 + 0.9.2-32 +- enable aes-ctr ciphers use the EVP engines from OpenSSL such as the AES-NI + +* Tue Dec 06 2011 Petr Lautrbach 5.9p1-14 + 0.9.2-32 +- warn about unsupported option UsePAM=no (#757545) + +* Mon Nov 21 2011 Tomas Mraz - 5.9p1-13 + 0.9.2-32 +- add back the restorecon call to ssh-copy-id - it might be needed on older + distributions (#739989) + +* Fri Nov 18 2011 Tomas Mraz - 5.9p1-12 + 0.9.2-32 +- still support /etc/sysconfig/sshd loading in sshd service (#754732) +- fix incorrect key permissions generated by sshd-keygen script (#754779) + +* Fri Oct 14 2011 Tomas Mraz - 5.9p1-11 + 0.9.2-32 +- remove unnecessary requires on initscripts +- set VerifyHostKeyDNS to ask in the default configuration (#739856) + +* Mon Sep 19 2011 Jan F. Chadima - 5.9p1-10 + 0.9.2-32 +- selinux sandbox rewrite +- two factor authentication tweaking + +* Wed Sep 14 2011 Jan F. Chadima - 5.9p1-9 + 0.9.2-32 +- coverity upgrade +- wipe off nonfunctional nss +- selinux sandbox tweaking + +* Tue Sep 13 2011 Jan F. Chadima - 5.9p1-8 + 0.9.2-32 +- coverity upgrade +- experimental selinux sandbox + +* Tue Sep 13 2011 Jan F. Chadima - 5.9p1-7 + 0.9.2-32 +- fully reanable auditing + +* Mon Sep 12 2011 Jan F. Chadima - 5.9p1-6 + 0.9.2-32 +- repair signedness in akc patch + +* Mon Sep 12 2011 Jan F. Chadima - 5.9p1-5 + 0.9.2-32 +- temporarily disable part of audit4 patch + +* Fri Sep 9 2011 Jan F. Chadima - 5.9p1-3 + 0.9.2-32 +- Coverity second pass +- Reenable akc patch + +* Thu Sep 8 2011 Jan F. Chadima - 5.9p1-2 + 0.9.2-32 +- Coverity first pass + +* Wed Sep 7 2011 Jan F. Chadima - 5.9p1-1 + 0.9.2-32 +- Rebase to 5.9p1 +- Add chroot sftp patch +- Add two factor auth patch + +* Tue Aug 23 2011 Jan F. Chadima - 5.8p2-21 + 0.9.2-31 +- ignore SIGPIPE in ssh keyscan + +* Tue Aug 9 2011 Jan F. Chadima - 5.8p2-20 + 0.9.2-31 +- save ssh-askpass's debuginfo + +* Mon Aug 8 2011 Jan F. Chadima - 5.8p2-19 + 0.9.2-31 +- compile ssh-askpass with corect CFLAGS + +* Mon Aug 8 2011 Jan F. Chadima - 5.8p2-18 + 0.9.2-31 +- improve selinux's change context log + +* Mon Aug 8 2011 Jan F. Chadima - 5.8p2-17 + 0.9.2-31 +- repair broken man pages + +* Mon Jul 25 2011 Jan F. Chadima - 5.8p2-16 + 0.9.2-31 +- rebuild due to broken rpmbiild + +* Thu Jul 21 2011 Jan F. Chadima - 5.8p2-15 + 0.9.2-31 +- Do not change context when run under unconfined_t + +* Thu Jul 14 2011 Jan F. Chadima - 5.8p2-14 + 0.9.2-31 +- Add postlogin to pam. (#718807) + +* Tue Jun 28 2011 Jan F. Chadima - 5.8p2-12 + 0.9.2-31 +- Systemd compatibility according to Mathieu Bridon +- Split out the host keygen into their own command, to ease future migration + to systemd. Compatitbility with the init script was kept. +- Migrate the package to full native systemd unit files, according to the Fedora + packaging guidelines. +- Prepate the unit files for running an ondemand server. (do not add it actually) + +* Tue Jun 21 2011 Jan F. Chadima - 5.8p2-10 + 0.9.2-31 +- Mention IPv6 usage in man pages + +* Mon Jun 20 2011 Jan F. Chadima - 5.8p2-9 + 0.9.2-31 +- Improve init script + +* Thu Jun 16 2011 Jan F. Chadima - 5.8p2-7 + 0.9.2-31 +- Add possibility to compile openssh without downstream patches + +* Thu Jun 9 2011 Jan F. Chadima - 5.8p2-6 + 0.9.2-31 +- remove stale control sockets (#706396) + +* Tue May 31 2011 Jan F. Chadima - 5.8p2-5 + 0.9.2-31 +- improove entropy manuals + +* Fri May 27 2011 Jan F. Chadima - 5.8p2-4 + 0.9.2-31 +- improove entropy handling +- concat ldap patches + +* Tue May 24 2011 Jan F. Chadima - 5.8p2-3 + 0.9.2-31 +- improove ldap manuals + +* Mon May 23 2011 Jan F. Chadima - 5.8p2-2 + 0.9.2-31 +- add gssapi forced command + +* Tue May 3 2011 Jan F. Chadima - 5.8p2-1 + 0.9.2-31 +- update the openssh version + +* Thu Apr 28 2011 Jan F. Chadima - 5.8p1-34 + 0.9.2-30 +- temporarily disabling systemd units + +* Wed Apr 27 2011 Jan F. Chadima - 5.8p1-33 + 0.9.2-30 +- add flags AI_V4MAPPED and AI_ADDRCONFIG to getaddrinfo + +* Tue Apr 26 2011 Jan F. Chadima - 5.8p1-32 + 0.9.2-30 +- update scriptlets + +* Fri Apr 22 2011 Jan F. Chadima - 5.8p1-30 + 0.9.2-30 +- add systemd units + +* Fri Apr 22 2011 Jan F. Chadima - 5.8p1-28 + 0.9.2-30 +- improving sshd -> passwd transation +- add template for .local domain to sshd_config + +* Thu Apr 21 2011 Jan F. Chadima - 5.8p1-27 + 0.9.2-30 +- the private keys may be 640 root:ssh_keys ssh_keysign is sgid + +* Wed Apr 20 2011 Jan F. Chadima - 5.8p1-26 + 0.9.2-30 +- improving sshd -> passwd transation + +* Tue Apr 5 2011 Jan F. Chadima - 5.8p1-25 + 0.9.2-30 +- the intermediate context is set to sshd_sftpd_t +- do not crash in packet.c if no connection + +* Thu Mar 31 2011 Jan F. Chadima - 5.8p1-24 + 0.9.2-30 +- resolve warnings in port_linux.c + +* Tue Mar 29 2011 Jan F. Chadima - 5.8p1-23 + 0.9.2-30 +- add /etc/sysconfig/sshd + +* Mon Mar 28 2011 Jan F. Chadima - 5.8p1-22 + 0.9.2-30 +- improve reseeding and seed source (documentation) + +* Tue Mar 22 2011 Jan F. Chadima - 5.8p1-20 + 0.9.2-30 +- use /dev/random or /dev/urandom for seeding prng +- improve periodical reseeding of random generator + +* Thu Mar 17 2011 Jan F. Chadima - 5.8p1-18 + 0.9.2-30 +- add periodical reseeding of random generator +- change selinux contex for internal sftp in do_usercontext +- exit(0) after sigterm + +* Thu Mar 10 2011 Jan F. Chadima - 5.8p1-17 + 0.9.2-30 +- improove ssh-ldap (documentation) + +* Tue Mar 8 2011 Jan F. Chadima - 5.8p1-16 + 0.9.2-30 +- improve session keys audit + +* Mon Mar 7 2011 Jan F. Chadima - 5.8p1-15 + 0.9.2-30 +- CVE-2010-4755 + +* Fri Mar 4 2011 Jan F. Chadima - 5.8p1-14 + 0.9.2-30 +- improove ssh-keycat (documentation) + +* Thu Mar 3 2011 Jan F. Chadima - 5.8p1-13 + 0.9.2-30 +- improve audit of logins and auths + +* Tue Mar 1 2011 Jan F. Chadima - 5.8p1-12 + 0.9.2-30 +- improove ssk-keycat + +* Mon Feb 28 2011 Jan F. Chadima - 5.8p1-11 + 0.9.2-30 +- add ssk-keycat + +* Fri Feb 25 2011 Jan F. Chadima - 5.8p1-10 + 0.9.2-30 +- reenable auth-keys ldap backend + +* Fri Feb 25 2011 Jan F. Chadima - 5.8p1-9 + 0.9.2-30 +- another audit improovements + +* Thu Feb 24 2011 Jan F. Chadima - 5.8p1-8 + 0.9.2-30 +- another audit improovements +- switchable fingerprint mode + +* Thu Feb 17 2011 Jan F. Chadima - 5.8p1-4 + 0.9.2-30 +- improve audit of server key management + +* Wed Feb 16 2011 Jan F. Chadima - 5.8p1-3 + 0.9.2-30 +- improve audit of logins and auths + +* Mon Feb 14 2011 Jan F. Chadima - 5.8p1-1 + 0.9.2-30 +- bump openssh version to 5.8p1 + +* Tue Feb 08 2011 Fedora Release Engineering - 5.6p1-30.1 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Mon Feb 7 2011 Jan F. Chadima - 5.6p1-30 + 0.9.2-29 +- clean the data structures in the non privileged process +- clean the data structures when roaming + +* Wed Feb 2 2011 Jan F. Chadima - 5.6p1-28 + 0.9.2-29 +- clean the data structures in the privileged process + +* Tue Jan 25 2011 Jan F. Chadima - 5.6p1-25 + 0.9.2-29 +- clean the data structures before exit net process + +* Mon Jan 17 2011 Jan F. Chadima - 5.6p1-24 + 0.9.2-29 +- make audit compatible with the fips mode + +* Fri Jan 14 2011 Jan F. Chadima - 5.6p1-23 + 0.9.2-29 +- add audit of destruction the server keys + +* Wed Jan 12 2011 Jan F. Chadima - 5.6p1-22 + 0.9.2-29 +- add audit of destruction the session keys + +* Fri Dec 10 2010 Jan F. Chadima - 5.6p1-21 + 0.9.2-29 +- reenable run sshd as non root user +- renable rekeying + +* Wed Nov 24 2010 Jan F. Chadima - 5.6p1-20 + 0.9.2-29 +- reapair clientloop crash (#627332) +- properly restore euid in case connect to the ssh-agent socket fails + +* Mon Nov 22 2010 Jan F. Chadima - 5.6p1-19 + 0.9.2-28 +- striped read permissions from suid and sgid binaries + +* Mon Nov 15 2010 Jan F. Chadima - 5.6p1-18 + 0.9.2-27 +- used upstream version of the biguid patch + +* Mon Nov 15 2010 Jan F. Chadima - 5.6p1-17 + 0.9.2-27 +- improoved kuserok patch + +* Fri Nov 5 2010 Jan F. Chadima - 5.6p1-16 + 0.9.2-27 +- add auditing the host based key ussage +- repait X11 abstract layer socket (#648896) + +* Wed Nov 3 2010 Jan F. Chadima - 5.6p1-15 + 0.9.2-27 +- add auditing the kex result + +* Tue Nov 2 2010 Jan F. Chadima - 5.6p1-14 + 0.9.2-27 +- add auditing the key ussage + +* Wed Oct 20 2010 Jan F. Chadima - 5.6p1-12 + 0.9.2-27 +- update gsskex patch (#645389) + +* Wed Oct 20 2010 Jan F. Chadima - 5.6p1-11 + 0.9.2-27 +- rebase linux audit according to upstream + +* Fri Oct 1 2010 Jan F. Chadima - 5.6p1-10 + 0.9.2-27 +- add missing headers to linux audit + +* Wed Sep 29 2010 Jan F. Chadima - 5.6p1-9 + 0.9.2-27 +- audit module now uses openssh audit framevork + +* Wed Sep 15 2010 Jan F. Chadima - 5.6p1-8 + 0.9.2-27 +- Add the GSSAPI kuserok switch to the kuserok patch + +* Wed Sep 15 2010 Jan F. Chadima - 5.6p1-7 + 0.9.2-27 +- Repaired the kuserok patch + +* Mon Sep 13 2010 Jan F. Chadima - 5.6p1-6 + 0.9.2-27 +- Repaired the problem with puting entries with very big uid into lastlog + +* Mon Sep 13 2010 Jan F. Chadima - 5.6p1-5 + 0.9.2-27 +- Merging selabel patch with the upstream version. (#632914) + +* Mon Sep 13 2010 Jan F. Chadima - 5.6p1-4 + 0.9.2-27 +- Tweaking selabel patch to work properly without selinux rules loaded. (#632914) + +* Wed Sep 8 2010 Tomas Mraz - 5.6p1-3 + 0.9.2-27 +- Make fipscheck hmacs compliant with FHS - requires new fipscheck + +* Fri Sep 3 2010 Jan F. Chadima - 5.6p1-2 + 0.9.2-27 +- Added -z relro -z now to LDFLAGS + +* Fri Sep 3 2010 Jan F. Chadima - 5.6p1-1 + 0.9.2-27 +- Rebased to openssh5.6p1 + +* Wed Jul 7 2010 Jan F. Chadima - 5.5p1-18 + 0.9.2-26 +- merged with newer bugzilla's version of authorized keys command patch + +* Wed Jun 30 2010 Jan F. Chadima - 5.5p1-17 + 0.9.2-26 +- improved the x11 patch according to upstream (#598671) + +* Fri Jun 25 2010 Jan F. Chadima - 5.5p1-16 + 0.9.2-26 +- improved the x11 patch (#598671) + +* Thu Jun 24 2010 Jan F. Chadima - 5.5p1-15 + 0.9.2-26 +- changed _PATH_UNIX_X to unexistent file name (#598671) + +* Wed Jun 23 2010 Jan F. Chadima - 5.5p1-14 + 0.9.2-26 +- sftp works in deviceless chroot again (broken from 5.5p1-3) + +* Tue Jun 8 2010 Jan F. Chadima - 5.5p1-13 + 0.9.2-26 +- add option to switch out krb5_kuserok + +* Fri May 21 2010 Jan F. Chadima - 5.5p1-12 + 0.9.2-26 +- synchronize uid and gid for the user sshd + +* Thu May 20 2010 Jan F. Chadima - 5.5p1-11 + 0.9.2-26 +- Typo in ssh-ldap.conf(5) and ssh-ladap-helper(8) + +* Fri May 14 2010 Jan F. Chadima - 5.5p1-10 + 0.9.2-26 +- Repair the reference in man ssh-ldap-helper(8) +- Repair the PubkeyAgent section in sshd_config(5) +- Provide example ldap.conf + +* Thu May 13 2010 Jan F. Chadima - 5.5p1-9 + 0.9.2-26 +- Make the Ldap configuration widely compatible +- create the aditional docs for LDAP support. + +* Thu May 6 2010 Jan F. Chadima - 5.5p1-8 + 0.9.2-26 +- Make LDAP config elements TLS_CACERT and TLS_REQCERT compatiple with pam_ldap (#589360) + +* Thu May 6 2010 Jan F. Chadima - 5.5p1-7 + 0.9.2-26 +- Make LDAP config element tls_checkpeer compatiple with nss_ldap (#589360) + +* Tue May 4 2010 Jan F. Chadima - 5.5p1-6 + 0.9.2-26 +- Comment spec.file +- Sync patches from upstream + +* Mon May 3 2010 Jan F. Chadima - 5.5p1-5 + 0.9.2-26 +- Create separate ldap package +- Tweak the ldap patch +- Rename stderr patch properly + +* Thu Apr 29 2010 Jan F. Chadima - 5.5p1-4 + 0.9.2-26 +- Added LDAP support + +* Mon Apr 26 2010 Jan F. Chadima - 5.5p1-3 + 0.9.2-26 +- Ignore .bashrc output to stderr in the subsystems + +* Tue Apr 20 2010 Jan F. Chadima - 5.5p1-2 + 0.9.2-26 +- Drop dependency on man + +* Fri Apr 16 2010 Jan F. Chadima - 5.5p1-1 + 0.9.2-26 +- Update to 5.5p1 + +* Fri Mar 12 2010 Jan F. Chadima - 5.4p1-3 + 0.9.2-25 +- repair configure script of pam_ssh_agent +- repair error mesage in ssh-keygen + +* Fri Mar 12 2010 Jan F. Chadima - 5.4p1-2 +- source krb5-devel profile script only if exists + +* Tue Mar 9 2010 Jan F. Chadima - 5.4p1-1 +- Update to 5.4p1 +- discontinued support for nss-keys +- discontinued support for scard + +* Wed Mar 3 2010 Jan F. Chadima - 5.4p1-0.snap20100302.1 +- Prepare update to 5.4p1 + +* Mon Feb 15 2010 Jan F. Chadima - 5.3p1-22 +- ImplicitDSOLinking (#564824) + +* Fri Jan 29 2010 Jan F. Chadima - 5.3p1-21 +- Allow to use hardware crypto if awailable (#559555) + +* Mon Jan 25 2010 Jan F. Chadima - 5.3p1-20 +- optimized FD_CLOEXEC on accept socket (#541809) + +* Mon Jan 25 2010 Tomas Mraz - 5.3p1-19 +- updated pam_ssh_agent_auth to new version from upstream (just + a licence change) + +* Thu Jan 21 2010 Jan F. Chadima - 5.3p1-18 +- optimized RAND_cleanup patch (#557166) + +* Wed Jan 20 2010 Jan F. Chadima - 5.3p1-17 +- add RAND_cleanup at the exit of each program using RAND (#557166) + +* Tue Jan 19 2010 Jan F. Chadima - 5.3p1-16 +- set FD_CLOEXEC on accepted socket (#541809) + +* Fri Jan 8 2010 Jan F. Chadima - 5.3p1-15 +- replaced define by global in macros + +* Tue Jan 5 2010 Jan F. Chadima - 5.3p1-14 +- Update the pka patch + +* Mon Dec 21 2009 Jan F. Chadima - 5.3p1-13 +- Update the audit patch + +* Fri Dec 4 2009 Jan F. Chadima - 5.3p1-12 +- Add possibility to autocreate only RSA key into initscript (#533339) + +* Fri Nov 27 2009 Jan F. Chadima - 5.3p1-11 +- Prepare NSS key patch for future SEC_ERROR_LOCKED_PASSWORD (#537411) + +* Tue Nov 24 2009 Jan F. Chadima - 5.3p1-10 +- Update NSS key patch (#537411, #356451) + +* Fri Nov 20 2009 Jan F. Chadima - 5.3p1-9 +- Add gssapi key exchange patch (#455351) + +* Fri Nov 20 2009 Jan F. Chadima - 5.3p1-8 +- Add public key agent patch (#455350) + +* Mon Nov 2 2009 Jan F. Chadima - 5.3p1-7 +- Repair canohost patch to allow gssapi to work when host is acessed via pipe proxy (#531849) + +* Thu Oct 29 2009 Jan F. Chadima - 5.3p1-6 +- Modify the init script to prevent it to hang during generating the keys (#515145) + +* Tue Oct 27 2009 Jan F. Chadima - 5.3p1-5 +- Add README.nss + +* Mon Oct 19 2009 Tomas Mraz - 5.3p1-4 +- Add pam_ssh_agent_auth module to a subpackage. + +* Fri Oct 16 2009 Jan F. Chadima - 5.3p1-3 +- Reenable audit. + +* Fri Oct 2 2009 Jan F. Chadima - 5.3p1-2 +- Upgrade to new wersion 5.3p1 + +* Tue Sep 29 2009 Jan F. Chadima - 5.2p1-29 +- Resolve locking in ssh-add (#491312) + +* Thu Sep 24 2009 Jan F. Chadima - 5.2p1-28 +- Repair initscript to be acord to guidelines (#521860) +- Add bugzilla# to application of edns and xmodifiers patch + +* Wed Sep 16 2009 Jan F. Chadima - 5.2p1-26 +- Changed pam stack to password-auth + +* Fri Sep 11 2009 Jan F. Chadima - 5.2p1-25 +- Dropped homechroot patch + +* Mon Sep 7 2009 Jan F. Chadima - 5.2p1-24 +- Add check for nosuid, nodev in homechroot + +* Tue Sep 1 2009 Jan F. Chadima - 5.2p1-23 +- add correct patch for ip-opts + +* Tue Sep 1 2009 Jan F. Chadima - 5.2p1-22 +- replace ip-opts patch by an upstream candidate version + +* Mon Aug 31 2009 Jan F. Chadima - 5.2p1-21 +- rearange selinux patch to be acceptable for upstream +- replace seftp patch by an upstream version + +* Fri Aug 28 2009 Jan F. Chadima - 5.2p1-20 +- merged xmodifiers to redhat patch +- merged gssapi-role to selinux patch +- merged cve-2007_3102 to audit patch +- sesftp patch only with WITH_SELINUX flag +- rearange sesftp patch according to upstream request + +* Wed Aug 26 2009 Jan F. Chadima - 5.2p1-19 +- minor change in sesftp patch + +* Fri Aug 21 2009 Tomas Mraz - 5.2p1-18 +- rebuilt with new openssl + +* Thu Jul 30 2009 Jan F. Chadima - 5.2p1-17 +- Added dnssec support. (#205842) + +* Sat Jul 25 2009 Fedora Release Engineering - 5.2p1-16 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Fri Jul 24 2009 Jan F. Chadima - 5.2p1-15 +- only INTERNAL_SFTP can be home-chrooted +- save _u and _r parts of context changing to sftpd_t + +* Fri Jul 17 2009 Jan F. Chadima - 5.2p1-14 +- changed internal-sftp context to sftpd_t + +* Fri Jul 3 2009 Jan F. Chadima - 5.2p1-13 +- changed home length path patch to upstream version + +* Tue Jun 30 2009 Jan F. Chadima - 5.2p1-12 +- create '~/.ssh/known_hosts' within proper context + +* Mon Jun 29 2009 Jan F. Chadima - 5.2p1-11 +- length of home path in ssh now limited by PATH_MAX +- correct timezone with daylight processing + +* Sat Jun 27 2009 Jan F. Chadima - 5.2p1-10 +- final version chroot %%h (sftp only) + +* Tue Jun 23 2009 Jan F. Chadima - 5.2p1-9 +- repair broken ls in chroot %%h + +* Fri Jun 12 2009 Jan F. Chadima - 5.2p1-8 +- add XMODIFIERS to exported environment (#495690) + +* Fri May 15 2009 Tomas Mraz - 5.2p1-6 +- allow only protocol 2 in the FIPS mode + +* Thu Apr 30 2009 Tomas Mraz - 5.2p1-5 +- do integrity verification only on binaries which are part + of the OpenSSH FIPS modules + +* Mon Apr 20 2009 Tomas Mraz - 5.2p1-4 +- log if FIPS mode is initialized +- make aes-ctr cipher modes work in the FIPS mode + +* Fri Apr 3 2009 Jan F. Chadima - 5.2p1-3 +- fix logging after chroot +- enable non root users to use chroot %%h in internal-sftp + +* Fri Mar 13 2009 Tomas Mraz - 5.2p1-2 +- add AES-CTR ciphers to the FIPS mode proposal + +* Mon Mar 9 2009 Jan F. Chadima - 5.2p1-1 +- upgrade to new upstream release + +* Thu Feb 26 2009 Fedora Release Engineering - 5.1p1-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Thu Feb 12 2009 Tomas Mraz - 5.1p1-7 +- drop obsolete triggers +- add testing FIPS mode support +- LSBize the initscript (#247014) + +* Fri Jan 30 2009 Tomas Mraz - 5.1p1-6 +- enable use of ssl engines (#481100) + +* Thu Jan 15 2009 Tomas Mraz - 5.1p1-5 +- remove obsolete --with-rsh (#478298) +- add pam_sepermit to allow blocking confined users in permissive mode + (#471746) +- move system-auth after pam_selinux in the session stack + +* Thu Dec 11 2008 Tomas Mraz - 5.1p1-4 +- set FD_CLOEXEC on channel sockets (#475866) +- adjust summary +- adjust nss-keys patch so it is applicable without selinux patches (#470859) + +* Fri Oct 17 2008 Tomas Mraz - 5.1p1-3 +- fix compatibility with some servers (#466818) + +* Thu Jul 31 2008 Tomas Mraz - 5.1p1-2 +- fixed zero length banner problem (#457326) + +* Wed Jul 23 2008 Tomas Mraz - 5.1p1-1 +- upgrade to new upstream release +- fixed a problem with public key authentication and explicitely + specified SELinux role + +* Wed May 21 2008 Tomas Mraz - 5.0p1-3 +- pass the connection socket to ssh-keysign (#447680) + +* Mon May 19 2008 Tomas Mraz - 5.0p1-2 +- add LANGUAGE to accepted/sent environment variables (#443231) +- use pam_selinux to obtain the user context instead of doing it itself +- unbreak server keep alive settings (patch from upstream) +- small addition to scp manpage + +* Mon Apr 7 2008 Tomas Mraz - 5.0p1-1 +- upgrade to new upstream (#441066) +- prevent initscript from killing itself on halt with upstart (#438449) +- initscript status should show that the daemon is running + only when the main daemon is still alive (#430882) + +* Thu Mar 6 2008 Tomas Mraz - 4.7p1-10 +- fix race on control master and cleanup stale control socket (#436311) + patches by David Woodhouse + +* Fri Feb 29 2008 Tomas Mraz - 4.7p1-9 +- set FD_CLOEXEC on client socket +- apply real fix for window size problem (#286181) from upstream +- apply fix for the spurious failed bind from upstream +- apply open handle leak in sftp fix from upstream + +* Tue Feb 12 2008 Dennis Gilmore - 4.7p1-8 +- we build for sparcv9 now and it needs -fPIE + +* Thu Jan 3 2008 Tomas Mraz - 4.7p1-7 +- fix gssapi auth with explicit selinux role requested (#427303) - patch + by Nalin Dahyabhai + +* Tue Dec 4 2007 Tomas Mraz - 4.7p1-6 +- explicitly source krb5-devel profile script + +* Tue Dec 04 2007 Release Engineering - 4.7p1-5 +- Rebuild for openssl bump + +* Tue Nov 20 2007 Tomas Mraz - 4.7p1-4 +- do not copy /etc/localtime into the chroot as it is not + necessary anymore (#193184) +- call setkeycreatecon when selinux context is established +- test for NULL privk when freeing key (#391871) - patch by + Pierre Ossman + +* Mon Sep 17 2007 Tomas Mraz - 4.7p1-2 +- revert default window size adjustments (#286181) + +* Thu Sep 6 2007 Tomas Mraz - 4.7p1-1 +- upgrade to latest upstream +- use libedit in sftp (#203009) +- fixed audit log injection problem (CVE-2007-3102) + +* Thu Aug 9 2007 Tomas Mraz - 4.5p1-8 +- fix sftp client problems on write error (#247802) +- allow disabling autocreation of server keys (#235466) + +* Wed Jun 20 2007 Tomas Mraz - 4.5p1-7 +- experimental NSS keys support +- correctly setup context when empty level requested (#234951) + +* Tue Mar 20 2007 Tomas Mraz - 4.5p1-6 +- mls level check must be done with default role same as requested + +* Mon Mar 19 2007 Tomas Mraz - 4.5p1-5 +- make profile.d/gnome-ssh-askpass.* regular files (#226218) + +* Tue Feb 27 2007 Tomas Mraz - 4.5p1-4 +- reject connection if requested mls range is not obtained (#229278) + +* Thu Feb 22 2007 Tomas Mraz - 4.5p1-3 +- improve Buildroot +- remove duplicate /etc/ssh from files + +* Tue Jan 16 2007 Tomas Mraz - 4.5p1-2 +- support mls on labeled networks (#220487) +- support mls level selection on unlabeled networks +- allow / in usernames in scp (only beginning /, ./, and ../ is special) + +* Thu Dec 21 2006 Tomas Mraz - 4.5p1-1 +- update to 4.5p1 (#212606) + +* Thu Nov 30 2006 Tomas Mraz - 4.3p2-14 +- fix gssapi with DNS loadbalanced clusters (#216857) + +* Tue Nov 28 2006 Tomas Mraz - 4.3p2-13 +- improved pam_session patch so it doesn't regress, the patch is necessary + for the pam_session_close to be called correctly as uid 0 + +* Fri Nov 10 2006 Tomas Mraz - 4.3p2-12 +- CVE-2006-5794 - properly detect failed key verify in monitor (#214641) + +* Thu Nov 2 2006 Tomas Mraz - 4.3p2-11 +- merge sshd initscript patches +- kill all ssh sessions when stop is called in halt or reboot runlevel +- remove -TERM option from killproc so we don't race on sshd restart + +* Mon Oct 2 2006 Tomas Mraz - 4.3p2-10 +- improve gssapi-no-spnego patch (#208102) +- CVE-2006-4924 - prevent DoS on deattack detector (#207957) +- CVE-2006-5051 - don't call cleanups from signal handler (#208459) + +* Wed Aug 23 2006 Tomas Mraz - 4.3p2-9 +- don't report duplicate syslog messages, use correct local time (#189158) +- don't allow spnego as gssapi mechanism (from upstream) +- fixed memleaks found by Coverity (from upstream) +- allow ip options except source routing (#202856) (patch by HP) + +* Tue Aug 8 2006 Tomas Mraz - 4.3p2-8 +- drop the pam-session patch from the previous build (#201341) +- don't set IPV6_V6ONLY sock opt when listening on wildcard addr (#201594) + +* Thu Jul 20 2006 Tomas Mraz - 4.3p2-7 +- dropped old ssh obsoletes +- call the pam_session_open/close from the monitor when privsep is + enabled so it is always called as root (patch by Darren Tucker) + +* Mon Jul 17 2006 Tomas Mraz - 4.3p2-6 +- improve selinux patch (by Jan Kiszka) +- upstream patch for buffer append space error (#191940) +- fixed typo in configure.ac (#198986) +- added pam_keyinit to pam configuration (#198628) +- improved error message when askpass dialog cannot grab + keyboard input (#198332) +- buildrequires xauth instead of xorg-x11-xauth +- fixed a few rpmlint warnings + +* Wed Jul 12 2006 Jesse Keating - 4.3p2-5.1 +- rebuild + +* Fri Apr 14 2006 Tomas Mraz - 4.3p2-5 +- don't request pseudoterminal allocation if stdin is not tty (#188983) + +* Thu Mar 2 2006 Tomas Mraz - 4.3p2-4 +- allow access if audit is not compiled in kernel (#183243) + +* Fri Feb 24 2006 Tomas Mraz - 4.3p2-3 +- enable the subprocess in chroot to send messages to system log +- sshd should prevent login if audit call fails + +* Tue Feb 21 2006 Tomas Mraz - 4.3p2-2 +- print error from scp if not remote (patch by Bjorn Augustsson #178923) + +* Mon Feb 13 2006 Tomas Mraz - 4.3p2-1 +- new version + +* Fri Feb 10 2006 Jesse Keating - 4.3p1-2.1 +- bump again for double-long bug on ppc(64) + +* Mon Feb 6 2006 Tomas Mraz - 4.3p1-2 +- fixed another place where syslog was called in signal handler +- pass locale environment variables to server, accept them there (#179851) + +* Wed Feb 1 2006 Tomas Mraz - 4.3p1-1 +- new version, dropped obsolete patches + +* Tue Dec 20 2005 Tomas Mraz - 4.2p1-10 +- hopefully make the askpass dialog less confusing (#174765) + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Tue Nov 22 2005 Tomas Mraz - 4.2p1-9 +- drop x11-ssh-askpass from the package +- drop old build_6x ifs from spec file +- improve gnome-ssh-askpass so it doesn't reveal number of passphrase + characters to person looking at the display +- less hackish fix for the __USE_GNU problem + +* Fri Nov 18 2005 Nalin Dahyabhai - 4.2p1-8 +- work around missing gccmakedep by wrapping makedepend in a local script +- remove now-obsolete build dependency on "xauth" + +* Thu Nov 17 2005 Warren Togami - 4.2p1-7 +- xorg-x11-devel -> libXt-devel +- rebuild for new xauth location so X forwarding works +- buildreq audit-libs-devel +- buildreq automake for aclocal +- buildreq imake for xmkmf +- -D_GNU_SOURCE in flags in order to get it to build + Ugly hack to workaround openssh defining __USE_GNU which is + not allowed and causes problems according to Ulrich Drepper + fix this the correct way after FC5test1 + +* Wed Nov 9 2005 Jeremy Katz - 4.2p1-6 +- rebuild against new openssl + +* Fri Oct 28 2005 Tomas Mraz 4.2p1-5 +- put back the possibility to skip SELinux patch +- add patch for user login auditing by Steve Grubb + +* Tue Oct 18 2005 Dan Walsh 4.2p1-4 +- Change selinux patch to use get_default_context_with_rolelevel in libselinux. + +* Thu Oct 13 2005 Tomas Mraz 4.2p1-3 +- Update selinux patch to use getseuserbyname + +* Fri Oct 7 2005 Tomas Mraz 4.2p1-2 +- use include instead of pam_stack in pam config +- use fork+exec instead of system in scp - CVE-2006-0225 (#168167) +- upstream patch for displaying authentication errors + +* Tue Sep 06 2005 Tomas Mraz 4.2p1-1 +- upgrade to a new upstream version + +* Tue Aug 16 2005 Tomas Mraz 4.1p1-5 +- use x11-ssh-askpass if openssh-askpass-gnome is not installed (#165207) +- install ssh-copy-id from contrib (#88707) + +* Wed Jul 27 2005 Tomas Mraz 4.1p1-4 +- don't deadlock on exit with multiple X forwarded channels (#152432) +- don't use X11 port which can't be bound on all IP families (#163732) + +* Wed Jun 29 2005 Tomas Mraz 4.1p1-3 +- fix small regression caused by the nologin patch (#161956) +- fix race in getpeername error checking (mindrot #1054) + +* Thu Jun 9 2005 Tomas Mraz 4.1p1-2 +- use only pam_nologin for nologin testing + +* Mon Jun 6 2005 Tomas Mraz 4.1p1-1 +- upgrade to a new upstream version +- call pam_loginuid as a pam session module + +* Mon May 16 2005 Tomas Mraz 4.0p1-3 +- link libselinux only to sshd (#157678) + +* Mon Apr 4 2005 Tomas Mraz 4.0p1-2 +- fixed Local/RemoteForward in ssh_config.5 manpage +- fix fatal when Local/RemoteForward is used and scp run (#153258) +- don't leak user validity when using krb5 authentication + +* Thu Mar 24 2005 Tomas Mraz 4.0p1-1 +- upgrade to 4.0p1 +- remove obsolete groups patch + +* Wed Mar 16 2005 Elliot Lee +- rebuilt + +* Mon Feb 28 2005 Nalin Dahyabhai 3.9p1-12 +- rebuild so that configure can detect that krb5_init_ets is gone now + +* Mon Feb 21 2005 Tomas Mraz 3.9p1-11 +- don't call syslog in signal handler +- allow password authentication when copying from remote + to remote machine (#103364) + +* Wed Feb 9 2005 Tomas Mraz +- add spaces to messages in initscript (#138508) + +* Tue Feb 8 2005 Tomas Mraz 3.9p1-10 +- enable trusted forwarding by default if X11 forwarding is + required by user (#137685 and duplicates) +- disable protocol 1 support by default in sshd server config (#88329) +- keep the gnome-askpass dialog above others (#69131) + +* Fri Feb 4 2005 Tomas Mraz +- change permissions on pam.d/sshd to 0644 (#64697) +- patch initscript so it doesn't kill opened sessions if + the sshd daemon isn't running anymore (#67624) + +* Mon Jan 3 2005 Bill Nottingham 3.9p1-9 +- don't use initlog + +* Mon Nov 29 2004 Thomas Woerner 3.9p1-8.1 +- fixed PIE build for all architectures + +* Mon Oct 4 2004 Nalin Dahyabhai 3.9p1-8 +- add a --enable-vendor-patchlevel option which allows a ShowPatchLevel option + to enable display of a vendor patch level during version exchange (#120285) +- configure with --disable-strip to build useful debuginfo subpackages + +* Mon Sep 20 2004 Bill Nottingham 3.9p1-7 +- when using gtk2 for askpass, don't buildprereq gnome-libs-devel + +* Tue Sep 14 2004 Nalin Dahyabhai 3.9p1-6 +- build + +* Mon Sep 13 2004 Nalin Dahyabhai +- disable ACSS support + +* Thu Sep 2 2004 Daniel Walsh 3.9p1-5 +- Change selinux patch to use get_default_context_with_role in libselinux. + +* Thu Sep 2 2004 Daniel Walsh 3.9p1-4 +- Fix patch + * Bad debug statement. + * Handle root/sysadm_r:kerberos + +* Thu Sep 2 2004 Daniel Walsh 3.9p1-3 +- Modify Colin Walter's patch to allow specifying rule during connection + +* Tue Aug 31 2004 Daniel Walsh 3.9p1-2 +- Fix TTY handling for SELinux + +* Tue Aug 24 2004 Daniel Walsh 3.9p1-1 +- Update to upstream + +* Sun Aug 1 2004 Alan Cox 3.8.1p1-5 +- Apply buildreq fixup patch (#125296) + +* Tue Jun 15 2004 Daniel Walsh 3.8.1p1-4 +- Clean up patch for upstream submission. + +* Tue Jun 15 2004 Elliot Lee +- rebuilt + +* Wed Jun 9 2004 Daniel Walsh 3.8.1p1-2 +- Remove use of pam_selinux and patch selinux in directly. + +* Mon Jun 7 2004 Nalin Dahyabhai 3.8.1p1-1 +- request gssapi-with-mic by default but not delegation (flag day for anyone + who used previous gssapi patches) +- no longer request x11 forwarding by default + +* Thu Jun 3 2004 Daniel Walsh 3.6.1p2-36 +- Change pam file to use open and close with pam_selinux + +* Tue Jun 1 2004 Nalin Dahyabhai 3.8.1p1-0 +- update to 3.8.1p1 +- add workaround from CVS to reintroduce passwordauth using pam + +* Tue Jun 1 2004 Daniel Walsh 3.6.1p2-35 +- Remove CLOSEXEC on STDERR + +* Tue Mar 16 2004 Daniel Walsh 3.6.1p2-34 + +* Wed Mar 03 2004 Phil Knirsch 3.6.1p2-33.30.1 +- Built RHLE3 U2 update package. + +* Wed Mar 3 2004 Daniel Walsh 3.6.1p2-33 +- Close file descriptors on exec + +* Mon Mar 1 2004 Thomas Woerner 3.6.1p2-32 +- fixed pie build + +* Thu Feb 26 2004 Daniel Walsh 3.6.1p2-31 +- Add restorecon to startup scripts + +* Thu Feb 26 2004 Daniel Walsh 3.6.1p2-30 +- Add multiple qualified to openssh + +* Mon Feb 23 2004 Daniel Walsh 3.6.1p2-29 +- Eliminate selinux code and use pam_selinux + +* Fri Feb 13 2004 Elliot Lee +- rebuilt + +* Mon Jan 26 2004 Daniel Walsh 3.6.1p2-27 +- turn off pie on ppc + +* Mon Jan 26 2004 Daniel Walsh 3.6.1p2-26 +- fix is_selinux_enabled + +* Wed Jan 14 2004 Daniel Walsh 3.6.1p2-25 +- Rebuild to grab shared libselinux + +* Wed Dec 3 2003 Daniel Walsh 3.6.1p2-24 +- turn on selinux + +* Tue Nov 18 2003 Nalin Dahyabhai +- un#ifdef out code for reporting password expiration in non-privsep + mode (#83585) + +* Mon Nov 10 2003 Nalin Dahyabhai +- add machinery to build with/without -fpie/-pie, default to doing so + +* Thu Nov 06 2003 David Woodhouse 3.6.1p2-23 +- Don't whinge about getsockopt failing (#109161) + +* Fri Oct 24 2003 Nalin Dahyabhai +- add missing buildprereq on zlib-devel (#104558) + +* Mon Oct 13 2003 Daniel Walsh 3.6.1p2-22 +- turn selinux off + +* Mon Oct 13 2003 Daniel Walsh 3.6.1p2-21.sel +- turn selinux on + +* Fri Sep 19 2003 Daniel Walsh 3.6.1p2-21 +- turn selinux off + +* Fri Sep 19 2003 Daniel Walsh 3.6.1p2-20.sel +- turn selinux on + +* Fri Sep 19 2003 Nalin Dahyabhai +- additional fix for apparently-never-happens double-free in buffer_free() +- extend fix for #103998 to cover SSH1 + +* Wed Sep 17 2003 Nalin Dahyabhai 3.6.1p2-19 +- rebuild + +* Wed Sep 17 2003 Nalin Dahyabhai 3.6.1p2-18 +- additional buffer manipulation cleanups from Solar Designer + +* Wed Sep 17 2003 Daniel Walsh 3.6.1p2-17 +- turn selinux off + +* Wed Sep 17 2003 Daniel Walsh 3.6.1p2-16.sel +- turn selinux on + +* Tue Sep 16 2003 Bill Nottingham 3.6.1p2-15 +- rebuild + +* Tue Sep 16 2003 Bill Nottingham 3.6.1p2-14 +- additional buffer manipulation fixes (CAN-2003-0695) + +* Tue Sep 16 2003 Daniel Walsh 3.6.1p2-13.sel +- turn selinux on + +* Tue Sep 16 2003 Nalin Dahyabhai 3.6.1p2-12 +- rebuild + +* Tue Sep 16 2003 Nalin Dahyabhai 3.6.1p2-11 +- apply patch to store the correct buffer size in allocated buffers + (CAN-2003-0693) +- skip the initial PAM authentication attempt with an empty password if + empty passwords are not permitted in our configuration (#103998) + +* Fri Sep 5 2003 Daniel Walsh 3.6.1p2-10 +- turn selinux off + +* Fri Sep 5 2003 Daniel Walsh 3.6.1p2-9.sel +- turn selinux on + +* Tue Aug 26 2003 Daniel Walsh 3.6.1p2-8 +- Add BuildPreReq gtk2-devel if gtk2 + +* Tue Aug 12 2003 Nalin Dahyabhai 3.6.1p2-7 +- rebuild + +* Tue Aug 12 2003 Nalin Dahyabhai 3.6.1p2-6 +- modify patch which clears the supplemental group list at startup to only + complain if setgroups() fails if sshd has euid == 0 +- handle krb5 installed in %%{_prefix} or elsewhere by using krb5-config + +* Mon Jul 28 2003 Daniel Walsh 3.6.1p2-5 +- Add SELinux patch + +* Tue Jul 22 2003 Nalin Dahyabhai 3.6.1p2-4 +- rebuild + +* Wed Jul 16 2003 Nalin Dahyabhai 3.6.1p2-3 +- rebuild + +* Wed Jul 16 2003 Nalin Dahyabhai 3.6.1p2-2 +- rebuild + +* Thu Jun 5 2003 Nalin Dahyabhai 3.6.1p2-1 +- update to 3.6.1p2 + +* Wed Jun 04 2003 Elliot Lee +6 rebuilt + +* Mon Mar 24 2003 Florian La Roche +- add patch for getsockopt() call to work on bigendian 64bit archs + +* Fri Feb 14 2003 Nalin Dahyabhai 3.5p1-6 +- move scp to the -clients subpackage, because it directly depends on ssh + which is also in -clients (#84329) + +* Mon Feb 10 2003 Nalin Dahyabhai 3.5p1-5 +- rebuild + +* Wed Jan 22 2003 Tim Powers +- rebuilt + +* Tue Jan 7 2003 Nalin Dahyabhai 3.5p1-3 +- rebuild + +* Tue Nov 12 2002 Nalin Dahyabhai 3.5p1-2 +- patch PAM configuration to use relative path names for the modules, allowing + us to not worry about which arch the modules are built for on multilib systems + +* Tue Oct 15 2002 Nalin Dahyabhai 3.5p1-1 +- update to 3.5p1, merging in filelist/perm changes from the upstream spec + +* Fri Oct 4 2002 Nalin Dahyabhai 3.4p1-3 +- merge + +* Thu Sep 12 2002 Than Ngo 3.4p1-2.1 +- fix to build on multilib systems + +* Thu Aug 29 2002 Curtis Zinzilieta 3.4p1-2gss +- added gssapi patches and uncommented patch here + +* Wed Aug 14 2002 Nalin Dahyabhai 3.4p1-2 +- pull patch from CVS to fix too-early free in ssh-keysign (#70009) + +* Thu Jun 27 2002 Nalin Dahyabhai 3.4p1-1 +- 3.4p1 +- drop anon mmap patch + +* Tue Jun 25 2002 Nalin Dahyabhai 3.3p1-2 +- rework the close-on-exit docs +- include configuration file man pages +- make use of nologin as the privsep shell optional + +* Mon Jun 24 2002 Nalin Dahyabhai 3.3p1-1 +- update to 3.3p1 +- merge in spec file changes from upstream (remove setuid from ssh, ssh-keysign) +- disable gtk2 askpass +- require pam-devel by filename rather than by package for erratum +- include patch from Solar Designer to work around anonymous mmap failures + +* Fri Jun 21 2002 Tim Powers +- automated rebuild + +* Fri Jun 7 2002 Nalin Dahyabhai 3.2.3p1-3 +- don't require autoconf any more + +* Fri May 31 2002 Nalin Dahyabhai 3.2.3p1-2 +- build gnome-ssh-askpass with gtk2 + +* Tue May 28 2002 Nalin Dahyabhai 3.2.3p1-1 +- update to 3.2.3p1 +- merge in spec file changes from upstream + +* Fri May 17 2002 Nalin Dahyabhai 3.2.2p1-1 +- update to 3.2.2p1 + +* Fri May 17 2002 Nalin Dahyabhai 3.1p1-4 +- drop buildreq on db1-devel +- require pam-devel by package name +- require autoconf instead of autoconf253 again + +* Tue Apr 2 2002 Nalin Dahyabhai 3.1p1-3 +- pull patch from CVS to avoid printing error messages when some of the + default keys aren't available when running ssh-add +- refresh to current revisions of Simon's patches + +* Thu Mar 21 2002 Nalin Dahyabhai 3.1p1-2gss +- reintroduce Simon's gssapi patches +- add buildprereq for autoconf253, which is needed to regenerate configure + after applying the gssapi patches +- refresh to the latest version of Markus's patch to build properly with + older versions of OpenSSL + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-2 +- bump and grind (through the build system) + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-1 +- require sharutils for building (mindrot #137) +- require db1-devel only when building for 6.x (#55105), which probably won't + work anyway (3.1 requires OpenSSL 0.9.6 to build), but what the heck +- require pam-devel by file (not by package name) again +- add Markus's patch to compile with OpenSSL 0.9.5a (from + http://bugzilla.mindrot.org/show_bug.cgi?id=141) and apply it if we're + building for 6.x + +* Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-0 +- update to 3.1p1 + +* Tue Mar 5 2002 Nalin Dahyabhai SNAP-20020305 +- update to SNAP-20020305 +- drop debug patch, fixed upstream + +* Wed Feb 20 2002 Nalin Dahyabhai SNAP-20020220 +- update to SNAP-20020220 for testing purposes (you've been warned, if there's + anything to be warned about, gss patches won't apply, I don't mind) + +* Wed Feb 13 2002 Nalin Dahyabhai 3.0.2p1-3 +- add patches from Simon Wilkinson and Nicolas Williams for GSSAPI key + exchange, authentication, and named key support + +* Wed Jan 23 2002 Nalin Dahyabhai 3.0.2p1-2 +- remove dependency on db1-devel, which has just been swallowed up whole + by gnome-libs-devel + +* Sat Dec 29 2001 Nalin Dahyabhai +- adjust build dependencies so that build6x actually works right (fix + from Hugo van der Kooij) + +* Tue Dec 4 2001 Nalin Dahyabhai 3.0.2p1-1 +- update to 3.0.2p1 + +* Fri Nov 16 2001 Nalin Dahyabhai 3.0.1p1-1 +- update to 3.0.1p1 + +* Tue Nov 13 2001 Nalin Dahyabhai +- update to current CVS (not for use in distribution) + +* Thu Nov 8 2001 Nalin Dahyabhai 3.0p1-1 +- merge some of Damien Miller changes from the upstream + 3.0p1 spec file and init script + +* Wed Nov 7 2001 Nalin Dahyabhai +- update to 3.0p1 +- update to x11-ssh-askpass 1.2.4.1 +- change build dependency on a file from pam-devel to the pam-devel package +- replace primes with moduli + +* Thu Sep 27 2001 Nalin Dahyabhai 2.9p2-9 +- incorporate fix from Markus Friedl's advisory for IP-based authorization bugs + +* Thu Sep 13 2001 Bernhard Rosenkraenzer 2.9p2-8 +- Merge changes to rescue build from current sysadmin survival cd + +* Thu Sep 6 2001 Nalin Dahyabhai 2.9p2-7 +- fix scp's server's reporting of file sizes, and build with the proper + preprocessor define to get large-file capable open(), stat(), etc. + (sftp has been doing this correctly all along) (#51827) +- configure without --with-ipv4-default on RHL 7.x and newer (#45987,#52247) +- pull cvs patch to fix support for /etc/nologin for non-PAM logins (#47298) +- mark profile.d scriptlets as config files (#42337) +- refer to Jason Stone's mail for zsh workaround for exit-hanging quasi-bug +- change a couple of log() statements to debug() statements (#50751) +- pull cvs patch to add -t flag to sshd (#28611) +- clear fd_sets correctly (one bit per FD, not one byte per FD) (#43221) + +* Mon Aug 20 2001 Nalin Dahyabhai 2.9p2-6 +- add db1-devel as a BuildPrerequisite (noted by Hans Ecke) + +* Thu Aug 16 2001 Nalin Dahyabhai +- pull cvs patch to fix remote port forwarding with protocol 2 + +* Thu Aug 9 2001 Nalin Dahyabhai +- pull cvs patch to add session initialization to no-pty sessions +- pull cvs patch to not cut off challengeresponse auth needlessly +- refuse to do X11 forwarding if xauth isn't there, handy if you enable + it by default on a system that doesn't have X installed (#49263) + +* Wed Aug 8 2001 Nalin Dahyabhai +- don't apply patches to code we don't intend to build (spotted by Matt Galgoci) + +* Mon Aug 6 2001 Nalin Dahyabhai +- pass OPTIONS correctly to initlog (#50151) + +* Wed Jul 25 2001 Nalin Dahyabhai +- switch to x11-ssh-askpass 1.2.2 + +* Wed Jul 11 2001 Nalin Dahyabhai +- rebuild in new environment + +* Mon Jun 25 2001 Nalin Dahyabhai +- disable the gssapi patch + +* Mon Jun 18 2001 Nalin Dahyabhai +- update to 2.9p2 +- refresh to a new version of the gssapi patch + +* Thu Jun 7 2001 Nalin Dahyabhai +- change Copyright: BSD to License: BSD +- add Markus Friedl's unverified patch for the cookie file deletion problem + so that we can verify it +- drop patch to check if xauth is present (was folded into cookie patch) +- don't apply gssapi patches for the errata candidate +- clear supplemental groups list at startup + +* Fri May 25 2001 Nalin Dahyabhai +- fix an error parsing the new default sshd_config +- add a fix from Markus Friedl (via openssh-unix-dev) for ssh-keygen not + dealing with comments right + +* Thu May 24 2001 Nalin Dahyabhai +- add in Simon Wilkinson's GSSAPI patch to give it some testing in-house, + to be removed before the next beta cycle because it's a big departure + from the upstream version + +* Thu May 3 2001 Nalin Dahyabhai +- finish marking strings in the init script for translation +- modify init script to source /etc/sysconfig/sshd and pass $OPTIONS to sshd + at startup (change merged from openssh.com init script, originally by + Pekka Savola) +- refuse to do X11 forwarding if xauth isn't there, handy if you enable + it by default on a system that doesn't have X installed + +* Wed May 2 2001 Nalin Dahyabhai +- update to 2.9 +- drop various patches that came from or went upstream or to or from CVS + +* Wed Apr 18 2001 Nalin Dahyabhai +- only require initscripts 5.00 on 6.2 (reported by Peter Bieringer) + +* Sun Apr 8 2001 Preston Brown +- remove explicit openssl requirement, fixes builddistro issue +- make initscript stop() function wait until sshd really dead to avoid + races in condrestart + +* Mon Apr 2 2001 Nalin Dahyabhai +- mention that challengereponse supports PAM, so disabling password doesn't + limit users to pubkey and rsa auth (#34378) +- bypass the daemon() function in the init script and call initlog directly, + because daemon() won't start a daemon it detects is already running (like + open connections) +- require the version of openssl we had when we were built + +* Fri Mar 23 2001 Nalin Dahyabhai +- make do_pam_setcred() smart enough to know when to establish creds and + when to reinitialize them +- add in a couple of other fixes from Damien for inclusion in the errata + +* Thu Mar 22 2001 Nalin Dahyabhai +- update to 2.5.2p2 +- call setcred() again after initgroups, because the "creds" could actually + be group memberships + +* Tue Mar 20 2001 Nalin Dahyabhai +- update to 2.5.2p1 (includes endianness fixes in the rijndael implementation) +- don't enable challenge-response by default until we find a way to not + have too many userauth requests (we may make up to six pubkey and up to + three password attempts as it is) +- remove build dependency on rsh to match openssh.com's packages more closely + +* Sat Mar 3 2001 Nalin Dahyabhai +- remove dependency on openssl -- would need to be too precise + +* Fri Mar 2 2001 Nalin Dahyabhai +- rebuild in new environment + +* Mon Feb 26 2001 Nalin Dahyabhai +- Revert the patch to move pam_open_session. +- Init script and spec file changes from Pekka Savola. (#28750) +- Patch sftp to recognize '-o protocol' arguments. (#29540) + +* Thu Feb 22 2001 Nalin Dahyabhai +- Chuck the closing patch. +- Add a trigger to add host keys for protocol 2 to the config file, now that + configuration file syntax requires us to specify it with HostKey if we + specify any other HostKey values, which we do. + +* Tue Feb 20 2001 Nalin Dahyabhai +- Redo patch to move pam_open_session after the server setuid()s to the user. +- Rework the nopam patch to use be picked up by autoconf. + +* Mon Feb 19 2001 Nalin Dahyabhai +- Update for 2.5.1p1. +- Add init script mods from Pekka Savola. +- Tweak the init script to match the CVS contrib script more closely. +- Redo patch to ssh-add to try to adding both identity and id_dsa to also try + adding id_rsa. + +* Fri Feb 16 2001 Nalin Dahyabhai +- Update for 2.5.0p1. +- Use $RPM_OPT_FLAGS instead of -O when building gnome-ssh-askpass +- Resync with parts of Damien Miller's openssh.spec from CVS, including + update of x11 askpass to 1.2.0. +- Only require openssl (don't prereq) because we generate keys in the init + script now. + +* Tue Feb 13 2001 Nalin Dahyabhai +- Don't open a PAM session until we've forked and become the user (#25690). +- Apply Andrew Bartlett's patch for letting pam_authenticate() know which + host the user is attempting a login from. +- Resync with parts of Damien Miller's openssh.spec from CVS. +- Don't expose KbdInt responses in debug messages (from CVS). +- Detect and handle errors in rsa_{public,private}_decrypt (from CVS). + +* Wed Feb 7 2001 Trond Eivind Glomsrxd +- i18n-tweak to initscript. + +* Tue Jan 23 2001 Nalin Dahyabhai +- More gettextizing. +- Close all files after going into daemon mode (needs more testing). +- Extract patch from CVS to handle auth banners (in the client). +- Extract patch from CVS to handle compat weirdness. + +* Fri Jan 19 2001 Nalin Dahyabhai +- Finish with the gettextizing. + +* Thu Jan 18 2001 Nalin Dahyabhai +- Fix a bug in auth2-pam.c (#23877) +- Gettextize the init script. + +* Wed Dec 20 2000 Nalin Dahyabhai +- Incorporate a switch for using PAM configs for 6.x, just in case. + +* Tue Dec 5 2000 Nalin Dahyabhai +- Incorporate Bero's changes for a build specifically for rescue CDs. + +* Wed Nov 29 2000 Nalin Dahyabhai +- Don't treat pam_setcred() failure as fatal unless pam_authenticate() has + succeeded, to allow public-key authentication after a failure with "none" + authentication. (#21268) + +* Tue Nov 28 2000 Nalin Dahyabhai +- Update to x11-askpass 1.1.1. (#21301) +- Don't second-guess fixpaths, which causes paths to get fixed twice. (#21290) + +* Mon Nov 27 2000 Nalin Dahyabhai +- Merge multiple PAM text messages into subsequent prompts when possible when + doing keyboard-interactive authentication. + +* Sun Nov 26 2000 Nalin Dahyabhai +- Disable the built-in MD5 password support. We're using PAM. +- Take a crack at doing keyboard-interactive authentication with PAM, and + enable use of it in the default client configuration so that the client + will try it when the server disallows password authentication. +- Build with debugging flags. Build root policies strip all binaries anyway. + +* Tue Nov 21 2000 Nalin Dahyabhai +- Use DESTDIR instead of %%makeinstall. +- Remove /usr/X11R6/bin from the path-fixing patch. + +* Mon Nov 20 2000 Nalin Dahyabhai +- Add the primes file from the latest snapshot to the main package (#20884). +- Add the dev package to the prereq list (#19984). +- Remove the default path and mimic login's behavior in the server itself. + +* Fri Nov 17 2000 Nalin Dahyabhai +- Resync with conditional options in Damien Miller's .spec file for an errata. +- Change libexecdir from %%{_libexecdir}/ssh to %%{_libexecdir}/openssh. + +* Tue Nov 7 2000 Nalin Dahyabhai +- Update to OpenSSH 2.3.0p1. +- Update to x11-askpass 1.1.0. +- Enable keyboard-interactive authentication. + +* Mon Oct 30 2000 Nalin Dahyabhai +- Update to ssh-askpass-x11 1.0.3. +- Change authentication related messages to be private (#19966). + +* Tue Oct 10 2000 Nalin Dahyabhai +- Patch ssh-keygen to be able to list signatures for DSA public key files + it generates. + +* Thu Oct 5 2000 Nalin Dahyabhai +- Add BuildPreReq on /usr/include/security/pam_appl.h to be sure we always + build PAM authentication in. +- Try setting SSH_ASKPASS if gnome-ssh-askpass is installed. +- Clean out no-longer-used patches. +- Patch ssh-add to try to add both identity and id_dsa, and to error only + when neither exists. + +* Mon Oct 2 2000 Nalin Dahyabhai +- Update x11-askpass to 1.0.2. (#17835) +- Add BuildPreReqs for /bin/login and /usr/bin/rsh so that configure will + always find them in the right place. (#17909) +- Set the default path to be the same as the one supplied by /bin/login, but + add /usr/X11R6/bin. (#17909) +- Try to handle obsoletion of ssh-server more cleanly. Package names + are different, but init script name isn't. (#17865) + +* Wed Sep 6 2000 Nalin Dahyabhai +- Update to 2.2.0p1. (#17835) +- Tweak the init script to allow proper restarting. (#18023) + +* Wed Aug 23 2000 Nalin Dahyabhai +- Update to 20000823 snapshot. +- Change subpackage requirements from %%{version} to %%{version}-%%{release} +- Back out the pipe patch. + +* Mon Jul 17 2000 Nalin Dahyabhai +- Update to 2.1.1p4, which includes fixes for config file parsing problems. +- Move the init script back. +- Add Damien's quick fix for wackiness. + +* Wed Jul 12 2000 Nalin Dahyabhai +- Update to 2.1.1p3, which includes fixes for X11 forwarding and strtok(). + +* Thu Jul 6 2000 Nalin Dahyabhai +- Move condrestart to server postun. +- Move key generation to init script. +- Actually use the right patch for moving the key generation to the init script. +- Clean up the init script a bit. + +* Wed Jul 5 2000 Nalin Dahyabhai +- Fix X11 forwarding, from mail post by Chan Shih-Ping Richard. + +* Sun Jul 2 2000 Nalin Dahyabhai +- Update to 2.1.1p2. +- Use of strtok() considered harmful. + +* Sat Jul 1 2000 Nalin Dahyabhai +- Get the build root out of the man pages. + +* Thu Jun 29 2000 Nalin Dahyabhai +- Add and use condrestart support in the init script. +- Add newer initscripts as a prereq. + +* Tue Jun 27 2000 Nalin Dahyabhai +- Build in new environment (release 2) +- Move -clients subpackage to Applications/Internet group + +* Fri Jun 9 2000 Nalin Dahyabhai +- Update to 2.2.1p1 + +* Sat Jun 3 2000 Nalin Dahyabhai +- Patch to build with neither RSA nor RSAref. +- Miscellaneous FHS-compliance tweaks. +- Fix for possibly-compressed man pages. + +* Wed Mar 15 2000 Damien Miller +- Updated for new location +- Updated for new gnome-ssh-askpass build + +* Sun Dec 26 1999 Damien Miller +- Added Jim Knoble's askpass + +* Mon Nov 15 1999 Damien Miller +- Split subpackages further based on patch from jim knoble + +* Sat Nov 13 1999 Damien Miller +- Added 'Obsoletes' directives + +* Tue Nov 09 1999 Damien Miller +- Use make install +- Subpackages + +* Mon Nov 08 1999 Damien Miller +- Added links for slogin +- Fixed perms on manpages + +* Sat Oct 30 1999 Damien Miller +- Renamed init script + +* Fri Oct 29 1999 Damien Miller +- Back to old binary names + +* Thu Oct 28 1999 Damien Miller +- Use autoconf +- New binary names + +* Wed Oct 27 1999 Damien Miller +- Initial RPMification, based on Jan "Yenya" Kasprzak's spec.