summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--add-loongarch.patch26
-rw-r--r--add-strict-scp-check-for-CVE-2020-15778.patch160
-rw-r--r--bugfix-openssh-add-option-check-username-splash.patch106
-rw-r--r--bugfix-openssh-fix-sftpserver.patch71
-rw-r--r--bugfix-sftp-when-parse_user_host_path-empty-path-should-be-allowed.patch44
-rw-r--r--feature-add-SMx-support.patch1507
-rw-r--r--feature-openssh-7.4-hima-sftpserver-oom-and-fix.patch911
-rw-r--r--openssh-4.3p2-askpass-grab-info.patch18
-rw-r--r--openssh-5.1p1-askpass-progress.patch83
-rw-r--r--openssh-5.8p2-sigpipe.patch14
-rw-r--r--openssh-5.9p1-ipv6man.patch24
-rw-r--r--openssh-6.4p1-fromto-remote.patch16
-rw-r--r--openssh-6.6.1p1-log-in-chroot.patch263
-rw-r--r--openssh-6.6.1p1-scp-non-existing-directory.patch14
-rw-r--r--openssh-6.6.1p1-selinux-contexts.patch133
-rw-r--r--openssh-6.6p1-GSSAPIEnablek5users.patch131
-rw-r--r--openssh-6.6p1-allow-ip-opts.patch42
-rw-r--r--openssh-6.6p1-force_krb.patch280
-rw-r--r--openssh-6.6p1-keycat.patch484
-rw-r--r--openssh-6.6p1-kuserok.patch289
-rw-r--r--openssh-6.6p1-privsep-selinux.patch121
-rw-r--r--openssh-6.7p1-coverity.patch366
-rw-r--r--openssh-6.7p1-sftp-force-permission.patch100
-rw-r--r--openssh-6.8p1-sshdT-output.patch12
-rw-r--r--openssh-7.1p2-audit-race-condition.patch187
-rw-r--r--openssh-7.2p2-k5login_directory.patch87
-rw-r--r--openssh-7.2p2-s390-closefrom.patch52
-rw-r--r--openssh-7.2p2-x11.patch53
-rw-r--r--openssh-7.3p1-x11-max-displays.patch213
-rw-r--r--openssh-7.4p1-systemd.patch98
-rw-r--r--openssh-7.5p1-sandbox.patch86
-rw-r--r--openssh-7.6p1-audit.patch2314
-rw-r--r--openssh-7.6p1-cleanup-selinux.patch283
-rw-r--r--openssh-7.7p1-gssapi-new-unique.patch632
-rw-r--r--openssh-7.7p1.patch105
-rw-r--r--openssh-7.8p1-UsePAM-warning.patch26
-rw-r--r--openssh-7.8p1-role-mls.patch867
-rw-r--r--openssh-7.8p1-scp-ipv6.patch16
-rw-r--r--openssh-8.0p1-crypto-policies.patch632
-rw-r--r--openssh-8.0p1-gssapi-keyex.patch4009
-rw-r--r--openssh-8.0p1-keygen-strip-doseol.patch12
-rw-r--r--openssh-8.0p1-openssl-kdf.patch137
-rw-r--r--openssh-8.0p1-pkcs11-uri.patch3059
-rw-r--r--openssh-8.0p1-preserve-pam-errors.patch44
-rw-r--r--openssh-8.2p1-visibility.patch40
-rw-r--r--openssh-8.2p1-x11-without-ipv6.patch30
-rw-r--r--openssh-8.7p1-ibmca.patch12
-rw-r--r--openssh-8.7p1-minrsabits.patch24
-rw-r--r--openssh-8.7p1-negotiate-supported-algs.patch117
-rw-r--r--openssh-8.7p1-recursive-scp.patch181
-rw-r--r--openssh-8.7p1-scp-kill-switch.patch46
-rw-r--r--openssh-8.7p1-ssh-manpage.patch53
-rw-r--r--openssh-9.3p1-merged-openssl-evp.patch1228
-rw-r--r--openssh-9.3p1-upstream-cve-2023-38408.patch130
-rw-r--r--openssh-9.3p1.tar.gz.asc16
-rw-r--r--openssh-Add-sw64-architecture.patch27
-rw-r--r--openssh.spec745
-rw-r--r--pam_ssh_agent-configure-c99.patch249
-rw-r--r--pam_ssh_agent-rmheaders36
-rw-r--r--pam_ssh_agent_auth-0.10.2-compat.patch992
-rw-r--r--pam_ssh_agent_auth-0.10.2-dereference.patch20
-rw-r--r--pam_ssh_agent_auth-0.10.3-seteuid.patch37
-rw-r--r--pam_ssh_agent_auth-0.10.4-rsasha2.patch19
-rw-r--r--pam_ssh_agent_auth-0.9.2-visibility.patch21
-rw-r--r--pam_ssh_agent_auth-0.9.3-agent_structure.patch96
-rw-r--r--pam_ssh_agent_auth-0.9.3-build.patch198
-rw-r--r--set-ssh-config.patch30
-rw-r--r--set-sshd-config.patch97
-rw-r--r--skip-scp-test-if-there-is-no-scp-on-remote-path-as-s.patch38
-rw-r--r--sources2
-rw-r--r--ssh-agent.service19
-rw-r--r--ssh-agent.socket14
-rw-r--r--ssh-keycat.pam8
-rw-r--r--ssh-keygen-bash-completion.sh63
-rw-r--r--sshd-keygen40
-rw-r--r--sshd-keygen.target5
-rw-r--r--sshd-keygen@.service11
-rw-r--r--sshd.pam17
-rw-r--r--sshd.service17
-rw-r--r--sshd.socket11
-rw-r--r--sshd.sysconfig7
-rw-r--r--sshd.tmpfiles1
-rw-r--r--sshd@.service10
84 files changed, 22836 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..0885c0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/openssh-9.3p1.tar.gz
+/pam_ssh_agent_auth-0.10.4.tar.gz
diff --git a/add-loongarch.patch b/add-loongarch.patch
new file mode 100644
index 0000000..ae53632
--- /dev/null
+++ b/add-loongarch.patch
@@ -0,0 +1,26 @@
+From b87df88468d90292e7c09b8cfd992011cd812a1c Mon Sep 17 00:00:00 2001
+From: zhaozhen <zhaozhen@loongson.cn>
+Date: Mon, 5 Dec 2022 13:01:36 +0000
+Subject: [PATCH] add loongarch64 support
+
+---
+ configure.ac | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 307e974..aa3e01b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -972,6 +972,9 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+ riscv64-*)
+ seccomp_audit_arch=AUDIT_ARCH_RISCV64
+ ;;
++ loongarch64-*)
++ seccomp_audit_arch=AUDIT_ARCH_LOONGARCHEL64
++ ;;
+ esac
+ if test "x$seccomp_audit_arch" != "x" ; then
+ AC_MSG_RESULT(["$seccomp_audit_arch"])
+--
+2.33.0
+
diff --git a/add-strict-scp-check-for-CVE-2020-15778.patch b/add-strict-scp-check-for-CVE-2020-15778.patch
new file mode 100644
index 0000000..3c3109c
--- /dev/null
+++ b/add-strict-scp-check-for-CVE-2020-15778.patch
@@ -0,0 +1,160 @@
+From 2e0b74242220a97926d006719d1ac6e113918e2b Mon Sep 17 00:00:00 2001
+From: seuzw <930zhaowei@163.com>
+Date: Thu, 20 May 2021 20:23:30 +0800
+Subject: [PATCH] add strict-scp-check for CVE-2020-15778
+
+---
+ servconf.c | 12 ++++++++++++
+ servconf.h | 1 +
+ session.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 64 insertions(+)
+
+diff --git a/servconf.c b/servconf.c
+index 333b802..0a7cfa4 100644
+--- a/servconf.c
++++ b/servconf.c
+@@ -91,6 +91,7 @@ initialize_server_options(ServerOptions *options)
+ {
+ memset(options, 0, sizeof(*options));
+
++ options->strict_scp_check = -1;
+ /* Portable-specific options */
+ options->use_pam = -1;
+
+@@ -309,6 +310,8 @@ fill_default_server_options(ServerOptions *options)
+ _PATH_HOST_XMSS_KEY_FILE, 0);
+ #endif /* WITH_XMSS */
+ }
++ if (options->strict_scp_check == -1)
++ options->strict_scp_check = 0;
+ /* No certificates by default */
+ if (options->num_ports == 0)
+ options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
+@@ -516,6 +519,7 @@ fill_default_server_options(ServerOptions *options)
+ /* Keyword tokens. */
+ typedef enum {
+ sBadOption, /* == unknown option */
++ sStrictScpCheck,
+ /* Portable-specific options */
+ sUsePAM,
+ /* Standard Options */
+@@ -573,6 +577,7 @@ static struct {
+ #else
+ { "usepam", sUnsupported, SSHCFG_GLOBAL },
+ #endif
++ { "strictscpcheck", sStrictScpCheck, SSHCFG_GLOBAL },
+ { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
+ /* Standard Options */
+ { "port", sPort, SSHCFG_GLOBAL },
+@@ -1391,6 +1396,11 @@ process_server_config_line_depth(ServerOptions *options, char *line,
+ /* Standard Options */
+ case sBadOption:
+ goto out;
++
++ case sStrictScpCheck:
++ intptr = &options->strict_scp_check;
++ goto parse_flag;
++
+ case sPort:
+ /* ignore ports from configfile if cmdline specifies ports */
+ if (options->ports_from_cmdline) {
+@@ -2666,6 +2676,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
+ dst->n = src->n; \
+ } while (0)
+
++ M_CP_INTOPT(strict_scp_check);
+ M_CP_INTOPT(password_authentication);
+ M_CP_INTOPT(gss_authentication);
+ M_CP_INTOPT(pubkey_authentication);
+@@ -2960,6 +2971,7 @@ dump_config(ServerOptions *o)
+ #ifdef USE_PAM
+ dump_cfg_fmtint(sUsePAM, o->use_pam);
+ #endif
++ dump_cfg_fmtint(sStrictScpCheck, o->strict_scp_check);
+ dump_cfg_int(sLoginGraceTime, o->login_grace_time);
+ dump_cfg_int(sX11DisplayOffset, o->x11_display_offset);
+ dump_cfg_int(sX11MaxDisplays, o->x11_max_displays);
+diff --git a/servconf.h b/servconf.h
+index cb73d2d..12c2053 100644
+--- a/servconf.h
++++ b/servconf.h
+@@ -203,6 +203,7 @@ typedef struct {
+ * disconnect the session
+ */
+
++ int strict_scp_check;
+ u_int num_authkeys_files; /* Files containing public keys */
+ char **authorized_keys_files;
+
+diff --git a/session.c b/session.c
+index dfbebba..1b67393 100644
+--- a/session.c
++++ b/session.c
+@@ -175,6 +175,51 @@ static char *auth_sock_dir = NULL;
+
+ /* removes the agent forwarding socket */
+
++
++int scp_check(const char *command)
++{
++ debug("Entering scp check");
++ int check = 0;
++ if (command == NULL) {
++ debug("scp check succeeded for shell mode");
++ return check;
++ }
++ int lc = strlen(command);
++ char special_characters[] = "|;&$><`\\!\n";
++ int ls = strlen(special_characters);
++ int count_char[128] = {0};
++
++ for (int i = 0; i < ls; i++) {
++ count_char[special_characters[i]] = 1;
++ }
++
++ char scp_prefix[6] = "scp -";
++ int lp = 5;
++
++ if (lc <= lp) {
++ debug("scp check succeeded for length");
++ return check;
++ }
++
++ for (int i = 0; i < lp; i++) {
++ if (command[i] - scp_prefix[i]) {
++ debug("scp check succeeded for prefix");
++ return check;
++ }
++ }
++
++ for (int i = lp; i < lc; i++) {
++ if (command[i] > 0 && command[i] < 128) {
++ if (count_char[command[i]]) {
++ check = 1;
++ debug("scp check failed at %d: %c", i, command[i]);
++ break;
++ }
++ }
++ }
++ return check;
++}
++
+ static void
+ auth_sock_cleanup_proc(struct passwd *pw)
+ {
+@@ -692,6 +737,12 @@ do_exec(struct ssh *ssh, Session *s, const char *command)
+ command = auth_opts->force_command;
+ forced = "(key-option)";
+ }
++
++ if (options.strict_scp_check && scp_check(command)) {
++ verbose("Special characters not allowed in scp");
++ return 1;
++ }
++
+ #ifdef GSSAPI
+ #ifdef KRB5 /* k5users_allowed_cmds only available w/ GSSAPI+KRB5 */
+ else if (k5users_allowed_cmds) {
+--
+1.8.3.1
+
diff --git a/bugfix-openssh-add-option-check-username-splash.patch b/bugfix-openssh-add-option-check-username-splash.patch
new file mode 100644
index 0000000..1c8af38
--- /dev/null
+++ b/bugfix-openssh-add-option-check-username-splash.patch
@@ -0,0 +1,106 @@
+From 74c1a37dfeab8e9cc39e5bc76891d1d9d66b7638 Mon Sep 17 00:00:00 2001
+From: wangqiang <wangqiang62@huawei.com>
+Date: Thu, 16 Apr 2020 15:58:30 +0800
+Subject: [PATCH] openssh: add option check username splash
+
+add a check to inhibit username contains splash
+add an option 'CheckUserSplash' so that user can turn off
+this check
+---
+ auth2.c | 4 +++-
+ servconf.c | 8 ++++++++
+ servconf.h | 1 +
+ sshd_config | 2 ++
+ 4 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/auth2.c b/auth2.c
+index 4d574bb..c480aab 100644
+--- a/auth2.c
++++ b/auth2.c
+@@ -278,11 +278,13 @@ input_userauth_request(int type, u_int32_t seq, struct ssh *ssh)
+ debug("userauth-request for user %s service %s method %s", user, service, method);
+ debug("attempt %d failures %d", authctxt->attempt, authctxt->failures);
+
++if (options.check_user_splash)
++{
+ #ifdef WITH_SELINUX
+ if ((role = strchr(user, '/')) != NULL)
+ *role++ = 0;
+ #endif
+-
++}
+ if ((style = strchr(user, ':')) != NULL)
+ *style++ = 0;
+
+diff --git a/servconf.c b/servconf.c
+index bcf69fd..b8340d8 100644
+--- a/servconf.c
++++ b/servconf.c
+@@ -199,6 +199,7 @@ initialize_server_options(ServerOptions *options)
+ options->ip_qos_interactive = -1;
+ options->ip_qos_bulk = -1;
+ options->version_addendum = NULL;
++ options->check_user_splash = -1;
+ options->fingerprint_hash = -1;
+ options->disable_forwarding = -1;
+ options->expose_userauth_info = -1;
+@@ -456,6 +457,8 @@ fill_default_server_options(ServerOptions *options)
+ options->ip_qos_bulk = IPTOS_DSCP_CS1;
+ if (options->version_addendum == NULL)
+ options->version_addendum = xstrdup("");
++ if (options->check_user_splash == -1)
++ options->check_user_splash = 1;
+ 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)
+@@ -557,6 +560,7 @@ typedef enum {
+ sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
+ sExposeAuthInfo, sRDomain, sPubkeyAuthOptions, sSecurityKeyProvider,
+ sRequiredRSASize, sChannelTimeout, sUnusedConnectionTimeout,
++ sCheckUserSplash,
+ sDeprecated, sIgnore, sUnsupported
+ } ServerOpCodes;
+
+@@ -730,6 +734,7 @@ static struct {
+ { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
+ { "disableforwarding", sDisableForwarding, SSHCFG_ALL },
+ { "exposeauthinfo", sExposeAuthInfo, SSHCFG_ALL },
++ { "checkusersplash", sCheckUserSplash, SSHCFG_GLOBAL },
+ { "rdomain", sRDomain, SSHCFG_ALL },
+ { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
+ { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
+@@ -1443,6 +1448,9 @@ process_server_config_line_depth(ServerOptions *options, char *line,
+ case sUsePAM:
+ intptr = &options->use_pam;
+ goto parse_flag;
++ case sCheckUserSplash:
++ intptr = &options->check_user_splash;
++ goto parse_flag;
+
+ /* Standard Options */
+ case sBadOption:
+diff --git a/servconf.h b/servconf.h
+index ccc0181..cb57dac 100644
+--- a/servconf.h
++++ b/servconf.h
+@@ -237,6 +237,7 @@ typedef struct {
+ int fingerprint_hash;
+ int expose_userauth_info;
+ u_int64_t timing_secret;
++ int check_user_splash; /* check whether splash exists in username, if exist, disable login */
+ char *sk_provider;
+ int required_rsa_size; /* minimum size of RSA keys */
+
+diff --git a/sshd_config b/sshd_config
+index 9851748..d57f11d 100644
+--- a/sshd_config
++++ b/sshd_config
+@@ -128,3 +128,5 @@ Subsystem sftp /usr/libexec/sftp-server
+ # AllowTcpForwarding no
+ # PermitTTY no
+ # ForceCommand cvs server
++#CheckUserSplash yes
++
+--
+2.23.0
+
diff --git a/bugfix-openssh-fix-sftpserver.patch b/bugfix-openssh-fix-sftpserver.patch
new file mode 100644
index 0000000..07aa3f5
--- /dev/null
+++ b/bugfix-openssh-fix-sftpserver.patch
@@ -0,0 +1,71 @@
+From 28bdd1cd8177f0af2827524fb79aa4d8ff52fdf7 Mon Sep 17 00:00:00 2001
+From: majun65 <majun65@huawei.com>
+Date: Mon, 11 Nov 2019 14:19:47 +0800
+Subject: [PATCH] Module:
+
+Signed-off-by: majun65 <majun65@huawei.com>
+---
+ sftp-server.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/sftp-server.c b/sftp-server.c
+index f39178d..392df9d 100644
+--- a/sftp-server.c
++++ b/sftp-server.c
+@@ -145,6 +145,11 @@ read_config_file(const char* pszPath, char(*szConfigPath)[MAXPATHLEN])
+ break;
+ }
+ //Fix bug exceed max permit dir 2013-10-18 end
++ if ( strlen(szBuffer) > MAXPATHLEN )
++ {
++ debug("[sftp-server]Exceed max number of realpath.\n");
++ break;
++ }
+ memcpy(szConfigPath[linenum-1], szBuffer , strlen(szBuffer));
+ if ( szConfigPath[linenum-1][strlen(szBuffer)-1] == '\n' )
+ {
+@@ -155,7 +160,7 @@ read_config_file(const char* pszPath, char(*szConfigPath)[MAXPATHLEN])
+ }
+ }
+ }
+-
++ free(szBuffer);
+ fclose(fd);
+ storage_flag = 1;
+ return RETURN_OK;
+@@ -417,7 +422,12 @@ ck_load_server_config(const char *filename, char *conf)
+
+ lenth += strlen(cp);
+ }
+-
++ if (lenth + 1 > BUF_MAX_LEN)
++ {
++ error("%s too big, the max size is %d!", filename, BUF_MAX_LEN);
++ fclose(f);
++ return 0;
++ }
+ memcpy(conf + lenth, "\0", 1);
+
+ fclose(f);
+@@ -1972,8 +1982,6 @@ process_extended_fstatvfs(u_int32_t id)
+ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
+ {
+ send_status(id, SSH2_FX_PERMISSION_DENIED);
+- free(path);
+-
+ return;
+ }
+
+@@ -2030,8 +2038,6 @@ process_extended_fsync(u_int32_t id)
+ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
+ {
+ send_status(id, SSH2_FX_PERMISSION_DENIED);
+- free(path);
+-
+ return;
+ }
+
+--
+2.19.1
+
+
diff --git a/bugfix-sftp-when-parse_user_host_path-empty-path-should-be-allowed.patch b/bugfix-sftp-when-parse_user_host_path-empty-path-should-be-allowed.patch
new file mode 100644
index 0000000..4878345
--- /dev/null
+++ b/bugfix-sftp-when-parse_user_host_path-empty-path-should-be-allowed.patch
@@ -0,0 +1,44 @@
+From 68fae005d551a1172085a2f91e5c4d53c9e07746 Mon Sep 17 00:00:00 2001
+From: xuchunmei <xuchunmei@huawei.com>
+Date: Sat, 9 Feb 2019 04:51:29 -0500
+Subject: [PATCH] sftp: when parse_user_host_path, empty path should be allowed
+
+when using "sftp root@[2001::16%eth0]", the error output:
+ssh: Could not resolve hostname [2001::16%eth0]: Name
+or service not known
+Connection closed.
+Connection closed
+
+fix sftp ipv6 login failed accidental like this:
+File "/root/!" not found.
+
+---
+ misc.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/misc.c b/misc.c
+index 7c4f395..1ec2aa8 100644
+--- a/misc.c
++++ b/misc.c
+@@ -554,6 +554,8 @@ colon(char *cp)
+ flag = 1;
+ if (*cp == ']' && *(cp+1) == ':' && flag)
+ return (cp+1);
++ if (*cp == ']' && *(cp+1) == '\0' && flag)
++ return (cp+1);
+ if (*cp == ':' && !flag)
+ return (cp);
+ if (*cp == '/')
+@@ -599,7 +601,8 @@ parse_user_host_path(const char *s, char **userp, char **hostp, char **pathp)
+ goto out;
+
+ /* Extract optional path */
+- *tmp++ = '\0';
++ if (*tmp != '\0')
++ *tmp++ = '\0';
+ if (*tmp == '\0')
+ tmp = ".";
+ path = xstrdup(tmp);
+--
+1.8.3.1
+
diff --git a/feature-add-SMx-support.patch b/feature-add-SMx-support.patch
new file mode 100644
index 0000000..e8b0f3c
--- /dev/null
+++ b/feature-add-SMx-support.patch
@@ -0,0 +1,1507 @@
+From d2e28809c673f914b49147ca3fa31e08b9e885d7 Mon Sep 17 00:00:00 2001
+From: renmingshuai <renmingshuai@huawei.com>
+Date: Sat, 29 Jul 2023 10:50:29 +0800
+Subject: [PATCH] feature add sm2
+
+---
+ Makefile.in | 4 +-
+ authfd.c | 2 +
+ authfile.c | 1 +
+ cipher.c | 1 +
+ digest-openssl.c | 1 +
+ digest.h | 3 +-
+ kex.c | 1 +
+ kex.h | 3 +
+ kexecdh.c | 23 +-
+ kexgen.c | 3 +
+ kexsm2.c | 406 ++++++++++++++++++++++++++
+ mac.c | 1 +
+ pathnames.h | 1 +
+ regress/agent.sh | 9 +
+ regress/keytype.sh | 2 +
+ regress/knownhosts-command.sh | 1 +
+ regress/misc/fuzz-harness/sig_fuzz.cc | 4 +
+ regress/unittests/kex/test_kex.c | 3 +
+ ssh-ecdsa.c | 6 +-
+ ssh-keygen.c | 12 +-
+ ssh-keyscan.c | 12 +-
+ ssh-sm2.c | 381 ++++++++++++++++++++++++
+ ssh_api.c | 2 +
+ sshconnect2.c | 1 +
+ sshd.c | 7 +
+ sshkey.c | 21 ++
+ sshkey.h | 2 +
+ 27 files changed, 899 insertions(+), 14 deletions(-)
+ create mode 100644 kexsm2.c
+ create mode 100644 ssh-sm2.c
+
+diff --git a/Makefile.in b/Makefile.in
+index 5fec5b3..7dcda3e 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -102,14 +102,14 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
+ log.o match.o moduli.o nchan.o packet.o \
+ readpass.o ttymodes.o xmalloc.o addr.o addrmatch.o \
+ atomicio.o dispatch.o mac.o misc.o utf8.o \
+- monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \
++ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-sm2.o ssh-ecdsa-sk.o \
+ ssh-ed25519-sk.o ssh-rsa.o dh.o \
+ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \
+ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \
+ poly1305.o chacha.o cipher-chachapoly.o cipher-chachapoly-libcrypto.o \
+ ssh-ed25519.o digest-openssl.o digest-libc.o \
+ hmac.o ed25519.o hash.o \
+- kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
++ kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o kexsm2.o \
+ kexgexc.o kexgexs.o \
+ kexsntrup761x25519.o sntrup761.o kexgen.o \
+ kexgssc.o \
+diff --git a/authfd.c b/authfd.c
+index 25a3636..bcc25a7 100644
+--- a/authfd.c
++++ b/authfd.c
+@@ -583,6 +583,8 @@ ssh_add_identity_constrained(int sock, struct sshkey *key,
+ case KEY_DSA_CERT:
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
++ case KEY_SM2:
++ case KEY_SM2_CERT:
+ case KEY_ECDSA_SK:
+ case KEY_ECDSA_SK_CERT:
+ #endif
+diff --git a/authfile.c b/authfile.c
+index 445f2dd..3884031 100644
+--- a/authfile.c
++++ b/authfile.c
+@@ -332,6 +332,7 @@ sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
+ case KEY_RSA:
+ case KEY_DSA:
+ case KEY_ECDSA:
++ case KEY_SM2:
+ #endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_XMSS:
+diff --git a/cipher.c b/cipher.c
+index 609450d..7f98413 100644
+--- a/cipher.c
++++ b/cipher.c
+@@ -86,6 +86,7 @@ static const struct sshcipher ciphers[] = {
+ #endif
+ { "chacha20-poly1305@openssh.com",
+ 8, 64, 0, 16, CFLAG_CHACHAPOLY, NULL },
++ { "sm4-ctr", 16, 16, 0, 0, 0, EVP_sm4_ctr },
+ { "none", 8, 0, 0, 0, CFLAG_NONE, NULL },
+
+ { NULL, 0, 0, 0, 0, 0, NULL }
+diff --git a/digest-openssl.c b/digest-openssl.c
+index 94730e9..fa92360 100644
+--- a/digest-openssl.c
++++ b/digest-openssl.c
+@@ -61,6 +61,7 @@ const struct ssh_digest digests[] = {
+ { SSH_DIGEST_SHA256, "SHA256", 32, EVP_sha256 },
+ { SSH_DIGEST_SHA384, "SHA384", 48, EVP_sha384 },
+ { SSH_DIGEST_SHA512, "SHA512", 64, EVP_sha512 },
++ { SSH_DIGEST_SM3, "SM3", 32, EVP_sm3 },
+ { -1, NULL, 0, NULL },
+ };
+
+diff --git a/digest.h b/digest.h
+index c7ceeb3..520722c 100644
+--- a/digest.h
++++ b/digest.h
+@@ -27,7 +27,8 @@
+ #define SSH_DIGEST_SHA256 2
+ #define SSH_DIGEST_SHA384 3
+ #define SSH_DIGEST_SHA512 4
+-#define SSH_DIGEST_MAX 5
++#define SSH_DIGEST_SM3 5
++#define SSH_DIGEST_MAX 6
+
+ struct sshbuf;
+ struct ssh_digest_ctx;
+diff --git a/kex.c b/kex.c
+index 0fbd0ca..e9dfcc2 100644
+--- a/kex.c
++++ b/kex.c
+@@ -125,6 +125,7 @@ static const struct kexalg kexalgs[] = {
+ SSH_DIGEST_SHA512 },
+ #endif
+ #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
++ { "sm2-sm3", KEX_SM2_SM3, NID_sm2, SSH_DIGEST_SM3 },
+ { NULL, 0, -1, -1},
+ };
+ static const struct kexalg gss_kexalgs[] = {
+diff --git a/kex.h b/kex.h
+index 0fac9d3..044ec18 100644
+--- a/kex.h
++++ b/kex.h
+@@ -102,6 +102,7 @@ enum kex_exchange {
+ KEX_ECDH_SHA2,
+ KEX_C25519_SHA256,
+ KEX_KEM_SNTRUP761X25519_SHA512,
++ KEX_SM2_SM3,
+ #ifdef GSSAPI
+ KEX_GSS_GRP1_SHA1,
+ KEX_GSS_GRP14_SHA1,
+@@ -287,6 +288,8 @@ int kexc25519_shared_key_ext(const u_char key[CURVE25519_SIZE],
+ __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
+ __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
+
++int SM2KAP_compute_key(void *out, size_t outlen, const EC_POINT *pub_key, const EC_KEY *eckey, int server);
++
+ #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH)
+ void dump_digest(const char *, const u_char *, int);
+ #endif
+diff --git a/kexecdh.c b/kexecdh.c
+index efb2e55..69ec13b 100644
+--- a/kexecdh.c
++++ b/kexecdh.c
+@@ -44,7 +44,7 @@
+
+ static int
+ kex_ecdh_dec_key_group(struct kex *, const struct sshbuf *, EC_KEY *key,
+- const EC_GROUP *, struct sshbuf **);
++ const EC_GROUP *, struct sshbuf **, int server);
+
+ int
+ kex_ecdh_keypair(struct kex *kex)
+@@ -124,7 +124,7 @@ kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob,
+ (r = sshbuf_get_u32(server_blob, NULL)) != 0)
+ goto out;
+ if ((r = kex_ecdh_dec_key_group(kex, client_blob, server_key, group,
+- shared_secretp)) != 0)
++ shared_secretp, 1)) != 0)
+ goto out;
+ *server_blobp = server_blob;
+ server_blob = NULL;
+@@ -136,7 +136,7 @@ kex_ecdh_enc(struct kex *kex, const struct sshbuf *client_blob,
+
+ static int
+ kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob,
+- EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp)
++ EC_KEY *key, const EC_GROUP *group, struct sshbuf **shared_secretp, int server)
+ {
+ struct sshbuf *buf = NULL;
+ BIGNUM *shared_secret = NULL;
+@@ -176,11 +176,20 @@ kex_ecdh_dec_key_group(struct kex *kex, const struct sshbuf *ec_blob,
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+- if (ECDH_compute_key(kbuf, klen, dh_pub, key, NULL) != (int)klen ||
++ if (kex->ec_nid == NID_sm2) {
++ if (SM2KAP_compute_key(kbuf, klen, dh_pub, key, server) != (int)klen ||
+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) {
+- r = SSH_ERR_LIBCRYPTO_ERROR;
+- goto out;
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ } else {
++ if (ECDH_compute_key(kbuf, klen, dh_pub, 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
+@@ -203,7 +212,7 @@ kex_ecdh_dec(struct kex *kex, const struct sshbuf *server_blob,
+ int r;
+
+ r = kex_ecdh_dec_key_group(kex, server_blob, kex->ec_client_key,
+- kex->ec_group, shared_secretp);
++ kex->ec_group, shared_secretp, 0);
+ EC_KEY_free(kex->ec_client_key);
+ kex->ec_client_key = NULL;
+ return r;
+diff --git a/kexgen.c b/kexgen.c
+index ca70484..4855d5c 100644
+--- a/kexgen.c
++++ b/kexgen.c
+@@ -111,6 +111,7 @@ kex_gen_client(struct ssh *ssh)
+ r = kex_dh_keypair(kex);
+ break;
+ case KEX_ECDH_SHA2:
++ case KEX_SM2_SM3:
+ r = kex_ecdh_keypair(kex);
+ break;
+ #endif
+@@ -182,6 +183,7 @@ input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh)
+ r = kex_dh_dec(kex, server_blob, &shared_secret);
+ break;
+ case KEX_ECDH_SHA2:
++ case KEX_SM2_SM3:
+ r = kex_ecdh_dec(kex, server_blob, &shared_secret);
+ break;
+ #endif
+@@ -298,6 +300,7 @@ input_kex_gen_init(int type, u_int32_t seq, struct ssh *ssh)
+ &shared_secret);
+ break;
+ case KEX_ECDH_SHA2:
++ case KEX_SM2_SM3:
+ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
+ &shared_secret);
+ break;
+diff --git a/kexsm2.c b/kexsm2.c
+new file mode 100644
+index 0000000..f507557
+--- /dev/null
++++ b/kexsm2.c
+@@ -0,0 +1,406 @@
++#include <openssl/err.h>
++#include <openssl/evp.h>
++#include <openssl/bn.h>
++#include <string.h>
++#include <openssl/ecdh.h>
++#include <openssl/ec.h>
++
++int sm2_compute_z_digest(uint8_t *out,
++ const EVP_MD *digest,
++ const uint8_t *id,
++ const size_t id_len,
++ const EC_KEY *key)
++{
++ int rc = 0;
++ const EC_GROUP *group = EC_KEY_get0_group(key);
++ BN_CTX *ctx = NULL;
++ EVP_MD_CTX *hash = NULL;
++ BIGNUM *p = NULL;
++ BIGNUM *a = NULL;
++ BIGNUM *b = NULL;
++ BIGNUM *xG = NULL;
++ BIGNUM *yG = NULL;
++ BIGNUM *xA = NULL;
++ BIGNUM *yA = NULL;
++ int p_bytes = 0;
++ uint8_t *buf = NULL;
++ uint16_t entl = 0;
++ uint8_t e_byte = 0;
++
++ hash = EVP_MD_CTX_new();
++ ctx = BN_CTX_new();
++ if (hash == NULL || ctx == NULL) {
++ goto done;
++ }
++
++ p = BN_CTX_get(ctx);
++ a = BN_CTX_get(ctx);
++ b = BN_CTX_get(ctx);
++ xG = BN_CTX_get(ctx);
++ yG = BN_CTX_get(ctx);
++ xA = BN_CTX_get(ctx);
++ yA = BN_CTX_get(ctx);
++
++ if (yA == NULL) {
++ goto done;
++ }
++
++ if (!EVP_DigestInit(hash, digest)) {
++ goto done;
++ }
++
++ /* Z = h(ENTL || ID || a || b || xG || yG || xA || yA) */
++
++ if (id_len >= (UINT16_MAX / 8)) {
++ /* too large */
++ goto done;
++ }
++
++ entl = (uint16_t)(8 * id_len);
++
++ e_byte = entl >> 8;
++ if (!EVP_DigestUpdate(hash, &e_byte, 1)) {
++ goto done;
++ }
++ e_byte = entl & 0xFF;
++ if (!EVP_DigestUpdate(hash, &e_byte, 1)) {
++ goto done;
++ }
++
++ if (id_len > 0 && !EVP_DigestUpdate(hash, id, id_len)) {
++ goto done;
++ }
++
++ if (!EC_GROUP_get_curve(group, p, a, b, ctx)) {
++ goto done;
++ }
++
++ p_bytes = BN_num_bytes(p);
++ buf = OPENSSL_zalloc(p_bytes);
++ if (buf == NULL) {
++ goto done;
++ }
++
++ if (BN_bn2binpad(a, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || BN_bn2binpad(b, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || !EC_POINT_get_affine_coordinates(group,
++ EC_GROUP_get0_generator(group),
++ xG, yG, ctx)
++ || BN_bn2binpad(xG, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || BN_bn2binpad(yG, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || !EC_POINT_get_affine_coordinates(group,
++ EC_KEY_get0_public_key(key),
++ xA, yA, ctx)
++ || BN_bn2binpad(xA, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || BN_bn2binpad(yA, buf, p_bytes) < 0
++ || !EVP_DigestUpdate(hash, buf, p_bytes)
++ || !EVP_DigestFinal(hash, out, NULL)) {
++ goto done;
++ }
++
++ rc = 1;
++
++ done:
++ OPENSSL_free(buf);
++ BN_CTX_free(ctx);
++ EVP_MD_CTX_free(hash);
++ return rc;
++}
++
++
++/* GM/T003_2012 Defined Key Derive Function */
++int kdf_gmt003_2012(unsigned char *out, size_t outlen, const unsigned char *Z, size_t Zlen, const unsigned char *SharedInfo, size_t SharedInfolen, const EVP_MD *md)
++{
++ EVP_MD_CTX *mctx = NULL;
++ unsigned int counter;
++ unsigned char ctr[4];
++ size_t mdlen;
++ int retval = 0;
++ unsigned char dgst[EVP_MAX_MD_SIZE];
++
++ if (!out || !outlen) return retval;
++ if (md == NULL) {
++ md = EVP_sm3();
++ }
++ mdlen = EVP_MD_size(md);
++ mctx = EVP_MD_CTX_new();
++ if (mctx == NULL) {
++ goto err;
++ }
++
++ for (counter = 1;; counter++) {
++ if (!EVP_DigestInit(mctx, md)) {
++ goto err;
++ }
++ ctr[0] = (unsigned char)((counter >> 24) & 0xFF);
++ ctr[1] = (unsigned char)((counter >> 16) & 0xFF);
++ ctr[2] = (unsigned char)((counter >> 8) & 0xFF);
++ ctr[3] = (unsigned char)(counter & 0xFF);
++
++ if (!EVP_DigestUpdate(mctx, Z, Zlen)) {
++ goto err;
++ }
++ if (!EVP_DigestUpdate(mctx, ctr, sizeof(ctr))) {
++ goto err;
++ }
++ if (!EVP_DigestUpdate(mctx, SharedInfo, SharedInfolen)) {
++ goto err;
++ }
++ if (!EVP_DigestFinal(mctx, dgst, NULL)) {
++ goto err;
++ }
++
++ if (outlen > mdlen) {
++ memcpy(out, dgst, mdlen);
++ out += mdlen;
++ outlen -= mdlen;
++ } else {
++ memcpy(out, dgst, outlen);
++ memset(dgst, 0, mdlen);
++ break;
++ }
++ }
++
++ retval = 1;
++
++err:
++ EVP_MD_CTX_free(mctx);
++ return retval;
++}
++
++int sm2_kap_compute_key(void *out, size_t outlen, int server,\
++ const uint8_t *peer_uid, int peer_uid_len, const uint8_t *self_uid, int self_uid_len, \
++ const EC_KEY *peer_ecdhe_key, const EC_KEY *self_ecdhe_key, const EC_KEY *peer_pub_key, const EC_KEY *self_eckey, \
++ const EVP_MD *md)
++{
++ BN_CTX *ctx = NULL;
++ EC_POINT *UorV = NULL;
++ const EC_POINT *Rs, *Rp;
++ BIGNUM *Xs = NULL, *Xp = NULL, *h = NULL, *t = NULL, *two_power_w = NULL, *order = NULL;
++ const BIGNUM *priv_key, *r;
++ const EC_GROUP *group;
++ int w;
++ int ret = -1;
++ size_t buflen, len;
++ unsigned char *buf = NULL;
++
++ if (outlen > INT_MAX) {
++ goto err;
++ }
++
++ if (!peer_pub_key || !self_eckey) {
++ goto err;
++ }
++
++ priv_key = EC_KEY_get0_private_key(self_eckey);
++ if (!priv_key) {
++ goto err;
++ }
++
++ if (!peer_ecdhe_key || !self_ecdhe_key) {
++ goto err;
++ }
++
++ Rs = EC_KEY_get0_public_key(self_ecdhe_key);
++ Rp = EC_KEY_get0_public_key(peer_ecdhe_key);
++ r = EC_KEY_get0_private_key(self_ecdhe_key);
++
++ if (!Rs || !Rp || !r) {
++ goto err;
++ }
++
++ ctx = BN_CTX_new();
++ Xs = BN_new();
++ Xp = BN_new();
++ h = BN_new();
++ t = BN_new();
++ two_power_w = BN_new();
++ order = BN_new();
++ if (!Xs || !Xp || !h || !t || !two_power_w || !order) {
++ goto err;
++ }
++
++ group = EC_KEY_get0_group(self_eckey);
++
++ /*Second: Caculate -- w*/
++ if (!EC_GROUP_get_order(group, order, ctx) || !EC_GROUP_get_cofactor(group, h, ctx)) {
++ goto err;
++ }
++
++ w = (BN_num_bits(order) + 1) / 2 - 1;
++ if (!BN_lshift(two_power_w, BN_value_one(), w)) {
++ goto err;
++ }
++
++ /*Third: Caculate -- X = 2 ^ w + (x & (2 ^ w - 1)) = 2 ^ w + (x mod 2 ^ w)*/
++ UorV = EC_POINT_new(group);
++
++ if (!UorV) {
++ goto err;
++ }
++
++ /*Test peer public key On curve*/
++ if (!EC_POINT_is_on_curve(group, Rp, ctx)) {
++ goto err;
++ }
++
++ /*Get x*/
++ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) == NID_X9_62_prime_field) {
++ if (!EC_POINT_get_affine_coordinates_GFp(group, Rs, Xs, NULL, ctx)) {
++ goto err;
++ }
++
++ if (!EC_POINT_get_affine_coordinates_GFp(group, Rp, Xp, NULL, ctx)) {
++ goto err;
++ }
++ }
++
++ /*x mod 2 ^ w*/
++ /*Caculate Self x*/
++ if (!BN_nnmod(Xs, Xs, two_power_w, ctx)) {
++ goto err;
++ }
++
++ if (!BN_add(Xs, Xs, two_power_w)) {
++ goto err;
++ }
++
++ /*Caculate Peer x*/
++ if (!BN_nnmod(Xp, Xp, two_power_w, ctx)) {
++ goto err;
++ }
++
++ if (!BN_add(Xp, Xp, two_power_w)) {
++ goto err;
++ }
++
++ /*Forth: Caculate t*/
++ if (!BN_mod_mul(t, Xs, r, order, ctx)) {
++ goto err;
++ }
++
++ if (!BN_mod_add(t, t, priv_key, order, ctx)) {
++ goto err;
++ }
++
++ /*Fifth: Caculate V or U*/
++ if (!BN_mul(t, t, h, ctx)) {
++ goto err;
++ }
++
++ /* [x]R */
++ if (!EC_POINT_mul(group, UorV, NULL, Rp, Xp, ctx)) {
++ goto err;
++ }
++
++ /* P + [x]R */
++ if (!EC_POINT_add(group, UorV, UorV, EC_KEY_get0_public_key(peer_pub_key), ctx)) {
++ goto err;
++ }
++
++ if (!EC_POINT_mul(group, UorV, NULL, UorV, t, ctx)) {
++ goto err;
++ }
++
++ /* Detect UorV is in */
++ if (EC_POINT_is_at_infinity(group, UorV)) {
++ goto err;
++ }
++
++ /*Sixth: Caculate Key -- Need Xuorv, Yuorv, Zc, Zs, klen*/
++ {
++ /*
++ size_t buflen, len;
++ unsigned char *buf = NULL;
++ */
++ size_t elemet_len, idx;
++
++ elemet_len = (size_t)((EC_GROUP_get_degree(group) + 7) / 8);
++ buflen = elemet_len * 2 + 32 * 2 + 1; /*add 1 byte tag*/
++ buf = (unsigned char *)OPENSSL_malloc(buflen + 10);
++ if (!buf) {
++ goto err;
++ }
++ memset(buf, 0, buflen + 10);
++ /*1 : Get public key for UorV, Notice: the first byte is a tag, not a valid char*/
++ idx = EC_POINT_point2oct(group, UorV, 4, buf, buflen, ctx);
++ if (!idx) {
++ goto err;
++ }
++
++ if (!server) {
++ /*SIDE A*/
++ len = buflen - idx;
++ if (!sm2_compute_z_digest( (unsigned char *)(buf + idx), md, (const uint8_t *)self_uid, self_uid_len, self_eckey)) {
++ goto err;
++ }
++ len = 32;
++ idx += len;
++ }
++
++ /*Caculate Peer Z*/
++ len = buflen - idx;
++ if (!sm2_compute_z_digest( (unsigned char *)(buf + idx), md, (const uint8_t *)peer_uid, peer_uid_len, peer_pub_key)) {
++ goto err;
++ }
++ len = 32;
++ idx += len;
++
++ if (server) {
++ /*SIDE B*/
++ len = buflen - idx;
++ if (!sm2_compute_z_digest( (unsigned char *)(buf + idx), md, (const uint8_t *)self_uid, self_uid_len, self_eckey)) {
++ goto err;
++ }
++ len = 32;
++ idx += len;
++ }
++
++ len = outlen;
++ if (!kdf_gmt003_2012(out, len, (const unsigned char *)(buf + 1), idx - 1, NULL, 0, md)) {
++ goto err;
++ }
++ }
++
++ ret = outlen;
++
++err:
++ if (Xs) BN_free(Xs);
++ if (Xp) BN_free(Xp);
++ if (h) BN_free(h);
++ if (t) BN_free(t);
++ if (two_power_w) BN_free(two_power_w);
++ if (order) BN_free(order);
++ if (UorV) EC_POINT_free(UorV);
++ if (buf) OPENSSL_free(buf);
++ if (ctx) BN_CTX_free(ctx);
++
++ return ret;
++}
++
++int SM2KAP_compute_key(void *out, size_t outlen, const EC_POINT *pub_key, const EC_KEY *eckey, int server)
++{
++ int ret = 0;
++ EC_KEY *pubkey = NULL;
++ unsigned char id[16] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8};
++
++ if ((pubkey = EC_KEY_new_by_curve_name(NID_sm2)) == NULL) {
++ return ret;
++ }
++
++ if (EC_KEY_set_public_key(pubkey, pub_key) != 1) {
++ ret = 0;
++ goto out;
++ }
++
++ ret = sm2_kap_compute_key(out, outlen, server, id, sizeof(id), id, sizeof(id), pubkey, eckey, pubkey, eckey, (EVP_MD*)EVP_sm3());
++
++out:
++ EC_KEY_free(pubkey);
++ return ret;
++}
+diff --git a/mac.c b/mac.c
+index bf051ba..2de17a0 100644
+--- a/mac.c
++++ b/mac.c
+@@ -65,6 +65,7 @@ static const struct macalg macs[] = {
+ { "hmac-md5-96", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 0 },
+ { "umac-64@openssh.com", SSH_UMAC, 0, 0, 128, 64, 0 },
+ { "umac-128@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 0 },
++ { "hmac-sm3", SSH_DIGEST, SSH_DIGEST_SM3, 0, 0, 0, 0 },
+
+ /* Encrypt-then-MAC variants */
+ { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 },
+diff --git a/pathnames.h b/pathnames.h
+index a094888..0a805ad 100644
+--- a/pathnames.h
++++ b/pathnames.h
+@@ -80,6 +80,7 @@
+ #define _PATH_SSH_CLIENT_ID_XMSS _PATH_SSH_USER_DIR "/id_xmss"
+ #define _PATH_SSH_CLIENT_ID_ECDSA_SK _PATH_SSH_USER_DIR "/id_ecdsa_sk"
+ #define _PATH_SSH_CLIENT_ID_ED25519_SK _PATH_SSH_USER_DIR "/id_ed25519_sk"
++#define _PATH_SSH_CLIENT_ID_SM2 _PATH_SSH_USER_DIR "/id_sm2"
+
+ /*
+ * Configuration file in user's home directory. This file need not be
+diff --git a/regress/agent.sh b/regress/agent.sh
+index 5f10606..3ab40b4 100644
+--- a/regress/agent.sh
++++ b/regress/agent.sh
+@@ -87,9 +87,18 @@ fi
+ for t in ${SSH_KEYTYPES}; do
+ trace "connect via agent using $t key"
+ if [ "$t" = "ssh-dss" ]; then
++ sed -i "/PubkeyAcceptedAlgorithms/d" $OBJ/ssh_proxy
++ sed -i "/PubkeyAcceptedAlgorithms/d" $OBJ/sshd_proxy
+ echo "PubkeyAcceptedAlgorithms +ssh-dss" >> $OBJ/ssh_proxy
+ echo "PubkeyAcceptedAlgorithms +ssh-dss" >> $OBJ/sshd_proxy
+ fi
++ if [ "$t" = "sm2" ]; then
++ sed -i "/PubkeyAcceptedAlgorithms/d" $OBJ/ssh_proxy
++ sed -i "/PubkeyAcceptedAlgorithms/d" $OBJ/sshd_proxy
++ echo "PubkeyAcceptedAlgorithms +sm2,sm2-cert" >> $OBJ/ssh_proxy
++ echo "PubkeyAcceptedAlgorithms +sm2,sm2-cert" >> $OBJ/sshd_proxy
++ fi
++
+ ${SSH} -F $OBJ/ssh_proxy -i $OBJ/$t-agent.pub -oIdentitiesOnly=yes \
+ somehost exit 52
+ r=$?
+diff --git a/regress/keytype.sh b/regress/keytype.sh
+index f1c0451..2665bd6 100644
+--- a/regress/keytype.sh
++++ b/regress/keytype.sh
+@@ -18,6 +18,7 @@ for i in ${SSH_KEYTYPES}; do
+ ecdsa-sha2-nistp521) ktypes="$ktypes ecdsa-521" ;;
+ sk-ssh-ed25519*) ktypes="$ktypes ed25519-sk" ;;
+ sk-ecdsa-sha2-nistp256*) ktypes="$ktypes ecdsa-sk" ;;
++ sm2) ktypes="$ktypes sm2-256" ;;
+ esac
+ done
+
+@@ -44,6 +45,7 @@ kname_to_ktype() {
+ rsa-*) echo rsa-sha2-512,rsa-sha2-256,ssh-rsa;;
+ ed25519-sk) echo sk-ssh-ed25519@openssh.com;;
+ ecdsa-sk) echo sk-ecdsa-sha2-nistp256@openssh.com;;
++ sm2-256) echo sm2;;
+ esac
+ }
+
+diff --git a/regress/knownhosts-command.sh b/regress/knownhosts-command.sh
+index 8472ec8..7f56fb1 100644
+--- a/regress/knownhosts-command.sh
++++ b/regress/knownhosts-command.sh
+@@ -41,6 +41,7 @@ ${SSH} -F $OBJ/ssh_proxy x true && fail "ssh connect succeeded with bad exit"
+ for keytype in ${SSH_HOSTKEY_TYPES} ; do
+ algs=$keytype
+ test "x$keytype" = "xssh-dss" && continue
++ test "x$keytype" = "xsm2" && continue
+ test "x$keytype" = "xssh-rsa" && algs=ssh-rsa,rsa-sha2-256,rsa-sha2-512
+ verbose "keytype $keytype"
+ cat > $OBJ/knownhosts_command << _EOF
+diff --git a/regress/misc/fuzz-harness/sig_fuzz.cc b/regress/misc/fuzz-harness/sig_fuzz.cc
+index b32502b..f260692 100644
+--- a/regress/misc/fuzz-harness/sig_fuzz.cc
++++ b/regress/misc/fuzz-harness/sig_fuzz.cc
+@@ -30,6 +30,7 @@ int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen)
+ static struct sshkey *ecdsa256 = generate_or_die(KEY_ECDSA, 256);
+ static struct sshkey *ecdsa384 = generate_or_die(KEY_ECDSA, 384);
+ static struct sshkey *ecdsa521 = generate_or_die(KEY_ECDSA, 521);
++ static struct sshkey *sm2 = generate_or_die(KEY_SM2, 256);
+ #endif
+ struct sshkey_sig_details *details = NULL;
+ static struct sshkey *ed25519 = generate_or_die(KEY_ED25519, 0);
+@@ -53,6 +54,9 @@ int LLVMFuzzerTestOneInput(const uint8_t* sig, size_t slen)
+ sshkey_verify(ecdsa521, sig, slen, (const u_char *)data, dlen, NULL, 0, &details);
+ sshkey_sig_details_free(details);
+ details = NULL;
++ sshkey_verify(sm2, sig, slen, (const u_char *)data, dlen, NULL, 0, &details);
++ sshkey_sig_details_free(details);
++ details = NULL;
+ #endif
+ sshkey_verify(ed25519, sig, slen, (const u_char *)data, dlen, NULL, 0, &details);
+ sshkey_sig_details_free(details);
+diff --git a/regress/unittests/kex/test_kex.c b/regress/unittests/kex/test_kex.c
+index c26761e..d335b29 100644
+--- a/regress/unittests/kex/test_kex.c
++++ b/regress/unittests/kex/test_kex.c
+@@ -151,6 +151,7 @@ do_kex_with_key(char *kex, int keytype, int bits)
+ #endif /* OPENSSL_HAS_ECC */
+ #endif /* WITH_OPENSSL */
+ server2->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
++ server2->kex->kex[KEX_SM2_SM3] = kex_gen_server;
+ server2->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
+ server2->kex->load_host_public_key = server->kex->load_host_public_key;
+ server2->kex->load_host_private_key = server->kex->load_host_private_key;
+@@ -185,6 +186,7 @@ do_kex(char *kex)
+ #endif /* OPENSSL_HAS_ECC */
+ #endif /* WITH_OPENSSL */
+ do_kex_with_key(kex, KEY_ED25519, 256);
++ do_kex_with_key(kex, KEY_SM2, 256);
+ }
+
+ void
+@@ -201,6 +203,7 @@ kex_tests(void)
+ do_kex("diffie-hellman-group-exchange-sha1");
+ do_kex("diffie-hellman-group14-sha1");
+ do_kex("diffie-hellman-group1-sha1");
++ do_kex("sm2-sm3");
+ # ifdef USE_SNTRUP761X25519
+ do_kex("sntrup761x25519-sha512@openssh.com");
+ # endif /* USE_SNTRUP761X25519 */
+diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c
+index b705157..5445ab5 100644
+--- a/ssh-ecdsa.c
++++ b/ssh-ecdsa.c
+@@ -256,7 +256,8 @@ ssh_ecdsa_sign(struct sshkey *key,
+ *sigp = NULL;
+
+ if (key == NULL || key->ecdsa == NULL ||
+- sshkey_type_plain(key->type) != KEY_ECDSA)
++ (sshkey_type_plain(key->type) != KEY_ECDSA &&
++ sshkey_type_plain(key->type) != KEY_SM2))
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
+@@ -332,7 +333,8 @@ ssh_ecdsa_verify(const struct sshkey *key,
+ unsigned char *sigb = NULL, *psig = NULL;
+
+ if (key == NULL || key->ecdsa == NULL ||
+- sshkey_type_plain(key->type) != KEY_ECDSA ||
++ (sshkey_type_plain(key->type) != KEY_ECDSA &&
++ sshkey_type_plain(key->type) != KEY_SM2) ||
+ sig == NULL || siglen == 0)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+diff --git a/ssh-keygen.c b/ssh-keygen.c
+index 0bff209..46f4998 100644
+--- a/ssh-keygen.c
++++ b/ssh-keygen.c
+@@ -193,6 +193,7 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp)
+ *bitsp = DEFAULT_BITS_DSA;
+ break;
+ case KEY_ECDSA:
++ case KEY_SM2:
+ if (name != NULL &&
+ (nid = sshkey_ecdsa_nid_from_name(name)) > 0)
+ *bitsp = sshkey_curve_nid_to_bits(nid);
+@@ -219,6 +220,10 @@ type_bits_valid(int type, const char *name, u_int32_t *bitsp)
+ fatal("Invalid RSA key length: maximum is %d bits",
+ OPENSSL_RSA_MAX_MODULUS_BITS);
+ break;
++ case KEY_SM2:
++ if (*bitsp != 256)
++ fatal("Invalid SM2 key length: must be 256 bits");
++ break;
+ case KEY_ECDSA:
+ if (sshkey_ecdsa_bits_to_nid(*bitsp) == -1)
+ #ifdef OPENSSL_HAS_NISTP521
+@@ -275,6 +280,9 @@ ask_filename(struct passwd *pw, const char *prompt)
+ case KEY_ECDSA:
+ name = _PATH_SSH_CLIENT_ID_ECDSA;
+ break;
++ case KEY_SM2:
++ name = _PATH_SSH_CLIENT_ID_SM2;
++ break;
+ case KEY_ECDSA_SK_CERT:
+ case KEY_ECDSA_SK:
+ name = _PATH_SSH_CLIENT_ID_ECDSA_SK;
+@@ -386,6 +394,7 @@ do_convert_to_pkcs8(struct sshkey *k)
+ break;
+ #ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
++ case KEY_SM2:
+ if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
+ fatal("PEM_write_EC_PUBKEY failed");
+ break;
+@@ -410,6 +419,7 @@ do_convert_to_pem(struct sshkey *k)
+ break;
+ #ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
++ case KEY_SM2:
+ if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa))
+ fatal("PEM_write_EC_PUBKEY failed");
+ break;
+@@ -3280,7 +3290,7 @@ usage(void)
+ fprintf(stderr,
+ "usage: ssh-keygen [-q] [-a rounds] [-b bits] [-C comment] [-f output_keyfile]\n"
+ " [-m format] [-N new_passphrase] [-O option]\n"
+- " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa]\n"
++ " [-t dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa | sm2]\n"
+ " [-w provider] [-Z cipher]\n"
+ " ssh-keygen -p [-a rounds] [-f keyfile] [-m format] [-N new_passphrase]\n"
+ " [-P old_passphrase] [-Z cipher]\n"
+diff --git a/ssh-keyscan.c b/ssh-keyscan.c
+index 245c73d..b402a21 100644
+--- a/ssh-keyscan.c
++++ b/ssh-keyscan.c
+@@ -68,9 +68,10 @@ int ssh_port = SSH_DEFAULT_PORT;
+ #define KT_XMSS (1<<4)
+ #define KT_ECDSA_SK (1<<5)
+ #define KT_ED25519_SK (1<<6)
++#define KT_SM2 (1<<7)
+
+ #define KT_MIN KT_DSA
+-#define KT_MAX KT_ED25519_SK
++#define KT_MAX KT_SM2
+
+ int get_cert = 0;
+ int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519|KT_ECDSA_SK|KT_ED25519_SK;
+@@ -267,6 +268,11 @@ keygrab_ssh2(con *c)
+ "ecdsa-sha2-nistp384,"
+ "ecdsa-sha2-nistp521";
+ break;
++ case KT_SM2:
++ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
++ "sm2-cert" :
++ "sm2";
++ break;
+ case KT_ECDSA_SK:
+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ?
+ "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com" :
+@@ -296,6 +302,7 @@ keygrab_ssh2(con *c)
+ c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
+ # ifdef OPENSSL_HAS_ECC
+ c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
++ c->c_ssh->kex->kex[KEX_SM2_SM3] = kex_gen_client;
+ # endif
+ #endif
+ c->c_ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
+@@ -789,6 +796,9 @@ main(int argc, char **argv)
+ case KEY_ECDSA:
+ get_keytypes |= KT_ECDSA;
+ break;
++ case KEY_SM2:
++ get_keytypes |= KT_SM2;
++ break;
+ case KEY_RSA:
+ get_keytypes |= KT_RSA;
+ break;
+diff --git a/ssh-sm2.c b/ssh-sm2.c
+new file mode 100644
+index 0000000..75e9731
+--- /dev/null
++++ b/ssh-sm2.c
+@@ -0,0 +1,381 @@
++#include "includes.h"
++#include <sys/types.h>
++#include <openssl/bn.h>
++#include <openssl/ecdsa.h>
++#include <openssl/evp.h>
++
++#include <string.h>
++#include "sshbuf.h"
++#include "ssherr.h"
++#include "digest.h"
++#include "sshkey.h"
++
++#include "openbsd-compat/openssl-compat.h"
++
++/* Reuse some ECDSA internals */
++extern struct sshkey_impl_funcs sshkey_ecdsa_funcs;
++
++const unsigned char *sm2_id = (const unsigned char *)"1234567812345678";
++
++static void
++ssh_sm2_cleanup(struct sshkey *k)
++{
++ EC_KEY_free(k->ecdsa);
++ k->ecdsa = NULL;
++}
++
++static int
++ssh_sm2_equal(const struct sshkey *a, const struct sshkey *b)
++{
++ if (!sshkey_ecdsa_funcs.equal(a, b))
++ return 0;
++ return 1;
++}
++
++static int
++ssh_sm2_serialize_public(const struct sshkey *key, struct sshbuf *b,
++ enum sshkey_serialize_rep opts)
++{
++ int r;
++
++ if ((r = sshkey_ecdsa_funcs.serialize_public(key, b, opts)) != 0)
++ return r;
++
++ return 0;
++}
++
++static int
++ssh_sm2_deserialize_public(const char *ktype, struct sshbuf *b,
++ struct sshkey *key)
++{
++ int r;
++
++ if ((r = sshkey_ecdsa_funcs.deserialize_public(ktype, b, key)) != 0)
++ return r;
++ return 0;
++}
++
++static int
++ssh_sm2_serialize_private(const struct sshkey *key, struct sshbuf *b,
++ enum sshkey_serialize_rep opts)
++{
++ int r;
++
++ if ((r = sshkey_ecdsa_funcs.serialize_private(key, b, opts)) != 0)
++ return r;
++
++ return 0;
++}
++
++static int
++ssh_sm2_deserialize_private(const char *ktype, struct sshbuf *b,
++ struct sshkey *key)
++{
++ int r;
++
++ if ((r = sshkey_ecdsa_funcs.deserialize_private(ktype, b, key)) != 0)
++ return r;
++
++ return 0;
++}
++
++static int
++ssh_sm2_generate(struct sshkey *k, int bits)
++{
++ EC_KEY *private;
++
++ k->ecdsa_nid = NID_sm2;
++ if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
++ return SSH_ERR_ALLOC_FAIL;
++ if (EC_KEY_generate_key(private) != 1) {
++ EC_KEY_free(private);
++ return SSH_ERR_LIBCRYPTO_ERROR;
++ }
++ EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
++ k->ecdsa = private;
++ return 0;
++}
++
++static int
++ssh_sm2_copy_public(const struct sshkey *from, struct sshkey *to)
++{
++ int r;
++
++ if ((r = sshkey_ecdsa_funcs.copy_public(from, to)) != 0)
++ return r;
++ return 0;
++}
++
++static int
++sm2_get_sig(EVP_PKEY *pkey, const u_char *data,
++ size_t datalen, u_char *sig, size_t *slen)
++{
++ EVP_PKEY_CTX *pctx = NULL;
++ EVP_MD_CTX *mctx = NULL;
++ int ret = SSH_ERR_INTERNAL_ERROR;
++
++ if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++ if ((mctx = EVP_MD_CTX_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++ if (EVP_PKEY_CTX_set1_id(pctx, sm2_id, 16) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ EVP_MD_CTX_set_pkey_ctx(mctx, pctx);
++
++ if ((EVP_DigestSignInit(mctx, NULL, EVP_sm3(), NULL, pkey)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((EVP_DigestSignUpdate(mctx, data, datalen)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((EVP_DigestSignFinal(mctx, sig, slen)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++ ret = 0;
++
++out:
++ EVP_PKEY_CTX_free(pctx);
++ EVP_MD_CTX_free(mctx);
++ return ret;
++}
++
++static int
++ssh_sm2_sign(struct sshkey *key,
++ u_char **sigp, size_t *lenp,
++ const u_char *data, size_t datalen,
++ const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
++{
++ u_char *sig = NULL;
++ size_t slen = 0;
++ int pkey_len = 0;
++ int r = 0;
++ int len = 0;
++ EVP_PKEY *key_sm2 = NULL;
++ struct sshbuf *b = NULL;
++ int ret = SSH_ERR_INTERNAL_ERROR;
++
++ if (lenp != NULL)
++ *lenp = 0;
++ if (sigp != NULL)
++ *sigp = NULL;
++
++ if (key == NULL || key->ecdsa == NULL ||
++ sshkey_type_plain(key->type) != KEY_SM2)
++ return SSH_ERR_INVALID_ARGUMENT;
++
++ if ((key_sm2 = EVP_PKEY_new()) == NULL) {
++ return SSH_ERR_ALLOC_FAIL;
++ }
++
++ if ((EVP_PKEY_set1_EC_KEY(key_sm2, key->ecdsa)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((pkey_len = EVP_PKEY_size(key_sm2)) == 0) {
++ ret = SSH_ERR_INVALID_ARGUMENT;
++ goto out;
++ }
++
++ slen = pkey_len;
++
++ if ((sig = OPENSSL_malloc(pkey_len)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if (ret = sm2_get_sig(key_sm2, data, datalen, sig, &slen)) {
++ goto out;
++ }
++
++ if ((b = sshbuf_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if ((r = sshbuf_put_cstring(b, "sm2")) != 0 ||
++ (r = sshbuf_put_string(b, sig, slen)) != 0)
++ goto out;
++ len = sshbuf_len(b);
++ if (sigp != NULL) {
++ if ((*sigp = malloc(len)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++ memcpy(*sigp, sshbuf_ptr(b), len);
++ }
++ if (lenp != NULL)
++ *lenp = len;
++ ret = 0;
++
++out:
++ EVP_PKEY_free(key_sm2);
++ if (sig != NULL) {
++ explicit_bzero(sig, slen);
++ OPENSSL_free(sig);
++ }
++ sshbuf_free(b);
++ return ret;
++}
++
++static int
++sm2_verify_sig(EVP_PKEY *pkey, const u_char *data,
++ size_t datalen, const u_char *sig, size_t slen)
++{
++ EVP_PKEY_CTX *pctx = NULL;
++ EVP_MD_CTX *mctx = NULL;
++ int ret = SSH_ERR_INTERNAL_ERROR;
++
++ if ((pctx = EVP_PKEY_CTX_new(pkey, NULL)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if ((mctx = EVP_MD_CTX_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if (EVP_PKEY_CTX_set1_id(pctx, sm2_id, 16) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++ EVP_MD_CTX_set_pkey_ctx(mctx, pctx);
++
++ if ((EVP_DigestVerifyInit(mctx, NULL, EVP_sm3(), NULL, pkey)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((EVP_DigestVerifyUpdate(mctx, data, datalen)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((EVP_DigestVerifyFinal(mctx, sig, slen)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ ret = 0;
++out:
++ EVP_PKEY_CTX_free(pctx);
++ EVP_MD_CTX_free(mctx);
++ return ret;
++}
++
++static int
++ssh_sm2_verify(const struct sshkey *key,
++ const u_char *signature, size_t signaturelen,
++ const u_char *data, size_t datalen, const char *alg, u_int compat,
++ struct sshkey_sig_details **detailsp)
++{
++ const u_char *sig = NULL;
++ char *ktype = NULL;
++ size_t slen = 0;
++ int pkey_len = 0;
++ int r = 0;
++ int len = 0;
++ EVP_PKEY *key_sm2 = NULL;
++ struct sshbuf *b = NULL;
++ int ret = SSH_ERR_INTERNAL_ERROR;
++
++ if (key == NULL ||
++ sshkey_type_plain(key->type) != KEY_SM2 ||
++ signature == NULL || signaturelen == 0)
++ return SSH_ERR_INVALID_ARGUMENT;
++
++ if ((b = sshbuf_from(signature, signaturelen)) == NULL)
++ return SSH_ERR_ALLOC_FAIL;
++
++ if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
++ (r = sshbuf_get_string_direct(b, &sig, &slen)) != 0)
++ goto out;
++
++ if (strcmp("sm2", ktype) != 0) {
++ ret = SSH_ERR_KEY_TYPE_MISMATCH;
++ goto out;
++ }
++
++ if (sshbuf_len(b) != 0) {
++ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
++ goto out;
++ }
++
++ if ((key_sm2 = EVP_PKEY_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if ((EVP_PKEY_set1_EC_KEY(key_sm2, key->ecdsa)) != 1) {
++ ret = SSH_ERR_INTERNAL_ERROR;
++ goto out;
++ }
++
++ if ((pkey_len = EVP_PKEY_size(key_sm2)) == 0) {
++ ret = SSH_ERR_INVALID_ARGUMENT;
++ goto out;
++ }
++
++ if (ret = sm2_verify_sig(key_sm2, data, datalen, sig, slen)) {
++ goto out;
++ }
++
++ ret = 0;
++out:
++ EVP_PKEY_free(key_sm2);
++ sshbuf_free(b);
++ free(ktype);
++ return ret;
++}
++
++static const struct sshkey_impl_funcs sshkey_sm2_funcs = {
++ /* .size = */ NULL,
++ /* .alloc = */ NULL,
++ /* .cleanup = */ ssh_sm2_cleanup,
++ /* .equal = */ ssh_sm2_equal,
++ /* .ssh_serialize_public = */ ssh_sm2_serialize_public,
++ /* .ssh_deserialize_public = */ ssh_sm2_deserialize_public,
++ /* .ssh_serialize_private = */ ssh_sm2_serialize_private,
++ /* .ssh_deserialize_private = */ssh_sm2_deserialize_private,
++ /* .generate = */ ssh_sm2_generate,
++ /* .copy_public = */ ssh_sm2_copy_public,
++ /* .sign = */ ssh_sm2_sign,
++ /* .verify = */ ssh_sm2_verify,
++};
++
++const struct sshkey_impl sshkey_sm2_impl = {
++ /* .name = */ "sm2",
++ /* .shortname = */ "SM2",
++ /* .sigalg = */ NULL,
++ /* .type = */ KEY_SM2,
++ /* .nid = */ NID_sm2,
++ /* .cert = */ 0,
++ /* .sigonly = */ 0,
++ /* .keybits = */ 256,
++ /* .funcs = */ &sshkey_sm2_funcs,
++};
++
++const struct sshkey_impl sshkey_sm2_cert_impl = {
++ /* .name = */ "sm2-cert",
++ /* .shortname = */ "SM2-CERT",
++ /* .sigalg = */ NULL,
++ /* .type = */ KEY_SM2_CERT,
++ /* .nid = */ NID_sm2,
++ /* .cert = */ 1,
++ /* .sigonly = */ 0,
++ /* .keybits = */ 256,
++ /* .funcs = */ &sshkey_sm2_funcs,
++};
+diff --git a/ssh_api.c b/ssh_api.c
+index d3c6617..adc2598 100644
+--- a/ssh_api.c
++++ b/ssh_api.c
+@@ -115,6 +115,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
+ ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+ # ifdef OPENSSL_HAS_ECC
+ ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
++ ssh->kex->kex[KEX_SM2_SM3] = kex_gen_server;
+ # endif
+ #endif /* WITH_OPENSSL */
+ ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_server;
+@@ -133,6 +134,7 @@ ssh_init(struct ssh **sshp, int is_server, struct kex_params *kex_params)
+ ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
+ # ifdef OPENSSL_HAS_ECC
+ ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
++ ssh->kex->kex[KEX_SM2_SM3] = kex_gen_client;
+ # endif
+ #endif /* WITH_OPENSSL */
+ ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
+diff --git a/sshconnect2.c b/sshconnect2.c
+index 3acfdb6..3fbff57 100644
+--- a/sshconnect2.c
++++ b/sshconnect2.c
+@@ -326,6 +326,7 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port,
+ ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client;
+ # ifdef OPENSSL_HAS_ECC
+ ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
++ ssh->kex->kex[KEX_SM2_SM3] = kex_gen_client;
+ # endif
+ # ifdef GSSAPI
+ if (options.gss_keyex) {
+diff --git a/sshd.c b/sshd.c
+index f366457..52c66ed 100644
+--- a/sshd.c
++++ b/sshd.c
+@@ -695,6 +695,7 @@ list_hostkey_types(void)
+ /* FALLTHROUGH */
+ case KEY_DSA:
+ case KEY_ECDSA:
++ case KEY_SM2:
+ case KEY_ED25519:
+ case KEY_ECDSA_SK:
+ case KEY_ED25519_SK:
+@@ -716,6 +717,7 @@ list_hostkey_types(void)
+ /* FALLTHROUGH */
+ case KEY_DSA_CERT:
+ case KEY_ECDSA_CERT:
++ case KEY_SM2_CERT:
+ case KEY_ED25519_CERT:
+ case KEY_ECDSA_SK_CERT:
+ case KEY_ED25519_SK_CERT:
+@@ -742,6 +744,7 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
+ case KEY_RSA_CERT:
+ case KEY_DSA_CERT:
+ case KEY_ECDSA_CERT:
++ case KEY_SM2_CERT:
+ case KEY_ED25519_CERT:
+ case KEY_ECDSA_SK_CERT:
+ case KEY_ED25519_SK_CERT:
+@@ -758,8 +761,10 @@ get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh)
+ continue;
+ switch (type) {
+ case KEY_ECDSA:
++ case KEY_SM2:
+ case KEY_ECDSA_SK:
+ case KEY_ECDSA_CERT:
++ case KEY_SM2_CERT:
+ case KEY_ECDSA_SK_CERT:
+ if (key->ecdsa_nid != nid)
+ continue;
+@@ -2012,6 +2017,7 @@ main(int ac, char **av)
+ case KEY_RSA:
+ case KEY_DSA:
+ case KEY_ECDSA:
++ case KEY_SM2:
+ case KEY_ED25519:
+ case KEY_ECDSA_SK:
+ case KEY_ED25519_SK:
+@@ -2573,6 +2579,7 @@ do_ssh2_kex(struct ssh *ssh)
+ kex->kex[KEX_DH_GEX_SHA256] = kexgex_server;
+ # ifdef OPENSSL_HAS_ECC
+ kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
++ kex->kex[KEX_SM2_SM3] = kex_gen_server;
+ # endif
+ # ifdef GSSAPI
+ if (options.gss_keyex) {
+diff --git a/sshkey.c b/sshkey.c
+index 1735159..1aee244 100644
+--- a/sshkey.c
++++ b/sshkey.c
+@@ -130,6 +130,8 @@ extern const struct sshkey_impl sshkey_dsa_cert_impl;
+ extern const struct sshkey_impl sshkey_xmss_impl;
+ extern const struct sshkey_impl sshkey_xmss_cert_impl;
+ #endif
++extern const struct sshkey_impl sshkey_sm2_impl;
++extern const struct sshkey_impl sshkey_sm2_cert_impl;
+
+ static int ssh_gss_equal(const struct sshkey *, const struct sshkey *)
+ {
+@@ -237,6 +239,8 @@ const struct sshkey_impl * const keyimpls[] = {
+ &sshkey_xmss_cert_impl,
+ #endif
+ &sshkey_gss_kex_impl,
++ &sshkey_sm2_impl,
++ &sshkey_sm2_cert_impl,
+ NULL
+ };
+
+@@ -340,6 +344,8 @@ key_type_is_ecdsa_variant(int type)
+ case KEY_ECDSA_CERT:
+ case KEY_ECDSA_SK:
+ case KEY_ECDSA_SK_CERT:
++ case KEY_SM2:
++ case KEY_SM2_CERT:
+ return 1;
+ }
+ return 0;
+@@ -548,6 +554,8 @@ sshkey_type_plain(int type)
+ return KEY_ED25519_SK;
+ case KEY_XMSS_CERT:
+ return KEY_XMSS;
++ case KEY_SM2_CERT:
++ return KEY_SM2;
+ default:
+ return type;
+ }
+@@ -564,6 +572,8 @@ sshkey_type_certified(int type)
+ return KEY_DSA_CERT;
+ case KEY_ECDSA:
+ return KEY_ECDSA_CERT;
++ case KEY_SM2:
++ return KEY_SM2_CERT;
+ case KEY_ECDSA_SK:
+ return KEY_ECDSA_SK_CERT;
+ case KEY_ED25519:
+@@ -670,6 +680,8 @@ sshkey_curve_name_to_nid(const char *name)
+ else if (strcmp(name, "nistp521") == 0)
+ return NID_secp521r1;
+ # endif /* OPENSSL_HAS_NISTP521 */
++ else if (strcmp(name, "sm2") == 0)
++ return NID_sm2;
+ else
+ return -1;
+ }
+@@ -686,6 +698,8 @@ sshkey_curve_nid_to_bits(int nid)
+ case NID_secp521r1:
+ return 521;
+ # endif /* OPENSSL_HAS_NISTP521 */
++ case NID_sm2:
++ return 256;
+ default:
+ return 0;
+ }
+@@ -720,6 +734,8 @@ sshkey_curve_nid_to_name(int nid)
+ case NID_secp521r1:
+ return "nistp521";
+ # endif /* OPENSSL_HAS_NISTP521 */
++ case NID_sm2:
++ return "sm2";
+ default:
+ return NULL;
+ }
+@@ -3424,6 +3440,7 @@ sshkey_private_to_blob_pem_pkcs8(struct sshkey *key, struct sshbuf *buf,
+ break;
+ #ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
++ case KEY_SM2:
+ if (format == SSHKEY_PRIVATE_PEM) {
+ success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
+ cipher, passphrase, len, NULL, NULL);
+@@ -3485,6 +3502,7 @@ sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
+ #ifdef WITH_OPENSSL
+ case KEY_DSA:
+ case KEY_ECDSA:
++ case KEY_SM2:
+ case KEY_RSA:
+ break; /* see below */
+ #endif /* WITH_OPENSSL */
+@@ -3665,6 +3683,9 @@ sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
+ prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
+ prv->type = KEY_ECDSA;
+ prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
++ if (prv->ecdsa_nid == NID_sm2) {
++ prv->type = KEY_SM2;
++ }
+ if (prv->ecdsa_nid == -1 ||
+ sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
+ sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
+diff --git a/sshkey.h b/sshkey.h
+index 8d662d1..c8d2662 100644
+--- a/sshkey.h
++++ b/sshkey.h
+@@ -68,6 +68,8 @@ enum sshkey_types {
+ KEY_DSA_CERT,
+ KEY_ECDSA_CERT,
+ KEY_ED25519_CERT,
++ KEY_SM2,
++ KEY_SM2_CERT,
+ KEY_XMSS,
+ KEY_XMSS_CERT,
+ KEY_ECDSA_SK,
+--
+2.23.0
+
diff --git a/feature-openssh-7.4-hima-sftpserver-oom-and-fix.patch b/feature-openssh-7.4-hima-sftpserver-oom-and-fix.patch
new file mode 100644
index 0000000..7af483c
--- /dev/null
+++ b/feature-openssh-7.4-hima-sftpserver-oom-and-fix.patch
@@ -0,0 +1,911 @@
+From 6d98c61e18fe65a52e21df9cece74675f9c18125 Mon Sep 17 00:00:00 2001
+From: shenyining <shenyining@huawei.com>
+Date: Thu, 16 Apr 2020 17:13:24 +0800
+Subject: [PATCH] sync patch, add new judgement and
+ delete default sftp-put-check.cfg
+
+Signed-off-by: shenyining <shenyining@huawei.com>
+
+---
+ sftp-server.c | 702 +++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 690 insertions(+), 12 deletions(-)
+
+diff --git a/sftp-server.c b/sftp-server.c
+index 5677aa3..4eb06d1 100644
+--- a/sftp-server.c
++++ b/sftp-server.c
+@@ -30,6 +30,12 @@
+ #include <sys/statvfs.h>
+ #endif
+
++/* add begin sftp oom fix */
++#include <sys/sysinfo.h>
++#include <sys/vfs.h>
++#include <linux/magic.h>
++/* add end sftp oom fix */
++
+ #include <dirent.h>
+ #include <errno.h>
+ #include <fcntl.h>
+@@ -57,6 +63,17 @@
+ #include "sftp.h"
+ #include "sftp-common.h"
+
++static int storage_flag = 0;
++/* add begin 2013/10/12 SR-0000287268 */
++#define RETURN_OK 0
++#define RETURN_ERROR -1
++#define FLAG_PROTECTDIR 0
++#define FLAG_PERMITOP 1
++/* add end 2013/10/12 SR-0000287268 */
++/*add for oom*/
++static int cflag = 0;
++/*add for oom end*/
++
+ char *sftp_realpath(const char *, char *); /* sftp-realpath.c */
+
+ /* Maximum data read that we are willing to accept */
+@@ -98,6 +115,452 @@ struct Stat {
+ Attrib attrib;
+ };
+
++/* add begin 2013/10/12 SR-0000287268*/
++#define MAX_DIR_NUM 100
++/* sftppermit path array */
++char szPermitPath[MAX_DIR_NUM][MAXPATHLEN] = {0};
++char szDenyPath[MAX_DIR_NUM][MAXPATHLEN] = {0};
++const char *pszPermitPath = "/usr/local/etc/sftppermit.config";
++const char *pszDenyPath = "/usr/local/etc/sftpdeny.config";
++char szPermitPath_other[MAX_DIR_NUM][MAXPATHLEN] = {0};
++char szDenyPath_other[MAX_DIR_NUM][MAXPATHLEN] = {0};
++const char *pszPermitPath_other = "/usr/local/etc/other_sftppermit.config";
++const char *pszDenyPath_other= "/usr/local/etc/other_sftpdeny.config";
++static int
++read_config_file(const char* pszPath, char(*szConfigPath)[MAXPATHLEN])
++{
++ FILE *fd = NULL;
++ char *szBuffer = NULL;
++ size_t len = 0;
++ unsigned long linenum = 0;
++
++ if (NULL == pszPath)
++ {
++ debug("[sftp-server]Config file %s is NULL.\n", pszPath);
++ return RETURN_ERROR;
++ }
++
++ if (NULL == (fd = fopen(pszPath, "r")))
++ {
++ debug("[sftp-server]Open config file %s failed.\n", pszPath);
++ return RETURN_ERROR;
++ }
++
++ while (RETURN_ERROR != getline(&szBuffer, &len, fd))
++ {
++ linenum++;
++ //Fix bug exceed max permit dir 2013-10-18 begin
++ if ( linenum > MAX_DIR_NUM )
++ {
++ debug("[sftp-server]Exceed max number of config dir.\n");
++ break;
++ }
++ //Fix bug exceed max permit dir 2013-10-18 end
++ memcpy(szConfigPath[linenum-1], szBuffer , strlen(szBuffer));
++ if ( szConfigPath[linenum-1][strlen(szBuffer)-1] == '\n' )
++ {
++ szConfigPath[linenum-1][strlen(szBuffer)-1] = '\0';
++ if ( szConfigPath[linenum-1][strlen(szBuffer)-2] == '\r' )
++ {
++ szConfigPath[linenum-1][strlen(szBuffer)-2] = '\0';
++ }
++ }
++ }
++
++ fclose(fd);
++ storage_flag = 1;
++ return RETURN_OK;
++}
++
++static int
++path_permition_check(const char *pszPath,int iflag)
++{
++ unsigned int iCount = 0;
++ char szResolvedname[MAXPATHLEN] = {0};
++ gid_t server_user_gid, local_user_gid;
++ int path_len = 0;
++
++ if(storage_flag != 1)
++ return RETURN_OK;
++
++ if(NULL == pszPath)
++ {
++ debug("[sftp-server]Inputed param for check is NULL.\n");
++ return RETURN_ERROR;
++ }
++
++ realpath(pszPath, szResolvedname);
++ local_user_gid = pw->pw_gid;
++ server_user_gid = local_user_gid;
++ if(NULL != szResolvedname)
++ {
++ if (server_user_gid == 0)
++ {
++ for (iCount=0; iCount < MAX_DIR_NUM ; iCount++)
++ {
++ path_len = strlen(szDenyPath[iCount]);
++ if((0 != szDenyPath[iCount][0]) && (szResolvedname == strstr(szResolvedname,szDenyPath[iCount]))){
++ if(szResolvedname[path_len] == '\0' || szResolvedname[path_len] == '/')
++ return RETURN_ERROR;
++ }
++ }
++
++ for (iCount=0; iCount < MAX_DIR_NUM ; iCount++)
++ {
++ path_len = strlen(szPermitPath[iCount]);
++ if((0 != szPermitPath[iCount][0])
++ && (szResolvedname == strstr(szResolvedname,szPermitPath[iCount]))
++ && (szResolvedname[path_len] == '\0' || szResolvedname[path_len] == '/'))
++ {
++ if ((FLAG_PROTECTDIR == iflag) && (0 == strcmp(szResolvedname,szPermitPath[iCount])))
++ {
++ debug("[sftp-server]Can't operate protected dir.\n");
++ return RETURN_ERROR;
++ }
++ return RETURN_OK;
++ }
++ }
++ }
++ else
++ {
++ for (iCount=0; iCount < MAX_DIR_NUM ; iCount++)
++ {
++ path_len = strlen(szDenyPath_other[iCount]);
++ if((0 != szDenyPath_other[iCount][0]) && (szResolvedname == strstr(szResolvedname,szDenyPath_other[iCount])))
++ if(szResolvedname[path_len] == '\0' || szResolvedname[path_len] == '/')
++ return RETURN_ERROR;
++ }
++
++ for (iCount=0; iCount < MAX_DIR_NUM ; iCount++)
++ {
++ path_len = strlen(szPermitPath_other[iCount]);
++ if((0 != szPermitPath_other[iCount][0])
++ && (szResolvedname == strstr(szResolvedname,szPermitPath_other[iCount]))
++ && (szResolvedname[path_len] == '\0' || szResolvedname[path_len] == '/'))
++ {
++ if ((FLAG_PROTECTDIR == iflag) && (0 == strcmp(szResolvedname,szPermitPath_other[iCount])))
++ {
++ debug("[sftp-server]Can't operate protected dir.\n");
++ return RETURN_ERROR;
++ }
++ return RETURN_OK;
++ }
++ }
++
++ }
++ }
++
++ return RETURN_ERROR;
++}
++/* add end 2013/10/12 SR-0000287268 */
++
++/* add begin sftp oom fix */
++#define BUF_MAX_LEN 4096 /*Max lenth*/
++#define MAX_LINE_LEN 80 /*Max line lenth*/
++
++#define DEFAULT_FILESIZE 4096
++#define DEFAULT_MEMSIZE 0
++
++const char *ck_config_file_name = "/usr/local/etc/sftp-put-check.cfg";
++
++typedef struct {
++ int max_file_size;
++ int min_freemem_size;
++}CheckOptions;
++
++static CheckOptions ckoptions;
++
++/* Keyword tokens. */
++typedef enum {
++ sBadOption,
++ sMaxFileSize,
++ sMinFreeMemSize
++} checkOpCodes;
++
++static struct {
++ const char *name;
++ checkOpCodes opcode;
++} keywords[] = {
++ { "MaxFileSize", sMaxFileSize },
++ { "MinFreeMemSize", sMinFreeMemSize },
++ { NULL, sBadOption }
++};
++
++static checkOpCodes
++ck_parse_token(const char *cp, const char *filename, int linenum)
++{
++ int i;
++ for (i = 0; keywords[i].name; i++)
++ {
++ if (strcasecmp(cp, keywords[i].name) == 0)
++ {
++ return keywords[i].opcode;
++ }
++ }
++
++ error("%s: line %d: Bad configuration option: %s", filename, linenum, cp);
++
++ return sBadOption;
++}
++static int
++ck_process_server_config_line(char *line, const char *filename, int linenum)
++{
++ char *cp, *arg, *endofnumber;
++ endofnumber = NULL;
++ cp = NULL;
++ arg = NULL;
++ int *intptr = NULL;
++ int value;
++ checkOpCodes opcode;
++
++ cp = line;
++ if ((arg = strdelim(&cp)) == NULL)
++ {
++ return 1;
++ }
++
++ /* Ignore leading whitespace */
++ if (*arg == '\0')
++ {
++ arg = strdelim(&cp);
++ }
++
++ if (!arg || !*arg || *arg == '#')
++ {
++ return 1;
++ }
++
++ opcode = ck_parse_token(arg, filename, linenum);
++
++ switch (opcode)
++ {
++ case sBadOption:
++ /* don't panic, but count bad ckoptions */
++ return 0;
++ case sMaxFileSize:
++ intptr = &ckoptions.max_file_size;
++ goto parse_int;
++
++ case sMinFreeMemSize:
++ intptr = &ckoptions.min_freemem_size;
++ goto parse_int;
++parse_int:
++ arg = strdelim(&cp);
++ if (!arg || *arg == '\0')
++ {
++ error("%.200s line %d: Missing argument.", filename, linenum);
++ return 0;
++ }
++
++ if (arg[0] < '0' || arg[0] > '9')
++ {
++ error("%.200s line %d: Bad number.", filename, linenum);
++ return 0;
++ }
++ /* Octal, decimal, or hex format? */
++ value = strtol(arg, &endofnumber, 0);
++ if (arg == endofnumber)
++ {
++ error("%.200s line %d: Bad number.", filename, linenum);
++ return 0;
++ }
++
++ *intptr = value;
++
++ break;
++ default:
++ error("%s line %d: Missing handler for opcode %s (%d) ", filename, linenum, arg, opcode);
++ break;
++ }
++
++ if ((arg = strdelim(&cp)) != NULL && *arg != '\0')
++ {
++ error("%s line %d: garbage at end of line; \"%.200s\". ", filename, linenum, arg);
++ return 0;
++ }
++
++ return 1;
++}
++
++static int
++ck_load_server_config(const char *filename, char *conf)
++{
++ char line[MAX_LINE_LEN + 1], *cp;
++ cp = NULL;
++ FILE *f;
++ int lineno = 0;
++ int lenth = 0;
++
++ if ((f = fopen(filename, "r")) == NULL)
++ {
++ error("Failed to open config file: %s ,use default setting", filename);
++ return 2;
++ }
++
++ while (fgets(line, sizeof(line), f))
++ {
++ lineno++;
++ if (strlen(line) > MAX_LINE_LEN)
++ {
++ error("%s line %d too long, the max length is %d", filename, lineno, MAX_LINE_LEN);
++ fclose(f);
++ return 0;
++ }
++ /*
++ * * Trim out comments and strip whitespace
++ * * NB - preserve newlines, they are needed to reproduce
++ * * line numbers later for error messages
++ * */
++ if ((cp = strchr(line, '#')) != NULL)
++ {
++ memcpy(cp, "\n", 2);
++ }
++ cp = line + strspn(line, " \t\r");
++
++ if(lenth + strlen(cp) > BUF_MAX_LEN)
++ {
++ error("%s too big, the max size is %d!", filename, BUF_MAX_LEN);
++ fclose(f);
++ return 0;
++ }
++
++ memcpy(conf + lenth, cp, strlen(cp));
++
++ lenth += strlen(cp);
++ }
++
++ memcpy(conf + lenth, "\0", 1);
++
++ fclose(f);
++
++ return 1;
++}
++
++static int
++ck_parse_server_config(const char *filename)
++{
++ int linenum, ret_load, bad_options = 0;
++ char *cp = NULL;
++ char *obuf = NULL;
++ char *cbuf = NULL;
++
++ obuf = cbuf = malloc(BUF_MAX_LEN);
++ if (cbuf == NULL)
++ {
++ error("Malloc: out of memory (allocating %lu bytes)", BUF_MAX_LEN);
++ return 0;
++ }
++
++ ret_load = ck_load_server_config(filename, cbuf);
++
++ if(ret_load == 0)
++ {
++ error("Config file %s is not set properly", filename);
++ free(obuf);
++ return 0;
++ }
++
++ if(ret_load == 2)
++ {
++ debug("Load config file %s error, use default setting", filename);
++ free(obuf);
++ return 1;
++ }
++
++ linenum = 1;
++ while ((cp = strsep(&cbuf, "\n")) != NULL)
++ {
++ if (!ck_process_server_config_line(cp, filename, linenum++) )
++ {
++ bad_options++;
++ }
++ }
++
++ free(obuf);
++
++ if (bad_options > 0)
++ {
++ error("%s: terminating, %d bad configuration ckoptions", filename, bad_options);
++ return 0;
++ }
++
++ return 1;
++}
++
++void
++initialize_check_options(CheckOptions *ckoptions)
++{
++ memset(ckoptions, 0, sizeof(*ckoptions));
++
++ ckoptions->max_file_size = DEFAULT_FILESIZE;
++
++ ckoptions->min_freemem_size = DEFAULT_MEMSIZE;
++}
++
++static int
++check_before_write(const char *path, u_int64_t size)
++{
++ struct sysinfo meminfo;
++ u_int64_t maxfilesize = 0;
++ u_int64_t minfreememsize = 0;
++
++ if (storage_flag != 1)
++ return 1;
++
++ if (NULL == path)
++ {
++ error("process_write: Upload file is NULL.");
++ return 0;
++ }
++
++ if (cflag == 0)
++ {
++ debug3("not put file to tmpfs or ramfs, do not need check free memory");
++ return 1;
++ }
++
++ debug("check file size and free mem info before write");
++
++ sysinfo(&meminfo);
++ maxfilesize = (u_int64_t)(ckoptions.max_file_size)*1024*1024;
++ minfreememsize = (u_int64_t)(ckoptions.min_freemem_size)*1024*1024;
++
++ logit("upload file :%s size %llu freeram %lu bytes MaxFileSize %lu bytes MinFreeMemSize %lu bytes.",
++ path, size, meminfo.freeram, maxfilesize, minfreememsize);
++
++ /*check file size*/
++ if (size >= maxfilesize){
++ error("process_write: file %s exceed %d MB, upload failed.", path, ckoptions.max_file_size);
++ return 0;
++ }
++
++ /*check free mem*/
++ if (meminfo.freeram <= minfreememsize){
++ error("process_write: Memory limit set to %d MB, no space(memeroy system) left, upload failed.",
++ ckoptions.min_freemem_size);
++ return 0;
++ }
++
++ return 1;
++}
++
++static void
++check_fstype(const char *path)
++{
++ struct statfs buf;
++
++ memset(&buf, 0, sizeof(buf));
++ if (statfs(path, &buf) !=0)
++ {
++ error("fstype unkown, do not check free memeroy.");
++ }
++ else if (buf.f_type == TMPFS_MAGIC || buf.f_type == RAMFS_MAGIC)
++ {
++ cflag = 1;
++ }
++}
++/* add end sftp oom fix */
++
+ /* Packet handlers */
+ static void process_open(u_int32_t id);
+ static void process_close(u_int32_t id);
+@@ -755,6 +1218,15 @@ process_open(u_int32_t id)
+ (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */
+ (r = decode_attrib(iqueue, &a)) != 0)
+ fatal_fr(r, "parse");
++ /* add begin 2013/10/12 SR-0000287268 */
++ if (RETURN_OK != path_permition_check(name,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++ /* add end 2013/10/12 SR-0000287268 */
+
+ debug3("request %u: open flags %d", id, pflags);
+ flags = flags_from_portable(pflags);
+@@ -788,6 +1260,8 @@ process_open(u_int32_t id)
+ (void) umask(old_umask); /* restore umask to something sane */
+ if (status != SSH2_FX_OK)
+ send_status(id, status);
++ if (storage_flag == 1)
++ check_fstype(name);
+ free(name);
+ }
+
+@@ -820,6 +1294,17 @@ process_read(u_int32_t id)
+ (r = sshbuf_get_u32(iqueue, &len)) != 0)
+ fatal_fr(r, "parse");
+
++ /* add begin 2013/10/12 SR-0000287268*/
++ char *path = NULL;
++ path = handle_to_name(handle);
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++
++ return;
++ }
++ /* add end 2013/10/12 SR-0000287268*/
++
+ debug("request %u: read \"%s\" (handle %d) off %llu len %u",
+ id, handle_to_name(handle), handle, (unsigned long long)off, len);
+ if ((fd = handle_to_fd(handle)) == -1)
+@@ -874,6 +1359,18 @@ process_write(u_int32_t id)
+ (r = sshbuf_get_string(iqueue, &data, &len)) != 0)
+ fatal_fr(r, "parse");
+
++ /* add begin 2013/10/12 SR-0000287268*/
++ char *path = NULL;
++ path = handle_to_name(handle);
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(data);
++
++ return;
++ }
++ /* add end 2013/10/12 SR-0000287268*/
++
+ debug("request %u: write \"%s\" (handle %d) off %llu len %zu",
+ id, handle_to_name(handle), handle, (unsigned long long)off, len);
+ fd = handle_to_fd(handle);
+@@ -888,17 +1385,30 @@ process_write(u_int32_t id)
+ strerror(errno));
+ } else {
+ /* XXX ATOMICIO ? */
+- ret = write(fd, data, len);
+- if (ret == -1) {
+- status = errno_to_portable(errno);
+- error_f("write \"%.100s\": %s",
+- handle_to_name(handle), strerror(errno));
+- } else if ((size_t)ret == len) {
+- status = SSH2_FX_OK;
+- handle_update_write(handle, ret);
+- } else {
+- debug2_f("nothing at all written");
++ /* add begin sftp oom fix */
++ if (storage_flag == 1)
++ debug("cflag is %d",cflag);
++ if (!check_before_write(handle_to_name(handle), off)){
++ error("check file size and free mem info before write failed");
++ unlink(handle_to_name(handle));
+ status = SSH2_FX_FAILURE;
++ send_status(id, status);
++ free(data);
++ sftp_server_cleanup_exit(1);
++ /* add end sftp oom fix */
++ } else {
++
++ ret = write(fd, data, len);
++ if (ret < 0) {
++ error("process_write: write failed");
++ status = errno_to_portable(errno);
++ } else if ((size_t)ret == len) {
++ status = SSH2_FX_OK;
++ handle_update_write(handle, ret);
++ } else {
++ debug2("nothing at all written");
++ status = SSH2_FX_FAILURE;
++ }
+ }
+ }
+ }
+@@ -917,6 +1427,16 @@ process_do_stat(u_int32_t id, int do_lstat)
+ if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ /* add begin 2013/10/12 SR-0000287268 */
++ if (RETURN_OK != path_permition_check(name,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++ /* add end 2013/10/12 SR-0000287268 */
++
+ debug3("request %u: %sstat", id, do_lstat ? "l" : "");
+ verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
+ r = do_lstat ? lstat(name, &st) : stat(name, &st);
+@@ -953,6 +1473,16 @@ process_fstat(u_int32_t id)
+
+ if ((r = get_handle(iqueue, &handle)) != 0)
+ fatal_fr(r, "parse");
++
++ char *path = NULL;
++ path = handle_to_name(handle);
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++
++ return;
++ }
++
+ debug("request %u: fstat \"%s\" (handle %u)",
+ id, handle_to_name(handle), handle);
+ fd = handle_to_fd(handle);
+@@ -1005,6 +1535,14 @@ process_setstat(u_int32_t id)
+ (r = decode_attrib(iqueue, &a)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(name,FLAG_PROTECTDIR))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++
+ debug("request %u: setstat name \"%s\"", id, name);
+ if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
+ logit("set \"%s\" size %llu",
+@@ -1059,6 +1597,13 @@ process_fsetstat(u_int32_t id)
+ else {
+ char *name = handle_to_name(handle);
+
++ if (RETURN_OK != path_permition_check(name,FLAG_PROTECTDIR))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++
++ return;
++ }
++
+ if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
+ logit("set \"%s\" size %llu",
+ name, (unsigned long long)a.size);
+@@ -1116,6 +1661,14 @@ process_opendir(u_int32_t id)
+ if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(path);
++
++ return;
++ }
++
+ debug3("request %u: opendir", id);
+ logit("opendir \"%s\"", path);
+ dirp = opendir(path);
+@@ -1170,6 +1723,9 @@ process_readdir(u_int32_t id)
+ strcmp(path, "/") ? "/" : "", dp->d_name);
+ if (lstat(pathname, &st) == -1)
+ continue;
++ if (RETURN_OK != path_permition_check(pathname,FLAG_PERMITOP)) {
++ continue;
++ }
+ stat_to_attrib(&st, &(stats[count].attrib));
+ stats[count].name = xstrdup(dp->d_name);
+ stats[count].long_name = ls_file(dp->d_name, &st,
+@@ -1202,6 +1758,14 @@ process_remove(u_int32_t id)
+ if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(name,FLAG_PROTECTDIR))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++
+ debug3("request %u: remove", id);
+ logit("remove name \"%s\"", name);
+ r = unlink(name);
+@@ -1221,6 +1785,14 @@ process_mkdir(u_int32_t id)
+ (r = decode_attrib(iqueue, &a)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(name,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++
+ mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
+ a.perm & 07777 : 0777;
+ debug3("request %u: mkdir", id);
+@@ -1240,6 +1812,14 @@ process_rmdir(u_int32_t id)
+ if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(name,FLAG_PROTECTDIR))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(name);
++
++ return;
++ }
++
+ debug3("request %u: rmdir", id);
+ logit("rmdir name \"%s\"", name);
+ r = rmdir(name);
+@@ -1264,8 +1844,12 @@ process_realpath(u_int32_t id)
+ }
+ debug3("request %u: realpath", id);
+ verbose("realpath \"%s\"", path);
+- if (sftp_realpath(path, resolvedname) == NULL) {
+- send_status(id, errno_to_portable(errno));
++ if ((sftp_realpath(path, resolvedname) == NULL)
++ || (RETURN_OK != path_permition_check(resolvedname,FLAG_PERMITOP))) {
++ if (storage_flag != 1)
++ send_status(id, errno_to_portable(errno));
++ else
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
+ } else {
+ Stat s;
+ attrib_clear(&s.attrib);
+@@ -1286,6 +1870,16 @@ process_rename(u_int32_t id)
+ (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if ((RETURN_OK != path_permition_check(oldpath,FLAG_PROTECTDIR))
++ || (RETURN_OK != path_permition_check(newpath,FLAG_PROTECTDIR)))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(oldpath);
++ free(newpath);
++
++ return;
++ }
++
+ debug3("request %u: rename", id);
+ logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
+ status = SSH2_FX_FAILURE;
+@@ -1345,6 +1939,14 @@ process_readlink(u_int32_t id)
+ if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(path);
++
++ return;
++ }
++
+ debug3("request %u: readlink", id);
+ verbose("readlink \"%s\"", path);
+ if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
+@@ -1370,6 +1972,16 @@ process_symlink(u_int32_t id)
+ (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if ((RETURN_OK != path_permition_check(oldpath,FLAG_PROTECTDIR))
++ || (RETURN_OK != path_permition_check(newpath,FLAG_PROTECTDIR)))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(oldpath);
++ free(newpath);
++
++ return;
++ }
++
+ debug3("request %u: symlink", id);
+ logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
+ /* this will fail if 'newpath' exists */
+@@ -1390,6 +2002,16 @@ process_extended_posix_rename(u_int32_t id)
+ (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if ((RETURN_OK != path_permition_check(oldpath,FLAG_PROTECTDIR))
++ || (RETURN_OK != path_permition_check(newpath,FLAG_PROTECTDIR)))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(oldpath);
++ free(newpath);
++
++ return;
++ }
++
+ debug3("request %u: posix-rename", id);
+ logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
+ r = rename(oldpath, newpath);
+@@ -1408,6 +2030,15 @@ process_extended_statvfs(u_int32_t id)
+
+ if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+ fatal_fr(r, "parse");
++
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(path);
++
++ return;
++ }
++
+ debug3("request %u: statvfs", id);
+ logit("statvfs \"%s\"", path);
+
+@@ -1426,6 +2057,17 @@ process_extended_fstatvfs(u_int32_t id)
+
+ if ((r = get_handle(iqueue, &handle)) != 0)
+ fatal_fr(r, "parse");
++
++ char *path = NULL;
++ path = handle_to_name(handle);
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(path);
++
++ return;
++ }
++
+ debug("request %u: fstatvfs \"%s\" (handle %u)",
+ id, handle_to_name(handle), handle);
+ if ((fd = handle_to_fd(handle)) < 0) {
+@@ -1448,6 +2090,15 @@ process_extended_hardlink(u_int32_t id)
+ (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+ fatal_fr(r, "parse");
+
++ if ((RETURN_OK != path_permition_check(oldpath,FLAG_PROTECTDIR))
++ || (RETURN_OK != path_permition_check(newpath,FLAG_PROTECTDIR)))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(oldpath);
++ free(newpath);
++ return;
++ }
++
+ debug3("request %u: hardlink", id);
+ logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
+ r = link(oldpath, newpath);
+@@ -1464,6 +2115,17 @@ process_extended_fsync(u_int32_t id)
+
+ if ((r = get_handle(iqueue, &handle)) != 0)
+ fatal_fr(r, "parse");
++
++ char *path = NULL;
++ path = handle_to_name(handle);
++ if (RETURN_OK != path_permition_check(path,FLAG_PERMITOP))
++ {
++ send_status(id, SSH2_FX_PERMISSION_DENIED);
++ free(path);
++
++ return;
++ }
++
+ debug3("request %u: fsync (handle %u)", id, handle);
+ verbose("fsync \"%s\"", handle_to_name(handle));
+ if ((fd = handle_to_fd(handle)) < 0)
+@@ -2006,6 +2668,22 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw, int reset_handle
+
+ log_init_handler(__progname, log_level, log_facility, log_stderr, reset_handler);
+
++ read_config_file(pszPermitPath, szPermitPath);
++ read_config_file(pszDenyPath, szDenyPath);
++ read_config_file(pszPermitPath_other, szPermitPath_other);
++ read_config_file(pszDenyPath_other, szDenyPath_other);
++
++ if (storage_flag == 1)
++ {
++ initialize_check_options(&ckoptions);
++ debug("Parse config file: %s", ck_config_file_name);
++ if(!ck_parse_server_config(ck_config_file_name))
++ {
++ error("Failed to parse config file: %s!", ck_config_file_name);
++ sftp_server_cleanup_exit(1);
++ }
++ }
++
+ /*
+ * On platforms where we can, avoid making /proc/self/{mem,maps}
+ * available to the user so that sftp access doesn't automatically
+--
+2.27.0
+
diff --git a/openssh-4.3p2-askpass-grab-info.patch b/openssh-4.3p2-askpass-grab-info.patch
new file mode 100644
index 0000000..120ed1b
--- /dev/null
+++ b/openssh-4.3p2-askpass-grab-info.patch
@@ -0,0 +1,18 @@
+diff -up openssh-8.6p1/contrib/gnome-ssh-askpass2.c.grab-info openssh-8.6p1/contrib/gnome-ssh-askpass2.c
+--- openssh-8.6p1/contrib/gnome-ssh-askpass2.c.grab-info 2021-04-19 13:57:11.720113536 +0200
++++ openssh-8.6p1/contrib/gnome-ssh-askpass2.c 2021-04-19 13:59:29.842163204 +0200
+@@ -70,8 +70,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/openssh-5.1p1-askpass-progress.patch b/openssh-5.1p1-askpass-progress.patch
new file mode 100644
index 0000000..ff609da
--- /dev/null
+++ b/openssh-5.1p1-askpass-progress.patch
@@ -0,0 +1,83 @@
+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 <unistd.h>
+
+ #include <X11/Xlib.h>
++#include <glib.h>
+ #include <gtk/gtk.h>
+ #include <gdk/gdkx.h>
+ #include <gdk/gdkkeysyms.h>
+@@ -81,14 +82,25 @@ ok_dialog(GtkWidget *entry, gpointer dia
+ return 1;
+ }
+
++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, int prompt_type)
+ {
+ const char *failed;
+ char *passphrase, *local;
+ int result, grab_tries, grab_server, grab_pointer;
+ int buttons, default_response;
+- GtkWidget *parent_window, *dialog, *entry;
++ GtkWidget *parent_window, *dialog, *entry, *progress, *hbox;
+ GdkGrabStatus status;
+ GdkColor fg, bg;
+ int fg_set = 0, bg_set = 0;
+@@ -104,14 +116,19 @@ passphrase_dialog(char *message)
+ gtk_widget_modify_bg(dialog, GTK_STATE_NORMAL, &bg);
+
+ if (prompt_type == PROMPT_ENTRY || prompt_type == PROMPT_NONE) {
++ 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();
+ if (fg_set)
+ gtk_widget_modify_fg(entry, GTK_STATE_NORMAL, &fg);
+ if (bg_set)
+ gtk_widget_modify_bg(entry, GTK_STATE_NORMAL, &bg);
+ 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);
+ if (prompt_type == PROMPT_ENTRY) {
+@@ -130,6 +145,22 @@ passphrase_dialog(char *message)
+ g_signal_connect(G_OBJECT(entry), "key_press_event",
+ G_CALLBACK(check_none), dialog);
+ }
++
++ 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);
++ g_signal_connect(G_OBJECT(entry), "changed",
++ G_CALLBACK(move_progress), progress);
++
+ }
+
+ /* Grab focus */
diff --git a/openssh-5.8p2-sigpipe.patch b/openssh-5.8p2-sigpipe.patch
new file mode 100644
index 0000000..554e346
--- /dev/null
+++ b/openssh-5.8p2-sigpipe.patch
@@ -0,0 +1,14 @@
+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,9 @@ main(int argc, char **argv)
+ if (maxfd > fdlim_get(0))
+ fdlim_set(maxfd);
+ fdcon = xcalloc(maxfd, sizeof(con));
++
++ signal(SIGPIPE, SIG_IGN);
++
+ read_wait = xcalloc(maxfd, sizeof(struct pollfd));
+ for (j = 0; j < maxfd; j++)
+ read_wait[j].fd = -1;
+
diff --git a/openssh-5.9p1-ipv6man.patch b/openssh-5.9p1-ipv6man.patch
new file mode 100644
index 0000000..ece1a73
--- /dev/null
+++ b/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/openssh-6.4p1-fromto-remote.patch b/openssh-6.4p1-fromto-remote.patch
new file mode 100644
index 0000000..4a7d849
--- /dev/null
+++ b/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/openssh-6.6.1p1-log-in-chroot.patch b/openssh-6.6.1p1-log-in-chroot.patch
new file mode 100644
index 0000000..941c694
--- /dev/null
+++ b/openssh-6.6.1p1-log-in-chroot.patch
@@ -0,0 +1,263 @@
+diff -up openssh-8.6p1/log.c.log-in-chroot openssh-8.6p1/log.c
+--- openssh-8.6p1/log.c.log-in-chroot 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/log.c 2021-04-19 14:43:08.544843434 +0200
+@@ -194,6 +194,11 @@ void
+ log_init(const char *av0, LogLevel level, SyslogFacility facility,
+ int on_stderr)
+ {
++ log_init_handler(av0, level, facility, on_stderr, 1);
++}
++
++void
++log_init_handler(const 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
+@@ -206,8 +211,10 @@ log_init(const char *av0, LogLevel level
+ 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-8.6p1/log.h.log-in-chroot openssh-8.6p1/log.h
+--- openssh-8.6p1/log.h.log-in-chroot 2021-04-19 14:43:08.544843434 +0200
++++ openssh-8.6p1/log.h 2021-04-19 14:56:46.931042176 +0200
+@@ -52,6 +52,7 @@ typedef enum {
+ typedef void (log_handler_fn)(LogLevel, int, const char *, void *);
+
+ void log_init(const char *, LogLevel, SyslogFacility, int);
++void log_init_handler(const char *, LogLevel, SyslogFacility, int, int);
+ LogLevel log_level_get(void);
+ int log_change_level(LogLevel);
+ int log_is_on_stderr(void);
+diff -up openssh-8.6p1/monitor.c.log-in-chroot openssh-8.6p1/monitor.c
+--- openssh-8.6p1/monitor.c.log-in-chroot 2021-04-19 14:43:08.526843298 +0200
++++ openssh-8.6p1/monitor.c 2021-04-19 14:55:25.286424043 +0200
+@@ -297,6 +297,8 @@ monitor_child_preauth(struct ssh *ssh, s
+ close(pmonitor->m_log_sendfd);
+ pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1;
+
++ pmonitor->m_state = "preauth";
++
+ authctxt = (Authctxt *)ssh->authctxt;
+ memset(authctxt, 0, sizeof(*authctxt));
+ ssh->authctxt = authctxt;
+@@ -408,6 +410,8 @@ monitor_child_postauth(struct ssh *ssh,
+ close(pmonitor->m_recvfd);
+ pmonitor->m_recvfd = -1;
+
++ pmonitor->m_state = "postauth";
++
+ monitor_set_child_handler(pmonitor->m_pid);
+ ssh_signal(SIGHUP, &monitor_child_handler);
+ ssh_signal(SIGTERM, &monitor_child_handler);
+@@ -480,7 +484,7 @@ monitor_read_log(struct monitor *pmonito
+ /* Log it */
+ if (log_level_name(level) == NULL)
+ fatal_f("invalid log level %u (corrupted message?)", level);
+- sshlogdirect(level, forced, "%s [preauth]", msg);
++ sshlogdirect(level, forced, "%s [%s]", msg, pmonitor->m_state);
+
+ sshbuf_free(logmsg);
+ free(msg);
+@@ -1868,13 +1872,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_f("/dev/log doesn't exist in %s chroot - will try to log via monitor using [postauth] suffix", chroot_dir);
++ do_logfds = 1;
++ }
++ free(dev_log_path);
++ }
++ monitor_openfds(mon, do_logfds);
+ }
+
+ #ifdef GSSAPI
+diff -up openssh-8.6p1/monitor.h.log-in-chroot openssh-8.6p1/monitor.h
+--- openssh-8.6p1/monitor.h.log-in-chroot 2021-04-19 14:43:08.527843305 +0200
++++ openssh-8.6p1/monitor.h 2021-04-19 14:43:08.545843441 +0200
+@@ -80,10 +80,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 ssh *, struct monitor *);
+diff -up openssh-8.6p1/session.c.log-in-chroot openssh-8.6p1/session.c
+--- openssh-8.6p1/session.c.log-in-chroot 2021-04-19 14:43:08.534843358 +0200
++++ openssh-8.6p1/session.c 2021-04-19 14:43:08.545843441 +0200
+@@ -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;
+@@ -661,6 +662,7 @@ do_exec(struct ssh *ssh, Session *s, con
+ 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;
+@@ -720,6 +722,10 @@ do_exec(struct ssh *ssh, Session *s, con
+ 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 ",
+@@ -1524,14 +1530,6 @@ child_close_fds(struct ssh *ssh)
+
+ /* Stop directing logs to a high-numbered fd before we close it */
+ log_redirect_stderr_to(NULL);
+-
+- /*
+- * 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);
+ }
+
+ /*
+@@ -1665,8 +1663,6 @@ do_child(struct ssh *ssh, Session *s, co
+ exit(1);
+ }
+
+- closefrom(STDERR_FILENO + 1);
+-
+ do_rc_files(ssh, s, shell);
+
+ /* restore SIGPIPE for child */
+@@ -1691,9 +1687,17 @@ do_child(struct ssh *ssh, Session *s, co
+ 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-8.6p1/sftp.h.log-in-chroot openssh-8.6p1/sftp.h
+--- openssh-8.6p1/sftp.h.log-in-chroot 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/sftp.h 2021-04-19 14:43:08.545843441 +0200
+@@ -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-8.6p1/sftp-server.c.log-in-chroot openssh-8.6p1/sftp-server.c
+--- openssh-8.6p1/sftp-server.c.log-in-chroot 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/sftp-server.c 2021-04-19 14:43:08.545843441 +0200
+@@ -1644,7 +1644,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)
+ {
+ int i, r, in, out, ch, skipargs = 0, log_stderr = 0;
+ ssize_t len, olen;
+@@ -1657,7 +1657,7 @@ sftp_server_main(int argc, char **argv,
+ extern char *__progname;
+
+ __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);
+
+@@ -1730,7 +1730,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-8.6p1/sftp-server-main.c.log-in-chroot openssh-8.6p1/sftp-server-main.c
+--- openssh-8.6p1/sftp-server-main.c.log-in-chroot 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/sftp-server-main.c 2021-04-19 14:43:08.545843441 +0200
+@@ -50,5 +50,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-8.6p1/sshd.c.log-in-chroot openssh-8.6p1/sshd.c
+--- openssh-8.6p1/sshd.c.log-in-chroot 2021-04-19 14:43:08.543843426 +0200
++++ openssh-8.6p1/sshd.c 2021-04-19 14:43:08.545843441 +0200
+@@ -559,7 +559,7 @@ privsep_postauth(struct ssh *ssh, Authct
+ }
+
+ /* New socket pair */
+- monitor_reinit(pmonitor);
++ monitor_reinit(pmonitor, options.chroot_directory);
+
+ pmonitor->m_pid = fork();
+ if (pmonitor->m_pid == -1)
+@@ -578,6 +578,11 @@ privsep_postauth(struct ssh *ssh, Authct
+
+ 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/openssh-6.6.1p1-scp-non-existing-directory.patch b/openssh-6.6.1p1-scp-non-existing-directory.patch
new file mode 100644
index 0000000..bb55c0b
--- /dev/null
+++ b/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)) == -1) {
+--
diff --git a/openssh-6.6.1p1-selinux-contexts.patch b/openssh-6.6.1p1-selinux-contexts.patch
new file mode 100644
index 0000000..fa9d591
--- /dev/null
+++ b/openssh-6.6.1p1-selinux-contexts.patch
@@ -0,0 +1,133 @@
+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,72 @@ 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;
++ struct stat sb;
++
++ contexts_path = selinux_openssh_contexts_path();
++ if (contexts_path == NULL) {
++ debug3_f("Failed to get the path to SELinux context");
++ return;
++ }
++
++ if ((contexts_file = fopen(contexts_path, "r")) == NULL) {
++ debug_f("Failed to open SELinux context file");
++ return;
++ }
++
++ if (fstat(fileno(contexts_file), &sb) != 0 ||
++ sb.st_uid != 0 || (sb.st_mode & 022) != 0) {
++ logit_f("SELinux context file needs to be owned by root"
++ " and not writable by anyone else");
++ fclose(contexts_file);
++ return;
++ }
++
++ 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_f("privsep_preauth is empty");
++ fclose(contexts_file);
++ return;
++ }
++ preauth_context = xstrdup(arg);
++ }
++ }
++ fclose(contexts_file);
++
++ if (preauth_context == NULL) {
++ debug_f("Unable to find 'privsep_preauth' option in"
++ " SELinux context file");
++ return;
++ }
++
++ 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
+@@ -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_f("setting context from '%s' to '%s'",
+ oldctx, newctx);
+ if (setcon(newctx) < 0)
+ do_log2(log_level, "%s: setcon %s from %s failed with %s",
+ __func__, newctx, oldctx, strerror(errno));
+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/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch
new file mode 100644
index 0000000..cccb3e0
--- /dev/null
+++ b/openssh-6.6p1-GSSAPIEnablek5users.patch
@@ -0,0 +1,131 @@
+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_store_rekey = -1;
+ options->gss_kex_algorithms = NULL;
+ options->use_kuserok = -1;
++ options->enable_k5users = -1;
+ options->password_authentication = -1;
+ options->kbd_interactive_authentication = -1;
+ options->permit_empty_passwd = -1;
+@@ -345,6 +346,8 @@ fill_default_server_options(ServerOption
+ #endif
+ 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, sHostbasedAcceptedAlgorithms,
+ sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize,
+ sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
+- sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
++ sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor,
+ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
+ sAcceptEnv, sSetEnv, sPermitTunnel,
+ sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
+@@ -497,14 +500,16 @@ static struct {
+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
+ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
++ { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL },
+ #else
+ { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
+ { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapikexalgorithms", 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 sMatch:
+ if (cmdline)
+ fatal("Match directive not supported as a command-line "
+@@ -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 yes
+ #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/openssh-6.6p1-allow-ip-opts.patch b/openssh-6.6p1-allow-ip-opts.patch
new file mode 100644
index 0000000..be8d340
--- /dev/null
+++ b/openssh-6.6p1-allow-ip-opts.patch
@@ -0,0 +1,42 @@
+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,32 @@ 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:
++ if (i + 1 < option_size && opts[i + 1] >= 2) {
++ i += opts[i + 1];
++ break;
++ }
++ /* FALLTHROUGH */
++ default:
++ /* Fail, fatally, if we detect either loose or strict
++ * or incorrect 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/openssh-6.6p1-force_krb.patch b/openssh-6.6p1-force_krb.patch
new file mode 100644
index 0000000..90f8322
--- /dev/null
+++ b/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 <sys/types.h>
+
+ #include <stdarg.h>
++#include <stdio.h>
+ #include <string.h>
++#include <unistd.h>
+
+ #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 <gssapi/gssapi_krb5.h>
+ #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
++
+ s->forced = 0;
+ if (forced != NULL) {
+ s->forced = 1;
+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/openssh-6.6p1-keycat.patch b/openssh-6.6p1-keycat.patch
new file mode 100644
index 0000000..529b508
--- /dev/null
+++ b/openssh-6.6p1-keycat.patch
@@ -0,0 +1,484 @@
+diff -up openssh/misc.c.keycat openssh/misc.c
+--- openssh/misc.c.keycat 2015-06-24 10:57:50.158849606 +0200
++++ openssh/misc.c 2015-06-24 11:04:23.989868638 +0200
+@@ -966,6 +966,13 @@ subprocess(const char *tag, struct passw
+ error("%s: dup2: %s", tag, strerror(errno));
+ _exit(1);
+ }
++#ifdef WITH_SELINUX
++ if (sshd_selinux_setup_env_variables() < 0) {
++ error ("failed to copy environment: %s",
++ strerror(errno));
++ _exit(127);
++ }
++#endif
+ if (env != NULL)
+ execve(av[0], av, env);
+ else
+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
+ ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass
+ SFTP_SERVER=$(libexecdir)/sftp-server
+ SSH_KEYSIGN=$(libexecdir)/ssh-keysign
++SSH_KEYCAT=$(libexecdir)/ssh-keycat
+ SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper
+ SSH_SK_HELPER=$(libexecdir)/ssh-sk-helper
+ PRIVSEP_PATH=@PRIVSEP_PATH@
+@@ -52,6 +52,7 @@ K5LIBS=@K5LIBS@
+ K5LIBS=@K5LIBS@
+ GSSLIBS=@GSSLIBS@
+ SSHDLIBS=@SSHDLIBS@
++KEYCATLIBS=@KEYCATLIBS@
+ LIBEDIT=@LIBEDIT@
+ LIBFIDO2=@LIBFIDO2@
+ AR=@AR@
+@@ -65,7 +66,7 @@ EXEEXT=@EXEEXT@
+
+ .SUFFIXES: .lo
+
+-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-sk-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-sk-helper$(EXEEXT) ssh-keycat$(EXEEXT)
+
+ XMSS_OBJS=\
+ ssh-xmss.o \
+@@ -190,6 +191,9 @@ ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT)
+ ssh-sk-helper$(EXEEXT): $(LIBCOMPAT) libssh.a $(SKHELPER_OBJS)
+ $(LD) -o $@ $(SKHELPER_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) $(LIBFIDO2) $(CHANNELLIBS)
+
++ssh-keycat$(EXEEXT): $(LIBCOMPAT) $(SSHDOBJS) libssh.a ssh-keycat.o uidswap.o
++ $(LD) -o $@ ssh-keycat.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(KEYCATLIBS) $(LIBS)
++
+ ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHKEYSCAN_OBJS)
+ $(LD) -o $@ $(SSHKEYSCAN_OBJS) $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) $(CHANNELLIBS)
+
+@@ -321,6 +325,7 @@ install-files:
+ $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT)
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT)
+ $(INSTALL) -m 0755 $(STRIP_OPT) ssh-sk-helper$(EXEEXT) $(DESTDIR)$(SSH_SK_HELPER)$(EXEEXT)
++ $(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 <tmraz@redhat.com>
++*/
++
++#define _GNU_SOURCE
++
++#include "config.h"
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <pwd.h>
++#include <fcntl.h>
++#include <unistd.h>
++#ifdef HAVE_STDINT_H
++#include <stdint.h>
++#endif
++
++#include <security/pam_appl.h>
++
++#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 <user-name>\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],
+ fi ]
+ )
+ AC_SUBST([SSHDLIBS])
++AC_SUBST([KEYCATLIBS])
+
+ # Check whether user wants Kerberos 5 support
+ KRB5_MSG="no"
+@@ -5031,6 +5034,9 @@ fi
+ if test ! -z "${SSHDLIBS}"; then
+ echo " +for sshd: ${SSHDLIBS}"
+ fi
++if test ! -z "${KEYCATLIBS}"; then
++echo " +for ssh-keycat: ${KEYCATLIBS}"
++fi
+
+ echo ""
+
diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch
new file mode 100644
index 0000000..6e2c76a
--- /dev/null
+++ b/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 openEuler 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
++ * openEuler) 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_strict_acceptor = -1;
+ options->gss_store_rekey = -1;
+ options->gss_kex_algorithms = NULL;
++ options->use_kuserok = -1;
+ options->password_authentication = -1;
+ options->kbd_interactive_authentication = -1;
+ options->permit_empty_passwd = -1;
+@@ -278,6 +279,8 @@ fill_default_server_options(ServerOption
+ 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->password_authentication == -1)
+ options->password_authentication = 1;
+ if (options->kbd_interactive_authentication == -1)
+@@ -399,7 +402,7 @@ typedef enum {
+ sPort, sHostKeyFile, sLoginGraceTime,
+ sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose,
+ sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
+- sKerberosGetAFSToken, sKerberosUniqueCCache, sPasswordAuthentication,
++ sKerberosGetAFSToken, sKerberosUniqueCCache, sKerberosUseKuserok, sPasswordAuthentication,
+ sKbdInteractiveAuthentication, sListenAddress, sAddressFamily,
+ sPrintMotd, sPrintLastLog, sIgnoreRhosts,
+ sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
+@@ -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
+ }
+ break;
+
++ case sKerberosUseKuserok:
++ intptr = &options->use_kuserok;
++ goto parse_flag;
++
+ case sMatch:
+ if (cmdline)
+ fatal("Match directive not supported as a command-line "
+@@ -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 no
diff --git a/openssh-6.6p1-privsep-selinux.patch b/openssh-6.6p1-privsep-selinux.patch
new file mode 100644
index 0000000..8047fc3
--- /dev/null
+++ b/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_f("done");
+ }
+
++void
++sshd_selinux_copy_context(void)
++{
++ security_context_t *ctx;
++
++ if (!ssh_selinux_enabled())
++ return;
++
++ if (getexeccon((security_context_t *)&ctx) != 0) {
++ logit_f("getexeccon failed with %s", strerror(errno));
++ return;
++ }
++ if (ctx != NULL) {
++ /* unset exec context before we will lose this capabililty */
++ if (setexeccon(NULL) != 0)
++ fatal_f("setexeccon failed with %s", strerror(errno));
++ if (setcon(ctx) != 0)
++ fatal_f("setcon failed with %s", 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/openssh-6.7p1-coverity.patch b/openssh-6.7p1-coverity.patch
new file mode 100644
index 0000000..494f4c6
--- /dev/null
+++ b/openssh-6.7p1-coverity.patch
@@ -0,0 +1,366 @@
+diff -up openssh-8.5p1/auth-krb5.c.coverity openssh-8.5p1/auth-krb5.c
+--- openssh-8.5p1/auth-krb5.c.coverity 2021-03-24 12:03:33.724967756 +0100
++++ openssh-8.5p1/auth-krb5.c 2021-03-24 12:03:33.782968159 +0100
+@@ -426,6 +426,7 @@ ssh_krb5_cc_new_unique(krb5_context ctx,
+ umask(old_umask);
+ if (tmpfd == -1) {
+ logit("mkstemp(): %.100s", strerror(oerrno));
++ free(ccname);
+ return oerrno;
+ }
+
+@@ -433,6 +434,7 @@ ssh_krb5_cc_new_unique(krb5_context ctx,
+ oerrno = errno;
+ logit("fchmod(): %.100s", strerror(oerrno));
+ close(tmpfd);
++ free(ccname);
+ return oerrno;
+ }
+ /* make sure the KRB5CCNAME is set for non-standard location */
+diff -up openssh-8.5p1/auth-options.c.coverity openssh-8.5p1/auth-options.c
+--- openssh-8.5p1/auth-options.c.coverity 2021-03-02 11:31:47.000000000 +0100
++++ openssh-8.5p1/auth-options.c 2021-03-24 12:03:33.782968159 +0100
+@@ -706,6 +708,7 @@ serialise_array(struct sshbuf *m, char *
+ return r;
+ }
+ /* success */
++ sshbuf_free(b);
+ return 0;
+ }
+
+diff -up openssh-8.5p1/gss-genr.c.coverity openssh-8.5p1/gss-genr.c
+--- openssh-8.5p1/gss-genr.c.coverity 2021-03-26 11:52:46.613942552 +0100
++++ openssh-8.5p1/gss-genr.c 2021-03-26 11:54:37.881726318 +0100
+@@ -167,8 +167,9 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup
+ enclen = __b64_ntop(digest,
+ ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
+ ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
+-
++#pragma GCC diagnostic ignored "-Wstringop-overflow"
+ cp = strncpy(s, kex, strlen(kex));
++#pragma pop
+ for ((p = strsep(&cp, ",")); p && *p != '\0';
+ (p = strsep(&cp, ","))) {
+ if (sshbuf_len(buf) != 0 &&
+diff -up openssh-8.5p1/kexgssc.c.coverity openssh-8.5p1/kexgssc.c
+--- openssh-8.5p1/kexgssc.c.coverity 2021-03-24 12:03:33.711967665 +0100
++++ openssh-8.5p1/kexgssc.c 2021-03-24 12:03:33.783968166 +0100
+@@ -98,8 +98,10 @@ kexgss_client(struct ssh *ssh)
+ default:
+ fatal_f("Unexpected KEX type %d", kex->kex_type);
+ }
+- if (r != 0)
++ if (r != 0) {
++ ssh_gssapi_delete_ctx(&ctxt);
+ return r;
++ }
+
+ token_ptr = GSS_C_NO_BUFFER;
+
+diff -up openssh-8.5p1/krl.c.coverity openssh-8.5p1/krl.c
+--- openssh-8.5p1/krl.c.coverity 2021-03-02 11:31:47.000000000 +0100
++++ openssh-8.5p1/krl.c 2021-03-24 12:03:33.783968166 +0100
+@@ -1209,6 +1209,7 @@ ssh_krl_from_blob(struct sshbuf *buf, st
+ sshkey_free(key);
+ sshbuf_free(copy);
+ sshbuf_free(sect);
++ /* coverity[leaked_storage : FALSE] */
+ return r;
+ }
+
+@@ -1261,6 +1262,7 @@ is_key_revoked(struct ssh_krl *krl, cons
+ return r;
+ erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb);
+ free(rb.blob);
++ rb.blob = NULL; /* make coverity happy */
+ if (erb != NULL) {
+ KRL_DBG(("revoked by key SHA1"));
+ return SSH_ERR_KEY_REVOKED;
+@@ -1271,6 +1273,7 @@ is_key_revoked(struct ssh_krl *krl, cons
+ return r;
+ erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha256s, &rb);
+ free(rb.blob);
++ rb.blob = NULL; /* make coverity happy */
+ if (erb != NULL) {
+ KRL_DBG(("revoked by key SHA256"));
+ return SSH_ERR_KEY_REVOKED;
+@@ -1282,6 +1285,7 @@ is_key_revoked(struct ssh_krl *krl, cons
+ return r;
+ erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
+ free(rb.blob);
++ rb.blob = NULL; /* make coverity happy */
+ if (erb != NULL) {
+ KRL_DBG(("revoked by explicit key"));
+ return SSH_ERR_KEY_REVOKED;
+diff -up openssh-8.5p1/loginrec.c.coverity openssh-8.5p1/loginrec.c
+--- openssh-8.5p1/loginrec.c.coverity 2021-03-24 13:18:53.793225885 +0100
++++ openssh-8.5p1/loginrec.c 2021-03-24 13:21:27.948404751 +0100
+@@ -690,9 +690,11 @@ construct_utmp(struct logininfo *li,
+ */
+
+ /* Use strncpy because we don't necessarily want null termination */
++ /* coverity[buffer_size_warning : FALSE] */
+ strncpy(ut->ut_name, li->username,
+ MIN_SIZEOF(ut->ut_name, li->username));
+ # ifdef HAVE_HOST_IN_UTMP
++ /* coverity[buffer_size_warning : FALSE] */
+ strncpy(ut->ut_host, li->hostname,
+ MIN_SIZEOF(ut->ut_host, li->hostname));
+ # endif
+@@ -1690,6 +1692,7 @@ record_failed_login(struct ssh *ssh, con
+
+ memset(&ut, 0, sizeof(ut));
+ /* strncpy because we don't necessarily want nul termination */
++ /* coverity[buffer_size_warning : FALSE] */
+ strncpy(ut.ut_user, username, sizeof(ut.ut_user));
+ strlcpy(ut.ut_line, "ssh:notty", sizeof(ut.ut_line));
+
+@@ -1699,6 +1702,7 @@ record_failed_login(struct ssh *ssh, con
+ ut.ut_pid = getpid();
+
+ /* strncpy because we don't necessarily want nul termination */
++ /* coverity[buffer_size_warning : FALSE] */
+ strncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
+
+ if (ssh_packet_connection_is_on_socket(ssh) &&
+diff -up openssh-8.5p1/misc.c.coverity openssh-8.5p1/misc.c
+--- openssh-8.5p1/misc.c.coverity 2021-03-24 12:03:33.745967902 +0100
++++ openssh-8.5p1/misc.c 2021-03-24 13:31:47.037079617 +0100
+@@ -1425,6 +1425,8 @@ sanitise_stdfd(void)
+ }
+ if (nullfd > STDERR_FILENO)
+ close(nullfd);
++ /* coverity[leaked_handle : FALSE]*/
++ /* coverity[leaked_handle : FALSE]*/
+ }
+
+ char *
+@@ -2511,6 +2513,7 @@ stdfd_devnull(int do_stdin, int do_stdou
+ }
+ if (devnull > STDERR_FILENO)
+ close(devnull);
++ /* coverity[leaked_handle : FALSE]*/
+ return ret;
+ }
+
+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(ssh, 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)
+@@ -1678,7 +1678,7 @@ mm_answer_pty(struct ssh *ssh, int sock,
+ s->ptymaster = s->ptyfd;
+
+ debug3_f("tty %s ptyfd %d", s->tty, s->ttyfd);
+-
++ /* coverity[leaked_handle : FALSE] */
+ return (0);
+
+ error:
+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_f("cannot allocate fds for pty");
+- 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-8.7p1/openbsd-compat/bsd-pselect.c.coverity openssh-8.7p1/openbsd-compat/bsd-pselect.c
+--- openssh-8.7p1/openbsd-compat/bsd-pselect.c.coverity 2021-08-30 16:36:11.357288009 +0200
++++ openssh-8.7p1/openbsd-compat/bsd-pselect.c 2021-08-30 16:37:21.791897976 +0200
+@@ -113,13 +113,13 @@ pselect_notify_setup(void)
+ static void
+ pselect_notify_parent(void)
+ {
+- if (notify_pipe[1] != -1)
++ if (notify_pipe[1] >= 0)
+ (void)write(notify_pipe[1], "", 1);
+ }
+ static void
+ pselect_notify_prepare(fd_set *readset)
+ {
+- if (notify_pipe[0] != -1)
++ if (notify_pipe[0] >= 0)
+ FD_SET(notify_pipe[0], readset);
+ }
+ static void
+@@ -127,8 +127,8 @@ pselect_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_f("reading");
+ FD_CLR(notify_pipe[0], readset);
+ }
+diff -up openssh-8.5p1/readconf.c.coverity openssh-8.5p1/readconf.c
+--- openssh-8.5p1/readconf.c.coverity 2021-03-24 12:03:33.778968131 +0100
++++ openssh-8.5p1/readconf.c 2021-03-24 12:03:33.785968180 +0100
+@@ -1847,6 +1847,7 @@ parse_pubkey_algos:
+ } else if (r != 0) {
+ error("%.200s line %d: glob failed for %s.",
+ filename, linenum, arg2);
++ free(arg2);
+ goto out;
+ }
+ free(arg2);
+diff -up openssh-8.7p1/scp.c.coverity openssh-8.7p1/scp.c
+--- openssh-8.7p1/scp.c.coverity 2021-08-30 16:23:35.389741329 +0200
++++ openssh-8.7p1/scp.c 2021-08-30 16:27:04.854555296 +0200
+@@ -186,11 +186,11 @@ 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 (do_cmd_pid2 > 1) {
+ kill(do_cmd_pid2, signo ? signo : SIGTERM);
+- waitpid(do_cmd_pid2, NULL, 0);
++ (void) waitpid(do_cmd_pid2, 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
+@@ -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-8.7p1/serverloop.c.coverity openssh-8.7p1/serverloop.c
+--- openssh-8.7p1/serverloop.c.coverity 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/serverloop.c 2021-08-30 16:28:22.416226981 +0200
+@@ -547,7 +547,7 @@ server_request_tun(struct ssh *ssh)
+ debug_f("invalid tun");
+ goto done;
+ }
+- if (auth_opts->force_tun_device != -1) {
++ if (auth_opts->force_tun_device >= 0) {
+ if (tun != SSH_TUNID_ANY &&
+ auth_opts->force_tun_device != (int)tun)
+ goto done;
+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)
+ pid = sshpid;
+ if (pid > 1) {
+ kill(pid, SIGTERM);
+- waitpid(pid, NULL, 0);
++ (void) waitpid(pid, 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
+@@ -869,6 +869,7 @@ sanitize_pkcs11_provider(const char *pro
+
+ if (pkcs11_uri_parse(provider, uri) != 0) {
+ error("Failed to parse PKCS#11 URI");
++ pkcs11_uri_cleanup(uri);
+ return NULL;
+ }
+ /* validate also provider from URI */
+@@ -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-8.5p1/ssh.c.coverity openssh-8.5p1/ssh.c
+--- openssh-8.5p1/ssh.c.coverity 2021-03-24 12:03:33.779968138 +0100
++++ openssh-8.5p1/ssh.c 2021-03-24 12:03:33.786968187 +0100
+@@ -1746,6 +1746,7 @@ control_persist_detach(void)
+ close(muxserver_sock);
+ muxserver_sock = -1;
+ options.control_master = SSHCTL_MASTER_NO;
++ /* coverity[leaked_handle: FALSE]*/
+ muxclient(options.control_path);
+ /* muxclient() doesn't return on success. */
+ fatal("Failed to connect to new control master");
+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(ssh);
+ setproctitle("%s", "[net]");
+- if (box != NULL)
++ if (box != NULL) {
+ ssh_sandbox_child(box);
++ free(box);
++ }
+
+ return 0;
+ }
+@@ -2519,8 +2524,11 @@ do_ssh2_kex(struct ssh *ssh)
+
+ if (newstr)
+ myproposal[PROPOSAL_KEX_ALGS] = newstr;
+- else
++ else {
+ fatal("No supported key exchange algorithms");
++ free(gss);
++ }
++ /* coverity[leaked_storage: FALSE]*/
+ }
+ #endif
+
+diff -up openssh-8.5p1/ssh-keygen.c.coverity openssh-8.5p1/ssh-keygen.c
+--- openssh-8.5p1/ssh-keygen.c.coverity 2021-03-24 12:03:33.780968145 +0100
++++ openssh-8.5p1/ssh-keygen.c 2021-03-24 12:03:33.787968194 +0100
+@@ -2332,6 +2332,9 @@ update_krl_from_file(struct passwd *pw,
+ r = ssh_krl_revoke_key_sha256(krl, blob, blen);
+ if (r != 0)
+ fatal_fr(r, "revoke key failed");
++ freezero(blob, blen);
++ blob = NULL;
++ blen = 0;
+ } else {
+ if (strncasecmp(cp, "key:", 4) == 0) {
+ cp += 4;
diff --git a/openssh-6.7p1-sftp-force-permission.patch b/openssh-6.7p1-sftp-force-permission.patch
new file mode 100644
index 0000000..1cfa309
--- /dev/null
+++ b/openssh-6.7p1-sftp-force-permission.patch
@@ -0,0 +1,100 @@
+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 denied_requests
+ .Op Fl p Ar allowed_requests
+ .Op Fl u Ar umask
++.Op Fl m Ar force_file_perms
+ .Ek
+ .Nm
+ .Fl Q Ar protocol_feature
+@@ -138,6 +139,12 @@ 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. Using both -m and -u switches makes the
++umask (-u) effective only for newly created directories and explicit mode (-m)
++for newly created files.
+ .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 denied_requests] "
+- "[-p allowed_requests] [-u umask]\n"
++ "[-p allowed_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/openssh-6.8p1-sshdT-output.patch b/openssh-6.8p1-sshdT-output.patch
new file mode 100644
index 0000000..156e66d
--- /dev/null
+++ b/openssh-6.8p1-sshdT-output.patch
@@ -0,0 +1,12 @@
+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);
+ dump_cfg_string(sMacs, o->macs);
+- 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 --git a/openssh-7.1p2-audit-race-condition.patch b/openssh-7.1p2-audit-race-condition.patch
new file mode 100644
index 0000000..b5895f7
--- /dev/null
+++ b/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_f("entering");
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ do {
++ blen = atomicio(read, fdin, buf, sizeof(buf));
++ if (blen == 0) /* closed pipe */
++ break;
++ if (blen != sizeof(buf)) {
++ error_f("Failed to read the buffer from child");
++ ret = -1;
++ break;
++ }
++
++ msg_len = get_u32(buf);
++ if (msg_len > 256 * 1024)
++ fatal_f("read: bad msg_len %d", msg_len);
++ sshbuf_reset(m);
++ if ((r = sshbuf_reserve(m, msg_len, NULL)) != 0)
++ fatal_fr(r, "buffer error");
++ if (atomicio(read, fdin, sshbuf_mutable_ptr(m), msg_len) != msg_len) {
++ error_f("Failed to read the the buffer content from the child");
++ 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_f("Failed to write the message to the monitor");
++ 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(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t);
+ void mm_audit_session_key_free_body(struct ssh *, int, pid_t, uid_t);
+ void mm_audit_destroy_sensitive_data(struct ssh *, 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(struct ssh *ssh);
++
+ #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(ssh);
++
+ /* 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(ssh);
++
+ /* 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(ssh, 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)
+ log_redirect_stderr_to(NULL);
+ }
+
++void
++child_destory_sensitive_data(struct ssh *ssh)
++{
++#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(ssh, 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(ssh, 0, 1);
++ /* XXX this will clean the rest but should not audit anymore */
++ /* packet_clear_keys(ssh); */
++
++#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
+
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
+
+- /* remove hostkey from the child's memory */
+- destroy_sensitive_data(ssh, 1);
+- ssh_packet_clear_keys(ssh);
+- /* 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(ssh, 0, 1);
+-
+ /* Force a password change */
+ if (s->authctxt->force_pwchange) {
+ do_setusercontext(pw);
diff --git a/openssh-7.2p2-k5login_directory.patch b/openssh-7.2p2-k5login_directory.patch
new file mode 100644
index 0000000..80e7678
--- /dev/null
+++ b/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,5 +375,21 @@ cleanup:
+ return (krb5_cc_resolve(ctx, ccname, ccache));
+ }
+ }
++
++/*
++ * 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);
++}
+ #endif /* !HEIMDAL */
+ #endif /* KRB5 */
+diff --git a/auth.h b/auth.h
+index f9d191c..c432d2f 100644
+--- a/auth.h
++++ b/auth.h
+@@ -222,6 +222,8 @@ int sys_auth_passwd(Authctxt *, const char *);
+
+ #if defined(KRB5) && !defined(HEIMDAL)
+ 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 /* AUTH_H */
+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_f("k5login_directory = %s (rv=%d)", 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_f("Checking existence of file %s", 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/openssh-7.2p2-s390-closefrom.patch b/openssh-7.2p2-s390-closefrom.patch
new file mode 100644
index 0000000..363538c
--- /dev/null
+++ b/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 <freude@de.ibm.com>
+
+---
+ 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 <freude@de.ibm.com>
++ */
++ 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);
+ return;
+
diff --git a/openssh-7.2p2-x11.patch b/openssh-7.2p2-x11.patch
new file mode 100644
index 0000000..0a19ecb
--- /dev/null
+++ b/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 == -1)
+ 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/openssh-7.3p1-x11-max-displays.patch b/openssh-7.3p1-x11-max-displays.patch
new file mode 100644
index 0000000..2b702d4
--- /dev/null
+++ b/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 IO 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) == -1) {
+ 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 *, time_t);
+ 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 {
+ sKerberosGetAFSToken, sKerberosUniqueCCache, sKerberosUseKuserok, 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 TrustedUserCAKeys ,
+ .Cm UnusedConnectionTimeout ,
+ .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/openssh-7.4p1-systemd.patch b/openssh-7.4p1-systemd.patch
new file mode 100644
index 0000000..1242aac
--- /dev/null
+++ b/openssh-7.4p1-systemd.patch
@@ -0,0 +1,98 @@
+commit 0e22b79bfde45a7cf7a2e51a68ec11c4285f3b31
+Author: Jakub Jelen <jjelen@redhat.com>
+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([K5LIBS])
+ AC_SUBST([CHANNELLIBS])
+
++# 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 <prot.h>
+ #endif
+
++#ifdef HAVE_SYSTEMD
++#include <systemd/sd-daemon.h>
++#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/openssh-7.5p1-sandbox.patch b/openssh-7.5p1-sandbox.patch
new file mode 100644
index 0000000..90640a0
--- /dev/null
+++ b/openssh-7.5p1-sandbox.patch
@@ -0,0 +1,86 @@
+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 <ebarretto@xxxxxxxxxxxxxxxxxx>
+---
+ 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_futex
+ SC_FUTEX(__NR_futex),
+ #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 <ebarretto@xxxxxxxxxxxxxxxxxx>
+---
+ 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
+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/openssh-7.6p1-audit.patch b/openssh-7.6p1-audit.patch
new file mode 100644
index 0000000..748c4b6
--- /dev/null
+++ b/openssh-7.6p1-audit.patch
@@ -0,0 +1,2314 @@
+diff -up openssh-8.6p1/audit-bsm.c.audit openssh-8.6p1/audit-bsm.c
+--- openssh-8.6p1/audit-bsm.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/audit-bsm.c 2021-04-19 16:47:35.753062106 +0200
+@@ -373,13 +373,26 @@ audit_connection_from(const char *host,
+ #endif
+ }
+
++int
++audit_run_command(struct ssh *ssh, const char *command)
++{
++ /* not implemented */
++ return 0;
++}
++
+ void
+-audit_run_command(const char *command)
++audit_end_command(struct ssh *ssh, int handle, const char *command)
+ {
+ /* not implemented */
+ }
+
+ void
++audit_count_session_open(void)
++{
++ /* not necessary */
++}
++
++void
+ audit_session_open(struct logininfo *li)
+ {
+ /* not implemented */
+@@ -391,6 +404,12 @@ audit_session_close(struct logininfo *li
+ /* not implemented */
+ }
+
++int
++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv)
++{
++ /* not implemented */
++}
++
+ void
+ audit_event(struct ssh *ssh, ssh_audit_event_t event)
+ {
+@@ -452,4 +471,28 @@ audit_event(struct ssh *ssh, ssh_audit_e
+ debug("%s: unhandled event %d", __func__, event);
+ }
+ }
++
++void
++audit_unsupported_body(struct ssh *ssh, int what)
++{
++ /* not implemented */
++}
++
++void
++audit_kex_body(struct ssh *ssh, int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid)
++{
++ /* not implemented */
++}
++
++void
++audit_session_key_free_body(struct ssh * ssh, int ctos, pid_t pid, uid_t uid)
++{
++ /* not implemented */
++}
++
++void
++audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid)
++{
++ /* not implemented */
++}
+ #endif /* BSM */
+diff -up openssh-8.6p1/audit.c.audit openssh-8.6p1/audit.c
+--- openssh-8.6p1/audit.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/audit.c 2021-04-19 16:47:35.753062106 +0200
+@@ -34,6 +34,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(struct ssh *ssh, int host_user, int *rv, const struct sshkey *key)
++{
++ char *fp;
++
++ fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_HEX);
++ if (audit_keyusage(ssh, host_user, fp, (*rv == 0)) == 0)
++ *rv = -SSH_ERR_INTERNAL_ERROR;
++ free(fp);
++}
++
++void
++audit_unsupported(struct ssh *ssh, int what)
++{
++ PRIVSEP(audit_unsupported_body(ssh, what));
++}
++
++void
++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs)
++{
++ PRIVSEP(audit_kex_body(ssh, ctos, enc, mac, comp, pfs, getpid(), getuid()));
++}
++
++void
++audit_session_key_free(struct ssh *ssh, int ctos)
++{
++ PRIVSEP(audit_session_key_free_body(ssh, ctos, getpid(), getuid()));
++}
++
+ # ifndef CUSTOM_SSH_AUDIT_EVENTS
+ /*
+ * Null implementations of audit functions.
+@@ -138,6 +171,17 @@ audit_event(struct ssh *ssh, ssh_audit_e
+ }
+
+ /*
++ * 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 +216,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
+-audit_run_command(const char *command)
++int
++audit_run_command(struct ssh *ssh, 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(struct ssh *ssh, 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(struct ssh *ssh, 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(struct ssh *ssh, int what)
++{
++ debug("audit unsupported protocol euid %d type %d", geteuid(), what);
++}
++
++/*
++ * This will be called on succesfull protocol negotiation.
++ */
++void
++audit_kex_body(struct ssh *ssh, 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(struct ssh *, 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(struct ssh *ssh, 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-8.6p1/audit.h.audit openssh-8.6p1/audit.h
+--- openssh-8.6p1/audit.h.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/audit.h 2021-04-19 16:47:35.753062106 +0200
+@@ -26,6 +26,7 @@
+ # define _SSH_AUDIT_H
+
+ #include "loginrec.h"
++#include "sshkey.h"
+
+ struct ssh;
+
+@@ -45,13 +46,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(struct ssh *, 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(struct ssh *, const char *);
++void audit_end_command(struct ssh *, int, const char *);
+ ssh_audit_event_t audit_classify_auth(const char *);
++int audit_keyusage(struct ssh *, int, char *, int);
++void audit_key(struct ssh *, int, int *, const struct sshkey *);
++void audit_unsupported(struct ssh *, int);
++void audit_kex(struct ssh *, int, char *, char *, char *, char *);
++void audit_unsupported_body(struct ssh *, int);
++void audit_kex_body(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t);
++void audit_session_key_free(struct ssh *, int ctos);
++void audit_session_key_free_body(struct ssh *, int ctos, pid_t, uid_t);
++void audit_destroy_sensitive_data(struct ssh *, const char *, pid_t, uid_t);
+
+ #endif /* _SSH_AUDIT_H */
+diff -up openssh-8.6p1/audit-linux.c.audit openssh-8.6p1/audit-linux.c
+--- openssh-8.6p1/audit-linux.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/audit-linux.c 2021-04-19 16:47:35.753062106 +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(struct ssh *ssh, 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(ssh), 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(ssh), 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,49 +176,210 @@ audit_connection_from(const char *host,
+ /* not implemented */
+ }
+
++int
++audit_run_command(struct ssh *ssh, const char *command)
++{
++ if (!user_login_count++)
++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL,
++ ssh_remote_ipaddr(ssh),
++ "ssh", 1, AUDIT_USER_LOGIN);
++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL,
++ ssh_remote_ipaddr(ssh),
++ "ssh", 1, AUDIT_USER_START);
++ return 0;
++}
++
+ void
+-audit_run_command(const char *command)
++audit_end_command(struct ssh *ssh, int handle, const char *command)
+ {
+- /* not implemented */
++ linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL,
++ ssh_remote_ipaddr(ssh),
++ "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(ssh),
++ "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
+ audit_event(struct ssh *ssh, ssh_audit_event_t event)
+ {
+ switch(event) {
+- case SSH_AUTH_SUCCESS:
+- case SSH_CONNECTION_CLOSE:
+ case SSH_NOLOGIN:
+- case SSH_LOGIN_EXCEED_MAXTRIES:
+ case SSH_LOGIN_ROOT_DENIED:
++ linux_audit_user_auth(-1, audit_username(),
++ 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(struct ssh *ssh, 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(ssh), (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))),
++ ssh_local_port(ssh));
++ 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(ssh), NULL, 0);
++ audit_close(audit_fd);
++#endif
++}
++
++const static char *direction[] = { "from-server", "from-client", "both" };
++
++void
++audit_kex_body(struct ssh *ssh, 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(ssh), (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))), ssh_local_port(ssh));
++ 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(ssh), 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(struct ssh *ssh, 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(ssh),
++ (s = get_local_ipaddr(ssh_packet_get_connection_in(ssh))),
++ ssh_local_port(ssh));
++ 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(ssh), 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(struct ssh *ssh, 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(ssh),
++ 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-8.6p1/auditstub.c.audit openssh-8.6p1/auditstub.c
+--- openssh-8.6p1/auditstub.c.audit 2021-04-19 16:47:35.754062114 +0200
++++ openssh-8.6p1/auditstub.c 2021-04-19 16:47:35.754062114 +0200
+@@ -0,0 +1,52 @@
++/* $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 <jchadima@redhat.com>
++ */
++
++#include <sys/types.h>
++
++struct ssh;
++
++void
++audit_unsupported(struct ssh *ssh, int n)
++{
++}
++
++void
++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs)
++{
++}
++
++void
++audit_session_key_free(struct ssh *ssh, int ctos)
++{
++}
++
++void
++audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid)
++{
++}
+diff -up openssh-8.6p1/auth2.c.audit openssh-8.6p1/auth2.c
+--- openssh-8.6p1/auth2.c.audit 2021-04-19 16:47:35.682061561 +0200
++++ openssh-8.6p1/auth2.c 2021-04-19 16:47:35.754062114 +0200
+@@ -298,9 +298,6 @@ input_userauth_request(int type, u_int32
+ authctxt->valid = 0;
+ /* Invalid user, fake password information */
+ authctxt->pw = fakepw();
+-#ifdef SSH_AUDIT_EVENTS
+- PRIVSEP(audit_event(ssh, SSH_INVALID_USER));
+-#endif
+ }
+ #ifdef USE_PAM
+ if (options.use_pam)
+diff -up openssh-8.6p1/auth2-hostbased.c.audit openssh-8.6p1/auth2-hostbased.c
+--- openssh-8.6p1/auth2-hostbased.c.audit 2021-04-19 16:47:35.656061361 +0200
++++ openssh-8.6p1/auth2-hostbased.c 2021-04-19 16:47:35.754062114 +0200
+@@ -158,7 +158,7 @@ userauth_hostbased(struct ssh *ssh)
+ authenticated = 0;
+ if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser,
+ chost, key)) &&
+- PRIVSEP(sshkey_verify(key, sig, slen,
++ PRIVSEP(hostbased_key_verify(ssh, key, sig, slen,
+ sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat, NULL)) == 0)
+ authenticated = 1;
+
+@@ -175,6 +175,20 @@ done:
+ return authenticated;
+ }
+
++int
++hostbased_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig,
++ size_t slen, const u_char *data, size_t datalen, const char *pkalg, u_int compat,
++ struct sshkey_sig_details **detailsp)
++{
++ int rv;
++
++ rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat, detailsp);
++#ifdef SSH_AUDIT_EVENTS
++ audit_key(ssh, 0, &rv, key);
++#endif
++ return rv;
++}
++
+ /* return 1 if given hostkey is allowed */
+ int
+ hostbased_key_allowed(struct ssh *ssh, struct passwd *pw,
+diff -up openssh-8.6p1/auth2-pubkey.c.audit openssh-8.6p1/auth2-pubkey.c
+--- openssh-8.6p1/auth2-pubkey.c.audit 2021-04-19 16:47:35.726061899 +0200
++++ openssh-8.6p1/auth2-pubkey.c 2021-04-19 16:47:35.754062114 +0200
+@@ -213,7 +213,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(ssh, key, sig, slen,
+ sshbuf_ptr(b), sshbuf_len(b),
+ (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL,
+ ssh->compat, &sig_details)) == 0) {
+@@ -305,6 +305,20 @@ done:
+ return authenticated;
+ }
+
++int
++user_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig,
++ size_t slen, const u_char *data, size_t datalen, const char *pkalg, u_int compat,
++ struct sshkey_sig_details **detailsp)
++{
++ int rv;
++
++ rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat, detailsp);
++#ifdef SSH_AUDIT_EVENTS
++ audit_key(ssh, 1, &rv, key);
++#endif
++ return rv;
++}
++
+ static int
+ match_principals_file(struct passwd *pw, char *file,
+ struct sshkey_cert *cert, struct sshauthopt **authoptsp)
+diff -up openssh-8.6p1/auth.c.audit openssh-8.6p1/auth.c
+--- openssh-8.6p1/auth.c.audit 2021-04-19 16:47:35.681061553 +0200
++++ openssh-8.6p1/auth.c 2021-04-19 16:47:35.754062114 +0200
+@@ -597,9 +597,6 @@ getpwnamallow(struct ssh *ssh, const cha
+ record_failed_login(ssh, user,
+ auth_get_canonical_hostname(ssh, options.use_dns), "ssh");
+ #endif
+-#ifdef SSH_AUDIT_EVENTS
+- audit_event(ssh, SSH_INVALID_USER);
+-#endif /* SSH_AUDIT_EVENTS */
+ return (NULL);
+ }
+ if (!allowed_user(ssh, pw))
+diff -up openssh-8.6p1/auth.h.audit openssh-8.6p1/auth.h
+--- openssh-8.6p1/auth.h.audit 2021-04-19 16:47:35.697061676 +0200
++++ openssh-8.6p1/auth.h 2021-04-19 16:47:35.754062114 +0200
+@@ -212,6 +214,8 @@ struct sshkey *get_hostkey_private_by_ty
+ int get_hostkey_index(struct sshkey *, int, struct ssh *);
+ int sshd_hostkey_sign(struct ssh *, struct sshkey *, struct sshkey *,
+ u_char **, size_t *, const u_char *, size_t, const char *);
++int hostbased_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t,
++ const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
+
+ /* Key / cert options linkage to auth layer */
+ const struct sshauthopt *auth_options(struct ssh *);
+@@ -239,6 +241,8 @@ struct passwd * getpwnamallow(struct ssh
+ char *, const char *, const char *, const char *, struct sshauthopt **);
+ int auth_check_authkeys_file(struct passwd *, FILE *, char *,
+ struct sshkey *, const char *, const char *, struct sshauthopt **);
++int user_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t,
++ const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
+ FILE *auth_openkeyfile(const char *, struct passwd *, int);
+ FILE *auth_openprincipals(const char *, struct passwd *, int);
+
+diff -up openssh-8.6p1/cipher.c.audit openssh-8.6p1/cipher.c
+--- openssh-8.6p1/cipher.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/cipher.c 2021-04-19 16:47:35.755062122 +0200
+@@ -64,25 +64,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
+@@ -422,7 +403,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) {
+ chachapoly_free(cc->cp_ctx);
+diff -up openssh-8.6p1/cipher.h.audit openssh-8.6p1/cipher.h
+--- openssh-8.6p1/cipher.h.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/cipher.h 2021-04-19 16:47:35.755062122 +0200
+@@ -47,7 +47,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-8.6p1/kex.c.audit openssh-8.6p1/kex.c
+--- openssh-8.6p1/kex.c.audit 2021-04-19 16:47:35.743062030 +0200
++++ openssh-8.6p1/kex.c 2021-04-19 16:47:35.755062122 +0200
+@@ -65,6 +65,7 @@
+ #include "sshbuf.h"
+ #include "digest.h"
+ #include "xmalloc.h"
++#include "audit.h"
+
+ #ifdef GSSAPI
+ #include "ssh-gss.h"
+@@ -816,12 +817,16 @@ kex_start_rekex(struct ssh *ssh)
+ }
+
+ static int
+-choose_enc(struct sshenc *enc, char *client, char *server)
++choose_enc(struct ssh *ssh, struct sshenc *enc, char *client, char *server)
+ {
+ char *name = match_list(client, server, NULL);
+
+- if (name == NULL)
++ if (name == NULL) {
++#ifdef SSH_AUDIT_EVENTS
++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_CIPHER);
++#endif
+ return SSH_ERR_NO_CIPHER_ALG_MATCH;
++ }
+ if ((enc->cipher = cipher_by_name(name)) == NULL) {
+ error_f("unsupported cipher %s", name);
+ free(name);
+@@ -842,8 +847,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, SSH_AUDIT_UNSUPPORTED_MAC);
++#endif
+ return SSH_ERR_NO_MAC_ALG_MATCH;
++ }
+ if (mac_setup(mac, name) < 0) {
+ error_f("unsupported MAC %s", name);
+ free(name);
+@@ -856,12 +865,16 @@ choose_mac(struct ssh *ssh, struct sshma
+ }
+
+ static int
+-choose_comp(struct sshcomp *comp, char *client, char *server)
++choose_comp(struct ssh *ssh, struct sshcomp *comp, char *client, char *server)
+ {
+ char *name = match_list(client, server, NULL);
+
+- if (name == NULL)
++ if (name == NULL) {
++#ifdef SSH_AUDIT_EVENTS
++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_COMPRESSION);
++#endif
+ return SSH_ERR_NO_COMPRESS_ALG_MATCH;
++ }
+ #ifdef WITH_ZLIB
+ if (strcmp(name, "zlib@openssh.com") == 0) {
+ comp->type = COMP_DELAYED;
+@@ -1002,7 +1015,7 @@ kex_choose_conf(struct ssh *ssh)
+ nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC;
+ nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC;
+ ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC;
+- if ((r = choose_enc(&newkeys->enc, cprop[nenc],
++ if ((r = choose_enc(ssh, &newkeys->enc, cprop[nenc],
+ sprop[nenc])) != 0) {
+ kex->failed_choice = peer[nenc];
+ peer[nenc] = NULL;
+@@ -1017,7 +1030,7 @@ kex_choose_conf(struct ssh *ssh)
+ peer[nmac] = NULL;
+ goto out;
+ }
+- if ((r = choose_comp(&newkeys->comp, cprop[ncomp],
++ if ((r = choose_comp(ssh, &newkeys->comp, cprop[ncomp],
+ sprop[ncomp])) != 0) {
+ kex->failed_choice = peer[ncomp];
+ peer[ncomp] = NULL;
+@@ -1040,6 +1053,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(ssh, mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name);
++#endif
+ }
+ /* XXX need runden? */
+ kex->we_need = need;
+@@ -1297,6 +1314,36 @@ dump_digest(const char *msg, const u_cha
+ }
+ #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));
++}
++
+ /*
+ * Send a plaintext error message to the peer, suffixed by \r\n.
+ * Only used during banner exchange, and there only for the server.
+diff -up openssh-8.6p1/kex.h.audit openssh-8.6p1/kex.h
+--- openssh-8.6p1/kex.h.audit 2021-04-19 16:47:35.683061568 +0200
++++ openssh-8.6p1/kex.h 2021-04-19 16:47:35.756062129 +0200
+@@ -226,6 +226,8 @@ int kexgss_client(struct ssh *);
+ int kexgss_server(struct ssh *);
+ #endif
+
++void newkeys_destroy(struct newkeys *newkeys);
++
+ int kex_dh_keypair(struct kex *);
+ int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
+ struct sshbuf **);
+diff -up openssh-8.6p1/mac.c.audit openssh-8.6p1/mac.c
+--- openssh-8.6p1/mac.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/mac.c 2021-04-19 16:47:35.756062129 +0200
+@@ -239,6 +239,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-8.6p1/mac.h.audit openssh-8.6p1/mac.h
+--- openssh-8.6p1/mac.h.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/mac.h 2021-04-19 16:47:35.756062129 +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-8.6p1/Makefile.in.audit openssh-8.6p1/Makefile.in
+--- openssh-8.6p1/Makefile.in.audit 2021-04-19 16:47:35.731061937 +0200
++++ openssh-8.6p1/Makefile.in 2021-04-19 16:47:35.756062129 +0200
+@@ -112,7 +112,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
+ kexsntrup761x25519.o sntrup761.o kexgen.o \
+ kexgssc.o \
+ sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
+- sshbuf-io.o
++ sshbuf-io.o auditstub.o
+
+ SKOBJS= ssh-sk-client.o
+
+diff -up openssh-8.6p1/monitor.c.audit openssh-8.6p1/monitor.c
+--- openssh-8.6p1/monitor.c.audit 2021-04-19 16:47:35.707061753 +0200
++++ openssh-8.6p1/monitor.c 2021-04-19 16:47:35.756062129 +0200
+@@ -93,6 +93,7 @@
+ #include "compat.h"
+ #include "ssh2.h"
+ #include "authfd.h"
++#include "audit.h"
+ #include "match.h"
+ #include "ssherr.h"
+ #include "sk-api.h"
+@@ -107,6 +108,8 @@ extern u_int utmp_len;
+ extern struct sshbuf *loginmsg;
+ extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */
+
++extern void destroy_sensitive_data(struct ssh *, int);
++
+ /* State exported from the child */
+ static struct sshbuf *child_state;
+
+@@ -157,6 +160,11 @@ int mm_answer_gss_updatecreds(struct ssh
+ #ifdef SSH_AUDIT_EVENTS
+ int mm_answer_audit_event(struct ssh *, int, struct sshbuf *);
+ int mm_answer_audit_command(struct ssh *, int, struct sshbuf *);
++int mm_answer_audit_end_command(struct ssh *, int, struct sshbuf *);
++int mm_answer_audit_unsupported_body(struct ssh *, int, struct sshbuf *);
++int mm_answer_audit_kex_body(struct ssh *, int, struct sshbuf *);
++int mm_answer_audit_session_key_free_body(struct ssh *, int, struct sshbuf *);
++int mm_answer_audit_server_key_free(struct ssh *, int, struct sshbuf *);
+ #endif
+
+ static Authctxt *authctxt;
+@@ -215,6 +223,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},
+@@ -249,6 +261,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}
+ };
+@@ -1444,8 +1461,10 @@ mm_answer_keyverify(struct ssh *ssh, int
+ int r, ret, req_presence = 0, req_verify = 0, valid_data = 0;
+ int encoded_ret;
+ struct sshkey_sig_details *sig_details = NULL;
++ int type = 0;
+
+- if ((r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 ||
++ if ((r = sshbuf_get_u32(m, &type)) != 0 ||
++ (r = sshbuf_get_string_direct(m, &blob, &bloblen)) != 0 ||
+ (r = sshbuf_get_string_direct(m, &signature, &signaturelen)) != 0 ||
+ (r = sshbuf_get_string_direct(m, &data, &datalen)) != 0 ||
+ (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0)
+@@ -1454,6 +1473,8 @@ mm_answer_keyverify(struct ssh *ssh, int
+ if (hostbased_cuser == NULL || hostbased_chost == NULL ||
+ !monitor_allowed_key(blob, bloblen))
+ fatal_f("bad key, not previously allowed");
++ if (type != key_blobtype)
++ fatal_f("bad key type");
+
+ /* Empty signature algorithm means NULL. */
+ if (*sigalg == '\0') {
+@@ -1469,14 +1490,19 @@ mm_answer_keyverify(struct ssh *ssh, int
+ case MM_USERKEY:
+ valid_data = monitor_valid_userblob(ssh, data, datalen);
+ auth_method = "publickey";
++ ret = user_key_verify(ssh, key, signature, signaturelen, data,
++ datalen, sigalg, ssh->compat, &sig_details);
+ break;
+ case MM_HOSTKEY:
+ valid_data = monitor_valid_hostbasedblob(data, datalen,
+ hostbased_cuser, hostbased_chost);
+ auth_method = "hostbased";
++ ret = hostbased_key_verify(ssh, key, signature, signaturelen, data,
++ datalen, sigalg, ssh->compat, &sig_details);
+ break;
+ default:
+ valid_data = 0;
++ ret = 0;
+ break;
+ }
+ if (!valid_data)
+@@ -1488,8 +1514,6 @@ mm_answer_keyverify(struct ssh *ssh, int
+ SSH_FP_DEFAULT)) == NULL)
+ fatal_f("sshkey_fingerprint failed");
+
+- ret = sshkey_verify(key, signature, signaturelen, data, datalen,
+- sigalg, ssh->compat, &sig_details);
+ debug3_f("%s %s signature using %s %s%s%s", auth_method,
+ sshkey_type(key), sigalg == NULL ? "default" : sigalg,
+ (ret == 0) ? "verified" : "unverified",
+@@ -1576,13 +1600,19 @@ mm_record_login(struct ssh *ssh, Session
+ }
+
+ static void
+-mm_session_close(Session *s)
++mm_session_close(struct ssh *ssh, Session *s)
+ {
+ debug3_f("session %d pid %ld", s->self, (long)s->pid);
+ if (s->ttyfd != -1) {
+ debug3_f("tty %s ptyfd %d", s->tty, s->ptyfd);
+ session_pty_cleanup2(s);
+ }
++#ifdef SSH_AUDIT_EVENTS
++ if (s->command != NULL) {
++ debug3_f("command %d", s->command_handle);
++ session_end_command2(ssh, s);
++ }
++#endif
+ session_unused(s->self);
+ }
+
+@@ -1649,7 +1679,7 @@ mm_answer_pty(struct ssh *ssh, int sock,
+
+ error:
+ if (s != NULL)
+- mm_session_close(s);
++ mm_session_close(ssh, s);
+ if ((r = sshbuf_put_u32(m, 0)) != 0)
+ fatal_fr(r, "assemble 0");
+ mm_request_send(sock, MONITOR_ANS_PTY, m);
+@@ -1668,7 +1698,7 @@ mm_answer_pty_cleanup(struct ssh *ssh, i
+ if ((r = sshbuf_get_cstring(m, &tty, NULL)) != 0)
+ fatal_fr(r, "parse tty");
+ if ((s = session_by_tty(tty)) != NULL)
+- mm_session_close(s);
++ mm_session_close(ssh, s);
+ sshbuf_reset(m);
+ free(tty);
+ return (0);
+@@ -1690,6 +1720,8 @@ mm_answer_term(struct ssh *ssh, int sock
+ sshpam_cleanup();
+ #endif
+
++ destroy_sensitive_data(ssh, 0);
++
+ while (waitpid(pmonitor->m_pid, &status, 0) == -1)
+ if (errno != EINTR)
+ exit(1);
+@@ -1736,12 +1768,47 @@ mm_answer_audit_command(struct ssh *ssh,
+ {
+ 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_f("error allocating a session");
++ s->command = cmd;
++#ifdef SSH_AUDIT_EVENTS
++ s->command_handle = audit_run_command(ssh, 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(struct ssh *ssh, int socket, struct sshbuf *m)
++{
++ int handle, r;
++ size_t len;
++ u_char *cmd = NULL;
++ Session *s;
++
++ debug3_f("entering");
++ if ((r = sshbuf_get_u32(m, &handle)) != 0 ||
++ (r = sshbuf_get_string(m, &cmd, &len)) != 0)
++ fatal_fr(r, "buffer error");
++
++ s = session_by_id(handle);
++ if (s == NULL || s->ttyfd != -1 || s->command == NULL ||
++ strcmp(s->command, cmd) != 0)
++ fatal_f("invalid handle");
++ mm_session_close(ssh, s);
+ free(cmd);
+ return (0);
+ }
+@@ -1813,6 +1880,7 @@ monitor_apply_keystate(struct ssh *ssh,
+ void
+ mm_get_keystate(struct ssh *ssh, struct monitor *pmonitor)
+ {
++ struct sshbuf *m;
+ debug3_f("Waiting for new keys");
+
+ if ((child_state = sshbuf_new()) == NULL)
+@@ -1820,6 +1888,19 @@ mm_get_keystate(struct ssh *ssh, struct
+ mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT,
+ child_state);
+ debug3_f("GOT new keys");
++
++#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(ssh, 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)
++ ;
++
+ }
+
+
+@@ -2111,3 +2192,102 @@ mm_answer_gss_updatecreds(struct ssh *ss
+
+ #endif /* GSSAPI */
+
++#ifdef SSH_AUDIT_EVENTS
++int
++mm_answer_audit_unsupported_body(struct ssh *ssh, int sock, struct sshbuf *m)
++{
++ int what, r;
++
++ if ((r = sshbuf_get_u32(m, &what)) != 0)
++ fatal_fr(r, "buffer error");
++
++ audit_unsupported_body(ssh, what);
++
++ sshbuf_reset(m);
++
++ mm_request_send(sock, MONITOR_ANS_AUDIT_UNSUPPORTED, m);
++ return 0;
++}
++
++int
++mm_answer_audit_kex_body(struct ssh *ssh, 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_fr(r, "buffer error");
++ pid = (pid_t) tmp;
++ if ((r = sshbuf_get_u64(m, &tmp)) != 0)
++ fatal_fr(r, "buffer error");
++ uid = (pid_t) tmp;
++
++ audit_kex_body(ssh, 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(struct ssh *ssh, 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_fr(r, "buffer error");
++ pid = (pid_t) tmp;
++ if ((r = sshbuf_get_u64(m, &tmp)) != 0)
++ fatal_fr(r, "buffer error");
++ uid = (uid_t) tmp;
++
++ audit_session_key_free_body(ssh, 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(struct ssh *ssh, 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_fr(r, "buffer error");
++ pid = (pid_t) tmp;
++ if ((r = sshbuf_get_u64(m, &tmp)) != 0)
++ fatal_fr(r, "buffer error");
++ uid = (uid_t) tmp;
++
++ audit_destroy_sensitive_data(ssh, fp, pid, uid);
++
++ free(fp);
++ sshbuf_reset(m);
++
++ return 0;
++}
++#endif /* SSH_AUDIT_EVENTS */
+diff -up openssh-8.6p1/monitor.h.audit openssh-8.6p1/monitor.h
+--- openssh-8.6p1/monitor.h.audit 2021-04-19 16:47:35.707061753 +0200
++++ openssh-8.6p1/monitor.h 2021-04-19 16:47:35.757062137 +0200
+@@ -65,7 +65,13 @@ enum monitor_reqtype {
+ MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107,
+ MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109,
+ MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
+- MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
++ MONITOR_REQ_AUDIT_EVENT = 112,
++ MONITOR_REQ_AUDIT_COMMAND = 114, MONITOR_ANS_AUDIT_COMMAND = 115,
++ MONITOR_REQ_AUDIT_END_COMMAND = 116,
++ MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119,
++ MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121,
++ MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123,
++ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124,
+
+ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
+ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
+diff -up openssh-8.6p1/monitor_wrap.c.audit openssh-8.6p1/monitor_wrap.c
+--- openssh-8.6p1/monitor_wrap.c.audit 2021-04-19 16:47:35.685061584 +0200
++++ openssh-8.6p1/monitor_wrap.c 2021-04-19 16:47:35.757062137 +0200
+@@ -520,7 +520,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 sshkey_sig_details **sig_detailsp)
+ {
+@@ -536,7 +536,8 @@ mm_sshkey_verify(const struct sshkey *ke
+ *sig_detailsp = NULL;
+ if ((m = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
+- 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)
+@@ -569,6 +570,22 @@ mm_sshkey_verify(const struct sshkey *ke
+ return 0;
+ }
+
++int
++mm_hostbased_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, size_t siglen,
++ const u_char *data, size_t datalen, const char *pkalg, u_int compat,
++ struct sshkey_sig_details **detailsp)
++{
++ return mm_sshkey_verify(MM_HOSTKEY, key, sig, siglen, data, datalen, pkalg, compat, detailsp);
++}
++
++int
++mm_user_key_verify(struct ssh *ssh, const struct sshkey *key, const u_char *sig, size_t siglen,
++ const u_char *data, size_t datalen, const char *pkalg, u_int compat,
++ struct sshkey_sig_details **detailsp)
++{
++ return mm_sshkey_verify(MM_USERKEY, key, sig, siglen, data, datalen, pkalg, compat, detailsp);
++}
++
+ void
+ mm_send_keystate(struct ssh *ssh, struct monitor *monitor)
+ {
+@@ -921,11 +938,12 @@ mm_audit_event(struct ssh *ssh, ssh_audi
+ sshbuf_free(m);
+ }
+
+-void
+-mm_audit_run_command(const char *command)
++int
++mm_audit_run_command(struct ssh *ssh, const char *command)
+ {
+ struct sshbuf *m;
+ int r;
++ int handle;
+
+ debug3("%s entering command %s", __func__, command);
+
+@@ -935,6 +953,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_fr(r, "buffer error");
++ sshbuf_free(m);
++
++ return (handle);
++}
++
++void
++mm_audit_end_command(struct ssh *ssh, int handle, const char *command)
++{
++ int r;
++ struct sshbuf *m;
++
++ debug3_f("entering command %s", command);
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_u32(m, handle)) != 0 ||
++ (r = sshbuf_put_cstring(m, command)) != 0)
++ fatal_fr(r, "buffer error");
++
++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_END_COMMAND, m);
+ sshbuf_free(m);
+ }
+ #endif /* SSH_AUDIT_EVENTS */
+@@ -1095,3 +1137,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc
+ }
+
+ #endif /* GSSAPI */
++#ifdef SSH_AUDIT_EVENTS
++void
++mm_audit_unsupported_body(struct ssh *ssh, int what)
++{
++ int r;
++ struct sshbuf *m;
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_u32(m, what)) != 0)
++ fatal_fr(r, "buffer error");
++
++ 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(struct ssh *ssh, 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_f("sshbuf_new failed");
++ if ((r = sshbuf_put_u32(m, ctos)) != 0 ||
++ (r = sshbuf_put_cstring(m, cipher)) != 0 ||
++ (r = sshbuf_put_cstring(m, (mac ? mac : "<implicit>"))) != 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_fr(r, "buffer error");
++
++ 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(struct ssh *ssh, int ctos, pid_t pid, uid_t uid)
++{
++ int r;
++ struct sshbuf *m;
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_u32(m, ctos)) != 0 ||
++ (r = sshbuf_put_u64(m, pid)) != 0 ||
++ (r = sshbuf_put_u64(m, uid)) != 0)
++ fatal_fr(r, "buffer error");
++
++ 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(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid)
++{
++ int r;
++ struct sshbuf *m;
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_cstring(m, fp)) != 0 ||
++ (r = sshbuf_put_u64(m, pid)) != 0 ||
++ (r = sshbuf_put_u64(m, uid)) != 0)
++ fatal_fr(r, "buffer error");
++
++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUDIT_SERVER_KEY_FREE, m);
++ sshbuf_free(m);
++}
++#endif /* SSH_AUDIT_EVENTS */
+diff -up openssh-8.6p1/monitor_wrap.h.audit openssh-8.6p1/monitor_wrap.h
+--- openssh-8.6p1/monitor_wrap.h.audit 2021-04-19 16:47:35.685061584 +0200
++++ openssh-8.6p1/monitor_wrap.h 2021-04-19 16:47:35.757062137 +0200
+@@ -61,7 +61,9 @@ int mm_user_key_allowed(struct ssh *, st
+ struct sshauthopt **);
+ int mm_hostbased_key_allowed(struct ssh *, 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(struct ssh *, const struct sshkey *, const u_char *, size_t,
++ const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
++int mm_user_key_verify(struct ssh*, const struct sshkey *, const u_char *, size_t,
+ const u_char *, size_t, const char *, u_int, struct sshkey_sig_details **);
+
+ #ifdef GSSAPI
+@@ -86,7 +88,12 @@ void mm_sshpam_free_ctx(void *);
+ #ifdef SSH_AUDIT_EVENTS
+ #include "audit.h"
+ void mm_audit_event(struct ssh *, ssh_audit_event_t);
+-void mm_audit_run_command(const char *);
++int mm_audit_run_command(struct ssh *ssh, const char *);
++void mm_audit_end_command(struct ssh *ssh, int, const char *);
++void mm_audit_unsupported_body(struct ssh *, int);
++void mm_audit_kex_body(struct ssh *, int, char *, char *, char *, char *, pid_t, uid_t);
++void mm_audit_session_key_free_body(struct ssh *, int, pid_t, uid_t);
++void mm_audit_destroy_sensitive_data(struct ssh *, const char *, pid_t, uid_t);
+ #endif
+
+ struct Session;
+diff -up openssh-8.6p1/packet.c.audit openssh-8.6p1/packet.c
+--- openssh-8.6p1/packet.c.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/packet.c 2021-04-19 16:48:46.885608837 +0200
+@@ -81,6 +81,7 @@
+ #endif
+
+ #include "xmalloc.h"
++#include "audit.h"
+ #include "compat.h"
+ #include "ssh2.h"
+ #include "cipher.h"
+@@ -506,6 +507,13 @@ ssh_packet_get_connection_out(struct ssh
+ return ssh->state->connection_out;
+ }
+
++static int
++packet_state_has_keys (const struct session_state *state)
++{
++ return state != NULL &&
++ (state->newkeys[MODE_IN] != NULL || state->newkeys[MODE_OUT] != NULL);
++}
++
+ /*
+ * Returns the IP-address of the remote host as a string. The returned
+ * string must not be freed.
+@@ -583,22 +591,19 @@ ssh_packet_close_internal(struct ssh *ss
+ {
+ struct session_state *state = ssh->state;
+ u_int mode;
++ u_int had_keys = packet_state_has_keys(state);
+
+ if (!state->initialized)
+ return;
+ state->initialized = 0;
+- if (do_close) {
+- if (state->connection_in == state->connection_out) {
+- close(state->connection_out);
+- } else {
+- close(state->connection_in);
+- close(state->connection_out);
+- }
+- }
+ sshbuf_free(state->input);
++ state->input = NULL;
+ sshbuf_free(state->output);
++ state->output = NULL;
+ sshbuf_free(state->outgoing_packet);
++ state->outgoing_packet = NULL;
+ sshbuf_free(state->incoming_packet);
++ state->incoming_packet = NULL;
+ for (mode = 0; mode < MODE_MAX; mode++) {
+ kex_free_newkeys(state->newkeys[mode]); /* current keys */
+ state->newkeys[mode] = NULL;
+@@ -634,8 +639,18 @@ ssh_packet_close_internal(struct ssh *ss
+ #endif /* WITH_ZLIB */
+ 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(ssh, 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);
+@@ -892,6 +907,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod
+ (unsigned long long)state->p_send.bytes,
+ (unsigned long long)state->p_send.blocks);
+ kex_free_newkeys(state->newkeys[mode]);
++ audit_session_key_free(ssh, mode);
+ state->newkeys[mode] = NULL;
+ }
+ /* note that both bytes and the seqnr are not reset */
+@@ -2173,6 +2189,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);
++ state->send_context = state->receive_context = NULL;
++
++ 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(struct ssh *ssh, int audit_it, int privsep)
++{
++ if (audit_it)
++ audit_it = packet_state_has_keys(ssh->state);
++ packet_destroy_state(ssh->state);
++ if (audit_it) {
++#ifdef SSH_AUDIT_EVENTS
++ if (privsep)
++ audit_session_key_free(ssh, MODE_MAX);
++ else
++ audit_session_key_free_body(ssh, 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-8.6p1/packet.h.audit openssh-8.6p1/packet.h
+--- openssh-8.6p1/packet.h.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/packet.h 2021-04-19 16:47:35.758062145 +0200
+@@ -218,4 +218,5 @@ const u_char *sshpkt_ptr(struct ssh *, s
+ # undef EC_POINT
+ #endif
+
++void packet_destroy_all(struct ssh *, int, int);
+ #endif /* PACKET_H */
+diff -up openssh-8.6p1/session.c.audit openssh-8.6p1/session.c
+--- openssh-8.6p1/session.c.audit 2021-04-19 16:47:35.722061868 +0200
++++ openssh-8.6p1/session.c 2021-04-19 16:47:35.758062145 +0200
+@@ -136,7 +136,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(struct ssh *, int);
+ extern struct sshbuf *loginmsg;
+ extern struct sshauthopt *auth_opts;
+ extern char *tun_fwd_ifnames; /* serverloop.c */
+@@ -644,6 +644,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;
+ ssh_packet_set_interactive(ssh, 1,
+@@ -736,15 +744,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(ssh, s->command));
+ #endif
+ if (s->ttyfd != -1)
+ ret = do_exec_pty(ssh, s, command);
+@@ -1550,8 +1562,11 @@ do_child(struct ssh *ssh, Session *s, co
+ sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id));
+
+ /* remove hostkey from the child's memory */
+- destroy_sensitive_data();
++ destroy_sensitive_data(ssh, 1);
+ ssh_packet_clear_keys(ssh);
++ /* 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(ssh, 0, 1);
+
+ /* Force a password change */
+ if (s->authctxt->force_pwchange) {
+@@ -1763,6 +1778,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;
+ }
+@@ -1843,6 +1861,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_f("unknown id %d", id);
++ session_dump();
++ return NULL;
++}
++
++Session *
+ session_by_tty(char *tty)
+ {
+ int i;
+@@ -2450,6 +2481,32 @@ session_exit_message(struct ssh *ssh, Se
+ chan_write_failed(ssh, c);
+ }
+
++#ifdef SSH_AUDIT_EVENTS
++void
++session_end_command2(struct ssh *ssh, Session *s)
++{
++ if (s->command != NULL) {
++ if (s->command_handle != -1)
++ audit_end_command(ssh, s->command_handle, s->command);
++ free(s->command);
++ s->command = NULL;
++ s->command_handle = -1;
++ }
++}
++
++static void
++session_end_command(struct ssh *ssh, Session *s)
++{
++ if (s->command != NULL) {
++ if (s->command_handle != -1)
++ PRIVSEP(audit_end_command(ssh, s->command_handle, s->command));
++ free(s->command);
++ s->command = NULL;
++ s->command_handle = -1;
++ }
++}
++#endif
++
+ void
+ session_close(struct ssh *ssh, Session *s)
+ {
+@@ -2463,6 +2520,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(ssh, s);
++#endif
+ free(s->term);
+ free(s->display);
+ free(s->x11_chanids);
+@@ -2537,14 +2598,14 @@ session_close_by_channel(struct ssh *ssh
+ }
+
+ void
+-session_destroy_all(struct ssh *ssh, void (*closefunc)(Session *))
++session_destroy_all(struct ssh *ssh, void (*closefunc)(struct ssh *ssh, Session *))
+ {
+ int i;
+ for (i = 0; i < sessions_nalloc; i++) {
+ Session *s = &sessions[i];
+ if (s->used) {
+ if (closefunc != NULL)
+- closefunc(s);
++ closefunc(ssh, s);
+ else
+ session_close(ssh, s);
+ }
+@@ -2671,6 +2732,15 @@ do_authenticated2(struct ssh *ssh, Authc
+ server_loop2(ssh, authctxt);
+ }
+
++static void
++do_cleanup_one_session(struct ssh *ssh, Session *s)
++{
++ session_pty_cleanup2(s);
++#ifdef SSH_AUDIT_EVENTS
++ session_end_command2(ssh, s);
++#endif
++}
++
+ void
+ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
+ {
+@@ -2734,7 +2804,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-8.6p1/session.h.audit openssh-8.6p1/session.h
+--- openssh-8.6p1/session.h.audit 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/session.h 2021-04-19 16:47:35.758062145 +0200
+@@ -61,6 +61,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 *);
+@@ -71,10 +77,12 @@ void session_unused(int);
+ int session_input_channel_req(struct ssh *, Channel *, const char *);
+ void session_close_by_pid(struct ssh *ssh, pid_t, int);
+ void session_close_by_channel(struct ssh *, int, int, void *);
+-void session_destroy_all(struct ssh *, void (*)(Session *));
++void session_destroy_all(struct ssh *, void (*)(struct ssh*, Session *));
+ void session_pty_cleanup2(Session *);
++void session_end_command2(struct ssh *ssh, 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-8.6p1/sshd.c.audit openssh-8.6p1/sshd.c
+--- openssh-8.6p1/sshd.c.audit 2021-04-19 16:47:35.727061907 +0200
++++ openssh-8.6p1/sshd.c 2021-04-19 16:47:35.759062152 +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"
+@@ -260,8 +261,8 @@ struct sshbuf *loginmsg;
+ struct passwd *privsep_pw = NULL;
+
+ /* Prototypes for various functions defined later in this file. */
+-void destroy_sensitive_data(void);
+-void demote_sensitive_data(void);
++void destroy_sensitive_data(struct ssh *, int);
++void demote_sensitive_data(struct ssh *);
+ static void do_ssh2_kex(struct ssh *);
+
+ static char *listener_proctitle;
+@@ -279,6 +280,15 @@ close_listen_socks(void)
+ num_listen_socks = 0;
+ }
+
++/*
++ * 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)
+ {
+@@ -377,18 +387,45 @@ grace_alarm_handler(int sig)
+ ssh_remote_port(the_active_state));
+ }
+
+-/* 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(struct ssh *ssh, 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(ssh, fp,
++ pid, uid));
++ else
++ audit_destroy_sensitive_data(ssh, 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;
+ }
+@@ -397,20 +434,38 @@ destroy_sensitive_data(void)
+
+ /* Demote private to public keys for network child */
+ void
+-demote_sensitive_data(void)
++demote_sensitive_data(struct ssh *ssh)
+ {
+ 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_from_private(
+ sensitive_data.host_keys[i], &tmp)) != 0)
+ fatal_r(r, "could not demote host %s key",
+ sshkey_type(sensitive_data.host_keys[i]));
+ sshkey_free(sensitive_data.host_keys[i]);
+ sensitive_data.host_keys[i] = tmp;
++ if (fp != NULL) {
++#ifdef SSH_AUDIT_EVENTS
++ audit_destroy_sensitive_data(ssh, fp, pid, uid);
++#endif
++ free(fp);
++ }
+ }
+ /* Certs do not need demotion */
+ }
+@@ -438,7 +493,7 @@ reseed_prngs(void)
+ }
+
+ static void
+-privsep_preauth_child(void)
++privsep_preauth_child(struct ssh *ssh)
+ {
+ gid_t gidset[1];
+
+@@ -453,7 +508,7 @@ privsep_preauth_child(void)
+ reseed_prngs();
+
+ /* Demote the private keys to public keys. */
+- demote_sensitive_data();
++ demote_sensitive_data(ssh);
+
+ #ifdef WITH_SELINUX
+ sshd_selinux_change_privsep_preauth_context();
+@@ -492,7 +547,7 @@ privsep_preauth(struct ssh *ssh)
+
+ 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) {
+@@ -537,7 +592,7 @@ privsep_preauth(struct ssh *ssh)
+ /* Arrange for logging to be sent to the monitor */
+ set_log_handler(mm_log_handler, pmonitor);
+
+- privsep_preauth_child();
++ privsep_preauth_child(ssh);
+ setproctitle("%s", "[net]");
+ if (box != NULL)
+ ssh_sandbox_child(box);
+@@ -589,7 +644,7 @@ privsep_postauth(struct ssh *ssh, Authct
+ set_log_handler(mm_log_handler, pmonitor);
+
+ /* Demote the private keys to public keys. */
+- demote_sensitive_data();
++ demote_sensitive_data(ssh);
+
+ reseed_prngs();
+
+@@ -1143,7 +1198,7 @@ server_listen(void)
+ * from this function are in a forked subprocess.
+ */
+ static void
+-server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s)
++server_accept_loop(struct ssh *ssh, int *sock_in, int *sock_out, int *newsock, int *config_s)
+ {
+ struct pollfd *pfd = NULL;
+ int i, j, ret, npfd;
+@@ -1204,6 +1259,7 @@ server_accept_loop(int *sock_in, int *so
+ if (received_sigterm) {
+ logit("Received signal %d; terminating.",
+ (int) received_sigterm);
++ destroy_sensitive_data(ssh, 0);
+ close_listen_socks();
+ if (options.pid_file != NULL)
+ unlink(options.pid_file);
+@@ -2098,7 +2154,7 @@ main(int ac, char **av)
+ #endif
+
+ /* Accept a connection and return in a forked child */
+- server_accept_loop(&sock_in, &sock_out,
++ server_accept_loop(ssh, &sock_in, &sock_out,
+ &newsock, config_s);
+ }
+
+@@ -2333,6 +2389,9 @@ main(int ac, char **av)
+ do_authenticated(ssh, authctxt);
+
+ /* The connection has been terminated. */
++ packet_destroy_all(ssh, 1, 1);
++ destroy_sensitive_data(ssh, 1);
++
+ ssh_packet_get_bytes(ssh, &ibytes, &obytes);
+ verbose("Transferred: sent %llu, received %llu bytes",
+ (unsigned long long)obytes, (unsigned long long)ibytes);
+@@ -2513,6 +2572,15 @@ do_ssh2_kex(struct ssh *ssh)
+ void
+ cleanup_exit(int i)
+ {
++ static int in_cleanup = 0;
++ int is_privsep_child;
++
++ /* cleanup_exit can be called at the very least from the privsep
++ wrappers used for auditing. Make sure we don't recurse
++ indefinitely. */
++ if (in_cleanup)
++ _exit(i);
++ in_cleanup = 1;
+ if (the_active_state != NULL && the_authctxt != NULL) {
+ do_cleanup(the_active_state, the_authctxt);
+ if (use_privsep && privsep_is_preauth &&
+@@ -2525,9 +2593,16 @@ cleanup_exit(int i)
+ }
+ }
+ }
++ is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0;
++ if (sensitive_data.host_keys != NULL && the_active_state != NULL)
++ destroy_sensitive_data(the_active_state, is_privsep_child);
++ if (the_active_state != NULL)
++ packet_destroy_all(the_active_state, 1, is_privsep_child);
+ #ifdef SSH_AUDIT_EVENTS
+ /* done after do_cleanup so it can cancel the PAM auth 'thread' */
+- if (the_active_state != NULL && (!use_privsep || mm_is_monitor()))
++ if (the_active_state != NULL &&
++ (the_authctxt == NULL || !the_authctxt->authenticated) &&
++ (!use_privsep || mm_is_monitor()))
+ audit_event(the_active_state, SSH_CONNECTION_ABANDON);
+ #endif
+ _exit(i);
+diff -up openssh-8.6p1/sshkey.c.audit openssh-8.6p1/sshkey.c
+--- openssh-8.6p1/sshkey.c.audit 2021-04-19 16:47:35.741062014 +0200
++++ openssh-8.6p1/sshkey.c 2021-04-19 16:47:35.759062152 +0200
+@@ -371,6 +371,38 @@ 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: {
++ const BIGNUM *d;
++ RSA_get0_key(k->rsa, NULL, NULL, &d);
++ return d != NULL;
++ }
++ case KEY_DSA_CERT:
++ 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:
++ 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-8.6p1/sshkey.h.audit openssh-8.6p1/sshkey.h
+--- openssh-8.6p1/sshkey.h.audit 2021-04-19 16:47:35.741062014 +0200
++++ openssh-8.6p1/sshkey.h 2021-04-19 16:47:35.759062152 +0200
+@@ -189,6 +189,7 @@ int sshkey_shield_private(struct sshke
+ int sshkey_unshield_private(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_is_sk(const struct sshkey *);
+ int sshkey_type_is_cert(int);
diff --git a/openssh-7.6p1-cleanup-selinux.patch b/openssh-7.6p1-cleanup-selinux.patch
new file mode 100644
index 0000000..f7cd50f
--- /dev/null
+++ b/openssh-7.6p1-cleanup-selinux.patch
@@ -0,0 +1,283 @@
+diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c
+--- openssh/auth2-pubkey.c.refactor 2019-04-04 13:19:12.188821236 +0200
++++ openssh/auth2-pubkey.c 2019-04-04 13:19:12.276822078 +0200
+@@ -72,6 +72,9 @@
+
+ /* import */
+ extern ServerOptions options;
++extern int inetd_flag;
++extern int rexeced_flag;
++extern Authctxt *the_authctxt;
+
+ static char *
+ format_key(const struct sshkey *key)
+@@ -511,7 +514,8 @@ match_principals_command(struct ssh *ssh
+ if ((pid = subprocess("AuthorizedPrincipalsCommand", command,
+ ac, av, &f,
+ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
+- runas_pw, temporarily_use_uid, restore_uid)) == 0)
++ runas_pw, temporarily_use_uid, restore_uid,
++ (inetd_flag && !rexeced_flag), the_authctxt)) == 0)
+ goto out;
+
+ uid_swapped = 1;
+@@ -981,7 +985,8 @@ user_key_command_allowed2(struct ssh *ss
+ if ((pid = subprocess("AuthorizedKeysCommand", command,
+ ac, av, &f,
+ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD,
+- runas_pw, temporarily_use_uid, restore_uid)) == 0)
++ runas_pw, temporarily_use_uid, restore_uid,
++ (inetd_flag && !rexeced_flag), the_authctxt)) == 0)
+ goto out;
+
+ uid_swapped = 1;
+diff -up openssh/misc.c.refactor openssh/misc.c
+--- openssh/misc.c.refactor 2019-04-04 13:19:12.235821686 +0200
++++ openssh/misc.c 2019-04-04 13:19:12.276822078 +0200
+@@ -756,7 +756,8 @@ auth_get_canonical_hostname(struct ssh *
+ pid_t
+ subprocess(const char *tag, const char *command,
+ int ac, char **av, FILE **child, u_int flags,
+- struct passwd *pw, privdrop_fn *drop_privs, privrestore_fn *restore_privs)
++ struct passwd *pw, privdrop_fn *drop_privs,
++ privrestore_fn *restore_privs, int inetd, void *the_authctxt)
+ {
+ FILE *f = NULL;
+ struct stat st;
+@@ -872,7 +873,7 @@ subprocess(const char *tag, struct passw
+ _exit(1);
+ }
+ #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/misc.h.refactor openssh/misc.h
+--- openssh/misc.h.refactor 2019-04-04 13:19:12.251821839 +0200
++++ openssh/misc.h 2019-04-04 13:19:12.276822078 +0200
+@@ -235,7 +235,7 @@ struct passwd *fakepw(void);
+ #define SSH_SUBPROCESS_UNSAFE_PATH (1<<3) /* Don't check for safe cmd */
+ #define SSH_SUBPROCESS_PRESERVE_ENV (1<<4) /* Keep parent environment */
+ pid_t subprocess(const char *, const char *, int, char **, FILE **, u_int,
+- struct passwd *, privdrop_fn *, privrestore_fn *);
++ struct passwd *, privdrop_fn *, privrestore_fn *, int, void *);
+
+ typedef struct arglist arglist;
+ struct arglist {
+diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/port-linux.h
+--- openssh/openbsd-compat/port-linux.h.refactor 2019-04-04 13:19:12.256821887 +0200
++++ openssh/openbsd-compat/port-linux.h 2019-04-04 13:19:12.276822078 +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 2019-04-04 13:19:12.256821887 +0200
++++ openssh/openbsd-compat/port-linux-sshd.c 2019-04-04 13:19:12.276822078 +0200
+@@ -49,11 +49,6 @@
+ #include <unistd.h>
+ #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)
+@@ -223,7 +218,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;
+@@ -241,8 +237,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;
+@@ -250,7 +246,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) {
+@@ -272,7 +268,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)
+@@ -333,7 +329,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;
+@@ -342,11 +339,11 @@ sshd_selinux_setup_variables(int(*set_it
+
+ debug3_f("setting execution context");
+
+- 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 = "";
+@@ -362,9 +359,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
+@@ -374,25 +372,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_f("security_getenforce() failed");
+@@ -410,7 +411,7 @@ sshd_selinux_setup_exec_context(char *pw
+
+ debug3_f("setting execution context");
+
+- 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 2019-04-04 13:19:12.204821389 +0200
++++ openssh/platform.c 2019-04-04 13:19:12.277822088 +0200
+@@ -32,6 +32,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)
+@@ -183,7 +186,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 2019-04-04 13:19:12.275822068 +0200
++++ openssh/sshd.c 2019-04-04 13:19:51.270195262 +0200
+@@ -158,7 +158,7 @@ int debug_flag = 0;
+ static int test_flag = 0;
+
+ /* Flag indicating that the daemon is being started from inetd. */
+-static int inetd_flag = 0;
++int inetd_flag = 0;
+
+ /* Flag indicating that sshd should not detach and become a daemon. */
+ static int no_daemon_flag = 0;
+@@ -171,7 +171,7 @@ static char **saved_argv;
+ static int saved_argc;
+
+ /* re-exec */
+-static int rexeced_flag = 0;
++int rexeced_flag = 0;
+ static int rexec_flag = 1;
+ static int rexec_argc = 0;
+ static char **rexec_argv;
+@@ -2192,7 +2192,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 -up openssh/sshconnect.c.refactor openssh/sshconnect.c
+--- openssh/sshconnect.c.refactor 2021-02-24 00:12:03.065325046 +0100
++++ openssh/sshconnect.c 2021-02-24 00:12:12.126449544 +0100
+@@ -892,7 +892,7 @@ load_hostkeys_command(struct hostkeys *h
+
+ if ((pid = subprocess(tag, command, ac, av, &f,
+ SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_UNSAFE_PATH|
+- SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL)) == 0)
++ SSH_SUBPROCESS_PRESERVE_ENV, NULL, NULL, NULL, 0, NULL)) == 0)
+ goto out;
+
+ load_hostkeys_file(hostkeys, hostfile_hostname, tag, f, 1);
diff --git a/openssh-7.7p1-gssapi-new-unique.patch b/openssh-7.7p1-gssapi-new-unique.patch
new file mode 100644
index 0000000..544932b
--- /dev/null
+++ b/openssh-7.7p1-gssapi-new-unique.patch
@@ -0,0 +1,632 @@
+diff -up openssh-8.6p1/auth.h.ccache_name openssh-8.6p1/auth.h
+--- openssh-8.6p1/auth.h.ccache_name 2021-04-19 14:05:10.820744325 +0200
++++ openssh-8.6p1/auth.h 2021-04-19 14:05:10.853744569 +0200
+@@ -83,6 +83,7 @@ struct Authctxt {
+ krb5_principal krb5_user;
+ char *krb5_ticket_file;
+ char *krb5_ccname;
++ int krb5_set_env;
+ #endif
+ struct sshbuf *loginmsg;
+
+@@ -231,7 +232,7 @@ struct passwd *fakepw(void);
+ int sys_auth_passwd(struct ssh *, const char *);
+
+ #if defined(KRB5) && !defined(HEIMDAL)
+-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 /* AUTH_H */
+diff -up openssh-8.6p1/auth-krb5.c.ccache_name openssh-8.6p1/auth-krb5.c
+--- openssh-8.6p1/auth-krb5.c.ccache_name 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/auth-krb5.c 2021-04-19 14:40:55.142832954 +0200
+@@ -51,6 +51,7 @@
+ #include <unistd.h>
+ #include <string.h>
+ #include <krb5.h>
++#include <profile.h>
+
+ extern ServerOptions options;
+
+@@ -77,7 +78,7 @@ auth_krb5_password(Authctxt *authctxt, c
+ #endif
+ krb5_error_code problem;
+ krb5_ccache ccache = NULL;
+- int len;
++ char *ticket_name = NULL;
+ char *client, *platform_client;
+ const char *errmsg;
+
+@@ -163,8 +164,8 @@ auth_krb5_password(Authctxt *authctxt, c
+ 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;
+
+@@ -179,15 +180,14 @@ auth_krb5_password(Authctxt *authctxt, c
+ 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
+
+@@ -223,11 +223,54 @@ auth_krb5_password(Authctxt *authctxt, c
+ 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;
+@@ -238,36 +281,188 @@ krb5_cleanup_proc(Authctxt *authctxt)
+ }
+ }
+
+-#ifndef HEIMDAL
++
++#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_f("called, template = %s", 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_f("unsupported token %s in %s", p_n, template);
++ /* unknown token, fallback to the default */
++ goto cleanup;
++ }
++ }
++
++ 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_f("called");
++ 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_f("returning with ccname = %s", *ccname);
++ return ret;
++}
++
+ krb5_error_code
+-ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) {
+- int tmpfd, ret, oerrno;
+- char ccname[40];
++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;
+
+- 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;
+- }
++ debug3_f("called");
++ 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;
+
+- if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) {
++ 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_f("setting default ccname to %s", 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_f("calling cc_new_unique(%s)", ccname);
++ ret = krb5_cc_new_unique(ctx, type, NULL, ccache);
++ free(type);
++ if (ret)
++ return ret;
++
++ debug3_f("calling cc_switch()");
++ 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
++ */
++ free(type);
++ debug3_f("calling cc_resolve(%s)", ccname);
++ return (krb5_cc_resolve(ctx, ccname, ccache));
++ }
+ }
+ #endif /* !HEIMDAL */
+ #endif /* KRB5 */
+diff -up openssh-8.6p1/gss-serv.c.ccache_name openssh-8.6p1/gss-serv.c
+--- openssh-8.6p1/gss-serv.c.ccache_name 2021-04-19 14:05:10.844744503 +0200
++++ openssh-8.6p1/gss-serv.c 2021-04-19 14:05:10.854744577 +0200
+@@ -413,13 +413,15 @@ ssh_gssapi_cleanup_creds(void)
+ }
+
+ /* 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 child's environment based
+@@ -499,9 +501,7 @@ ssh_gssapi_rekey_creds(void) {
+ char *envstr;
+ #endif
+
+- if (gssapi_client.store.filename == NULL &&
+- gssapi_client.store.envval == NULL &&
+- gssapi_client.store.envvar == NULL)
++ if (gssapi_client.store.envval == NULL)
+ return;
+
+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
+diff -up openssh-8.6p1/gss-serv-krb5.c.ccache_name openssh-8.6p1/gss-serv-krb5.c
+--- openssh-8.6p1/gss-serv-krb5.c.ccache_name 2021-04-19 14:05:10.852744562 +0200
++++ openssh-8.6p1/gss-serv-krb5.c 2021-04-19 14:05:10.854744577 +0200
+@@ -267,7 +267,7 @@ ssh_gssapi_krb5_cmdok(krb5_principal pri
+ /* 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;
+@@ -276,14 +276,15 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+ OM_uint32 maj_status, min_status;
+ const char *new_ccname, *new_cctype;
+ 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
+@@ -297,14 +298,14 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+ 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 */
+
+@@ -313,7 +314,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+ 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))) {
+@@ -322,7 +323,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+ 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);
+@@ -331,32 +332,21 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+ 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);
+- client->store.filename = NULL;
+-#else
+- if (new_ccname[0] == ':')
+- new_ccname++;
+ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname);
+- if (strcmp(new_cctype, "DIR") == 0) {
+- char *p;
+- p = strrchr(client->store.envval, '/');
+- if (p)
+- *p = '\0';
++
++ if (set_env) {
++ client->store.envvar = "KRB5CCNAME";
+ }
+ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0))
+ client->store.filename = xstrdup(new_ccname);
+-#endif
+
+ #ifdef USE_PAM
+- if (options.use_pam)
++ if (options.use_pam && set_env)
+ do_pam_putenv(client->store.envvar, client->store.envval);
+ #endif
+
+@@ -364,7 +354,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl
+
+ client->store.data = krb_context;
+
+- return;
++ return set_env;
+ }
+
+ int
+diff -up openssh-8.6p1/servconf.c.ccache_name openssh-8.6p1/servconf.c
+--- openssh-8.6p1/servconf.c.ccache_name 2021-04-19 14:05:10.848744532 +0200
++++ openssh-8.6p1/servconf.c 2021-04-19 14:05:10.854744577 +0200
+@@ -136,6 +136,7 @@ initialize_server_options(ServerOptions
+ 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;
+@@ -359,6 +360,8 @@ fill_default_server_options(ServerOption
+ 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)
+@@ -506,7 +509,7 @@ typedef enum {
+ sPort, sHostKeyFile, sLoginGraceTime,
+ sPermitRootLogin, sLogFacility, sLogLevel, sLogVerbose,
+ sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
+- sKerberosGetAFSToken, sPasswordAuthentication,
++ sKerberosGetAFSToken, sKerberosUniqueCCache, sPasswordAuthentication,
+ sKbdInteractiveAuthentication, sListenAddress, sAddressFamily,
+ sPrintMotd, sPrintLastLog, sIgnoreRhosts,
+ sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost,
+@@ -593,11 +597,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 },
+@@ -1573,6 +1579,10 @@ process_server_config_line_depth(ServerO
+ 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;
+@@ -2891,6 +2901,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 -up openssh-8.6p1/servconf.h.ccache_name openssh-8.6p1/servconf.h
+--- openssh-8.6p1/servconf.h.ccache_name 2021-04-19 14:05:10.848744532 +0200
++++ openssh-8.6p1/servconf.h 2021-04-19 14:05:10.855744584 +0200
+@@ -140,6 +140,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 -up openssh-8.6p1/session.c.ccache_name openssh-8.6p1/session.c
+--- openssh-8.6p1/session.c.ccache_name 2021-04-19 14:05:10.852744562 +0200
++++ openssh-8.6p1/session.c 2021-04-19 14:05:10.855744584 +0200
+@@ -1038,7 +1038,8 @@ do_setup_env(struct ssh *ssh, Session *s
+ /* Allow any GSSAPI methods that we've used to alter
+ * the child's 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. */
+@@ -1114,7 +1115,7 @@ do_setup_env(struct ssh *ssh, Session *s
+ }
+ #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 -up openssh-8.6p1/sshd.c.ccache_name openssh-8.6p1/sshd.c
+--- openssh-8.6p1/sshd.c.ccache_name 2021-04-19 14:05:10.849744540 +0200
++++ openssh-8.6p1/sshd.c 2021-04-19 14:05:10.855744584 +0200
+@@ -2284,7 +2284,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 -up openssh-8.6p1/sshd_config.5.ccache_name openssh-8.6p1/sshd_config.5
+--- openssh-8.6p1/sshd_config.5.ccache_name 2021-04-19 14:05:10.849744540 +0200
++++ openssh-8.6p1/sshd_config.5 2021-04-19 14:05:10.856744592 +0200
+@@ -939,6 +939,14 @@ Specifies whether to automatically destr
+ 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 -up openssh-8.6p1/ssh-gss.h.ccache_name openssh-8.6p1/ssh-gss.h
+--- openssh-8.6p1/ssh-gss.h.ccache_name 2021-04-19 14:05:10.852744562 +0200
++++ openssh-8.6p1/ssh-gss.h 2021-04-19 14:05:10.855744584 +0200
+@@ -114,7 +114,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;
+
+@@ -175,7 +175,7 @@ int ssh_gssapi_userok(char *name, struct
+ 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/openssh-7.7p1.patch b/openssh-7.7p1.patch
new file mode 100644
index 0000000..85ebc82
--- /dev/null
+++ b/openssh-7.7p1.patch
@@ -0,0 +1,105 @@
+diff -up openssh/ssh_config.redhat openssh/ssh_config
+--- openssh/ssh_config.redhat 2020-02-11 23:28:35.000000000 +0100
++++ openssh/ssh_config 2020-02-13 18:13:39.180641839 +0100
+@@ -43,3 +43,10 @@
+ # ProxyCommand ssh -q -W %h:%p gateway.example.com
+ # RekeyLimit 1G 1h
+ # UserKnownHostsFile ~/.ssh/known_hosts.d/%k
++#
++# This system is following system-wide crypto policy.
++# To modify the crypto properties (Ciphers, MACs, ...), create a *.conf
++# file under /etc/ssh/ssh_config.d/ which will be automatically
++# included below. For more information, see manual page for
++# update-crypto-policies(8) and ssh_config(5).
++Include /etc/ssh/ssh_config.d/*.conf
+diff -up openssh/ssh_config_redhat.redhat openssh/ssh_config_redhat
+--- openssh/ssh_config_redhat.redhat 2020-02-13 18:13:39.180641839 +0100
++++ openssh/ssh_config_redhat 2020-02-13 18:13:39.180641839 +0100
+@@ -0,0 +1,15 @@
++# The options here are in the "Match final block" to be applied as the last
++# options and could be potentially overwritten by the user configuration
++Match final all
++ # Follow system-wide Crypto Policy, if defined:
++ Include /etc/crypto-policies/back-ends/openssh.config
++
++ 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
++
++# Uncomment this if you want to use .local domain
++# Host *.local
+diff -up openssh/sshd_config.0.redhat openssh/sshd_config.0
+--- openssh/sshd_config.0.redhat 2020-02-12 14:30:04.000000000 +0100
++++ openssh/sshd_config.0 2020-02-13 18:13:39.181641855 +0100
+@@ -970,9 +970,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/sshd_config.5.redhat openssh/sshd_config.5
+--- openssh/sshd_config.5.redhat 2020-02-11 23:28:35.000000000 +0100
++++ openssh/sshd_config.5 2020-02-13 18:13:39.181641855 +0100
+@@ -1614,7 +1614,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/sshd_config.redhat openssh/sshd_config
+--- openssh/sshd_config.redhat 2020-02-11 23:28:35.000000000 +0100
++++ openssh/sshd_config 2020-02-13 18:20:16.349913681 +0100
+@@ -10,6 +10,14 @@
+ # possible, but leave them commented. Uncommented options override the
+ # default value.
+
++# To modify the system-wide sshd configuration, create a *.conf file under
++# /etc/ssh/sshd_config.d/ which will be automatically included below
++Include /etc/ssh/sshd_config.d/*.conf
++
++# 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
+diff -up openssh/sshd_config_redhat.redhat openssh/sshd_config_redhat
+--- openssh/sshd_config_redhat.redhat 2020-02-13 18:14:02.268006439 +0100
++++ openssh/sshd_config_redhat 2020-02-13 18:19:20.765035947 +0100
+@@ -0,0 +1,22 @@
++# This system is following system-wide crypto policy. The changes to
++# crypto properties (Ciphers, MACs, ...) will not have any effect in
++# this or following included files. To override some configuration option,
++# write it before this block or include it before this file.
++# Please, see manual pages for update-crypto-policies(8) and sshd_config(5).
++Include /etc/crypto-policies/back-ends/opensshserver.config
++
++SyslogFacility AUTHPRIV
++
++ChallengeResponseAuthentication no
++
++GSSAPIAuthentication yes
++GSSAPICleanupCredentials no
++
++UsePAM yes
++
++X11Forwarding 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
++
diff --git a/openssh-7.8p1-UsePAM-warning.patch b/openssh-7.8p1-UsePAM-warning.patch
new file mode 100644
index 0000000..48d2b32
--- /dev/null
+++ b/openssh-7.8p1-UsePAM-warning.patch
@@ -0,0 +1,26 @@
+diff -up openssh-8.6p1/sshd.c.log-usepam-no openssh-8.6p1/sshd.c
+--- openssh-8.6p1/sshd.c.log-usepam-no 2021-04-19 14:00:45.099735129 +0200
++++ openssh-8.6p1/sshd.c 2021-04-19 14:03:21.140920974 +0200
+@@ -1749,6 +1749,10 @@ main(int ac, char **av)
+ parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
+ cfg, &includes, NULL, rexeced_flag);
+
++ /* 'UsePAM no' is not supported in openEuler */
++ if (! options.use_pam)
++ logit("WARNING: 'UsePAM no' is not supported in openEuler and may cause several problems.");
++
+ #ifdef WITH_OPENSSL
+ if (options.moduli_file != NULL)
+ dh_set_moduli_file(options.moduli_file);
+diff -up openssh-8.6p1/sshd_config.log-usepam-no openssh-8.6p1/sshd_config
+--- openssh-8.6p1/sshd_config.log-usepam-no 2021-04-19 14:00:45.098735121 +0200
++++ openssh-8.6p1/sshd_config 2021-04-19 14:00:45.099735129 +0200
+@@ -87,6 +87,8 @@ 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 KbdInteractiveAuthentication to 'no'.
++# WARNING: 'UsePAM no' is not supported in openEuler and may cause several
++# problems.
+ #UsePAM no
+
+ #AllowAgentForwarding yes
diff --git a/openssh-7.8p1-role-mls.patch b/openssh-7.8p1-role-mls.patch
new file mode 100644
index 0000000..4dc460a
--- /dev/null
+++ b/openssh-7.8p1-role-mls.patch
@@ -0,0 +1,867 @@
+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 = NULL, *service = NULL, *method = NULL, *style = NULL;
++#ifdef WITH_SELINUX
++ char *role = NULL;
++#endif
+ int r, 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(ssh);
+ if (auth2_setup_methods_lists(authctxt) != 0)
+ ssh_packet_disconnect(ssh,
+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_f("sshbuf_new failed");
+ mic.value = p;
+ mic.length = len;
+- ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
++#ifdef WITH_SELINUX
++ if (authctxt->role && authctxt->role[0] != 0)
++ xasprintf(&micuser, "%s/%s", authctxt->user, authctxt->role);
++ else
++#endif
++ micuser = authctxt->user;
++ ssh_gssapi_buildmic(b, micuser, authctxt->service,
+ "gssapi-with-mic", ssh->kex->session_id);
+
+ 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_stringb(b, ssh->kex->session_id)) != 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, method)) != 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;
+ char *compound;
+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/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-8.6p1/monitor.c.role-mls openssh-8.6p1/monitor.c
+--- openssh-8.6p1/monitor.c.role-mls 2021-04-16 05:55:25.000000000 +0200
++++ openssh-8.6p1/monitor.c 2021-05-21 14:21:56.719414087 +0200
+@@ -117,6 +117,9 @@ int mm_answer_sign(struct ssh *, int, st
+ int mm_answer_pwnamallow(struct ssh *, int, struct sshbuf *);
+ int mm_answer_auth2_read_banner(struct ssh *, int, struct sshbuf *);
+ int mm_answer_authserv(struct ssh *, int, struct sshbuf *);
++#ifdef WITH_SELINUX
++int mm_answer_authrole(struct ssh *, int, struct sshbuf *);
++#endif
+ int mm_answer_authpassword(struct ssh *, int, struct sshbuf *);
+ int mm_answer_bsdauthquery(struct ssh *, int, struct sshbuf *);
+ int mm_answer_bsdauthrespond(struct ssh *, int, struct sshbuf *);
+@@ -195,6 +198,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
+@@ -803,6 +809,9 @@ mm_answer_pwnamallow(struct ssh *ssh, in
+
+ /* 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
+@@ -877,6 +886,26 @@ key_base_type_match(const char *method,
+ return found;
+ }
+
++#ifdef WITH_SELINUX
++int
++mm_answer_authrole(struct ssh *ssh, int sock, struct sshbuf *m)
++{
++ int r;
++ monitor_permit_authentications(1);
++
++ if ((r = sshbuf_get_cstring(m, &authctxt->role, NULL)) != 0)
++ fatal_f("buffer error: %s", ssh_err(r));
++ debug3_f("role=%s", authctxt->role);
++
++ if (strlen(authctxt->role) == 0) {
++ free(authctxt->role);
++ authctxt->role = NULL;
++ }
++
++ return (0);
++}
++#endif
++
+ int
+ mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m)
+ {
+@@ -1251,7 +1280,7 @@ monitor_valid_userblob(struct ssh *ssh,
+ struct sshbuf *b;
+ struct sshkey *hostkey = NULL;
+ const u_char *p;
+- char *userstyle, *cp;
++ char *userstyle, *s, *cp;
+ size_t len;
+ u_char type;
+ int hostbound = 0, r, fail = 0;
+@@ -1282,6 +1311,8 @@ monitor_valid_userblob(struct ssh *ssh,
+ fail++;
+ if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0)
+ fatal_fr(r, "parse userstyle");
++ if ((s = strchr(cp, '/')) != NULL)
++ *s = '\0';
+ xasprintf(&userstyle, "%s%s%s", authctxt->user,
+ authctxt->style ? ":" : "",
+ authctxt->style ? authctxt->style : "");
+@@ -1317,7 +1348,7 @@ monitor_valid_hostbasedblob(const u_char
+ {
+ struct sshbuf *b;
+ const u_char *p;
+- char *cp, *userstyle;
++ char *cp, *s, *userstyle;
+ size_t len;
+ int r, fail = 0;
+ u_char type;
+@@ -1338,6 +1370,8 @@ monitor_valid_hostbasedblob(const u_char
+ fail++;
+ if ((r = sshbuf_get_cstring(b, &cp, NULL)) != 0)
+ fatal_fr(r, "parse userstyle");
++ if ((s = strchr(cp, '/')) != 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_f("entering");
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_cstring(m, role ? role : "")) != 0)
++ fatal_f("buffer error: %s", 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);
+ const u_char *, size_t, const char *, const char *,
+ const char *, u_int compat);
+ void mm_inform_authserv(char *, char *);
++#ifdef WITH_SELINUX
++void mm_inform_authrole(char *);
++#endif
+ struct passwd *mm_getpwnamallow(struct ssh *, 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-prngd.o \
+ port-solaris.o \
+ port-net.o \
+- port-uw.o
++ port-uw.o \
++ port-linux-sshd.o
+
+ .c.o:
+ $(CC) $(CFLAGS_NOPIE) $(PICFLAG) $(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)
+-{
+- char *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_f("getexeccon: %s", 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,421 @@
++/*
++ * Copyright (c) 2005 Daniel Walsh <dwalsh@redhat.com>
++ * Copyright (c) 2014 Petr Lautrbach <plautrba@redhat.com>
++ *
++ * 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 <errno.h>
++#include <stdarg.h>
++#include <string.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++#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 <selinux/selinux.h>
++#include <selinux/context.h>
++#include <selinux/get_context_list.h>
++#include <selinux/get_default_type.h>
++
++#ifdef HAVE_LINUX_AUDIT
++#include <libaudit.h>
++#include <unistd.h>
++#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_f("src:%s dst:%s", 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_f("current connection level '%s'", 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_f("Failed to get default SELinux security "
++ "context for %s", 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_f("setting execution context");
++
++ 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_f("security_getenforce() failed");
++ case 0:
++ error_f("SELinux PAM variable setup failure. Continuing in permissive mode.");
++ break;
++ default:
++ fatal_f("SELinux PAM variable setup failure. Aborting connection.");
++ }
++ }
++ return;
++ }
++
++ debug3_f("setting execution context");
++
++ r = sshd_selinux_getctxbyname(pwname, &default_ctx, &user_ctx);
++ if (r >= 0) {
++ r = setexeccon(user_ctx);
++ if (r < 0) {
++ error_f("Failed to set SELinux execution context %s for %s",
++ user_ctx, pwname);
++ }
++#ifdef HAVE_SETKEYCREATECON
++ else if (setkeycreatecon(user_ctx) < 0) {
++ error_f("Failed to set SELinux keyring creation context %s for %s",
++ 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_f("security_getenforce() failed");
++ case 0:
++ error_f("ELinux failure. Continuing in permissive mode.");
++ break;
++ default:
++ fatal_f("SELinux failure. Aborting connection.");
++ }
++ }
++ if (user_ctx != NULL && user_ctx != default_ctx)
++ freecon(user_ctx);
++ if (default_ctx != NULL)
++ freecon(default_ctx);
++
++ debug3_f("done");
++}
++
++#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/openssh-7.8p1-scp-ipv6.patch b/openssh-7.8p1-scp-ipv6.patch
new file mode 100644
index 0000000..8ae0948
--- /dev/null
+++ b/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/openssh-8.0p1-crypto-policies.patch b/openssh-8.0p1-crypto-policies.patch
new file mode 100644
index 0000000..86c08db
--- /dev/null
+++ b/openssh-8.0p1-crypto-policies.patch
@@ -0,0 +1,632 @@
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh_config.5 openssh-9.3p1-patched/ssh_config.5
+--- openssh-9.3p1/ssh_config.5 2023-06-07 10:26:48.284590156 +0200
++++ openssh-9.3p1-patched/ssh_config.5 2023-06-07 10:26:00.623052194 +0200
+@@ -378,17 +378,13 @@
+ causes no CNAMEs to be considered for canonicalization.
+ This is the default behaviour.
+ .It Cm CASignatureAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies which algorithms are allowed for signing of certificates
+ by certificate authorities (CAs).
+-The default is:
+-.Bd -literal -offset indent
+-ssh-ed25519,ecdsa-sha2-nistp256,
+-ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
+-.Pp
+ If the specified list begins with a
+ .Sq +
+ character, then the specified algorithms will be appended to the default set
+@@ -450,20 +446,25 @@
+ (the default),
+ the check will not be executed.
+ .It Cm Ciphers
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the ciphers allowed and their order of preference.
+ Multiple ciphers must be comma-separated.
+ If the specified list begins with a
+ .Sq +
+-character, then the specified ciphers will be appended to the default set
+-instead of replacing them.
++character, then the specified ciphers will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified ciphers (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified ciphers will be placed at the head of the
+-default set.
++built-in openssh default set.
+ .Pp
+ The supported ciphers are:
+ .Bd -literal -offset indent
+@@ -479,13 +480,6 @@
+ chacha20-poly1305@openssh.com
+ .Ed
+ .Pp
+-The default is:
+-.Bd -literal -offset indent
+-chacha20-poly1305@openssh.com,
+-aes128-ctr,aes192-ctr,aes256-ctr,
+-aes128-gcm@openssh.com,aes256-gcm@openssh.com
+-.Ed
+-.Pp
+ The list of available ciphers may also be obtained using
+ .Qq ssh -Q cipher .
+ .It Cm ClearAllForwardings
+@@ -885,6 +879,11 @@
+ The default is
+ .Dq no .
+ .It Cm GSSAPIKexAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ The list of key exchange algorithms that are offered for GSSAPI
+ key exchange. Possible values are
+ .Bd -literal -offset 3n
+@@ -897,10 +896,8 @@
+ gss-curve25519-sha256-
+ .Ed
+ .Pp
+-The default is
+-.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
+-gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
+ This option only applies to connections using GSSAPI.
++.Pp
+ .It Cm HashKnownHosts
+ Indicates that
+ .Xr ssh 1
+@@ -919,36 +916,25 @@
+ but may be manually hashed using
+ .Xr ssh-keygen 1 .
+ .It Cm HostbasedAcceptedAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the signature algorithms that will be used for hostbased
+ authentication as a comma-separated list of patterns.
+ Alternately if the specified list begins with a
+ .Sq +
+ character, then the specified signature algorithms will be appended
+-to the default set instead of replacing them.
++to the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified signature algorithms (including wildcards)
+-will be removed from the default set instead of replacing them.
++will be removed from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified signature algorithms will be placed
+-at the head of the default set.
+-The default for this option is:
+-.Bd -literal -offset 3n
+-ssh-ed25519-cert-v01@openssh.com,
+-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-ecdsa-sha2-nistp384-cert-v01@openssh.com,
+-ecdsa-sha2-nistp521-cert-v01@openssh.com,
+-sk-ssh-ed25519-cert-v01@openssh.com,
+-sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-rsa-sha2-512-cert-v01@openssh.com,
+-rsa-sha2-256-cert-v01@openssh.com,
+-ssh-ed25519,
+-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
++at the head of the built-in openssh default set.
+ .Pp
+ The
+ .Fl Q
+@@ -1001,6 +987,17 @@
+ .Pp
+ The list of available signature algorithms may also be obtained using
+ .Qq ssh -Q HostKeyAlgorithms .
++.Pp
++The proposed
++.Cm HostKeyAlgorithms
++during KEX are limited to the set of algorithms that is defined in
++.Cm PubkeyAcceptedAlgorithms
++and therefore they are indirectly affected by system-wide
++.Xr crypto_policies 7 .
++.Xr crypto_policies 7 can not handle the list of host key algorithms directly as doing so
++would break the order given by the
++.Pa known_hosts
++file.
+ .It Cm HostKeyAlias
+ Specifies an alias that should be used instead of the
+ real host name when looking up or saving the host key
+@@ -1232,30 +1229,25 @@
+ and
+ .Cm pam .
+ .It Cm KexAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the available KEX (Key Exchange) algorithms.
+ Multiple algorithms must be comma-separated.
+ If the specified list begins with a
+ .Sq +
+-character, then the specified algorithms will be appended to the default set
+-instead of replacing them.
++character, then the specified methods will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
+-The default is:
+-.Bd -literal -offset indent
+-sntrup761x25519-sha512@openssh.com,
+-curve25519-sha256,curve25519-sha256@libssh.org,
+-ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,
+-diffie-hellman-group-exchange-sha256,
+-diffie-hellman-group16-sha512,
+-diffie-hellman-group18-sha512,
+-diffie-hellman-group14-sha256
+-.Ed
++built-in openssh default set.
+ .Pp
+ The list of available key exchange algorithms may also be obtained using
+ .Qq ssh -Q kex .
+@@ -1365,37 +1357,33 @@
+ file.
+ This option is intended for debugging and no overrides are enabled by default.
+ .It Cm MACs
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the MAC (message authentication code) algorithms
+ in order of preference.
+ The MAC algorithm is used for data integrity protection.
+ Multiple algorithms must be comma-separated.
+ If the specified list begins with a
+ .Sq +
+-character, then the specified algorithms will be appended to the default set
+-instead of replacing them.
++character, then the specified algorithms will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
++built-in openssh default set.
+ .Pp
+ The algorithms that contain
+ .Qq -etm
+ calculate the MAC after encryption (encrypt-then-mac).
+ These are considered safer and their use recommended.
+ .Pp
+-The default is:
+-.Bd -literal -offset indent
+-umac-64-etm@openssh.com,umac-128-etm@openssh.com,
+-hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,
+-hmac-sha1-etm@openssh.com,
+-umac-64@openssh.com,umac-128@openssh.com,
+-hmac-sha2-256,hmac-sha2-512,hmac-sha1
+-.Ed
+-.Pp
+ The list of available MAC algorithms may also be obtained using
+ .Qq ssh -Q mac .
+ .It Cm NoHostAuthenticationForLocalhost
+@@ -1567,39 +1555,31 @@
+ The default is
+ .Cm no .
+ .It Cm PubkeyAcceptedAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the signature algorithms that will be used for public key
+ authentication as a comma-separated list of patterns.
+ If the specified list begins with a
+ .Sq +
+-character, then the algorithms after it will be appended to the default
+-instead of replacing it.
++character, then the algorithms after it will be appended to the built-in
++openssh default instead of replacing it.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
+-The default for this option is:
+-.Bd -literal -offset 3n
+-ssh-ed25519-cert-v01@openssh.com,
+-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-ecdsa-sha2-nistp384-cert-v01@openssh.com,
+-ecdsa-sha2-nistp521-cert-v01@openssh.com,
+-sk-ssh-ed25519-cert-v01@openssh.com,
+-sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-rsa-sha2-512-cert-v01@openssh.com,
+-rsa-sha2-256-cert-v01@openssh.com,
+-ssh-ed25519,
+-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
++built-in openssh default set.
+ .Pp
+ The list of available signature algorithms may also be obtained using
+ .Qq ssh -Q PubkeyAcceptedAlgorithms .
++.Pp
++This option affects also
++.Cm HostKeyAlgorithms
+ .It Cm PubkeyAuthentication
+ Specifies whether to try public key authentication.
+ The argument to this keyword must be
+@@ -2265,7 +2245,9 @@
+ This file must be world-readable.
+ .El
+ .Sh SEE ALSO
+-.Xr ssh 1
++.Xr ssh 1 ,
++.Xr crypto-policies 7 ,
++.Xr update-crypto-policies 8
+ .Sh AUTHORS
+ .An -nosplit
+ OpenSSH is a derivative of the original and free
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/sshd_config.5 openssh-9.3p1-patched/sshd_config.5
+--- openssh-9.3p1/sshd_config.5 2023-06-07 10:26:48.277590077 +0200
++++ openssh-9.3p1-patched/sshd_config.5 2023-06-07 10:26:00.592051845 +0200
+@@ -379,17 +379,13 @@
+ then no banner is displayed.
+ By default, no banner is displayed.
+ .It Cm CASignatureAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies which algorithms are allowed for signing of certificates
+ by certificate authorities (CAs).
+-The default is:
+-.Bd -literal -offset indent
+-ssh-ed25519,ecdsa-sha2-nistp256,
+-ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
+-.Pp
+ If the specified list begins with a
+ .Sq +
+ character, then the specified algorithms will be appended to the default set
+@@ -525,20 +521,25 @@
+ indicating not to
+ .Xr chroot 2 .
+ .It Cm Ciphers
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the ciphers allowed.
+ Multiple ciphers must be comma-separated.
+ If the specified list begins with a
+ .Sq +
+-character, then the specified ciphers will be appended to the default set
+-instead of replacing them.
++character, then the specified ciphers will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified ciphers (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified ciphers will be placed at the head of the
+-default set.
++built-in openssh default set.
+ .Pp
+ The supported ciphers are:
+ .Pp
+@@ -565,13 +566,6 @@
+ chacha20-poly1305@openssh.com
+ .El
+ .Pp
+-The default is:
+-.Bd -literal -offset indent
+-chacha20-poly1305@openssh.com,
+-aes128-ctr,aes192-ctr,aes256-ctr,
+-aes128-gcm@openssh.com,aes256-gcm@openssh.com
+-.Ed
+-.Pp
+ The list of available ciphers may also be obtained using
+ .Qq ssh -Q cipher .
+ .It Cm ClientAliveCountMax
+@@ -766,53 +760,43 @@
+ .Cm GSSAPIKeyExchange
+ needs to be enabled in the server and also used by the client.
+ .It Cm GSSAPIKexAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ 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-,
+-gss-group14-sha256-,
+-gss-group16-sha512-,
+-gss-nistp256-sha256-,
++gss-gex-sha1-
++gss-group1-sha1-
++gss-group14-sha1-
++gss-group14-sha256-
++gss-group16-sha512-
++gss-nistp256-sha256-
+ gss-curve25519-sha256-
+ .Ed
+-.Pp
+-The default is
+-.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
+-gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
+ This option only applies to connections using GSSAPI.
+ .It Cm HostbasedAcceptedAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the signature algorithms that will be accepted for hostbased
+ authentication as a list of comma-separated patterns.
+ Alternately if the specified list begins with a
+ .Sq +
+ character, then the specified signature algorithms will be appended to
+-the default set instead of replacing them.
++the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified signature algorithms (including wildcards)
+-will be removed from the default set instead of replacing them.
++will be removed from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified signature algorithms will be placed at
+-the head of the default set.
+-The default for this option is:
+-.Bd -literal -offset 3n
+-ssh-ed25519-cert-v01@openssh.com,
+-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-ecdsa-sha2-nistp384-cert-v01@openssh.com,
+-ecdsa-sha2-nistp521-cert-v01@openssh.com,
+-sk-ssh-ed25519-cert-v01@openssh.com,
+-sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-rsa-sha2-512-cert-v01@openssh.com,
+-rsa-sha2-256-cert-v01@openssh.com,
+-ssh-ed25519,
+-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
++the head of the built-in openssh default set.
+ .Pp
+ The list of available signature algorithms may also be obtained using
+ .Qq ssh -Q HostbasedAcceptedAlgorithms .
+@@ -879,25 +863,14 @@
+ .Ev SSH_AUTH_SOCK
+ environment variable.
+ .It Cm HostKeyAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the host key signature algorithms
+ that the server offers.
+ The default for this option is:
+-.Bd -literal -offset 3n
+-ssh-ed25519-cert-v01@openssh.com,
+-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-ecdsa-sha2-nistp384-cert-v01@openssh.com,
+-ecdsa-sha2-nistp521-cert-v01@openssh.com,
+-sk-ssh-ed25519-cert-v01@openssh.com,
+-sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-rsa-sha2-512-cert-v01@openssh.com,
+-rsa-sha2-256-cert-v01@openssh.com,
+-ssh-ed25519,
+-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
+-.Pp
+ The list of available signature algorithms may also be obtained using
+ .Qq ssh -Q HostKeyAlgorithms .
+ .It Cm IgnoreRhosts
+@@ -1044,20 +1017,25 @@
+ The default is
+ .Cm yes .
+ .It Cm KexAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the available KEX (Key Exchange) algorithms.
+ Multiple algorithms must be comma-separated.
+ Alternately if the specified list begins with a
+ .Sq +
+-character, then the specified algorithms will be appended to the default set
+-instead of replacing them.
++character, then the specified methods will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
++built-in openssh default set.
+ The supported algorithms are:
+ .Pp
+ .Bl -item -compact -offset indent
+@@ -1089,16 +1067,6 @@
+ sntrup761x25519-sha512@openssh.com
+ .El
+ .Pp
+-The default is:
+-.Bd -literal -offset indent
+-sntrup761x25519-sha512@openssh.com,
+-curve25519-sha256,curve25519-sha256@libssh.org,
+-ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,
+-diffie-hellman-group-exchange-sha256,
+-diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,
+-diffie-hellman-group14-sha256
+-.Ed
+-.Pp
+ The list of available key exchange algorithms may also be obtained using
+ .Qq ssh -Q KexAlgorithms .
+ .It Cm ListenAddress
+@@ -1184,21 +1152,26 @@
+ file.
+ This option is intended for debugging and no overrides are enabled by default.
+ .It Cm MACs
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the available MAC (message authentication code) algorithms.
+ The MAC algorithm is used for data integrity protection.
+ Multiple algorithms must be comma-separated.
+ If the specified list begins with a
+ .Sq +
+-character, then the specified algorithms will be appended to the default set
+-instead of replacing them.
++character, then the specified algorithms will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
++built-in openssh default set.
+ .Pp
+ The algorithms that contain
+ .Qq -etm
+@@ -1241,15 +1214,6 @@
+ umac-128-etm@openssh.com
+ .El
+ .Pp
+-The default is:
+-.Bd -literal -offset indent
+-umac-64-etm@openssh.com,umac-128-etm@openssh.com,
+-hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,
+-hmac-sha1-etm@openssh.com,
+-umac-64@openssh.com,umac-128@openssh.com,
+-hmac-sha2-256,hmac-sha2-512,hmac-sha1
+-.Ed
+-.Pp
+ The list of available MAC algorithms may also be obtained using
+ .Qq ssh -Q mac .
+ .It Cm Match
+@@ -1633,36 +1597,25 @@
+ The default is
+ .Cm yes .
+ .It Cm PubkeyAcceptedAlgorithms
++The default is handled system-wide by
++.Xr crypto-policies 7 .
++Information about defaults, how to modify the defaults and how to customize existing policies with sub-policies are present in manual page
++.Xr update-crypto-policies 8 .
++.Pp
+ Specifies the signature algorithms that will be accepted for public key
+ authentication as a list of comma-separated patterns.
+ Alternately if the specified list begins with a
+ .Sq +
+-character, then the specified algorithms will be appended to the default set
+-instead of replacing them.
++character, then the specified algorithms will be appended to the built-in
++openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq -
+ character, then the specified algorithms (including wildcards) will be removed
+-from the default set instead of replacing them.
++from the built-in openssh default set instead of replacing them.
+ If the specified list begins with a
+ .Sq ^
+ character, then the specified algorithms will be placed at the head of the
+-default set.
+-The default for this option is:
+-.Bd -literal -offset 3n
+-ssh-ed25519-cert-v01@openssh.com,
+-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-ecdsa-sha2-nistp384-cert-v01@openssh.com,
+-ecdsa-sha2-nistp521-cert-v01@openssh.com,
+-sk-ssh-ed25519-cert-v01@openssh.com,
+-sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,
+-rsa-sha2-512-cert-v01@openssh.com,
+-rsa-sha2-256-cert-v01@openssh.com,
+-ssh-ed25519,
+-ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,
+-sk-ssh-ed25519@openssh.com,
+-sk-ecdsa-sha2-nistp256@openssh.com,
+-rsa-sha2-512,rsa-sha2-256
+-.Ed
++built-in openssh default set.
+ .Pp
+ The list of available signature algorithms may also be obtained using
+ .Qq ssh -Q PubkeyAcceptedAlgorithms .
+@@ -2131,7 +2084,9 @@
+ .El
+ .Sh SEE ALSO
+ .Xr sftp-server 8 ,
+-.Xr sshd 8
++.Xr sshd 8 ,
++.Xr crypto-policies 7 ,
++.Xr update-crypto-policies 8
+ .Sh AUTHORS
+ .An -nosplit
+ OpenSSH is a derivative of the original and free
diff --git a/openssh-8.0p1-gssapi-keyex.patch b/openssh-8.0p1-gssapi-keyex.patch
new file mode 100644
index 0000000..f3e3f52
--- /dev/null
+++ b/openssh-8.0p1-gssapi-keyex.patch
@@ -0,0 +1,4009 @@
+diff --git a/Makefile.in b/Makefile.in
+index e7549470..b68c1710 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -109,6 +109,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
+ kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \
+ kexgexc.o kexgexs.o \
+ kexsntrup761x25519.o sntrup761.o kexgen.o \
++ kexgssc.o \
+ sftp-realpath.o platform-pledge.o platform-tracing.o platform-misc.o \
+ sshbuf-io.o
+
+@@ -125,7 +126,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \
+ auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
+ auth2-none.o auth2-passwd.o auth2-pubkey.o auth2-pubkeyfile.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 \
+ srclimit.o sftp-server.o sftp-common.o \
+ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \
+@@ -523,7 +523,7 @@ regress-prep:
+ ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
+
+ REGRESSLIBS=libssh.a $(LIBCOMPAT)
+-TESTLIBS=$(LIBS) $(CHANNELLIBS)
++TESTLIBS=$(LIBS) $(CHANNELLIBS) $(GSSLIBS)
+
+ regress/modpipe$(EXEEXT): $(srcdir)/regress/modpipe.c $(REGRESSLIBS)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/modpipe.c \
+diff -up a/auth.c.gsskex b/auth.c
+--- a/auth.c.gsskex 2021-08-20 06:03:49.000000000 +0200
++++ b/auth.c 2021-08-27 12:41:51.262788953 +0200
+@@ -402,7 +402,8 @@ auth_root_allowed(struct ssh *ssh, const
+ case PERMIT_NO_PASSWD:
+ if (strcmp(method, "publickey") == 0 ||
+ strcmp(method, "hostbased") == 0 ||
+- strcmp(method, "gssapi-with-mic") == 0)
++ strcmp(method, "gssapi-with-mic") == 0 ||
++ strcmp(method, "gssapi-keyex") == 0)
+ return 1;
+ break;
+ case PERMIT_FORCED_ONLY:
+@@ -730,97 +731,6 @@ fakepw(void)
+ }
+
+ /*
+- * 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 based on conflation of hostnames and IP addresses.
+- */
+-
+-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) == -1) {
+- debug("getpeername failed: %.100s", strerror(errno));
+- return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop);
+- }
+- return xstrdup(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.
+diff --git a/auth2-gss.c b/auth2-gss.c
+index 9351e042..d6446c0c 100644
+--- a/auth2-gss.c
++++ b/auth2-gss.c
+@@ -1,7 +1,7 @@
+ /* $OpenBSD: auth2-gss.c,v 1.33 2021/12/19 22:12:07 djm Exp $ */
+
+ /*
+- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -54,6 +54,48 @@ static int input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh);
+ 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 r, authenticated = 0;
++ struct sshbuf *b = NULL;
++ gss_buffer_desc mic, gssbuf;
++ u_char *p;
++ size_t len;
++
++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal_fr(r, "parsing");
++
++ if ((b = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++
++ mic.value = p;
++ mic.length = len;
++
++ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service,
++ "gssapi-keyex", ssh->kex->session_id);
++
++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
++ fatal_f("sshbuf_mutable_ptr failed");
++ 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, 1));
++
++ 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 +302,8 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh)
+ if ((r = sshpkt_get_end(ssh)) != 0)
+ fatal_fr(r, "parse packet");
+
+- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user));
++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user,
++ authctxt->pw, 1));
+
+ if ((!use_privsep || mm_is_monitor()) &&
+ (displayname = ssh_gssapi_displayname()) != NULL)
+@@ -306,7 +349,8 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
+ 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, 0));
+ else
+ logit("GSSAPI MIC check failed");
+
+@@ -326,6 +370,12 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh)
+ return 0;
+ }
+
++Authmethod method_gsskeyex = {
++ "gssapi-keyex",
++ userauth_gsskeyex,
++ &options.gss_authentication
++};
++
+ Authmethod method_gssapi = {
+ "gssapi-with-mic",
+ NULL,
+diff --git a/auth2.c b/auth2.c
+index 0e776224..1c217268 100644
+--- a/auth2.c
++++ b/auth2.c
+@@ -73,6 +73,7 @@ extern Authmethod method_passwd;
+ extern Authmethod method_kbdint;
+ extern Authmethod method_hostbased;
+ #ifdef GSSAPI
++extern Authmethod method_gsskeyex;
+ extern Authmethod method_gssapi;
+ #endif
+
+@@ -80,6 +81,7 @@ Authmethod *authmethods[] = {
+ &method_none,
+ &method_pubkey,
+ #ifdef GSSAPI
++ &method_gsskeyex,
+ &method_gssapi,
+ #endif
+ &method_passwd,
+diff --git a/canohost.c b/canohost.c
+index abea9c6e..8e81b519 100644
+--- a/canohost.c
++++ b/canohost.c
+@@ -35,6 +35,99 @@
+ #include "canohost.h"
+ #include "misc.h"
+
++/*
++ * 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?)
++ */
++
++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) == -1) {
++ debug("getpeername failed: %.100s", strerror(errno));
++ return xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(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 xstrdup(ntop);
++ }
++ return xstrdup(name);
++}
++
+ void
+ ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
+ {
+diff --git a/canohost.h b/canohost.h
+index 26d62855..0cadc9f1 100644
+--- a/canohost.h
++++ b/canohost.h
+@@ -15,6 +15,9 @@
+ #ifndef _CANOHOST_H
+ #define _CANOHOST_H
+
++struct ssh;
++
++char *remote_hostname(struct ssh *);
+ char *get_peer_ipaddr(int);
+ int get_peer_port(int);
+ char *get_local_ipaddr(int);
+diff --git a/clientloop.c b/clientloop.c
+index ebd0dbca..1bdac6a4 100644
+--- a/clientloop.c
++++ b/clientloop.c
+@@ -112,6 +112,10 @@
+ #include "ssherr.h"
+ #include "hostfile.h"
+
++#ifdef GSSAPI
++#include "ssh-gss.h"
++#endif
++
+ /* Permitted RSA signature algorithms for UpdateHostkeys proofs */
+ #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256"
+
+@@ -1379,6 +1383,14 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
+ /* Do channel operations. */
+ channel_after_poll(ssh, pfd, npfd_active);
+
++#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. */
+ if (conn_in_ready)
+ client_process_net_input(ssh);
+diff --git a/configure.ac b/configure.ac
+index b689db4b..efafb6bd 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -674,6 +674,30 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+ [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 <Security/AuthSession.h>],
++ [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 <Kerberos/Kerberos.h>],
++ [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 --git a/gss-genr.c b/gss-genr.c
+index d56257b4..763a63ff 100644
+--- a/gss-genr.c
++++ b/gss-genr.c
+@@ -1,7 +1,7 @@
+ /* $OpenBSD: gss-genr.c,v 1.28 2021/01/27 10:05:28 djm Exp $ */
+
+ /*
+- * Copyright (c) 2001-2007 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
+@@ -41,9 +41,33 @@
+ #include "sshbuf.h"
+ #include "log.h"
+ #include "ssh2.h"
++#include "cipher.h"
++#include "sshkey.h"
++#include "kex.h"
++#include "digest.h"
++#include "packet.h"
+
+ #include "ssh-gss.h"
+
++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(void) {
++ return (gss_enc2oid != NULL);
++}
++
+ /* sshbuf_get for gss_buffer_desc */
+ int
+ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
+@@ -62,6 +86,159 @@ ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g)
+ return 0;
+ }
+
++/* sshpkt_get of gss_buffer_desc */
++int
++ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *ssh, gss_buffer_desc *g)
++{
++ int r;
++ u_char *p;
++ size_t len;
++
++ if ((r = sshpkt_get_string(ssh, &p, &len)) != 0)
++ return r;
++ g->value = p;
++ g->length = len;
++ return 0;
++}
++
++/*
++ * Return a list of the gss-group1-sha1 mechanisms supported by this program
++ *
++ * We test mechanisms to ensure that we can use them, to avoid starting
++ * a key exchange with a bad mechanism
++ */
++
++char *
++ssh_gssapi_client_mechanisms(const char *host, const char *client,
++ const char *kex) {
++ gss_OID_set gss_supported = NULL;
++ 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, kex);
++}
++
++char *
++ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check,
++ const char *host, const char *client, const char *kex) {
++ struct sshbuf *buf = NULL;
++ size_t i;
++ int r = SSH_ERR_ALLOC_FAIL;
++ int oidpos, enclen;
++ char *mechs, *encoded;
++ u_char digest[SSH_DIGEST_MAX_LENGTH];
++ char deroid[2];
++ struct ssh_digest_ctx *md = NULL;
++ char *s, *cp, *p;
++
++ 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_f("sshbuf_new failed");
++
++ 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)) {
++
++ deroid[0] = SSH_GSS_OIDTYPE;
++ deroid[1] = gss_supported->elements[i].length;
++
++ if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL ||
++ (r = ssh_digest_update(md, deroid, 2)) != 0 ||
++ (r = ssh_digest_update(md,
++ gss_supported->elements[i].elements,
++ gss_supported->elements[i].length)) != 0 ||
++ (r = ssh_digest_final(md, digest, sizeof(digest))) != 0)
++ fatal_fr(r, "digest failed");
++ ssh_digest_free(md);
++ md = NULL;
++
++ encoded = xmalloc(ssh_digest_bytes(SSH_DIGEST_MD5)
++ * 2);
++ enclen = __b64_ntop(digest,
++ ssh_digest_bytes(SSH_DIGEST_MD5), encoded,
++ ssh_digest_bytes(SSH_DIGEST_MD5) * 2);
++
++ cp = strncpy(s, kex, strlen(kex));
++ for ((p = strsep(&cp, ",")); p && *p != '\0';
++ (p = strsep(&cp, ","))) {
++ if (sshbuf_len(buf) != 0 &&
++ (r = sshbuf_put_u8(buf, ',')) != 0)
++ fatal_fr(r, "sshbuf_put_u8 error");
++ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 ||
++ (r = sshbuf_put(buf, encoded, enclen)) != 0)
++ fatal_fr(r, "sshbuf_put error");
++ }
++
++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]);
++ gss_enc2oid[oidpos].encoded = encoded;
++ oidpos++;
++ }
++ }
++ free(s);
++ gss_enc2oid[oidpos].oid = NULL;
++ gss_enc2oid[oidpos].encoded = NULL;
++
++ if ((mechs = sshbuf_dup_string(buf)) == NULL)
++ fatal_f("sshbuf_dup_string failed");
++
++ 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;
++
++#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++;
++
++ if (gss_enc2oid[i].oid != NULL && ctx != NULL)
++ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid);
++
++ return gss_enc2oid[i].oid;
++}
++
+ /* Check that the OID in a data stream matches that in the context */
+ int
+ ssh_gssapi_check_oid(Gssctxt *ctx, void *data, size_t len)
+@@ -218,7 +398,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int deleg_creds, gss_buffer_desc *recv_tok,
+ }
+
+ 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);
+
+@@ -247,9 +427,43 @@ ssh_gssapi_import_name(Gssctxt *ctx, const char *host)
+ return (ctx->major);
+ }
+
++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 +471,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash)
+ 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, const struct sshbuf *session_id)
+@@ -273,11 +500,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service,
+ }
+
+ 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 +519,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
+ 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 +532,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host)
+ 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 --git a/gss-serv-krb5.c b/gss-serv-krb5.c
+index a151bc1e..8d2b677f 100644
+--- a/gss-serv-krb5.c
++++ b/gss-serv-krb5.c
+@@ -1,7 +1,7 @@
+ /* $OpenBSD: gss-serv-krb5.c,v 1.9 2018/07/09 21:37:55 markus Exp $ */
+
+ /*
+- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
++ * Copyright (c) 2001-2007 Simon Wilkinson. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
+ krb5_error_code problem;
+ krb5_principal princ;
+ OM_uint32 maj_status, min_status;
+- int len;
++ const char *new_ccname, *new_cctype;
+ const char *errmsg;
+
+ if (client->creds == NULL) {
+@@ -180,11 +180,26 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
+ return;
+ }
+
+- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache));
++ new_cctype = krb5_cc_get_type(krb_context, ccache);
++ new_ccname = krb5_cc_get_name(krb_context, ccache);
++
+ client->store.envvar = "KRB5CCNAME";
+- len = strlen(client->store.filename) + 6;
+- client->store.envval = xmalloc(len);
+- snprintf(client->store.envval, len, "FILE:%s", client->store.filename);
++#ifdef USE_CCAPI
++ xasprintf(&client->store.envval, "API:%s", new_ccname);
++ client->store.filename = NULL;
++#else
++ if (new_ccname[0] == ':')
++ new_ccname++;
++ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname);
++ if (strcmp(new_cctype, "DIR") == 0) {
++ char *p;
++ p = strrchr(client->store.envval, '/');
++ if (p)
++ *p = '\0';
++ }
++ if ((strcmp(new_cctype, "FILE") == 0) || (strcmp(new_cctype, "DIR") == 0))
++ client->store.filename = xstrdup(new_ccname);
++#endif
+
+ #ifdef USE_PAM
+ if (options.use_pam)
+@@ -193,9 +208,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client)
+
+ krb5_cc_close(krb_context, ccache);
+
++ client->store.data = krb_context;
++
+ return;
+ }
+
++int
++ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store,
++ ssh_gssapi_client *client)
++{
++ krb5_ccache ccache = NULL;
++ krb5_principal principal = NULL;
++ char *name = NULL;
++ krb5_error_code problem;
++ OM_uint32 maj_status, min_status;
++
++ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) {
++ logit("krb5_cc_resolve(): %.100s",
++ krb5_get_err_text(krb_context, problem));
++ return 0;
++ }
++
++ /* Find out who the principal in this cache is */
++ if ((problem = krb5_cc_get_principal(krb_context, ccache,
++ &principal))) {
++ logit("krb5_cc_get_principal(): %.100s",
++ krb5_get_err_text(krb_context, problem));
++ krb5_cc_close(krb_context, ccache);
++ return 0;
++ }
++
++ if ((problem = krb5_unparse_name(krb_context, principal, &name))) {
++ logit("krb5_unparse_name(): %.100s",
++ krb5_get_err_text(krb_context, problem));
++ krb5_free_principal(krb_context, principal);
++ krb5_cc_close(krb_context, ccache);
++ return 0;
++ }
++
++
++ if (strcmp(name,client->exportedname.value)!=0) {
++ debug("Name in local credentials cache differs. Not storing");
++ krb5_free_principal(krb_context, principal);
++ krb5_cc_close(krb_context, ccache);
++ krb5_free_unparsed_name(krb_context, name);
++ return 0;
++ }
++ krb5_free_unparsed_name(krb_context, name);
++
++ /* Name matches, so lets get on with it! */
++
++ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) {
++ logit("krb5_cc_initialize(): %.100s",
++ krb5_get_err_text(krb_context, problem));
++ krb5_free_principal(krb_context, principal);
++ krb5_cc_close(krb_context, ccache);
++ return 0;
++ }
++
++ krb5_free_principal(krb_context, principal);
++
++ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds,
++ ccache))) {
++ logit("gss_krb5_copy_ccache() failed. Sorry!");
++ krb5_cc_close(krb_context, ccache);
++ return 0;
++ }
++
++ return 1;
++}
++
+ ssh_gssapi_mech gssapi_kerberos_mech = {
+ "toWM5Slw5Ew8Mqkay+al2g==",
+ "Kerberos",
+@@ -203,7 +285,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = {
+ NULL,
+ &ssh_gssapi_krb5_userok,
+ NULL,
+- &ssh_gssapi_krb5_storecreds
++ &ssh_gssapi_krb5_storecreds,
++ &ssh_gssapi_krb5_updatecreds
+ };
+
+ #endif /* KRB5 */
+diff --git a/gss-serv.c b/gss-serv.c
+index ab3a15f0..6ce56e92 100644
+--- a/gss-serv.c
++++ b/gss-serv.c
+@@ -1,7 +1,7 @@
+ /* $OpenBSD: gss-serv.c,v 1.32 2020/03/13 03:17:07 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
+@@ -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, 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;
+@@ -140,6 +142,29 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss_OID oid)
+ return (ssh_gssapi_acquire_cred(*ctx));
+ }
+
++/* Unprivileged */
++char *
++ssh_gssapi_server_mechanisms(void) {
++ if (supported_oids == NULL)
++ ssh_gssapi_prepare_supported_oids();
++ return (ssh_gssapi_kex_mechs(supported_oids,
++ &ssh_gssapi_server_check_mech, NULL, NULL,
++ options.gss_kex_algorithms));
++}
++
++/* 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 +175,9 @@ ssh_gssapi_supported_oids(gss_OID_set *oidset)
+ 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 +303,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);
++ }
+
+- gss_buffer_desc ename;
++ ctx->major = gss_compare_name(&ctx->minor, client->name,
++ new_name, &equal);
++
++ if (GSS_ERROR(ctx->major)) {
++ ssh_gssapi_error(ctx);
++ return (ctx->major);
++ }
++
++ if (!equal) {
++ debug("Rekeyed credentials have different name");
++ return GSS_S_COMPLETE;
++ }
++
++ debug("Marking rekeyed credentials for export");
++
++ gss_release_name(&ctx->minor, &client->name);
++ gss_release_cred(&ctx->minor, &client->creds);
++ client->name = new_name;
++ client->creds = ctx->client_creds;
++ ctx->client_creds = GSS_C_NO_CREDENTIAL;
++ client->updated = 1;
++ return GSS_S_COMPLETE;
++ }
+
+ client->mech = NULL;
+
+@@ -292,6 +359,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+ 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 +383,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+ 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 +395,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client)
+ 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_f("krb5_cc_resolve(): %.100s",
++ krb5_get_err_text(gssapi_client.store.data, problem));
++ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) {
++ debug_f("krb5_cc_destroy(): %.100s",
++ krb5_get_err_text(gssapi_client.store.data, problem));
++ } else {
++ krb5_free_context(gssapi_client.store.data);
++ gssapi_client.store.data = NULL;
++ }
+ }
+ }
+
+@@ -356,19 +441,23 @@ ssh_gssapi_do_child(char ***envp, u_int *envsizep)
+
+ /* Privileged */
+ int
+-ssh_gssapi_userok(char *user)
++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");
+ 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 +471,90 @@ ssh_gssapi_userok(char *user)
+ return (0);
+ }
+
+-/* Privileged */
+-OM_uint32
+-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
++/* These bits are only used for rekeying. The unpriviledged child is running
++ * as the user, the monitor is root.
++ *
++ * In the child, we want to :
++ * *) Ask the monitor to store our credentials into the store we specify
++ * *) If it succeeds, maybe do a PAM update
++ */
++
++/* Stuff for PAM */
++
++#ifdef USE_PAM
++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg,
++ struct pam_response **resp, void *data)
+ {
+- ctx->major = gss_verify_mic(&ctx->minor, ctx->context,
+- gssbuf, gssmic, NULL);
++ return (PAM_CONV_ERR);
++}
++#endif
+
+- return (ctx->major);
++void
++ssh_gssapi_rekey_creds(void) {
++ int ok;
++#ifdef USE_PAM
++ int ret;
++ pam_handle_t *pamh = NULL;
++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL};
++ char *envstr;
++#endif
++
++ if (gssapi_client.store.filename == NULL &&
++ gssapi_client.store.envval == NULL &&
++ gssapi_client.store.envvar == NULL)
++ return;
++
++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store));
++
++ if (!ok)
++ return;
++
++ debug("Rekeyed credentials stored successfully");
++
++ /* Actually managing to play with the ssh pam stack from here will
++ * be next to impossible. In any case, we may want different options
++ * for rekeying. So, use our own :)
++ */
++#ifdef USE_PAM
++ if (!use_privsep) {
++ debug("Not even going to try and do PAM with privsep disabled");
++ return;
++ }
++
++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name,
++ &pamconv, &pamh);
++ if (ret)
++ return;
++
++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar,
++ gssapi_client.store.envval);
++
++ ret = pam_putenv(pamh, envstr);
++ if (!ret)
++ pam_setcred(pamh, PAM_REINITIALIZE_CRED);
++ pam_end(pamh, PAM_SUCCESS);
++#endif
++}
++
++int
++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) {
++ int ok = 0;
++
++ /* Check we've got credentials to store */
++ if (!gssapi_client.updated)
++ return 0;
++
++ gssapi_client.updated = 0;
++
++ temporarily_use_uid(gssapi_client.store.owner);
++ if (gssapi_client.mech && gssapi_client.mech->updatecreds)
++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client);
++ else
++ debug("No update function for this mechanism");
++
++ restore_uid();
++
++ return ok;
+ }
+
+ /* Privileged */
+diff --git a/kex.c b/kex.c
+index ce85f043..574c7609 100644
+--- a/kex.c
++++ b/kex.c
+@@ -57,6 +57,10 @@
+ #include "digest.h"
+ #include "xmalloc.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 *);
+@@ -115,15 +120,28 @@ static const struct kexalg kexalgs[] = {
+ #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */
+ { NULL, 0, -1, -1},
+ };
++static const struct kexalg gss_kexalgs[] = {
++#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 },
++ { 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, 0, -1, -1},
++};
+
+-char *
+-kex_alg_list(char sep)
++static char *
++kex_alg_list_internal(char sep, const struct kexalg *algs)
+ {
+ char *ret = NULL, *tmp;
+ size_t nlen, rlen = 0;
+ const struct kexalg *k;
+
+- for (k = kexalgs; k->name != NULL; k++) {
++ for (k = algs; k->name != NULL; k++) {
+ if (ret != NULL)
+ ret[rlen++] = sep;
+ nlen = strlen(k->name);
+@@ -138,6 +156,18 @@ kex_alg_list(char sep)
+ return ret;
+ }
+
++char *
++kex_alg_list(char sep)
++{
++ return kex_alg_list_internal(sep, kexalgs);
++}
++
++char *
++kex_gss_alg_list(char sep)
++{
++ return kex_alg_list_internal(sep, gss_kexalgs);
++}
++
+ static const struct kexalg *
+ kex_alg_by_name(const char *name)
+ {
+@@ -147,6 +177,10 @@ kex_alg_by_name(const char *name)
+ if (strcmp(k->name, name) == 0)
+ return k;
+ }
++ for (k = gss_kexalgs; k->name != NULL; k++) {
++ if (strncmp(k->name, name, strlen(k->name)) == 0)
++ return k;
++ }
+ return NULL;
+ }
+
+@@ -315,6 +349,29 @@ kex_assemble_names(char **listp, const char *def, const char *all)
+ return r;
+ }
+
++/* Validate GSS KEX method name list */
++int
++kex_gss_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;
++}
++
+ /*
+ * Fill out a proposal array with dynamically allocated values, which may
+ * be modified as required for compatibility reasons.
+@@ -698,6 +755,9 @@ kex_free(struct kex *kex)
+ sshbuf_free(kex->server_version);
+ sshbuf_free(kex->client_pub);
+ sshbuf_free(kex->session_id);
++#ifdef GSSAPI
++ free(kex->gss_host);
++#endif /* GSSAPI */
+ sshbuf_free(kex->initial_sig);
+ sshkey_free(kex->initial_hostkey);
+ free(kex->failed_choice);
+diff --git a/kex.h b/kex.h
+index a5ae6ac0..fe714141 100644
+--- a/kex.h
++++ b/kex.h
+@@ -102,6 +102,15 @@ enum kex_exchange {
+ KEX_ECDH_SHA2,
+ KEX_C25519_SHA256,
+ KEX_KEM_SNTRUP761X25519_SHA512,
++#ifdef GSSAPI
++ KEX_GSS_GRP1_SHA1,
++ KEX_GSS_GRP14_SHA1,
++ KEX_GSS_GRP14_SHA256,
++ KEX_GSS_GRP16_SHA512,
++ KEX_GSS_GEX_SHA1,
++ KEX_GSS_NISTP256_SHA256,
++ KEX_GSS_C25519_SHA256,
++#endif
+ KEX_MAX
+ };
+
+@@ -153,6 +162,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 *failed_choice;
+ int (*verify_host_key)(struct sshkey *, struct ssh *);
+ struct sshkey *(*load_host_public_key)(int, int, struct ssh *);
+@@ -174,8 +189,10 @@ struct kex {
+
+ int kex_names_valid(const char *);
+ char *kex_alg_list(char);
++char *kex_gss_alg_list(char);
+ char *kex_names_cat(const char *, const char *);
+ int kex_assemble_names(char **, const char *, const char *);
++int kex_gss_names_valid(const char *);
+ void kex_proposal_populate_entries(struct ssh *, char *prop[PROPOSAL_MAX],
+ const char *, const char *, const char *, const char *, const char *);
+ void kex_proposal_free_entries(char *prop[PROPOSAL_MAX]);
+@@ -202,6 +219,12 @@ int kexgex_client(struct ssh *);
+ int kexgex_server(struct ssh *);
+ int kex_gen_client(struct ssh *);
+ int kex_gen_server(struct ssh *);
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++int kexgssgex_client(struct ssh *);
++int kexgssgex_server(struct ssh *);
++int kexgss_client(struct ssh *);
++int kexgss_server(struct ssh *);
++#endif
+
+ int kex_dh_keypair(struct kex *);
+ int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **,
+@@ -234,6 +257,12 @@ int kexgex_hash(int, const struct sshbuf *, const struct sshbuf *,
+ const BIGNUM *, const u_char *, size_t,
+ u_char *, size_t *);
+
++int kex_gen_hash(int hash_alg, const struct sshbuf *client_version,
++ const struct sshbuf *server_version, const struct sshbuf *client_kexinit,
++ const struct sshbuf *server_kexinit, const struct sshbuf *server_host_key_blob,
++ const struct sshbuf *client_pub, const struct sshbuf *server_pub,
++ const struct sshbuf *shared_secret, u_char *hash, size_t *hashlen);
++
+ void kexc25519_keygen(u_char key[CURVE25519_SIZE], u_char pub[CURVE25519_SIZE])
+ __attribute__((__bounded__(__minbytes__, 1, CURVE25519_SIZE)))
+ __attribute__((__bounded__(__minbytes__, 2, CURVE25519_SIZE)));
+diff --git a/kexdh.c b/kexdh.c
+index 67133e33..edaa4676 100644
+--- a/kexdh.c
++++ b/kexdh.c
+@@ -48,13 +48,23 @@ kex_dh_keygen(struct kex *kex)
+ {
+ switch (kex->kex_type) {
+ case KEX_DH_GRP1_SHA1:
++#ifdef GSSAPI
++ case KEX_GSS_GRP1_SHA1:
++#endif
+ kex->dh = dh_new_group1();
+ break;
+ case KEX_DH_GRP14_SHA1:
+ case KEX_DH_GRP14_SHA256:
++#ifdef GSSAPI
++ case KEX_GSS_GRP14_SHA1:
++ case KEX_GSS_GRP14_SHA256:
++#endif
+ kex->dh = dh_new_group14();
+ break;
+ case KEX_DH_GRP16_SHA512:
++#ifdef GSSAPI
++ case KEX_GSS_GRP16_SHA512:
++#endif
+ kex->dh = dh_new_group16();
+ break;
+ case KEX_DH_GRP18_SHA512:
+diff --git a/kexgen.c b/kexgen.c
+index 69348b96..c0e8c2f4 100644
+--- a/kexgen.c
++++ b/kexgen.c
+@@ -44,7 +44,7 @@
+ static int input_kex_gen_init(int, u_int32_t, struct ssh *);
+ static int input_kex_gen_reply(int type, u_int32_t seq, struct ssh *ssh);
+
+-static int
++int
+ kex_gen_hash(
+ int hash_alg,
+ const struct sshbuf *client_version,
+diff --git a/kexgssc.c b/kexgssc.c
+new file mode 100644
+index 00000000..f6e1405e
+--- /dev/null
++++ b/kexgssc.c
+@@ -0,0 +1,600 @@
++/*
++ * 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"
++
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++
++#include "includes.h"
++
++#include <openssl/crypto.h>
++#include <openssl/bn.h>
++
++#include <string.h>
++
++#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 "ssherr.h"
++
++#include "ssh-gss.h"
++
++int
++kexgss_client(struct ssh *ssh)
++{
++ struct kex *kex = ssh->kex;
++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
++ recv_tok = GSS_C_EMPTY_BUFFER,
++ gssbuf, msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
++ Gssctxt *ctxt;
++ OM_uint32 maj_status, min_status, ret_flags;
++ struct sshbuf *server_blob = NULL;
++ struct sshbuf *shared_secret = NULL;
++ struct sshbuf *server_host_key_blob = NULL;
++ struct sshbuf *empty = NULL;
++ u_char *msg;
++ int type = 0;
++ int first = 1;
++ u_char hash[SSH_DIGEST_MAX_LENGTH];
++ size_t hashlen;
++ u_char c;
++ 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");
++
++ /* Step 1 */
++ switch (kex->kex_type) {
++ case KEX_GSS_GRP1_SHA1:
++ case KEX_GSS_GRP14_SHA1:
++ case KEX_GSS_GRP14_SHA256:
++ case KEX_GSS_GRP16_SHA512:
++ r = kex_dh_keypair(kex);
++ break;
++ case KEX_GSS_NISTP256_SHA256:
++ r = kex_ecdh_keypair(kex);
++ break;
++ case KEX_GSS_C25519_SHA256:
++ r = kex_c25519_keypair(kex);
++ break;
++ default:
++ fatal_f("Unexpected KEX type %d", kex->kex_type);
++ }
++ if (r != 0)
++ return r;
++
++ token_ptr = GSS_C_NO_BUFFER;
++
++ do {
++ 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)) {
++ /* XXX Useles code: Missing send? */
++ if (send_tok.length != 0) {
++ if ((r = sshpkt_start(ssh,
++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value,
++ send_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ fatal("gss_init_context failed");
++ }
++
++ /* If we've got an old receive buffer get rid of it */
++ if (token_ptr != GSS_C_NO_BUFFER)
++ gss_release_buffer(&min_status, &recv_tok);
++
++ 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) {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value,
++ send_tok.length)) != 0 ||
++ (r = sshpkt_put_stringb(ssh, kex->client_pub)) != 0)
++ fatal("failed to construct packet: %s", ssh_err(r));
++ first = 0;
++ } else {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value,
++ send_tok.length)) != 0)
++ fatal("failed to construct packet: %s", ssh_err(r));
++ }
++ if ((r = sshpkt_send(ssh)) != 0)
++ fatal("failed to send packet: %s", ssh_err(r));
++ gss_release_buffer(&min_status, &send_tok);
++
++ /* If we've sent them data, they should reply */
++ do {
++ type = ssh_packet_read(ssh);
++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
++ debug("Received KEXGSS_HOSTKEY");
++ if (server_host_key_blob)
++ fatal("Server host key received more than once");
++ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
++ fatal("Failed to read server host key: %s", ssh_err(r));
++ }
++ } 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");
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("Failed to read token: %s", ssh_err(r));
++ break;
++ case SSH2_MSG_KEXGSS_COMPLETE:
++ debug("Received GSSAPI_COMPLETE");
++ if (msg_tok.value != NULL)
++ fatal("Received GSSAPI_COMPLETE twice?");
++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &msg_tok)) != 0)
++ fatal("Failed to read message: %s", ssh_err(r));
++
++ /* Is there a token included? */
++ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ if (c) {
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
++ ssh, &recv_tok)) != 0)
++ fatal("Failed to read token: %s", ssh_err(r));
++ /* If we're already complete - protocol error */
++ if (maj_status == GSS_S_COMPLETE)
++ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
++ } else {
++ /* No token included */
++ if (maj_status != GSS_S_COMPLETE)
++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
++ }
++ if ((r = sshpkt_get_end(ssh)) != 0) {
++ fatal("Expecting end of packet.");
++ }
++ break;
++ case SSH2_MSG_KEXGSS_ERROR:
++ debug("Received Error");
++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt_get failed: %s", ssh_err(r));
++ fatal("GSSAPI Error: \n%.400s", msg);
++ default:
++ sshpkt_disconnect(ssh, "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 server_blob and msg_tok
++ */
++
++ if (type != SSH2_MSG_KEXGSS_COMPLETE)
++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
++
++ /* compute shared secret */
++ switch (kex->kex_type) {
++ case KEX_GSS_GRP1_SHA1:
++ case KEX_GSS_GRP14_SHA1:
++ case KEX_GSS_GRP14_SHA256:
++ case KEX_GSS_GRP16_SHA512:
++ r = kex_dh_dec(kex, server_blob, &shared_secret);
++ break;
++ case KEX_GSS_C25519_SHA256:
++ if (sshbuf_ptr(server_blob)[sshbuf_len(server_blob)] & 0x80)
++ fatal("The received key has MSB of last octet set!");
++ r = kex_c25519_dec(kex, server_blob, &shared_secret);
++ break;
++ case KEX_GSS_NISTP256_SHA256:
++ if (sshbuf_len(server_blob) != 65)
++ fatal("The received NIST-P256 key did not match"
++ "expected length (expected 65, got %zu)", sshbuf_len(server_blob));
++
++ if (sshbuf_ptr(server_blob)[0] != POINT_CONVERSION_UNCOMPRESSED)
++ fatal("The received NIST-P256 key does not have first octet 0x04");
++
++ r = kex_ecdh_dec(kex, server_blob, &shared_secret);
++ break;
++ default:
++ r = SSH_ERR_INVALID_ARGUMENT;
++ break;
++ }
++ if (r != 0)
++ goto out;
++
++ if ((empty = sshbuf_new()) == NULL) {
++ r = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ hashlen = sizeof(hash);
++ if ((r = kex_gen_hash(
++ kex->hash_alg,
++ kex->client_version,
++ kex->server_version,
++ kex->my,
++ kex->peer,
++ (server_host_key_blob ? server_host_key_blob : empty),
++ kex->client_pub,
++ server_blob,
++ shared_secret,
++ hash, &hashlen)) != 0)
++ fatal_f("Unexpected KEX type %d", 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)))
++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
++
++ gss_release_buffer(&min_status, &msg_tok);
++
++ 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);
++
++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
++ r = kex_send_newkeys(ssh);
++
++out:
++ explicit_bzero(hash, sizeof(hash));
++ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key));
++ sshbuf_free(empty);
++ sshbuf_free(server_host_key_blob);
++ sshbuf_free(server_blob);
++ sshbuf_free(shared_secret);
++ sshbuf_free(kex->client_pub);
++ kex->client_pub = NULL;
++ return r;
++}
++
++int
++kexgssgex_client(struct ssh *ssh)
++{
++ struct kex *kex = ssh->kex;
++ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER,
++ recv_tok = GSS_C_EMPTY_BUFFER, gssbuf,
++ msg_tok = GSS_C_EMPTY_BUFFER, *token_ptr;
++ Gssctxt *ctxt;
++ OM_uint32 maj_status, min_status, ret_flags;
++ struct sshbuf *shared_secret = NULL;
++ BIGNUM *p = NULL;
++ BIGNUM *g = NULL;
++ struct sshbuf *buf = NULL;
++ struct sshbuf *server_host_key_blob = NULL;
++ struct sshbuf *server_blob = NULL;
++ BIGNUM *dh_server_pub = NULL;
++ u_char *msg;
++ int type = 0;
++ int first = 1;
++ u_char hash[SSH_DIGEST_MAX_LENGTH];
++ size_t hashlen;
++ const BIGNUM *pub_key, *dh_p, *dh_g;
++ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
++ struct sshbuf *empty = NULL;
++ u_char c;
++ 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");
++
++ debug("Doing group exchange");
++ nbits = dh_estimate(kex->dh_need * 8);
++
++ kex->min = DH_GRP_MIN;
++ kex->max = DH_GRP_MAX;
++ kex->nbits = nbits;
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUPREQ)) != 0 ||
++ (r = sshpkt_put_u32(ssh, min)) != 0 ||
++ (r = sshpkt_put_u32(ssh, nbits)) != 0 ||
++ (r = sshpkt_put_u32(ssh, max)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("Failed to construct a packet: %s", ssh_err(r));
++
++ if ((r = ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0)
++ fatal("Error: %s", ssh_err(r));
++
++ if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
++ (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("shpkt_get_bignum2 failed: %s", ssh_err(r));
++
++ 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);
++
++ if ((kex->dh = dh_new_group(g, p)) == NULL)
++ fatal("dn_new_group() failed");
++ p = g = NULL; /* belong to kex->dh now */
++
++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
++ goto out;
++ DH_get0_key(kex->dh, &pub_key, NULL);
++
++ 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)) {
++ /* XXX Useles code: Missing send? */
++ if (send_tok.length != 0) {
++ if ((r = sshpkt_start(ssh,
++ SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value,
++ send_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ fatal("gss_init_context failed");
++ }
++
++ /* If we've got an old receive buffer get rid of it */
++ if (token_ptr != GSS_C_NO_BUFFER)
++ gss_release_buffer(&min_status, &recv_tok);
++
++ 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) {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_INIT)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value,
++ send_tok.length)) != 0 ||
++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ first = 0;
++ } else {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh,send_tok.value,
++ send_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ if ((r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt_send failed: %s", ssh_err(r));
++ gss_release_buffer(&min_status, &send_tok);
++
++ /* If we've sent them data, they should reply */
++ do {
++ type = ssh_packet_read(ssh);
++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
++ debug("Received KEXGSS_HOSTKEY");
++ if (server_host_key_blob)
++ fatal("Server host key received more than once");
++ if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ } 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");
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ break;
++ case SSH2_MSG_KEXGSS_COMPLETE:
++ debug("Received GSSAPI_COMPLETE");
++ if (msg_tok.value != NULL)
++ fatal("Received GSSAPI_COMPLETE twice?");
++ if ((r = sshpkt_getb_froms(ssh, &server_blob)) != 0 ||
++ (r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &msg_tok)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ /* Is there a token included? */
++ if ((r = sshpkt_get_u8(ssh, &c)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ if (c) {
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(
++ ssh, &recv_tok)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ /* If we're already complete - protocol error */
++ if (maj_status == GSS_S_COMPLETE)
++ sshpkt_disconnect(ssh, "Protocol error: received token when complete");
++ } else {
++ /* No token included */
++ if (maj_status != GSS_S_COMPLETE)
++ sshpkt_disconnect(ssh, "Protocol error: did not receive final token");
++ }
++ break;
++ case SSH2_MSG_KEXGSS_ERROR:
++ debug("Received Error");
++ if ((r = sshpkt_get_u32(ssh, &maj_status)) != 0 ||
++ (r = sshpkt_get_u32(ssh, &min_status)) != 0 ||
++ (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 ||
++ (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* lang tag */
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ fatal("GSSAPI Error: \n%.400s", msg);
++ default:
++ sshpkt_disconnect(ssh, "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 */
++ if ((buf = sshbuf_new()) == NULL ||
++ (r = sshbuf_put_stringb(buf, server_blob)) != 0 ||
++ (r = sshbuf_get_bignum2(buf, &dh_server_pub)) != 0)
++ goto out;
++ sshbuf_free(buf);
++ buf = NULL;
++
++ if ((shared_secret = sshbuf_new()) == NULL) {
++ r = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
++ goto out;
++ if ((empty = sshbuf_new()) == NULL) {
++ r = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
++ hashlen = sizeof(hash);
++ if ((r = kexgex_hash(
++ kex->hash_alg,
++ kex->client_version,
++ kex->server_version,
++ kex->my,
++ kex->peer,
++ (server_host_key_blob ? server_host_key_blob : empty),
++ kex->min, kex->nbits, kex->max,
++ dh_p, dh_g,
++ pub_key,
++ dh_server_pub,
++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
++ hash, &hashlen)) != 0)
++ fatal("Failed to calculate hash: %s", ssh_err(r));
++
++ 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)))
++ sshpkt_disconnect(ssh, "Hash's MIC didn't verify");
++
++ gss_release_buffer(&min_status, &msg_tok);
++
++ 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 */
++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
++ r = kex_send_newkeys(ssh);
++out:
++ sshbuf_free(buf);
++ sshbuf_free(server_blob);
++ sshbuf_free(empty);
++ explicit_bzero(hash, sizeof(hash));
++ DH_free(kex->dh);
++ kex->dh = NULL;
++ BN_clear_free(dh_server_pub);
++ sshbuf_free(shared_secret);
++ sshbuf_free(server_host_key_blob);
++ return r;
++}
++
++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
+diff --git a/kexgsss.c b/kexgsss.c
+new file mode 100644
+index 00000000..60bc02de
+--- /dev/null
++++ b/kexgsss.c
+@@ -0,0 +1,474 @@
++/*
++ * 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"
++
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++
++#include <string.h>
++
++#include <openssl/crypto.h>
++#include <openssl/bn.h>
++
++#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"
++#include "ssherr.h"
++
++extern ServerOptions options;
++
++int
++kexgss_server(struct ssh *ssh)
++{
++ struct kex *kex = ssh->kex;
++ 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;
++ struct sshbuf *shared_secret = NULL;
++ struct sshbuf *client_pubkey = NULL;
++ struct sshbuf *server_pubkey = NULL;
++ struct sshbuf *empty = sshbuf_new();
++ int type = 0;
++ gss_OID oid;
++ char *mechs;
++ u_char hash[SSH_DIGEST_MAX_LENGTH];
++ size_t hashlen;
++ 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()) {
++ mechs = ssh_gssapi_server_mechanisms();
++ free(mechs);
++ }
++
++ debug2_f("Identifying %s", kex->name);
++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
++ if (oid == GSS_C_NO_OID)
++ fatal("Unknown gssapi mechanism");
++
++ debug2_f("Acquiring credentials");
++
++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
++ fatal("Unable to acquire credentials for the server");
++
++ do {
++ debug("Wait SSH2_MSG_KEXGSS_INIT");
++ type = ssh_packet_read(ssh);
++ switch(type) {
++ case SSH2_MSG_KEXGSS_INIT:
++ if (client_pubkey != NULL)
++ fatal("Received KEXGSS_INIT after initialising");
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_getb_froms(ssh, &client_pubkey)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ switch (kex->kex_type) {
++ case KEX_GSS_GRP1_SHA1:
++ case KEX_GSS_GRP14_SHA1:
++ case KEX_GSS_GRP14_SHA256:
++ case KEX_GSS_GRP16_SHA512:
++ r = kex_dh_enc(kex, client_pubkey, &server_pubkey,
++ &shared_secret);
++ break;
++ case KEX_GSS_NISTP256_SHA256:
++ r = kex_ecdh_enc(kex, client_pubkey, &server_pubkey,
++ &shared_secret);
++ break;
++ case KEX_GSS_C25519_SHA256:
++ r = kex_c25519_enc(kex, client_pubkey, &server_pubkey,
++ &shared_secret);
++ break;
++ default:
++ fatal_f("Unexpected KEX type %d", kex->kex_type);
++ }
++ if (r != 0)
++ goto out;
++
++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
++ break;
++ case SSH2_MSG_KEXGSS_CONTINUE:
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ break;
++ default:
++ sshpkt_disconnect(ssh,
++ "Protocol error: didn't expect packet type %d",
++ type);
++ }
++
++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
++ &send_tok, &ret_flags));
++
++ gss_release_buffer(&min_status, &recv_tok);
++
++ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
++ fatal("Zero length token output when incomplete");
++
++ if (client_pubkey == NULL)
++ fatal("No client public key");
++
++ if (maj_status & GSS_S_CONTINUE_NEEDED) {
++ debug("Sending GSSAPI_CONTINUE");
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ gss_release_buffer(&min_status, &send_tok);
++ }
++ } while (maj_status & GSS_S_CONTINUE_NEEDED);
++
++ if (GSS_ERROR(maj_status)) {
++ if (send_tok.length > 0) {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ 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");
++
++ hashlen = sizeof(hash);
++ if ((r = kex_gen_hash(
++ kex->hash_alg,
++ kex->client_version,
++ kex->server_version,
++ kex->peer,
++ kex->my,
++ empty,
++ client_pubkey,
++ server_pubkey,
++ shared_secret,
++ hash, &hashlen)) != 0)
++ goto out;
++
++ gssbuf.value = hash;
++ gssbuf.length = hashlen;
++
++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
++ fatal("Couldn't get MIC");
++
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
++ (r = sshpkt_put_stringb(ssh, server_pubkey)) != 0 ||
++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ if (send_tok.length != 0) {
++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ } else {
++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ if ((r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt_send failed: %s", ssh_err(r));
++
++ 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);
++
++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
++ r = 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();
++out:
++ sshbuf_free(empty);
++ explicit_bzero(hash, sizeof(hash));
++ sshbuf_free(shared_secret);
++ sshbuf_free(client_pubkey);
++ sshbuf_free(server_pubkey);
++ return r;
++}
++
++int
++kexgssgex_server(struct ssh *ssh)
++{
++ struct kex *kex = ssh->kex;
++ 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;
++ struct sshbuf *shared_secret = NULL;
++ int type = 0;
++ gss_OID oid;
++ char *mechs;
++ u_char hash[SSH_DIGEST_MAX_LENGTH];
++ size_t hashlen;
++ BIGNUM *dh_client_pub = NULL;
++ const BIGNUM *pub_key, *dh_p, *dh_g;
++ int min = -1, max = -1, nbits = -1;
++ int cmin = -1, cmax = -1; /* client proposal */
++ struct sshbuf *empty = sshbuf_new();
++ 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_f("Identifying %s", kex->name);
++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
++ if (oid == GSS_C_NO_OID)
++ fatal("Unknown gssapi mechanism");
++
++ debug2_f("Acquiring credentials");
++
++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
++ fatal("Unable to acquire credentials for the server");
++
++ /* 5. S generates an ephemeral key pair (do the allocations early) */
++ debug("Doing group exchange");
++ ssh_packet_read_expect(ssh, SSH2_MSG_KEXGSS_GROUPREQ);
++ /* store client proposal to provide valid signature */
++ if ((r = sshpkt_get_u32(ssh, &cmin)) != 0 ||
++ (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
++ (r = sshpkt_get_u32(ssh, &cmax)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ kex->nbits = nbits;
++ kex->min = cmin;
++ kex->max = cmax;
++ min = MAX(DH_GRP_MIN, cmin);
++ max = MIN(DH_GRP_MAX, cmax);
++ nbits = MAXIMUM(DH_GRP_MIN, nbits);
++ nbits = MINIMUM(DH_GRP_MAX, nbits);
++ if (max < min || nbits < min || max < nbits)
++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
++ min, nbits, max);
++ kex->dh = PRIVSEP(choose_dh(min, nbits, max));
++ if (kex->dh == NULL) {
++ sshpkt_disconnect(ssh, "Protocol error: no matching group found");
++ fatal("Protocol error: no matching group found");
++ }
++
++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_GROUP)) != 0 ||
++ (r = sshpkt_put_bignum2(ssh, dh_p)) != 0 ||
++ (r = sshpkt_put_bignum2(ssh, dh_g)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ if ((r = ssh_packet_write_wait(ssh)) != 0)
++ fatal("ssh_packet_write_wait: %s", ssh_err(r));
++
++ /* Compute our exchange value in parallel with the client */
++ if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
++ goto out;
++
++ do {
++ debug("Wait SSH2_MSG_GSSAPI_INIT");
++ type = ssh_packet_read(ssh);
++ switch(type) {
++ case SSH2_MSG_KEXGSS_INIT:
++ if (dh_client_pub != NULL)
++ fatal("Received KEXGSS_INIT after initialising");
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_get_bignum2(ssh, &dh_client_pub)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
++ break;
++ case SSH2_MSG_KEXGSS_CONTINUE:
++ if ((r = ssh_gssapi_sshpkt_get_buffer_desc(ssh,
++ &recv_tok)) != 0 ||
++ (r = sshpkt_get_end(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ break;
++ default:
++ sshpkt_disconnect(ssh,
++ "Protocol error: didn't expect packet type %d",
++ type);
++ }
++
++ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
++ &send_tok, &ret_flags));
++
++ gss_release_buffer(&min_status, &recv_tok);
++
++ 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");
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ gss_release_buffer(&min_status, &send_tok);
++ }
++ } while (maj_status & GSS_S_CONTINUE_NEEDED);
++
++ if (GSS_ERROR(maj_status)) {
++ if (send_tok.length > 0) {
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_CONTINUE)) != 0 ||
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ 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");
++
++ /* calculate shared secret */
++ if ((shared_secret = sshbuf_new()) == NULL) {
++ r = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++ if ((r = kex_dh_compute_key(kex, dh_client_pub, shared_secret)) != 0)
++ goto out;
++
++ DH_get0_key(kex->dh, &pub_key, NULL);
++ DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
++ hashlen = sizeof(hash);
++ if ((r = kexgex_hash(
++ kex->hash_alg,
++ kex->client_version,
++ kex->server_version,
++ kex->peer,
++ kex->my,
++ empty,
++ cmin, nbits, cmax,
++ dh_p, dh_g,
++ dh_client_pub,
++ pub_key,
++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
++ hash, &hashlen)) != 0)
++ fatal("kexgex_hash failed: %s", ssh_err(r));
++
++ gssbuf.value = hash;
++ gssbuf.length = hashlen;
++
++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt, &gssbuf, &msg_tok))))
++ fatal("Couldn't get MIC");
++
++ if ((r = sshpkt_start(ssh, SSH2_MSG_KEXGSS_COMPLETE)) != 0 ||
++ (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
++ (r = sshpkt_put_string(ssh, msg_tok.value, msg_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ if (send_tok.length != 0) {
++ if ((r = sshpkt_put_u8(ssh, 1)) != 0 || /* true */
++ (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++ } else {
++ if ((r = sshpkt_put_u8(ssh, 0)) != 0) /* false */
++ fatal("sshpkt failed: %s", ssh_err(r));
++ }
++ if ((r = sshpkt_send(ssh)) != 0)
++ fatal("sshpkt failed: %s", ssh_err(r));
++
++ 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 */
++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0)
++ r = 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();
++out:
++ sshbuf_free(empty);
++ explicit_bzero(hash, sizeof(hash));
++ DH_free(kex->dh);
++ kex->dh = NULL;
++ BN_clear_free(dh_client_pub);
++ sshbuf_free(shared_secret);
++ return r;
++}
++#endif /* defined(GSSAPI) && defined(WITH_OPENSSL) */
+diff --git a/monitor.c b/monitor.c
+index 2ce89fe9..ebf76c7f 100644
+--- a/monitor.c
++++ b/monitor.c
+@@ -148,6 +148,8 @@ int mm_answer_gss_setup_ctx(struct ssh *, int, struct sshbuf *);
+ int mm_answer_gss_accept_ctx(struct ssh *, int, struct sshbuf *);
+ int mm_answer_gss_userok(struct ssh *, int, struct sshbuf *);
+ int mm_answer_gss_checkmic(struct ssh *, int, struct sshbuf *);
++int mm_answer_gss_sign(struct ssh *, int, struct sshbuf *);
++int mm_answer_gss_updatecreds(struct ssh *, int, struct sshbuf *);
+ #endif
+
+ #ifdef SSH_AUDIT_EVENTS
+@@ -220,11 +222,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(struct ssh *ssh, struct monitor *pmonitor)
+ /* 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) {
+@@ -376,8 +376,15 @@ monitor_child_preauth(struct ssh *ssh, s
+ if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) {
+ auth_log(ssh, authenticated, partial,
+ auth_method, auth_submethod);
+- if (!partial && !authenticated)
++ if (!partial && !authenticated) {
++#ifdef GSSAPI
++ /* If gssapi-with-mic failed, MONITOR_REQ_GSSCHECKMIC is disabled.
++ * We have to reenable it to try again for gssapi-keyex */
++ if (strcmp(auth_method, "gssapi-with-mic") == 0 && options.gss_keyex)
++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1);
++#endif
+ authctxt->failures++;
++ }
+ if (authenticated || partial) {
+ auth2_update_session_info(authctxt,
+ auth_method, auth_submethod);
+@@ -406,6 +419,10 @@ monitor_child_postauth(struct ssh *ssh, struct monitor *pmonitor)
+ 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);
+@@ -1713,6 +1730,17 @@ monitor_apply_keystate(struct ssh *ssh, struct monitor *pmonitor)
+ # ifdef OPENSSL_HAS_ECC
+ kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
+ # endif
++# 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_GRP14_SHA256] = kexgss_server;
++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
++ }
++# endif
+ #endif /* WITH_OPENSSL */
+ kex->kex[KEX_C25519_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
+@@ -1806,8 +1834,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
+ u_char *p;
+ int r;
+
+- if (!options.gss_authentication)
+- fatal_f("GSSAPI authentication not enabled");
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
+
+ if ((r = sshbuf_get_string(m, &p, &len)) != 0)
+ fatal_fr(r, "parse");
+@@ -1839,8 +1867,8 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
+ OM_uint32 flags = 0; /* GSI needs this */
+ int r;
+
+- if (!options.gss_authentication)
+- fatal_f("GSSAPI authentication not enabled");
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
+
+ if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0)
+ fatal_fr(r, "ssh_gssapi_get_buffer_desc");
+@@ -1860,6 +1888,7 @@ mm_answer_gss_accept_ctx(struct ssh *ssh, int sock, struct sshbuf *m)
+ 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);
+ }
+@@ -1871,8 +1900,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
+ OM_uint32 ret;
+ int r;
+
+- if (!options.gss_authentication)
+- fatal_f("GSSAPI authentication not enabled");
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
+
+ if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 ||
+ (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0)
+@@ -1898,13 +1927,17 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m)
+ int
+ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
+ {
+- int r, authenticated;
++ int r, authenticated, kex;
+ const char *displayname;
+
+- if (!options.gss_authentication)
+- fatal_f("GSSAPI authentication not enabled");
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
+
+- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user);
++ if ((r = sshbuf_get_u32(m, &kex)) != 0)
++ fatal_fr(r, "buffer error");
++
++ authenticated = authctxt->valid &&
++ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex);
+
+ sshbuf_reset(m);
+ if ((r = sshbuf_put_u32(m, authenticated)) != 0)
+@@ -1913,7 +1946,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
+ debug3_f("sending result %d", 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);
+@@ -1921,5 +1958,84 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m)
+ /* Monitor loop will terminate if authenticated */
+ return (authenticated);
+ }
++
++int
++mm_answer_gss_sign(struct ssh *ssh, int socket, struct sshbuf *m)
++{
++ gss_buffer_desc data;
++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER;
++ OM_uint32 major, minor;
++ size_t len;
++ u_char *p = NULL;
++ int r;
++
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
++
++ if ((r = sshbuf_get_string(m, &p, &len)) != 0)
++ fatal_fr(r, "buffer error");
++ data.value = p;
++ data.length = len;
++ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */
++ if (data.length != 20 && data.length != 32 && data.length != 64)
++ fatal_f("data length incorrect: %d", (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_fr(r, "buffer error");
++
++ 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(struct ssh *ssh, int socket, struct sshbuf *m) {
++ ssh_gssapi_ccache store;
++ int r, ok;
++
++ if (!options.gss_authentication && !options.gss_keyex)
++ fatal_f("GSSAPI not enabled");
++
++ if ((r = sshbuf_get_string(m, (u_char **)&store.filename, NULL)) != 0 ||
++ (r = sshbuf_get_string(m, (u_char **)&store.envvar, NULL)) != 0 ||
++ (r = sshbuf_get_string(m, (u_char **)&store.envval, NULL)) != 0)
++ fatal_fr(r, "buffer error");
++
++ ok = ssh_gssapi_update_creds(&store);
++
++ free(store.filename);
++ free(store.envvar);
++ free(store.envval);
++
++ sshbuf_reset(m);
++ if ((r = sshbuf_put_u32(m, ok)) != 0)
++ fatal_fr(r, "buffer error");
++
++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m);
++
++ return(0);
++}
++
+ #endif /* GSSAPI */
+
+diff --git a/monitor.h b/monitor.h
+index 683e5e07..2b1a2d59 100644
+--- a/monitor.h
++++ b/monitor.h
+@@ -63,6 +63,8 @@ enum monitor_reqtype {
+ MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111,
+ MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113,
+
++ MONITOR_REQ_GSSSIGN = 150, MONITOR_ANS_GSSSIGN = 151,
++ MONITOR_REQ_GSSUPCREDS = 152, MONITOR_ANS_GSSUPCREDS = 153,
+ };
+
+ struct ssh;
+diff --git a/monitor_wrap.c b/monitor_wrap.c
+index 001a8fa1..6edb509a 100644
+--- a/monitor_wrap.c
++++ b/monitor_wrap.c
+@@ -993,13 +993,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic)
+ }
+
+ int
+-mm_ssh_gssapi_userok(char *user)
++mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex)
+ {
+ struct sshbuf *m;
+ int r, authenticated = 0;
+
+ if ((m = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
++ if ((r = sshbuf_put_u32(m, kex)) != 0)
++ fatal_fr(r, "buffer error");
+
+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m);
+ mm_request_receive_expect(pmonitor->m_recvfd,
+@@ -1012,4 +1014,57 @@ mm_ssh_gssapi_userok(char *user)
+ debug3_f("user %sauthenticated", 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_f("sshbuf_new failed");
++ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0)
++ fatal_fr(r, "buffer error");
++
++ 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 = ssh_gssapi_get_buffer_desc(m, hash)) != 0)
++ fatal_fr(r, "buffer error");
++
++ sshbuf_free(m);
++
++ return (major);
++}
++
++int
++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store)
++{
++ struct sshbuf *m;
++ int r, ok;
++
++ if ((m = sshbuf_new()) == NULL)
++ fatal_f("sshbuf_new failed");
++
++ if ((r = sshbuf_put_cstring(m,
++ store->filename ? store->filename : "")) != 0 ||
++ (r = sshbuf_put_cstring(m,
++ store->envvar ? store->envvar : "")) != 0 ||
++ (r = sshbuf_put_cstring(m,
++ store->envval ? store->envval : "")) != 0)
++ fatal_fr(r, "buffer error");
++
++ 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_fr(r, "buffer error");
++
++ sshbuf_free(m);
++
++ return (ok);
++}
++
+ #endif /* GSSAPI */
+diff --git a/monitor_wrap.h b/monitor_wrap.h
+index 23ab096a..485590c1 100644
+--- a/monitor_wrap.h
++++ b/monitor_wrap.h
+@@ -64,8 +64,10 @@ 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);
++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 *);
+ #endif
+
+ #ifdef USE_PAM
+diff -up a/readconf.c.gsskex b/readconf.c
+--- a/readconf.c.gsskex 2021-08-20 06:03:49.000000000 +0200
++++ b/readconf.c 2021-08-27 12:25:42.556421509 +0200
+@@ -67,6 +67,7 @@
+ #include "uidswap.h"
+ #include "myproposal.h"
+ #include "digest.h"
++#include "ssh-gss.h"
+
+ /* Format of the configuration file:
+
+@@ -161,6 +162,8 @@ typedef enum {
+ oClearAllForwardings, oNoHostAuthenticationForLocalhost,
+ oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
+ oAddressFamily, oGssAuthentication, oGssDelegateCreds,
++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey,
++ oGssServerIdentity, oGssKexAlgorithms,
+ oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
+ oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist,
+ oHashKnownHosts,
+@@ -206,10 +209,22 @@ static struct {
+ /* Sometimes-unsupported options */
+ #if defined(GSSAPI)
+ { "gssapiauthentication", oGssAuthentication },
++ { "gssapikeyexchange", oGssKeyEx },
+ { "gssapidelegatecredentials", oGssDelegateCreds },
++ { "gssapitrustdns", oGssTrustDns },
++ { "gssapiclientidentity", oGssClientIdentity },
++ { "gssapiserveridentity", oGssServerIdentity },
++ { "gssapirenewalforcesrekey", oGssRenewalRekey },
++ { "gssapikexalgorithms", oGssKexAlgorithms },
+ # else
+ { "gssapiauthentication", oUnsupported },
++ { "gssapikeyexchange", oUnsupported },
+ { "gssapidelegatecredentials", oUnsupported },
++ { "gssapitrustdns", oUnsupported },
++ { "gssapiclientidentity", oUnsupported },
++ { "gssapiserveridentity", oUnsupported },
++ { "gssapirenewalforcesrekey", oUnsupported },
++ { "gssapikexalgorithms", oUnsupported },
+ #endif
+ #ifdef ENABLE_PKCS11
+ { "pkcs11provider", oPKCS11Provider },
+@@ -1113,10 +1128,42 @@ 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 oGssKexAlgorithms:
++ arg = argv_next(&ac, &av);
++ if (!arg || *arg == '\0')
++ fatal("%.200s line %d: Missing argument.",
++ filename, linenum);
++ if (!kex_gss_names_valid(arg))
++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
++ filename, linenum, arg ? arg : "<NONE>");
++ if (*activep && options->gss_kex_algorithms == NULL)
++ options->gss_kex_algorithms = xstrdup(arg);
++ break;
++
+ case oBatchMode:
+ intptr = &options->batch_mode;
+ goto parse_flag;
+@@ -2306,7 +2353,13 @@ initialize_options(Options * options)
+ options->fwd_opts.streamlocal_bind_unlink = -1;
+ options->pubkey_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->gss_kex_algorithms = NULL;
+ options->password_authentication = -1;
+ options->kbd_interactive_authentication = -1;
+ options->kbd_interactive_devices = NULL;
+@@ -2463,8 +2516,18 @@ fill_default_options(Options * options)
+ options->pubkey_authentication = SSH_PUBKEY_AUTH_ALL;
+ 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;
++#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)
+@@ -3246,7 +3309,14 @@ 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_fmtint(oGssRenewalRekey, o->gss_renewal_rekey);
++ dump_cfg_string(oGssClientIdentity, o->gss_client_identity);
++ dump_cfg_string(oGssServerIdentity, o->gss_server_identity);
++ 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 a/readconf.h.gsskex b/readconf.h
+--- a/readconf.h.gsskex 2021-08-27 12:05:29.248142431 +0200
++++ b/readconf.h 2021-08-27 12:22:19.270679852 +0200
+@@ -39,7 +39,13 @@ typedef struct {
+ int pubkey_authentication; /* Try ssh2 pubkey authentication. */
+ int hostbased_authentication; /* ssh2's rhosts_rsa */
+ 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 */
++ 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 a/servconf.c.gsskex b/servconf.c
+--- a/servconf.c.gsskex 2021-08-20 06:03:49.000000000 +0200
++++ b/servconf.c 2021-08-27 12:28:15.887735189 +0200
+@@ -70,6 +70,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);
+@@ -136,8 +137,11 @@ 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->gss_kex_algorithms = NULL;
+ options->password_authentication = -1;
+ options->kbd_interactive_authentication = -1;
+ options->permit_empty_passwd = -1;
+@@ -356,10 +360,18 @@ 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;
++#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)
+@@ -506,6 +518,7 @@ typedef enum {
+ sHostKeyAlgorithms, sPerSourceMaxStartups, sPerSourceNetBlockSize,
+ sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile,
+ sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor,
++ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey,
+ sAcceptEnv, sSetEnv, sPermitTunnel,
+ sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory,
+ sUsePrivilegeSeparation, sAllowAgentForwarding,
+@@ -587,12 +600,22 @@ static struct {
+ #ifdef GSSAPI
+ { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
+ { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL },
++ { "gssapicleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL },
+ { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL },
++ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL },
++ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL },
++ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL },
+ #else
+ { "gssapiauthentication", sUnsupported, SSHCFG_ALL },
+ { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL },
++ { "gssapicleanupcreds", sUnsupported, SSHCFG_GLOBAL },
+ { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL },
++ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL },
++ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL },
++ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL },
+ #endif
++ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL },
++ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL },
+ { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
+ { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
+ { "challengeresponseauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, /* alias */
+@@ -1576,6 +1599,10 @@ process_server_config_line_depth(ServerO
+ 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;
+@@ -1584,6 +1611,22 @@ process_server_config_line_depth(ServerO
+ intptr = &options->gss_strict_acceptor;
+ goto parse_flag;
+
++ case sGssStoreRekey:
++ intptr = &options->gss_store_rekey;
++ goto parse_flag;
++
++ case sGssKexAlgorithms:
++ arg = argv_next(&ac, &av);
++ if (!arg || *arg == '\0')
++ fatal("%.200s line %d: Missing argument.",
++ filename, linenum);
++ if (!kex_gss_names_valid(arg))
++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.",
++ filename, linenum, arg ? arg : "<NONE>");
++ if (*activep && options->gss_kex_algorithms == NULL)
++ options->gss_kex_algorithms = xstrdup(arg);
++ break;
++
+ case sPasswordAuthentication:
+ intptr = &options->password_authentication;
+ goto parse_flag;
+@@ -2892,6 +2935,10 @@ 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);
++ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms);
+ #endif
+ dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication);
+ dump_cfg_fmtint(sKbdInteractiveAuthentication,
+diff --git a/servconf.h b/servconf.h
+index 4202a2d0..3f47ea25 100644
+--- a/servconf.h
++++ b/servconf.h
+@@ -132,8 +132,11 @@ 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;
++ 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 --git a/session.c b/session.c
+index 8c0e54f7..06a33442 100644
+--- a/session.c
++++ b/session.c
+@@ -2678,13 +2678,19 @@ do_cleanup(struct ssh *ssh, Authctxt *authctxt)
+
+ #ifdef KRB5
+ if (options.kerberos_ticket_cleanup &&
+- authctxt->krb5_ctx)
++ authctxt->krb5_ctx) {
++ temporarily_use_uid(authctxt->pw);
+ krb5_cleanup_proc(authctxt);
++ restore_uid();
++ }
+ #endif
+
+ #ifdef GSSAPI
+- if (options.gss_cleanup_creds)
++ if (options.gss_cleanup_creds) {
++ temporarily_use_uid(authctxt->pw);
+ ssh_gssapi_cleanup_creds();
++ restore_uid();
++ }
+ #endif
+
+ /* remove agent socket */
+diff --git a/ssh-gss.h b/ssh-gss.h
+index 36180d07..70dd3665 100644
+--- a/ssh-gss.h
++++ b/ssh-gss.h
+@@ -1,6 +1,6 @@
+ /* $OpenBSD: ssh-gss.h,v 1.15 2021/01/27 10:05:28 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,34 @@
+
+ #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_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_GRP14_SHA256_ID "," \
++ KEX_GSS_GRP16_SHA512_ID "," \
++ KEX_GSS_NISTP256_SHA256_ID "," \
++ KEX_GSS_C25519_SHA256_ID "," \
++ KEX_GSS_GRP14_SHA1_ID "," \
++ KEX_GSS_GEX_SHA1_ID
++
+ typedef struct {
+ char *filename;
+ char *envvar;
+ char *envval;
++ struct passwd *owner;
+ void *data;
+ } ssh_gssapi_ccache;
+
+@@ -72,8 +92,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 +107,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 +118,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);
+@@ -109,6 +134,7 @@ OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *);
+
+ struct sshbuf;
+ int ssh_gssapi_get_buffer_desc(struct sshbuf *, gss_buffer_desc *);
++int ssh_gssapi_sshpkt_get_buffer_desc(struct ssh *, gss_buffer_desc *);
+
+ OM_uint32 ssh_gssapi_import_name(Gssctxt *, const char *);
+ OM_uint32 ssh_gssapi_init_ctx(Gssctxt *, int,
+@@ -123,17 +149,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 *, const struct sshbuf *);
+-int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *);
++int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *);
++OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *);
++int ssh_gssapi_credentials_updated(Gssctxt *);
+
+ /* In the server */
++typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *,
++ const char *);
++char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *);
++char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *,
++ const char *, 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 *, 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);
+ void ssh_gssapi_storecreds(void);
+ const char *ssh_gssapi_displayname(void);
+
++char *ssh_gssapi_server_mechanisms(void);
++int ssh_gssapi_oid_table_ok(void);
++
++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store);
++void ssh_gssapi_rekey_creds(void);
++
+ #endif /* GSSAPI */
+
+ #endif /* _SSH_GSS_H */
+diff --git a/ssh.1 b/ssh.1
+index 60de6087..db5c65bc 100644
+--- a/ssh.1
++++ b/ssh.1
+@@ -503,7 +503,13 @@ For full details of the options listed below, and their possible values, see
+ .It GatewayPorts
+ .It GlobalKnownHostsFile
+ .It GSSAPIAuthentication
++.It GSSAPIKeyExchange
++.It GSSAPIClientIdentity
+ .It GSSAPIDelegateCredentials
++.It GSSAPIKexAlgorithms
++.It GSSAPIRenewalForcesRekey
++.It GSSAPIServerIdentity
++.It GSSAPITrustDns
+ .It HashKnownHosts
+ .It Host
+ .It HostbasedAcceptedAlgorithms
+@@ -579,6 +585,8 @@ flag),
+ (supported message integrity codes),
+ .Ar kex
+ (key exchange algorithms),
++.Ar kex-gss
++(GSSAPI key exchange algorithms),
+ .Ar key
+ (key types),
+ .Ar key-cert
+diff --git a/ssh.c b/ssh.c
+index 15aee569..110cf9c1 100644
+--- a/ssh.c
++++ b/ssh.c
+@@ -747,6 +747,8 @@ main(int ac, char **av)
+ else if (strcmp(optarg, "kex") == 0 ||
+ strcasecmp(optarg, "KexAlgorithms") == 0)
+ cp = kex_alg_list('\n');
++ else if (strcmp(optarg, "kex-gss") == 0)
++ cp = kex_gss_alg_list('\n');
+ else if (strcmp(optarg, "key") == 0)
+ cp = sshkey_alg_list(0, 0, 0, '\n');
+ else if (strcmp(optarg, "key-cert") == 0)
+@@ -772,8 +774,8 @@ main(int ac, char **av)
+ } else if (strcmp(optarg, "help") == 0) {
+ cp = xstrdup(
+ "cipher\ncipher-auth\ncompression\nkex\n"
+- "key\nkey-cert\nkey-plain\nkey-sig\nmac\n"
+- "protocol-version\nsig");
++ "kex-gss\nkey\nkey-cert\nkey-plain\n"
++ "key-sig\nmac\nprotocol-version\nsig");
+ }
+ if (cp == NULL)
+ fatal("Unsupported query \"%s\"", optarg);
+diff --git a/ssh_config b/ssh_config
+index 5e8ef548..1ff999b6 100644
+--- a/ssh_config
++++ b/ssh_config
+@@ -24,6 +24,8 @@
+ # HostbasedAuthentication no
+ # GSSAPIAuthentication no
+ # GSSAPIDelegateCredentials no
++# GSSAPIKeyExchange no
++# GSSAPITrustDNS no
+ # BatchMode no
+ # CheckHostIP yes
+ # AddressFamily any
+diff --git a/ssh_config.5 b/ssh_config.5
+index 06a32d31..3f490697 100644
+--- a/ssh_config.5
++++ b/ssh_config.5
+@@ -766,10 +766,68 @@ 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 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
++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 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-,
++gss-group14-sha256-,
++gss-group16-sha512-,
++gss-nistp256-sha256-,
++gss-curve25519-sha256-
++.Ed
++.Pp
++The default is
++.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
++gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
++This option only applies to connections using GSSAPI.
+ .It Cm HashKnownHosts
+ Indicates that
+ .Xr ssh 1
+diff --git a/sshconnect2.c b/sshconnect2.c
+index af00fb30..03bc87eb 100644
+--- a/sshconnect2.c
++++ b/sshconnect2.c
+@@ -80,8 +80,6 @@
+ #endif
+
+ /* import */
+-extern char *client_version_string;
+-extern char *server_version_string;
+ extern Options options;
+
+ /*
+@@ -163,6 +161,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
+ char *s, *all_key, *hkalgs = NULL;
+ int r, use_known_hosts_order = 0;
+
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++ char *orig = NULL, *gss = NULL;
++ char *gss_host = NULL;
++#endif
++
+ xxx_host = host;
+ xxx_hostaddr = hostaddr;
+ xxx_conn_info = cinfo;
+@@ -206,6 +209,42 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
+ kex_proposal_populate_entries(ssh, myproposal, s, options.ciphers,
+ options.macs, compression_alg_list(options.compression),
+ hkalgs ? hkalgs : options.hostkeyalgorithms);
++
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++ if (options.gss_keyex) {
++ /* Add the GSSAPI mechanisms currently supported on this
++ * client to the key exchange algorithm proposal */
++ orig = myproposal[PROPOSAL_KEX_ALGS];
++
++ if (options.gss_server_identity) {
++ gss_host = xstrdup(options.gss_server_identity);
++ } else if (options.gss_trust_dns) {
++ gss_host = remote_hostname(ssh);
++ /* Fall back to specified host if we are using proxy command
++ * and can not use DNS on that socket */
++ if (strcmp(gss_host, "UNKNOWN") == 0) {
++ free(gss_host);
++ gss_host = xstrdup(host);
++ }
++ } else {
++ gss_host = xstrdup(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(&myproposal[PROPOSAL_KEX_ALGS],
++ "%s,%s", gss, orig);
++
++ /* If we've got GSSAPI algorithms, then we also support the
++ * 'null' hostkey, as a last resort */
++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS];
++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS],
++ "%s,null", orig);
++ }
++ }
++#endif
+
+ free(hkalgs);
+
+@@ -224,17 +256,47 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port)
+ # ifdef OPENSSL_HAS_ECC
+ ssh->kex->kex[KEX_ECDH_SHA2] = kex_gen_client;
+ # endif
+-#endif
++# ifdef GSSAPI
++ if (options.gss_keyex) {
++ ssh->kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client;
++ ssh->kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client;
++ ssh->kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client;
++ ssh->kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client;
++ ssh->kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_client;
++ ssh->kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_client;
++ ssh->kex->kex[KEX_GSS_C25519_SHA256] = kexgss_client;
++ }
++# endif
++#endif /* WITH_OPENSSL */
+ ssh->kex->kex[KEX_C25519_SHA256] = kex_gen_client;
+ ssh->kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_client;
+ ssh->kex->verify_host_key=&verify_host_key_callback;
+
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++ if (options.gss_keyex) {
++ ssh->kex->gss_deleg_creds = options.gss_deleg_creds;
++ ssh->kex->gss_trust_dns = options.gss_trust_dns;
++ ssh->kex->gss_client = options.gss_client_identity;
++ ssh->kex->gss_host = gss_host;
++ }
++#endif
++
+ ssh_dispatch_run_fatal(ssh, DISPATCH_BLOCK, &ssh->kex->done);
+
+ /* remove ext-info from the KEX proposals for rekeying */
+ free(myproposal[PROPOSAL_KEX_ALGS]);
+ myproposal[PROPOSAL_KEX_ALGS] =
+ compat_kex_proposal(ssh, options.kex_algorithms);
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++ /* 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(ssh->kex->my, myproposal)) != 0)
+ fatal_r(r, "kex_prop2buf");
+
+@@ -330,6 +392,7 @@ static int input_gssapi_response(int type, u_int32_t, struct ssh *);
+ static int input_gssapi_token(int type, u_int32_t, struct ssh *);
+ static int input_gssapi_error(int, u_int32_t, struct ssh *);
+ static int input_gssapi_errtok(int, u_int32_t, struct ssh *);
++static int userauth_gsskeyex(struct ssh *);
+ #endif
+
+ void userauth(struct ssh *, char *);
+@@ -346,6 +409,11 @@ static char *authmethods_get(void);
+
+ Authmethod authmethods[] = {
+ #ifdef GSSAPI
++ {"gssapi-keyex",
++ userauth_gsskeyex,
++ NULL,
++ &options.gss_keyex,
++ NULL},
+ {"gssapi-with-mic",
+ userauth_gssapi,
+ userauth_gssapi_cleanup,
+@@ -716,12 +784,32 @@ userauth_gssapi(struct ssh *ssh)
+ OM_uint32 min;
+ int r, ok = 0;
+ gss_OID mech = NULL;
++ char *gss_host = NULL;
++
++ if (options.gss_server_identity) {
++ gss_host = xstrdup(options.gss_server_identity);
++ } else if (options.gss_trust_dns) {
++ gss_host = remote_hostname(ssh);
++ /* Fall back to specified host if we are using proxy command
++ * and can not use DNS on that socket */
++ if (strcmp(gss_host, "UNKNOWN") == 0) {
++ free(gss_host);
++ gss_host = xstrdup(authctxt->host);
++ }
++ } else {
++ gss_host = xstrdup(authctxt->host);
++ }
+
+ /* Try one GSSAPI method at a time, rather than sending them all at
+ * once. */
+
+ if (authctxt->gss_supported_mechs == NULL)
+- gss_indicate_mechs(&min, &authctxt->gss_supported_mechs);
++ if (GSS_ERROR(gss_indicate_mechs(&min,
++ &authctxt->gss_supported_mechs))) {
++ authctxt->gss_supported_mechs = NULL;
++ free(gss_host);
++ return 0;
++ }
+
+ /* Check to see whether the mechanism is usable before we offer it */
+ while (authctxt->mech_tried < authctxt->gss_supported_mechs->count &&
+@@ -730,13 +811,15 @@ userauth_gssapi(struct ssh *ssh)
+ elements[authctxt->mech_tried];
+ /* My DER encoding requires length<128 */
+ if (mech->length < 128 && ssh_gssapi_check_mechanism(&gssctxt,
+- mech, authctxt->host)) {
++ mech, gss_host, options.gss_client_identity)) {
+ ok = 1; /* Mechanism works */
+ } else {
+ authctxt->mech_tried++;
+ }
+ }
+
++ free(gss_host);
++
+ if (!ok || mech == NULL)
+ return 0;
+
+@@ -976,6 +1059,55 @@ input_gssapi_error(int type, u_int32_t plen, struct ssh *ssh)
+ free(lang);
+ return r;
+ }
++
++int
++userauth_gsskeyex(struct ssh *ssh)
++{
++ struct sshbuf *b = NULL;
++ Authctxt *authctxt = ssh->authctxt;
++ gss_buffer_desc gssbuf;
++ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER;
++ OM_uint32 ms;
++ int r;
++
++ 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_f("sshbuf_new failed");
++
++ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service,
++ "gssapi-keyex", ssh->kex->session_id);
++
++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL)
++ fatal_f("sshbuf_mutable_ptr failed");
++ gssbuf.length = sshbuf_len(b);
++
++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) {
++ sshbuf_free(b);
++ return (0);
++ }
++
++ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 ||
++ (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 ||
++ (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 ||
++ (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 ||
++ (r = sshpkt_put_string(ssh, mic.value, mic.length)) != 0 ||
++ (r = sshpkt_send(ssh)) != 0)
++ fatal_fr(r, "parsing");
++
++ sshbuf_free(b);
++ gss_release_buffer(&ms, &mic);
++
++ return (1);
++}
++
+ #endif /* GSSAPI */
+
+ static int
+diff --git a/sshd.c b/sshd.c
+index 60b2aaf7..d92f03aa 100644
+--- a/sshd.c
++++ b/sshd.c
+@@ -817,8 +817,8 @@ notify_hostkeys(struct ssh *ssh)
+ }
+ debug3_f("sent %u hostkeys", nkeys);
+ if (nkeys == 0)
+- fatal_f("no hostkeys");
+- if ((r = sshpkt_send(ssh)) != 0)
++ debug3_f("no hostkeys");
++ else if ((r = sshpkt_send(ssh)) != 0)
+ sshpkt_fatal(ssh, r, "%s: send", __func__);
+ sshbuf_free(buf);
+ }
+@@ -1852,7 +1852,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);
+ }
+@@ -2347,6 +2348,48 @@ do_ssh2_kex(struct ssh *ssh)
+
+ free(hkalgs);
+
++#if defined(GSSAPI) && defined(WITH_OPENSSL)
++ {
++ 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(ssh, myproposal)) != 0)
+ fatal_r(r, "kex_setup");
+@@ -2362,7 +2405,18 @@ do_ssh2_kex(struct ssh *ssh)
+ # ifdef OPENSSL_HAS_ECC
+ kex->kex[KEX_ECDH_SHA2] = kex_gen_server;
+ # endif
+-#endif
++# 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_GRP14_SHA256] = kexgss_server;
++ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server;
++ kex->kex[KEX_GSS_GEX_SHA1] = kexgssgex_server;
++ kex->kex[KEX_GSS_NISTP256_SHA256] = kexgss_server;
++ kex->kex[KEX_GSS_C25519_SHA256] = kexgss_server;
++ }
++# endif
++#endif /* WITH_OPENSSL */
+ kex->kex[KEX_C25519_SHA256] = kex_gen_server;
+ kex->kex[KEX_KEM_SNTRUP761X25519_SHA512] = kex_gen_server;
+ kex->load_host_public_key=&get_hostkey_public_by_type;
+diff --git a/sshd_config b/sshd_config
+index 19b7c91a..2c48105f 100644
+--- a/sshd_config
++++ b/sshd_config
+@@ -69,6 +69,8 @@ AuthorizedKeysFile .ssh/authorized_keys
+ # GSSAPI options
+ #GSSAPIAuthentication no
+ #GSSAPICleanupCredentials yes
++#GSSAPIStrictAcceptorCheck yes
++#GSSAPIKeyExchange no
+
+ # Set this to 'yes' to enable PAM authentication, account processing,
+ # and session processing. If this is enabled, PAM authentication will
+diff --git a/sshd_config.5 b/sshd_config.5
+index 70ccea44..f6b41a2f 100644
+--- a/sshd_config.5
++++ b/sshd_config.5
+@@ -646,6 +646,11 @@ Specifies whether to automatically destroy the user's credentials cache
+ 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
++.Cm no .
+ .It Cm GSSAPIStrictAcceptorCheck
+ Determines whether to be strict about the identity of the GSSAPI acceptor
+ a client authenticates against.
+@@ -660,6 +665,32 @@ 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 .
++.Pp
++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-,
++gss-group14-sha256-,
++gss-group16-sha512-,
++gss-nistp256-sha256-,
++gss-curve25519-sha256-
++.Ed
++.Pp
++The default is
++.Dq gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,
++gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- .
++This option only applies to connections using GSSAPI.
+ .It Cm HostbasedAcceptedAlgorithms
+ Specifies the signature algorithms that will be accepted for hostbased
+ authentication as a list of comma-separated patterns.
+diff --git a/sshkey.c b/sshkey.c
+index 57995ee6..fd5b7724 100644
+--- a/sshkey.c
++++ b/sshkey.c
+@@ -127,6 +127,75 @@ static const struct keytype keytypes[] = {
+ extern const struct sshkey_impl sshkey_xmss_impl;
+ extern const struct sshkey_impl sshkey_xmss_cert_impl;
+ #endif
++
++static int ssh_gss_equal(const struct sshkey *, const struct sshkey *)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_serialize_public(const struct sshkey *, struct sshbuf *,
++ enum sshkey_serialize_rep)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_deserialize_public(const char *, struct sshbuf *,
++ struct sshkey *)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_serialize_private(const struct sshkey *, struct sshbuf *,
++ enum sshkey_serialize_rep)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_deserialize_private(const char *, struct sshbuf *,
++ struct sshkey *)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_copy_public(const struct sshkey *, struct sshkey *)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static int ssh_gss_verify(const struct sshkey *, const u_char *, size_t,
++ const u_char *, size_t, const char *, u_int,
++ struct sshkey_sig_details **)
++{
++ return SSH_ERR_FEATURE_UNSUPPORTED;
++}
++
++static const struct sshkey_impl_funcs sshkey_gss_funcs = {
++ /* .size = */ NULL,
++ /* .alloc = */ NULL,
++ /* .cleanup = */ NULL,
++ /* .equal = */ ssh_gss_equal,
++ /* .ssh_serialize_public = */ ssh_gss_serialize_public,
++ /* .ssh_deserialize_public = */ ssh_gss_deserialize_public,
++ /* .ssh_serialize_private = */ ssh_gss_serialize_private,
++ /* .ssh_deserialize_private = */ ssh_gss_deserialize_private,
++ /* .generate = */ NULL,
++ /* .copy_public = */ ssh_gss_copy_public,
++ /* .sign = */ NULL,
++ /* .verify = */ ssh_gss_verify,
++};
++
++/* The struct is intentionally dummy and has no gss calls */
++static const struct sshkey_impl sshkey_gss_kex_impl = {
++ /* .name = */ "null",
++ /* .shortname = */ "null",
++ /* .sigalg = */ NULL,
++ /* .type = */ KEY_NULL,
++ /* .nid = */ 0,
++ /* .cert = */ 0,
++ /* .sigonly = */ 0,
++ /* .keybits = */ 0, /* FIXME */
++ /* .funcs = */ &sshkey_gss_funcs,
++};
+
+ const struct sshkey_impl * const keyimpls[] = {
+ &sshkey_ed25519_impl,
+@@ -154,6 +154,7 @@ static const struct keytype keytypes[] = {
+ &sshkey_xmss_impl,
+ &sshkey_xmss_cert_impl,
+ #endif
++ &sshkey_gss_kex_impl,
+ NULL
+ };
+
+@@ -255,7 +256,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep)
+
+ for (i = 0; keyimpls[i] != NULL; i++) {
+ impl = keyimpls[i];
+- if (impl->name == NULL)
++ if (impl->name == NULL || impl->type == KEY_NULL)
+ continue;
+ if (!include_sigonly && impl->sigonly)
+ continue;
+diff --git a/sshkey.h b/sshkey.h
+index 71a3fddc..37a43a67 100644
+--- a/sshkey.h
++++ b/sshkey.h
+@@ -69,6 +69,7 @@ enum sshkey_types {
+ KEY_ECDSA_SK_CERT,
+ KEY_ED25519_SK,
+ KEY_ED25519_SK_CERT,
++ KEY_NULL,
+ KEY_UNSPEC
+ };
+
diff --git a/openssh-8.0p1-keygen-strip-doseol.patch b/openssh-8.0p1-keygen-strip-doseol.patch
new file mode 100644
index 0000000..3117a7a
--- /dev/null
+++ b/openssh-8.0p1-keygen-strip-doseol.patch
@@ -0,0 +1,12 @@
+diff -up openssh-8.0p1/ssh-keygen.c.strip-doseol openssh-8.0p1/ssh-keygen.c
+--- openssh-8.0p1/ssh-keygen.c.strip-doseol 2021-03-18 17:41:34.472404994 +0100
++++ openssh-8.0p1/ssh-keygen.c 2021-03-18 17:41:55.255538761 +0100
+@@ -901,7 +901,7 @@ do_fingerprint(struct passwd *pw)
+ while (getline(&line, &linesize, f) != -1) {
+ lnum++;
+ cp = line;
+- cp[strcspn(cp, "\n")] = '\0';
++ cp[strcspn(cp, "\r\n")] = '\0';
+ /* Trim leading space and comments */
+ cp = line + strspn(line, " \t");
+ if (*cp == '#' || *cp == '\0')
diff --git a/openssh-8.0p1-openssl-kdf.patch b/openssh-8.0p1-openssl-kdf.patch
new file mode 100644
index 0000000..5d76a4f
--- /dev/null
+++ b/openssh-8.0p1-openssl-kdf.patch
@@ -0,0 +1,137 @@
+commit 2c3ef499bfffce3cfd315edeebf202850ba4e00a
+Author: Jakub Jelen <jjelen@redhat.com>
+Date: Tue Apr 16 15:35:18 2019 +0200
+
+ Use the new OpenSSL KDF
+
+diff --git a/configure.ac b/configure.ac
+index 2a455e4e..e01c3d43 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -2712,6 +2712,7 @@ if test "x$openssl" = "xyes" ; then
+ HMAC_CTX_init \
+ RSA_generate_key_ex \
+ RSA_get_default_method \
++ EVP_KDF_CTX_new_id \
+ ])
+
+ # OpenSSL_add_all_algorithms may be a macro.
+diff --git a/kex.c b/kex.c
+index b6f041f4..1fbce2bb 100644
+--- a/kex.c
++++ b/kex.c
+@@ -38,6 +38,9 @@
+ #ifdef WITH_OPENSSL
+ #include <openssl/crypto.h>
+ #include <openssl/dh.h>
++# ifdef HAVE_EVP_KDF_CTX_NEW_ID
++# include <openssl/kdf.h>
++# endif
+ #endif
+
+ #include "ssh.h"
+@@ -942,6 +945,95 @@ kex_choose_conf(struct ssh *ssh)
+ return r;
+ }
+
++#ifdef HAVE_EVP_KDF_CTX_NEW_ID
++static const EVP_MD *
++digest_to_md(int digest_type)
++{
++ switch (digest_type) {
++ case SSH_DIGEST_SHA1:
++ return EVP_sha1();
++ case SSH_DIGEST_SHA256:
++ return EVP_sha256();
++ case SSH_DIGEST_SHA384:
++ return EVP_sha384();
++ case SSH_DIGEST_SHA512:
++ return EVP_sha512();
++ }
++ return NULL;
++}
++
++static int
++derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
++ const struct sshbuf *shared_secret, u_char **keyp)
++{
++ struct kex *kex = ssh->kex;
++ EVP_KDF_CTX *ctx = NULL;
++ u_char *key = NULL;
++ int r, key_len;
++
++ if ((key_len = ssh_digest_bytes(kex->hash_alg)) == 0)
++ return SSH_ERR_INVALID_ARGUMENT;
++ key_len = ROUNDUP(need, key_len);
++ if ((key = calloc(1, key_len)) == NULL) {
++ r = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ ctx = EVP_KDF_CTX_new_id(EVP_KDF_SSHKDF);
++ if (!ctx) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_MD, digest_to_md(kex->hash_alg));
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_KEY,
++ sshbuf_ptr(shared_secret), sshbuf_len(shared_secret));
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_XCGHASH, hash, hashlen);
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_TYPE, id);
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ r = EVP_KDF_ctrl(ctx, EVP_KDF_CTRL_SET_SSHKDF_SESSION_ID,
++ sshbuf_ptr(kex->session_id), sshbuf_len(kex->session_id));
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ r = EVP_KDF_derive(ctx, key, key_len);
++ if (r != 1) {
++ r = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++#ifdef DEBUG_KEX
++ fprintf(stderr, "key '%c'== ", id);
++ dump_digest("key", key, key_len);
++#endif
++ *keyp = key;
++ key = NULL;
++ r = 0;
++
++out:
++ free (key);
++ EVP_KDF_CTX_free(ctx);
++ if (r < 0) {
++ return r;
++ }
++ return 0;
++}
++#else
+ static int
+ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
+ const struct sshbuf *shared_secret, u_char **keyp)
+@@ -1004,6 +1096,7 @@ derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen,
+ ssh_digest_free(hashctx);
+ return r;
+ }
++#endif /* HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID */
+
+ #define NKEYS 6
+ int
+
diff --git a/openssh-8.0p1-pkcs11-uri.patch b/openssh-8.0p1-pkcs11-uri.patch
new file mode 100644
index 0000000..affdd72
--- /dev/null
+++ b/openssh-8.0p1-pkcs11-uri.patch
@@ -0,0 +1,3059 @@
+diff -up openssh-8.7p1/configure.ac.pkcs11-uri openssh-8.7p1/configure.ac
+--- openssh-8.7p1/configure.ac.pkcs11-uri 2021-08-30 13:07:43.646699953 +0200
++++ openssh-8.7p1/configure.ac 2021-08-30 13:07:43.662700088 +0200
+@@ -1985,12 +1985,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
+ ]
+ )
+@@ -2019,6 +2021,40 @@ AC_SEARCH_LIBS([dlopen], [dl])
+ AC_CHECK_FUNCS([dlopen])
+ AC_CHECK_DECL([RTLD_NOW], [], [], [#include <dlfcn.h>])
+
++# 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])
+@@ -5624,6 +5660,7 @@ echo " BSD Auth support
+ echo " Random number source: $RAND_MSG"
+ echo " Privsep sandbox style: $SANDBOX_STYLE"
+ echo " PKCS#11 support: $enable_pkcs11"
++echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG"
+ echo " U2F/FIDO support: $enable_sk"
+
+ echo ""
+diff -up openssh-8.7p1/Makefile.in.pkcs11-uri openssh-8.7p1/Makefile.in
+--- openssh-8.7p1/Makefile.in.pkcs11-uri 2021-08-30 13:07:43.571699324 +0200
++++ openssh-8.7p1/Makefile.in 2021-08-30 13:07:43.663700096 +0200
+@@ -103,7 +103,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
+ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-ecdsa-sk.o \
+ ssh-ed25519-sk.o ssh-rsa.o dh.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 cipher-chachapoly-libcrypto.o \
+ ssh-ed25519.o digest-openssl.o digest-libc.o \
+ hmac.o ed25519.o hash.o \
+@@ -302,6 +302,8 @@ clean: regressclean
+ rm -f regress/unittests/sshsig/test_sshsig$(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/sk-dummy/*.o
+ rm -f regress/misc/sk-dummy/*.lo
+ rm -f regress/misc/sk-dummy/sk-dummy.so
+@@ -339,6 +341,8 @@ distclean: regressclean
+ rm -f regress/unittests/sshsig/test_sshsig
+ 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/sk-dummy/*.o
+ rm -f regress/misc/sk-dummy/*.lo
+ rm -f regress/misc/sk-dummy/sk-dummy.so
+@@ -513,6 +517,7 @@ regress-prep:
+ $(MKDIR_P) `pwd`/regress/unittests/sshkey
+ $(MKDIR_P) `pwd`/regress/unittests/sshsig
+ $(MKDIR_P) `pwd`/regress/unittests/utf8
++ $(MKDIR_P) `pwd`/regress/unittests/pkcs11
+ $(MKDIR_P) `pwd`/regress/misc/sk-dummy
+ [ -f `pwd`/regress/Makefile ] || \
+ ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile
+@@ -677,6 +682,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT
+ regress/unittests/test_helper/libtest_helper.a \
+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(TESTLIBS)
+
++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 -lcrypto $(LIBS)
++
+ # These all need to be compiled -fPIC, so they are treated differently.
+ SK_DUMMY_OBJS=\
+ regress/misc/sk-dummy/sk-dummy.lo \
+@@ -711,7 +726,8 @@ regress-unit-binaries: regress-prep $(RE
+ regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \
+ regress/unittests/sshkey/test_sshkey$(EXEEXT) \
+ regress/unittests/sshsig/test_sshsig$(EXEEXT) \
+- regress/unittests/utf8/test_utf8$(EXEEXT)
++ regress/unittests/utf8/test_utf8$(EXEEXT) \
++ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \
+
+ tests: file-tests t-exec interop-tests unit
+ echo all tests passed
+diff -up openssh-8.7p1/regress/agent-pkcs11.sh.pkcs11-uri openssh-8.7p1/regress/agent-pkcs11.sh
+--- openssh-8.7p1/regress/agent-pkcs11.sh.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/regress/agent-pkcs11.sh 2021-08-30 13:07:43.663700096 +0200
+@@ -113,7 +113,7 @@ else
+ done
+
+ 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 -up openssh-8.7p1/regress/Makefile.pkcs11-uri openssh-8.7p1/regress/Makefile
+--- openssh-8.7p1/regress/Makefile.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/regress/Makefile 2021-08-30 13:07:43.663700096 +0200
+@@ -122,7 +122,8 @@ CLEANFILES= *.core actual agent-key.* au
+ 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 rsa_ssh2_cr.prv \
++ revoked-* rsa rsa-agent rsa-agent.pub rsa-agent-cert.pub \
++ rsa.pub rsa_ssh2_cr.prv 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 \
+@@ -252,8 +253,9 @@ unit:
+ V="" ; \
+ test "x${USE_VALGRIND}" = "x" || \
+ V=${.CURDIR}/valgrind-unit.sh ; \
+- $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
+- $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
++ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \
++ $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \
++ $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \
+ -d ${.CURDIR}/unittests/sshkey/testdata ; \
+ $$V ${.OBJDIR}/unittests/sshsig/test_sshsig \
+ -d ${.CURDIR}/unittests/sshsig/testdata ; \
+diff -up openssh-8.7p1/regress/pkcs11.sh.pkcs11-uri openssh-8.7p1/regress/pkcs11.sh
+--- openssh-8.7p1/regress/pkcs11.sh.pkcs11-uri 2021-08-30 13:07:43.663700096 +0200
++++ openssh-8.7p1/regress/pkcs11.sh 2021-08-30 13:07:43.663700096 +0200
+@@ -0,0 +1,349 @@
++#
++# Copyright (c) 2017 Red Hat
++#
++# Authors: Jakub Jelen <jjelen@redhat.com>
++#
++# 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"
++
++try_token_libs() {
++ for _lib in "$@" ; do
++ if test -f "$_lib" ; then
++ verbose "Using token library $_lib"
++ TEST_SSH_PKCS11="$_lib"
++ return
++ fi
++ done
++ echo "skipped: Unable to find PKCS#11 token library"
++ exit 0
++}
++
++try_token_libs \
++ /usr/local/lib/softhsm/libsofthsm2.so \
++ /usr/lib64/pkcs11/libsofthsm2.so \
++ /usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so
++
++TEST_SSH_PIN=1234
++TEST_SSH_SOPIN=12345678
++if [ "x$TEST_SSH_SSHPKCS11HELPER" != "x" ]; then
++ SSH_PKCS11_HELPER="${TEST_SSH_SSHPKCS11HELPER}"
++ export SSH_PKCS11_HELPER
++fi
++
++test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist"
++
++# setup environment for softhsm token
++DIR=$OBJ/SOFTHSM
++rm -rf $DIR
++TOKEN=$DIR/tokendir
++mkdir -p $TOKEN
++SOFTHSM2_CONF=$DIR/softhsm2.conf
++export SOFTHSM2_CONF
++cat > $SOFTHSM2_CONF << EOF
++# SoftHSM v2 configuration file
++directories.tokendir = ${TOKEN}
++objectstore.backend = file
++# ERROR, WARNING, INFO, DEBUG
++log.level = DEBUG
++# If CKF_REMOVABLE_DEVICE flag should be set
++slots.removable = false
++EOF
++out=$(softhsm2-util --init-token --free --label token-slot-0 --pin "$TEST_SSH_PIN" --so-pin "$TEST_SSH_SOPIN")
++slot=$(echo -- $out | sed 's/.* //')
++
++# prevent ssh-agent from calling ssh-askpass
++SSH_ASKPASS=/usr/bin/true
++export SSH_ASKPASS
++unset DISPLAY
++# We need interactive access to test PKCS# since it prompts for PIN
++sed -i 's/.*BatchMode.*//g' $OBJ/ssh_proxy
++
++# 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) }' "$@"
++}
++
++trace "generating keys"
++ID1="02"
++ID2="04"
++RSA=${DIR}/RSA
++EC=${DIR}/EC
++openssl genpkey -algorithm rsa > $RSA
++openssl pkcs8 -nocrypt -in $RSA |\
++ softhsm2-util --slot "$slot" --label "SSH RSA Key $ID1" --id $ID1 \
++ --pin "$TEST_SSH_PIN" --import /dev/stdin
++openssl genpkey \
++ -genparam \
++ -algorithm ec \
++ -pkeyopt ec_paramgen_curve:prime256v1 |\
++ openssl genpkey \
++ -paramfile /dev/stdin > $EC
++openssl pkcs8 -nocrypt -in $EC |\
++ softhsm2-util --slot "$slot" --label "SSH ECDSA Key $ID2" --id $ID2 \
++ --pin "$TEST_SSH_PIN" --import /dev/stdin
++
++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 "FAIL: keygen fails to enumerate keys on PKCS#11 token"
++fi
++grep "pkcs11:" $OBJ/token_keys > /dev/null
++if [ $? -ne 0 ]; then
++ fail "FAIL: The keys from ssh-keygen do not contain PKCS#11 URI as a comment"
++fi
++
++# Set the ECDSA key to authorized keys
++grep "ECDSA" $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 "FAIL: ssh connect with pkcs11 failed (exit code $r)"
++fi
++
++trace "Connect with PKCS#11 URI"
++trace " (ECDSA 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 "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
++fi
++
++trace " (RSA 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 "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
++fi
++
++trace "Connect with PKCS#11 URI including PIN should not prompt"
++trace " (ECDSA key should succeed)"
++${SSH} -F $OBJ/ssh_proxy -i \
++ "pkcs11:id=%${ID2}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
++r=$?
++if [ $r -ne 5 ]; then
++ fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
++fi
++
++trace " (RSA key should fail)"
++${SSH} -F $OBJ/ssh_proxy -i \
++ "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}&pin-value=${TEST_SSH_PIN}" somehost exit 5
++r=$?
++if [ $r -eq 5 ]; then
++ fail "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, ECDSA should succeed)"
++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
++ -i "pkcs11:object=SSH%20ECDSA%20Key%2004?module-path=${TEST_SSH_PKCS11}" somehost exit 5
++r=$?
++if [ $r -ne 5 ]; then
++ fail "FAIL: ssh connect with PKCS#11 URI failed (exit code $r)"
++fi
++
++trace " (by object label, RSA key should fail)"
++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
++ -i "pkcs11:object=SSH%20RSA%20Key%2002?module-path=${TEST_SSH_PKCS11}" somehost exit 5
++r=$?
++if [ $r -eq 5 ]; then
++ fail "FAIL: ssh connect with PKCS#11 URI succeeded (should fail)"
++fi
++
++trace " (by token label, ECDSA key should succeed)"
++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \
++ -i "pkcs11:id=%${ID2};token=token-slot-0?module-path=${TEST_SSH_PKCS11}" somehost exit 5
++r=$?
++if [ $r -ne 5 ]; then
++ fail "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=token-slot-99?module-path=${TEST_SSH_PKCS11}" somehost exit 5
++r=$?
++if [ $r -eq 5 ]; then
++ fail "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 " (ECDSA key should succeed)"
++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
++r=$?
++if [ $r -ne 5 ]; then
++ fail "FAIL: ssh connect with PKCS#11 URI in config failed (exit code $r)"
++fi
++
++# Set the RSA key as authorized
++grep "RSA" $OBJ/token_keys > $OBJ/authorized_keys_$USER
++
++trace " (RSA key should fail)"
++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5
++r=$?
++if [ $r -eq 5 ]; then
++ fail "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:?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 "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 " (RSA 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 "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 "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` > /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 "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 "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 "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 "FAIL: ssh-add -d failed with whole provider: exit code $r"
++ fi
++
++ trace "add only RSA 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 "FAIL ssh-add failed with RSA key: exit code $r"
++ fi
++
++ trace " pkcs11 connect via agent (RSA key)"
++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5
++ r=$?
++ if [ $r -ne 5 ]; then
++ fail "FAIL: ssh connect failed with RSA key (exit code $r)"
++ fi
++
++ trace " remove RSA pkcs11 key"
++ ${SSHADD} -d "pkcs11:id=%${ID1}?module-path=${TEST_SSH_PKCS11}" \
++ > /dev/null 2>&1
++ r=$?
++ if [ $r -ne 0 ]; then
++ fail "FAIL: ssh-add -d failed with RSA key: exit code $r"
++ fi
++
++ trace "add only ECDSA 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 "FAIL: ssh-add failed with second key: exit code $r"
++ fi
++
++ trace " pkcs11 connect via agent (ECDSA key should fail)"
++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5
++ r=$?
++ if [ $r -eq 5 ]; then
++ fail "FAIL: ssh connect passed with ECDSA key (should fail)"
++ fi
++
++ trace "add also the RSA 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 "FAIL: ssh-add failed with first key: exit code $r"
++ fi
++
++ trace " remove ECDSA 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 ECDSA 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 "FAIL: ssh-add -d passed with non-existing key (should fail)"
++ fi
++
++ trace " pkcs11 connect via agent (the RSA key should be still usable)"
++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5
++ r=$?
++ if [ $r -ne 5 ]; then
++ fail "ssh connect failed with RSA key (after removing ECDSA): exit code $r"
++ fi
++
++ trace "kill agent"
++ ${SSHAGENT} -k > /dev/null
++fi
+diff -up openssh-8.7p1/regress/unittests/Makefile.pkcs11-uri openssh-8.7p1/regress/unittests/Makefile
+--- openssh-8.7p1/regress/unittests/Makefile.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/regress/unittests/Makefile 2021-08-30 13:07:43.663700096 +0200
+@@ -2,6 +2,6 @@
+
+ REGRESS_FAIL_EARLY?= yes
+ SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion
+-SUBDIR+=authopt misc sshsig
++SUBDIR+=authopt misc sshsig pkcs11
+
+ .include <bsd.subdir.mk>
+diff -up openssh-8.7p1/regress/unittests/pkcs11/tests.c.pkcs11-uri openssh-8.7p1/regress/unittests/pkcs11/tests.c
+--- openssh-8.7p1/regress/unittests/pkcs11/tests.c.pkcs11-uri 2021-08-30 13:07:43.664700104 +0200
++++ openssh-8.7p1/regress/unittests/pkcs11/tests.c 2021-08-30 13:07:43.664700104 +0200
+@@ -0,0 +1,337 @@
++/*
++ * Copyright (c) 2017 Red Hat
++ *
++ * Authors: Jakub Jelen <jjelen@redhat.com>
++ *
++ * 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 <locale.h>
++#include <string.h>
++
++#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, 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, char *pin)
++{
++ 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;
++ uri->pin = pin;
++ return uri;
++}
++
++static void
++test_parse_valid(void)
++{
++ /* path arguments */
++ check_parse("pkcs11:id=%01",
++ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL, NULL));
++ check_parse("pkcs11:id=%00%01",
++ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL, NULL));
++ check_parse("pkcs11:token=SSH%20Keys",
++ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL, NULL));
++ check_parse("pkcs11:library-manufacturer=OpenSC",
++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));
++ check_parse("pkcs11:manufacturer=piv_II",
++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
++ check_parse("pkcs11:object=SIGN%20Key",
++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key", NULL));
++ /* 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, NULL));
++ check_parse("pkcs11:?pin-value=123456",
++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, "123456"));
++
++ /* combinations */
++ /* ID SHOULD be percent encoded */
++ check_parse("pkcs11:token=SSH%20Key;id=0",
++ compose_uri("0", 1, "SSH Key", NULL, 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, 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", NULL));
++ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so&pin-value=123456",
++ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL, "123456"));
++
++ /* 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, NULL));
++ /* Percent character needs to be percent-encoded */
++ check_parse("pkcs11:token=%25",
++ compose_uri(NULL, 0, "%", NULL, 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, NULL));
++ /* MUST NOT contain duplicate attributes of the same name */
++ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1);
++ /* MUST NOT contain duplicate attributes of the same name */
++ check_parse_rv("pkcs11:?pin-value=111111&pin-value=123456", 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, NULL));
++ check_gen("pkcs11:id=%00%01",
++ compose_uri("\x00\x01", 2, NULL, 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, NULL));
++ /* library-manufacturer is not implmented now */
++ /*check_gen("pkcs11:library-manufacturer=OpenSC",
++ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL, NULL));*/
++ check_gen("pkcs11:manufacturer=piv_II",
++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL, NULL));
++ check_gen("pkcs11:object=RSA%20Key",
++ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key", NULL));
++ /* 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, NULL));
++
++ /* combinations */
++ check_gen("pkcs11:id=%02;token=SSH%20Keys",
++ compose_uri("\x02", 1, "SSH Keys", NULL, 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, NULL));
++ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II",
++ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key", NULL));
++
++ /* empty path component matches everything */
++ check_gen("pkcs11:", EMPTY_URI);
++
++}
++
++void
++check_encode(char *source, size_t len, char *allow_list, 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, allow_list);
++ 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 allow list 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 allow list 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&amp;#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 -up openssh-8.7p1/ssh-add.c.pkcs11-uri openssh-8.7p1/ssh-add.c
+--- openssh-8.7p1/ssh-add.c.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-add.c 2021-08-30 13:07:43.664700104 +0200
+@@ -68,6 +68,7 @@
+ #include "ssh-sk.h"
+ #include "sk-api.h"
+ #include "hostfile.h"
++#include "ssh-pkcs11-uri.h"
+
+ /* argv0 */
+ extern char *__progname;
+@@ -229,6 +230,34 @@ delete_all(int agent_fd, int qflag)
+ return ret;
+ }
+
++#ifdef ENABLE_PKCS11
++static int update_card(int, int, const char *, int, struct dest_constraint **, size_t, char *);
++
++int
++update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag,
++ struct dest_constraint **dest_constraints, size_t ndest_constraints)
++{
++ char *pin = NULL;
++ 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");
++ if (uri->pin != NULL) {
++ pin = strdup(uri->pin);
++ if (pin == NULL) {
++ fatal("Failed to dupplicate string");
++ }
++ /* pin is freed in the update_card() */
++ }
++ pkcs11_uri_cleanup(uri);
++
++ return update_card(agent_fd, adding, pkcs11_uri, qflag,
++ dest_constraints, ndest_constraints, pin);
++}
++#endif
++
+ static int
+ add_file(int agent_fd, const char *filename, int key_only, int qflag,
+ const char *skprovider, struct dest_constraint **dest_constraints,
+@@ -445,12 +472,11 @@ add_file(int agent_fd, const char *filen
+
+ static int
+ update_card(int agent_fd, int add, const char *id, int qflag,
+- struct dest_constraint **dest_constraints, size_t ndest_constraints)
++ struct dest_constraint **dest_constraints, size_t ndest_constraints, char *pin)
+ {
+- char *pin = NULL;
+ int r, ret = -1;
+
+- if (add) {
++ if (add && pin == NULL) {
+ if ((pin = read_passphrase("Enter passphrase for PKCS#11: ",
+ RP_ALLOW_STDIN)) == NULL)
+ return -1;
+@@ -630,6 +656,14 @@ static int
+ const char *skprovider, struct dest_constraint **dest_constraints,
+ size_t ndest_constraints)
+ {
++#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, qflag,
++ dest_constraints, ndest_constraints);
++ }
++#endif
+ if (deleting) {
+ if (delete_file(agent_fd, file, key_only, qflag) == -1)
+ return -1;
+@@ -813,7 +846,7 @@ main(int argc, char **argv)
+ }
+ if (pkcs11provider != NULL) {
+ if (update_card(agent_fd, !deleting, pkcs11provider,
+- qflag, dest_constraints, ndest_constraints) == -1)
++ qflag, dest_constraints, ndest_constraints, NULL) == -1)
+ ret = 1;
+ goto done;
+ }
+diff -up openssh-8.7p1/ssh-agent.c.pkcs11-uri openssh-8.7p1/ssh-agent.c
+--- openssh-8.7p1/ssh-agent.c.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-agent.c 2021-08-30 13:07:43.664700104 +0200
+@@ -847,10 +847,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 PKCS#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, allowed_providers, 0) != 1) {
++ verbose("refusing PKCS#11 provider \"%.100s\": "
++ "not allowed", 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;
+ char **comments = NULL;
+ int r, i, count = 0, success = 0, confirm = 0;
+ u_int seconds = 0;
+@@ -869,33 +931,28 @@ process_add_smartcard_key(SocketEntry *e
+ error_f("failed to parse constraints");
+ 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, allowed_providers, 0) != 1) {
+- verbose("refusing PKCS#11 add of \"%.100s\": "
+- "provider not allowed", canonical_provider);
++
++ sane_uri = sanitize_pkcs11_provider(provider);
++ if (sane_uri == NULL)
+ goto send;
+- }
+- debug_f("add %.100s", canonical_provider);
++
+ if (lifetime && !death)
+ death = monotime() + lifetime;
+
+- count = pkcs11_add_provider(canonical_provider, pin, &keys, &comments);
++ debug_f("add %.100s", sane_uri);
++ count = pkcs11_add_provider(sane_uri, pin, &keys, &comments);
+ for (i = 0; i < count; i++) {
+ k = keys[i];
+ if (lookup_identity(k) == NULL) {
+ id = xcalloc(1, sizeof(Identity));
+ id->key = k;
+ keys[i] = NULL; /* transferred */
+- id->provider = xstrdup(canonical_provider);
++ id->provider = xstrdup(sane_uri);
+ if (*comments[i] != '\0') {
+ id->comment = comments[i];
+ comments[i] = NULL; /* transferred */
+ } else {
+- id->comment = xstrdup(canonical_provider);
++ id->comment = xstrdup(sane_uri);
+ }
+ id->death = death;
+ id->confirm = confirm;
+@@ -910,6 +967,7 @@ process_add_smartcard_key(SocketEntry *e
+ send:
+ free(pin);
+ free(provider);
++ free(sane_uri);
+ free(keys);
+ free(comments);
+ free_dest_constraints(dest_constraints, ndest_constraints);
+@@ -918,7 +976,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;
+
+@@ -930,30 +988,29 @@ process_remove_smartcard_key(SocketEntry
+ }
+ 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_f("remove %.100s", canonical_provider);
++ debug_f("remove %.100s", 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_f("pkcs11_del_provider failed");
+ send:
+ free(provider);
++ free(sane_uri);
+ send_status(e, success);
+ }
+ #endif /* ENABLE_PKCS11 */
+diff -up openssh-8.7p1/ssh_config.5.pkcs11-uri openssh-8.7p1/ssh_config.5
+--- openssh-8.7p1/ssh_config.5.pkcs11-uri 2021-08-30 13:07:43.578699383 +0200
++++ openssh-8.7p1/ssh_config.5 2021-08-30 13:07:43.664700104 +0200
+@@ -1111,6 +1111,21 @@ 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 arguments
++.Cm module-path
++and
++.Cm pin-value
++). 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 -up openssh-8.7p1/ssh.c.pkcs11-uri openssh-8.7p1/ssh.c
+--- openssh-8.7p1/ssh.c.pkcs11-uri 2021-08-30 13:07:43.578699383 +0200
++++ openssh-8.7p1/ssh.c 2021-08-30 13:07:43.666700121 +0200
+@@ -826,6 +826,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) == -1)
+ fprintf(stderr, "Warning: Identity file %s "
+@@ -1681,6 +1689,7 @@ main(int ac, char **av)
+ #ifdef ENABLE_PKCS11
+ (void)pkcs11_del_provider(options.pkcs11_provider);
+ #endif
++ pkcs11_terminate();
+
+ skip_connect:
+ exit_status = ssh_session2(ssh, cinfo);
+@@ -2197,6 +2206,45 @@ ssh_session2(struct ssh *ssh, const stru
+ 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 PKCS#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, NULL)) > 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(const struct ssh_conn_info *cinfo)
+@@ -2211,11 +2259,6 @@ load_public_identity_files(const struct
+ 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 = NULL;
+- char **comments = NULL;
+- int nkeys;
+-#endif /* PKCS11 */
+
+ n_ids = n_certs = 0;
+ memset(identity_files, 0, sizeof(identity_files));
+@@ -2228,33 +2271,46 @@ load_public_identity_files(const struct
+ 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, &comments)) > 0) {
+- for (i = 0; i < nkeys; i++) {
+- if (n_ids >= SSH_MAX_IDENTITY_FILES) {
+- sshkey_free(keys[i]);
+- free(comments[i]);
+- continue;
+- }
+- identity_keys[n_ids] = keys[i];
+- identity_files[n_ids] = comments[i]; /* transferred */
+- n_ids++;
+- }
+- free(keys);
+- free(comments);
++ /* 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 PKCS#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 = default_client_percent_dollar_expand(cp, cinfo);
+ free(cp);
+ check_load(sshkey_load_public(filename, &public, NULL),
+diff -up openssh-8.7p1/ssh-keygen.c.pkcs11-uri openssh-8.7p1/ssh-keygen.c
+--- openssh-8.7p1/ssh-keygen.c.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-keygen.c 2021-08-30 13:07:43.666700121 +0200
+@@ -860,8 +860,11 @@ do_download(struct passwd *pw)
+ free(fp);
+ } else {
+ (void) sshkey_write(keys[i], stdout); /* XXX check */
+- fprintf(stdout, "%s%s\n",
+- *(comments[i]) == '\0' ? "" : " ", comments[i]);
++ if (*(comments[i]) != '\0') {
++ fprintf(stdout, " %s", comments[i]);
++ }
++ (void) pkcs11_uri_write(keys[i], stdout);
++ fprintf(stdout, "\n");
+ }
+ free(comments[i]);
+ sshkey_free(keys[i]);
+diff -up openssh-8.7p1/ssh-pkcs11-client.c.pkcs11-uri openssh-8.7p1/ssh-pkcs11-client.c
+--- openssh-8.7p1/ssh-pkcs11-client.c.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-pkcs11-client.c 2021-08-30 13:07:43.666700121 +0200
+@@ -323,6 +323,8 @@ pkcs11_add_provider(char *name, char *pi
+ u_int nkeys, i;
+ struct sshbuf *msg;
+
++ debug_f("called, name = %s", name);
++
+ if (fd < 0 && pkcs11_start_helper() < 0)
+ return (-1);
+
+@@ -342,6 +344,7 @@ pkcs11_add_provider(char *name, char *pi
+ *keysp = xcalloc(nkeys, sizeof(struct sshkey *));
+ if (labelsp)
+ *labelsp = xcalloc(nkeys, sizeof(char *));
++ debug_f("nkeys = %u", nkeys);
+ for (i = 0; i < nkeys; i++) {
+ /* XXX clean up properly instead of fatal() */
+ if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 ||
+diff -up openssh-8.7p1/ssh-pkcs11.c.pkcs11-uri openssh-8.7p1/ssh-pkcs11.c
+--- openssh-8.7p1/ssh-pkcs11.c.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-pkcs11.c 2021-08-30 13:12:27.709084157 +0200
+@@ -55,8 +55,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;
+@@ -65,6 +65,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;
+ };
+
+@@ -75,6 +82,7 @@ struct pkcs11_key {
+ CK_ULONG slotidx;
+ char *keyid;
+ int keyid_len;
++ char *label;
+ };
+
+ int pkcs11_interactive = 0;
+@@ -106,26 +114,61 @@ 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_f("provider \"%s\" refcount %d valid %d",
+- p->name, p->refcount, p->valid);
+- if (!p->valid)
++ debug_f("%p refcount %d valid %d", 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_f("%p refcount %d", m, m->refcount);
++ if (--m->refcount <= 0) {
++ pkcs11_module_finalize(m);
++ if (m->valid)
++ error_f("%p still valid", m);
++ free(m->slotlist);
++ free(m->slotinfo);
++ free(m->module_path);
++ free(m);
++ }
++}
++
++/*
++ * finalize a provider shared library, it's no longer usable.
++ * however, there might still be keys referencing this provider,
++ * so the actual 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_f("%p refcount %d valid %d", 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);
+ }
+
+ /*
+@@ -137,11 +180,9 @@ pkcs11_provider_unref(struct pkcs11_prov
+ {
+ debug_f("provider \"%s\" refcount %d", p->name, p->refcount);
+ if (--p->refcount <= 0) {
+- if (p->valid)
+- error_f("provider \"%s\" still valid", p->name);
+ free(p->name);
+- free(p->slotlist);
+- free(p->slotinfo);
++ if (p->module)
++ pkcs11_module_unref(p->module);
+ free(p);
+ }
+ }
+@@ -159,6 +200,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)
+@@ -173,19 +228,55 @@ 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_f("called, provider_id = %s", provider_id);
++
++ if (provider_id == NULL)
++ return 0;
++
++ uri = pkcs11_uri_init();
++ if (uri == NULL)
++ fatal("Failed to init PKCS#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_f("called with provider %s", 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;
+ }
+
+ static RSA_METHOD *rsa_method;
+@@ -195,6 +286,55 @@ static EC_KEY_METHOD *ec_key_method;
+ static int ec_key_idx = 0;
+ #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */
+
++/*
++ * 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? */
++ switch (key->type) {
++ case KEY_RSA:
++ k11 = RSA_get_ex_data(key->rsa, rsa_idx);
++ break;
++#ifdef HAVE_EC_KEY_METHOD_NEW
++ case KEY_ECDSA:
++ k11 = EC_KEY_get_ex_data(key->ecdsa, ec_key_idx);
++ break;
++#endif
++ default:
++ error("Unknown key type %d", key->type);
++ return -1;
++ }
++ if (k11 == NULL) {
++ error("Failed to get ex_data for key type %d", key->type);
++ 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;
++}
++
+ /* release a wrapped object */
+ static void
+ pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx,
+@@ -208,6 +348,7 @@ pkcs11_k11_free(void *parent, void *ptr,
+ if (k11->provider)
+ pkcs11_provider_unref(k11->provider);
+ free(k11->keyid);
++ free(k11->label);
+ free(k11);
+ }
+
+@@ -222,8 +363,8 @@ pkcs11_find(struct pkcs11_provider *p, C
+ 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);
+@@ -262,12 +403,12 @@ pkcs11_login_slot(struct pkcs11_provider
+ else {
+ snprintf(prompt, sizeof(prompt), "Enter PIN for '%s': ",
+ si->token.label);
+- if ((pin = read_passphrase(prompt, RP_ALLOW_EOF)) == NULL) {
++ if ((pin = read_passphrase(prompt, RP_ALLOW_EOF|RP_ALLOW_STDIN)) == NULL) {
+ debug_f("no pin specified");
+ return (-1); /* bail out */
+ }
+ }
+- rv = provider->function_list->C_Login(si->session, type, (u_char *)pin,
++ rv = provider->module->function_list->C_Login(si->session, type, (u_char *)pin,
+ (pin != NULL) ? strlen(pin) : 0);
+ if (pin != NULL)
+ freezero(pin, strlen(pin));
+@@ -297,13 +438,14 @@ pkcs11_login_slot(struct pkcs11_provider
+ static int
+ pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type)
+ {
+- if (k11 == NULL || k11->provider == NULL || !k11->provider->valid) {
++ if (k11 == NULL || k11->provider == NULL || !k11->provider->valid ||
++ k11->provider->module == NULL || !k11->provider->module->valid) {
+ error("no pkcs11 (valid) provider found");
+ return (-1);
+ }
+
+ return pkcs11_login_slot(k11->provider,
+- &k11->provider->slotinfo[k11->slotidx], type);
++ &k11->provider->module->slotinfo[k11->slotidx], type);
+ }
+
+
+@@ -319,13 +461,14 @@ pkcs11_check_obj_bool_attrib(struct pkcs
+
+ *val = 0;
+
+- if (!k11->provider || !k11->provider->valid) {
++ if (!k11->provider || !k11->provider->valid ||
++ !k11->provider->module || !k11->provider->module->valid) {
+ error("no pkcs11 (valid) provider found");
+ 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];
+
+ attr.type = type;
+ attr.pValue = &flag;
+@@ -356,13 +499,14 @@ pkcs11_get_key(struct pkcs11_key *k11, C
+ int always_auth = 0;
+ int did_login = 0;
+
+- if (!k11->provider || !k11->provider->valid) {
++ if (!k11->provider || !k11->provider->valid ||
++ !k11->provider->module || !k11->provider->module->valid) {
+ error("no pkcs11 (valid) provider found");
+ 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_login(k11, CKU_USER) < 0) {
+@@ -439,8 +583,8 @@ pkcs11_rsa_private_encrypt(int flen, con
+ 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];
+ tlen = RSA_size(rsa);
+
+ /* XXX handle CKR_BUFFER_TOO_SMALL */
+@@ -484,7 +628,7 @@ pkcs11_rsa_start_wrapper(void)
+ /* 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;
+
+@@ -502,6 +646,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *
+ 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;
++ }
++
+ RSA_set_method(rsa, rsa_method);
+ RSA_set_ex_data(rsa, rsa_idx, k11);
+ return (0);
+@@ -532,8 +682,8 @@ ecdsa_do_sign(const unsigned char *dgst,
+ return (NULL);
+ }
+
+- f = k11->provider->function_list;
+- si = &k11->provider->slotinfo[k11->slotidx];
++ f = k11->provider->module->function_list;
++ si = &k11->provider->module->slotinfo[k11->slotidx];
+
+ siglen = ECDSA_size(ec);
+ sig = xmalloc(siglen);
+@@ -598,7 +748,7 @@ pkcs11_ecdsa_start_wrapper(void)
+
+ static int
+ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx,
+- CK_ATTRIBUTE *keyid_attrib, EC_KEY *ec)
++ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ec)
+ {
+ struct pkcs11_key *k11;
+
+@@ -614,6 +764,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider
+ 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;
++ }
++
+ EC_KEY_set_method(ec, ec_key_method);
+ EC_KEY_set_ex_data(ec, ec_key_idx, k11);
+
+@@ -650,8 +806,8 @@ pkcs11_open_session(struct pkcs11_provid
+ CK_SESSION_HANDLE session;
+ int login_required, ret;
+
+- f = p->function_list;
+- si = &p->slotinfo[slotidx];
++ f = p->module->function_list;
++ si = &p->module->slotinfo[slotidx];
+
+ login_required = si->token.flags & CKF_LOGIN_REQUIRED;
+
+@@ -661,9 +817,9 @@ pkcs11_open_session(struct pkcs11_provid
+ error("pin required");
+ return (-SSH_PKCS11_ERR_PIN_REQUIRED);
+ }
+- 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 != NULL && strlen(pin) != 0) {
+@@ -699,7 +855,8 @@ static struct sshkey *
+ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+ CK_OBJECT_HANDLE *obj)
+ {
+- CK_ATTRIBUTE key_attr[3];
++ CK_ATTRIBUTE key_attr[4];
++ int nattr = 4;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+@@ -713,14 +870,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+
+ memset(&key_attr, 0, sizeof(key_attr));
+ key_attr[0].type = CKA_ID;
+- key_attr[1].type = CKA_EC_POINT;
+- key_attr[2].type = CKA_EC_PARAMS;
++ key_attr[1].type = CKA_LABEL;
++ key_attr[2].type = CKA_EC_POINT;
++ key_attr[3].type = CKA_EC_PARAMS;
+
+- session = p->slotinfo[slotidx].session;
+- f = p->function_list;
++ session = p->module->slotinfo[slotidx].session;
++ f = p->module->function_list;
+
+ /* figure out size of the attributes */
+- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ return (NULL);
+@@ -731,19 +889,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+ * ensure that none of the others are zero length.
+ * XXX assumes CKA_ID is always first.
+ */
+- if (key_attr[1].ulValueLen == 0 ||
+- key_attr[2].ulValueLen == 0) {
++ if (key_attr[2].ulValueLen == 0 ||
++ key_attr[3].ulValueLen == 0) {
+ error("invalid attribute length");
+ return (NULL);
+ }
+
+ /* allocate buffers for attributes */
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ if (key_attr[i].ulValueLen > 0)
+ key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
+
+ /* retrieve ID, public point and curve parameters of EC key */
+- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ goto fail;
+@@ -755,8 +913,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+ goto fail;
+ }
+
+- attrp = key_attr[2].pValue;
+- group = d2i_ECPKParameters(NULL, &attrp, key_attr[2].ulValueLen);
++ attrp = key_attr[3].pValue;
++ group = d2i_ECPKParameters(NULL, &attrp, key_attr[3].ulValueLen);
+ if (group == NULL) {
+ ossl_error("d2i_ECPKParameters failed");
+ goto fail;
+@@ -767,13 +925,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+ goto fail;
+ }
+
+- if (key_attr[1].ulValueLen <= 2) {
++ if (key_attr[2].ulValueLen <= 2) {
+ error("CKA_EC_POINT too small");
+ goto fail;
+ }
+
+- attrp = key_attr[1].pValue;
+- octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[1].ulValueLen);
++ attrp = key_attr[2].pValue;
++ octet = d2i_ASN1_OCTET_STRING(NULL, &attrp, key_attr[2].ulValueLen);
+ if (octet == NULL) {
+ ossl_error("d2i_ASN1_OCTET_STRING failed");
+ goto fail;
+@@ -790,7 +948,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+ goto fail;
+ }
+
+- if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], ec))
++ if (pkcs11_ecdsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], ec))
+ goto fail;
+
+ key = sshkey_new(KEY_UNSPEC);
+@@ -806,7 +964,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_
+ ec = NULL; /* now owned by key */
+
+ fail:
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ free(key_attr[i].pValue);
+ if (ec)
+ EC_KEY_free(ec);
+@@ -823,7 +981,8 @@ static struct sshkey *
+ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+ CK_OBJECT_HANDLE *obj)
+ {
+- CK_ATTRIBUTE key_attr[3];
++ CK_ATTRIBUTE key_attr[4];
++ int nattr = 4;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+@@ -834,14 +993,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
+
+ memset(&key_attr, 0, sizeof(key_attr));
+ key_attr[0].type = CKA_ID;
+- key_attr[1].type = CKA_MODULUS;
+- key_attr[2].type = CKA_PUBLIC_EXPONENT;
++ key_attr[1].type = CKA_LABEL;
++ key_attr[2].type = CKA_MODULUS;
++ key_attr[3].type = CKA_PUBLIC_EXPONENT;
+
+- session = p->slotinfo[slotidx].session;
+- f = p->function_list;
++ session = p->module->slotinfo[slotidx].session;
++ f = p->module->function_list;
+
+ /* figure out size of the attributes */
+- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ return (NULL);
+@@ -852,19 +1012,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
+ * ensure that none of the others are zero length.
+ * XXX assumes CKA_ID is always first.
+ */
+- if (key_attr[1].ulValueLen == 0 ||
+- key_attr[2].ulValueLen == 0) {
++ if (key_attr[2].ulValueLen == 0 ||
++ key_attr[3].ulValueLen == 0) {
+ error("invalid attribute length");
+ return (NULL);
+ }
+
+ /* allocate buffers for attributes */
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ if (key_attr[i].ulValueLen > 0)
+ key_attr[i].pValue = xcalloc(1, key_attr[i].ulValueLen);
+
+ /* retrieve ID, modulus and public exponent of RSA key */
+- rv = f->C_GetAttributeValue(session, *obj, key_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ goto fail;
+@@ -876,8 +1036,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
+ goto fail;
+ }
+
+- rsa_n = BN_bin2bn(key_attr[1].pValue, key_attr[1].ulValueLen, NULL);
+- rsa_e = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
++ rsa_n = BN_bin2bn(key_attr[2].pValue, key_attr[2].ulValueLen, NULL);
++ rsa_e = BN_bin2bn(key_attr[3].pValue, key_attr[3].ulValueLen, NULL);
+ if (rsa_n == NULL || rsa_e == NULL) {
+ error("BN_bin2bn failed");
+ goto fail;
+@@ -886,7 +1046,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
+ fatal_f("set key");
+ rsa_n = rsa_e = NULL; /* transferred */
+
+- if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], rsa))
++ if (pkcs11_rsa_wrap(p, slotidx, &key_attr[0], &key_attr[1], rsa))
+ goto fail;
+
+ key = sshkey_new(KEY_UNSPEC);
+@@ -901,7 +1061,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_pr
+ rsa = NULL; /* now owned by key */
+
+ fail:
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ free(key_attr[i].pValue);
+ RSA_free(rsa);
+
+@@ -912,7 +1072,8 @@ static int
+ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx,
+ CK_OBJECT_HANDLE *obj, struct sshkey **keyp, char **labelp)
+ {
+- CK_ATTRIBUTE cert_attr[3];
++ CK_ATTRIBUTE cert_attr[4];
++ int nattr = 4;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+@@ -936,14 +1097,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+
+ memset(&cert_attr, 0, sizeof(cert_attr));
+ cert_attr[0].type = CKA_ID;
+- cert_attr[1].type = CKA_SUBJECT;
+- cert_attr[2].type = CKA_VALUE;
++ cert_attr[1].type = CKA_LABEL;
++ cert_attr[2].type = CKA_SUBJECT;
++ cert_attr[3].type = CKA_VALUE;
+
+- session = p->slotinfo[slotidx].session;
+- f = p->function_list;
++ session = p->module->slotinfo[slotidx].session;
++ f = p->module->function_list;
+
+ /* figure out size of the attributes */
+- rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ return -1;
+@@ -955,18 +1117,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+ * XXX assumes CKA_ID is always first.
+ */
+ if (cert_attr[1].ulValueLen == 0 ||
+- cert_attr[2].ulValueLen == 0) {
++ cert_attr[2].ulValueLen == 0 ||
++ cert_attr[3].ulValueLen == 0) {
+ error("invalid attribute length");
+ return -1;
+ }
+
+ /* allocate buffers for attributes */
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ if (cert_attr[i].ulValueLen > 0)
+ cert_attr[i].pValue = xcalloc(1, cert_attr[i].ulValueLen);
+
+ /* retrieve ID, subject and value of certificate */
+- rv = f->C_GetAttributeValue(session, *obj, cert_attr, 3);
++ rv = f->C_GetAttributeValue(session, *obj, cert_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_GetAttributeValue failed: %lu", rv);
+ goto out;
+@@ -980,8 +1143,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+ subject = xstrdup("invalid subject");
+ X509_NAME_free(x509_name);
+
+- cp = cert_attr[2].pValue;
+- if ((x509 = d2i_X509(NULL, &cp, cert_attr[2].ulValueLen)) == NULL) {
++ cp = cert_attr[3].pValue;
++ if ((x509 = d2i_X509(NULL, &cp, cert_attr[3].ulValueLen)) == NULL) {
+ error("d2i_x509 failed");
+ goto out;
+ }
+@@ -1001,7 +1164,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+ goto out;
+ }
+
+- if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa))
++ if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa))
+ goto out;
+
+ key = sshkey_new(KEY_UNSPEC);
+@@ -1031,7 +1194,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+ goto out;
+ }
+
+- if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec))
++ if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec))
+ goto out;
+
+ key = sshkey_new(KEY_UNSPEC);
+@@ -1051,7 +1214,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_p
+ goto out;
+ }
+ out:
+- for (i = 0; i < 3; i++)
++ for (i = 0; i < nattr; i++)
+ free(cert_attr[i].pValue);
+ X509_free(x509);
+ RSA_free(rsa);
+@@ -1102,11 +1265,12 @@ note_key(struct pkcs11_provider *p, CK_U
+ */
+ static int
+ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx,
+- struct sshkey ***keysp, char ***labelsp, int *nkeys)
++ struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
+ {
+ struct sshkey *key = NULL;
+ CK_OBJECT_CLASS key_class;
+- CK_ATTRIBUTE key_attr[1];
++ CK_ATTRIBUTE key_attr[3];
++ int nattr = 1;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+@@ -1123,10 +1287,23 @@ pkcs11_fetch_certs(struct pkcs11_provide
+ key_attr[0].pValue = &key_class;
+ key_attr[0].ulValueLen = sizeof(key_class);
+
+- session = p->slotinfo[slotidx].session;
+- f = p->function_list;
++ if (uri->id != NULL) {
++ key_attr[nattr].type = CKA_ID;
++ key_attr[nattr].pValue = uri->id;
++ key_attr[nattr].ulValueLen = uri->id_len;
++ nattr++;
++ }
++ if (uri->object != NULL) {
++ key_attr[nattr].type = CKA_LABEL;
++ key_attr[nattr].pValue = uri->object;
++ key_attr[nattr].ulValueLen = strlen(uri->object);
++ nattr++;
++ }
++
++ session = p->module->slotinfo[slotidx].session;
++ f = p->module->function_list;
+
+- rv = f->C_FindObjectsInit(session, key_attr, 1);
++ rv = f->C_FindObjectsInit(session, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_FindObjectsInit failed: %lu", rv);
+ goto fail;
+@@ -1207,11 +1384,12 @@ fail:
+ */
+ static int
+ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx,
+- struct sshkey ***keysp, char ***labelsp, int *nkeys)
++ struct sshkey ***keysp, char ***labelsp, int *nkeys, struct pkcs11_uri *uri)
+ {
+ struct sshkey *key = NULL;
+ CK_OBJECT_CLASS key_class;
+- CK_ATTRIBUTE key_attr[2];
++ CK_ATTRIBUTE key_attr[3];
++ int nattr = 1;
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST *f = NULL;
+ CK_RV rv;
+@@ -1227,10 +1405,23 @@ pkcs11_fetch_keys(struct pkcs11_provider
+ key_attr[0].pValue = &key_class;
+ key_attr[0].ulValueLen = sizeof(key_class);
+
+- session = p->slotinfo[slotidx].session;
+- f = p->function_list;
++ if (uri->id != NULL) {
++ key_attr[nattr].type = CKA_ID;
++ key_attr[nattr].pValue = uri->id;
++ key_attr[nattr].ulValueLen = uri->id_len;
++ nattr++;
++ }
++ if (uri->object != NULL) {
++ key_attr[nattr].type = CKA_LABEL;
++ key_attr[nattr].pValue = uri->object;
++ key_attr[nattr].ulValueLen = strlen(uri->object);
++ nattr++;
++ }
+
+- rv = f->C_FindObjectsInit(session, key_attr, 1);
++ session = p->module->slotinfo[slotidx].session;
++ f = p->module->function_list;
++
++ rv = f->C_FindObjectsInit(session, key_attr, nattr);
+ if (rv != CKR_OK) {
+ error("C_FindObjectsInit failed: %lu", rv);
+ goto fail;
+@@ -1499,16 +1690,10 @@ pkcs11_ecdsa_generate_private_key(struct
+ }
+ #endif /* WITH_PKCS11_KEYGEN */
+
+-/*
+- * register a new provider, fails if provider already exists. if
+- * keyp is provided, fetch keys.
+- */
+ static int
+-pkcs11_register_provider(char *provider_id, char *pin,
+- struct sshkey ***keyp, char ***labelsp,
+- struct pkcs11_provider **providerp, CK_ULONG user)
++pkcs11_initialize_provider(struct pkcs11_uri *uri, struct pkcs11_provider **providerp)
+ {
+- int nkeys, need_finalize = 0;
++ int need_finalize = 0;
+ int ret = -1;
+ struct pkcs11_provider *p = NULL;
+ void *handle = NULL;
+@@ -1517,164 +1702,298 @@ pkcs11_register_provider(char *provider_
+ CK_FUNCTION_LIST *f = NULL;
+ CK_TOKEN_INFO *token;
+ CK_ULONG i;
++ char *provider_module = NULL;
++ struct pkcs11_module *m = NULL;
+
+- if (providerp == NULL)
++ /* 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_f("No module path provided");
+ goto fail;
+- *providerp = NULL;
++#endif
++ } else {
++ provider_module = strdup(uri->module_path);
++ }
+
+- if (keyp != NULL)
+- *keyp = NULL;
+- if (labelsp != NULL)
+- *labelsp = NULL;
++ p = xcalloc(1, sizeof(*p));
++ p->name = pkcs11_uri_get(uri);
+
+- if (pkcs11_provider_lookup(provider_id) != NULL) {
+- debug_f("provider already registered: %s", provider_id);
+- goto fail;
++ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL
++ && m->valid) {
++ debug_f("provider module already initialized: %s", 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 */
++ *providerp = p;
++ return 0;
++ } else {
++ m = xcalloc(1, sizeof(*m));
++ p->module = m;
++ m->refcount++;
+ }
++
+ /* open shared pkcs11-library */
+- 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;
++
++ p->module->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(m->info.manufacturerID, sizeof(m->info.manufacturerID));
++ if (uri->lib_manuf != NULL &&
++ strcmp(uri->lib_manuf, m->info.manufacturerID)) {
++ debug_f("Skipping provider %s not matching library_manufacturer",
++ m->info.manufacturerID);
+ goto fail;
+ }
+- rmspace(p->info.manufacturerID, sizeof(p->info.manufacturerID));
+- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription));
++ 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) {
+- debug_f("provider %s returned no slots", provider_id);
++ if (m->nslots == 0) {
++ debug_f("provider %s returned no slots", provider_module);
+ ret = -SSH_PKCS11_ERR_NO_SLOTS;
+ 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));
+ 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))
++ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo));
++ m->valid = 1;
++ 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, (u_long)i, rv);
+- continue;
+- }
+- if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
+- debug2_f("ignoring uninitialised token in "
+- "provider %s slot %lu", provider_id, (u_long)i);
++ "failed: %lu", provider_module, (u_long)i, rv);
++ token->flags = 0;
+ 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 */
++ *providerp = p;
++ return 0;
++
++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);
++ if (m) {
++ free(m->slotlist);
++ free(m);
++ }
++ if (p) {
++ free(p->name);
++ free(p);
++ }
++ if (handle)
++ dlclose(handle);
++ return ret;
++}
++
++/*
++ * register a new provider, fails if provider already exists. if
++ * keyp is provided, fetch keys.
++ */
++static int
++pkcs11_register_provider_by_uri(struct pkcs11_uri *uri, char *pin,
++ struct sshkey ***keyp, char ***labelsp, struct pkcs11_provider **providerp,
++ CK_ULONG user)
++{
++ int nkeys;
++ int ret = -1;
++ struct pkcs11_provider *p = NULL;
++ CK_ULONG i;
++ CK_TOKEN_INFO *token;
++ char *provider_uri = NULL;
++
++ if (providerp == NULL)
++ goto fail;
++ *providerp = NULL;
++
++ if (keyp != NULL)
++ *keyp = NULL;
++
++ if ((ret = pkcs11_initialize_provider(uri, &p)) != 0) {
++ goto fail;
++ }
++
++ provider_uri = pkcs11_uri_get(uri);
++ if (pin == NULL && uri->pin != NULL) {
++ pin = uri->pin;
++ }
++ nkeys = 0;
++ for (i = 0; i < p->module->nslots; i++) {
++ token = &p->module->slotinfo[i].token;
++ if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) {
++ debug2_f("ignoring uninitialised token in "
++ "provider %s slot %lu", provider_uri, (u_long)i);
++ continue;
++ }
++ if (uri->token != NULL &&
++ strcmp(token->label, uri->token) != 0) {
++ debug2_f("ignoring token not matching label (%s) "
++ "specified by PKCS#11 URI in slot %lu",
++ token->label, (unsigned long)i);
++ continue;
++ }
++ if (uri->manuf != NULL &&
++ strcmp(token->manufacturerID, uri->manuf) != 0) {
++ debug2_f("ignoring token not matching requrested "
++ "manufacturerID (%s) specified by PKCS#11 URI in "
++ "slot %lu", 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 keyp is provided)
++ * open session if not yet openend, login with pin and
++ * retrieve public keys (if keyp is provided)
+ */
+- if ((ret = pkcs11_open_session(p, i, pin, user)) != 0 ||
++ if ((p->module->slotinfo[i].session != 0 ||
++ (ret = pkcs11_open_session(p, i, pin, user)) != 0) && /* ??? */
+ keyp == NULL)
+ continue;
+- pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
+- pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
+- if (nkeys == 0 && !p->slotinfo[i].logged_in &&
++ pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
++ pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
++ if (nkeys == 0 && !p->module->slotinfo[i].logged_in &&
+ pkcs11_interactive) {
+ /*
+ * Some tokens require login before they will
+ * expose keys.
+ */
+- if (pkcs11_login_slot(p, &p->slotinfo[i],
++ debug3_f("Trying to login as there were no keys found");
++ if (pkcs11_login_slot(p, &p->module->slotinfo[i],
+ CKU_USER) < 0) {
+ error("login failed");
+ continue;
+ }
+- pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys);
+- pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys);
++ pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
++ pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
++ }
++ if (nkeys == 0 && uri->object != NULL) {
++ debug3_f("No keys found. Retrying without label (%s) ",
++ uri->object);
++ /* Try once more without the label filter */
++ char *label = uri->object;
++ uri->object = NULL; /* XXX clone uri? */
++ pkcs11_fetch_keys(p, i, keyp, labelsp, &nkeys, uri);
++ pkcs11_fetch_certs(p, i, keyp, labelsp, &nkeys, uri);
++ uri->object = label;
+ }
+ }
++ pin = NULL; /* Will be cleaned up with URI */
+
+ /* now owned by caller */
+ *providerp = p;
+
+- TAILQ_INSERT_TAIL(&pkcs11_providers, p, next);
+- p->refcount++; /* add to provider list */
+-
++ free(provider_uri);
+ return (nkeys);
+ 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->name);
+- free(p->slotlist);
+- free(p->slotinfo);
+- free(p);
++ TAILQ_REMOVE(&pkcs11_providers, p, next);
++ pkcs11_provider_unref(p);
+ }
+- if (handle)
+- dlclose(handle);
+ if (ret > 0)
+ ret = -1;
+ return (ret);
+ }
+
+-/*
+- * register a new provider and get number of keys hold by the token,
+- * fails if provider already exists
+- */
++static int
++pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp,
++ char ***labelsp, struct pkcs11_provider **providerp, CK_ULONG user)
++{
++ struct pkcs11_uri *uri = NULL;
++ int r;
++
++ debug_f("called, provider_id = %s", provider_id);
++
++ uri = pkcs11_uri_init();
++ if (uri == NULL)
++ fatal("failed to init PKCS#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);
++ }
++
++ r = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, providerp, user);
++ pkcs11_uri_cleanup(uri);
++
++ return r;
++}
++
+ int
+-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp,
+- char ***labelsp)
++pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin,
++ struct sshkey ***keyp, char ***labelsp)
+ {
+ struct pkcs11_provider *p = NULL;
+ int nkeys;
++ char *provider_uri = pkcs11_uri_get(uri);
++
++ debug_f("called, provider_uri = %s", provider_uri);
+
+- nkeys = pkcs11_register_provider(provider_id, pin, keyp, labelsp,
+- &p, CKU_USER);
++ nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, labelsp, &p, CKU_USER);
+
+ /* no keys found or some other error, de-register provider */
+ if (nkeys <= 0 && p != NULL) {
+@@ -1683,7 +2002,37 @@ pkcs11_add_provider(char *provider_id, c
+ pkcs11_provider_unref(p);
+ }
+ if (nkeys == 0)
+- debug_f("provider %s returned no keys", provider_id);
++ debug_f("provider %s returned no keys", provider_uri);
++
++ free(provider_uri);
++ return nkeys;
++}
++
++/*
++ * register a new provider and get number of keys hold by the token,
++ * fails if provider already exists
++ */
++int
++pkcs11_add_provider(char *provider_id, char *pin,
++ struct sshkey ***keyp, char ***labelsp)
++{
++ struct pkcs11_uri *uri;
++ int nkeys;
++
++ uri = pkcs11_uri_init();
++ if (uri == NULL)
++ fatal("Failed to init PKCS#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);
++ }
++
++ nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp, labelsp);
++ pkcs11_uri_cleanup(uri);
+
+ return (nkeys);
+ }
+diff -up openssh-8.7p1/ssh-pkcs11.h.pkcs11-uri openssh-8.7p1/ssh-pkcs11.h
+--- openssh-8.7p1/ssh-pkcs11.h.pkcs11-uri 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/ssh-pkcs11.h 2021-08-30 13:07:43.666700121 +0200
+@@ -22,10 +22,14 @@
+ #define SSH_PKCS11_ERR_PIN_REQUIRED 4
+ #define SSH_PKCS11_ERR_PIN_LOCKED 5
+
++#include "ssh-pkcs11-uri.h"
++
+ int pkcs11_init(int);
+ void pkcs11_terminate(void);
+ int pkcs11_add_provider(char *, char *, struct sshkey ***, char ***);
++int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***, char ***);
+ int pkcs11_del_provider(char *);
++int pkcs11_uri_write(const struct sshkey *, FILE *);
+ #ifdef WITH_PKCS11_KEYGEN
+ struct sshkey *
+ pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int,
+diff -up openssh-8.7p1/ssh-pkcs11-uri.c.pkcs11-uri openssh-8.7p1/ssh-pkcs11-uri.c
+--- openssh-8.7p1/ssh-pkcs11-uri.c.pkcs11-uri 2021-08-30 13:07:43.667700130 +0200
++++ openssh-8.7p1/ssh-pkcs11-uri.c 2021-08-30 13:07:43.667700130 +0200
+@@ -0,0 +1,419 @@
++/*
++ * Copyright (c) 2017 Red Hat
++ *
++ * Authors: Jakub Jelen <jjelen@redhat.com>
++ *
++ * 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 <stdio.h>
++#include <string.h>
++
++#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"
++#define PKCS11_URI_PIN_VALUE "pin-value"
++
++/* Keyword tokens. */
++typedef enum {
++ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath,
++ pPinValue, 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 },
++ { PKCS11_URI_PIN_VALUE, pPinValue },
++ { 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 *allow_list)
++{
++ 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(allow_list, 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)
++{
++ if (pkcs11 == NULL) {
++ return;
++ }
++
++ free(pkcs11->id);
++ free(pkcs11->module_path);
++ free(pkcs11->token);
++ free(pkcs11->object);
++ free(pkcs11->lib_manuf);
++ free(pkcs11->manuf);
++ if (pkcs11->pin)
++ freezero(pkcs11->pin, strlen(pkcs11->pin));
++ 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_f("The '%s' does not look like PKCS#11 URI", uri);
++ return -1;
++ }
++
++ if (pkcs11 == NULL) {
++ error_f("Bad arguments. The pkcs11 can't be null");
++ return -1;
++ }
++
++ /* skip URI schema name */
++ p = strdup(uri);
++ str1 = p;
++
++ /* everything before ? */
++ tok = strtok_r(str1, "?", &saveptr1);
++ if (tok == NULL) {
++ error_f("pk11-path expected, got EOF");
++ rv = -1;
++ goto out;
++ }
++
++ /* 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, *arg = NULL;
++ pkcs11uriOpCodes opcode;
++ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2);
++ if (tok == NULL)
++ break;
++ opcode = parse_token(tok);
++ if (opcode != pBadOption)
++ arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
++
++ switch (opcode) {
++ case pId:
++ /* CKA_ID */
++ if (pkcs11->id != NULL) {
++ verbose_f("The id already set in the PKCS#11 URI");
++ rv = -1;
++ goto out;
++ }
++ len = percent_decode(arg, &pkcs11->id);
++ if (len <= 0) {
++ verbose_f("Failed to percent-decode CKA_ID: %s", arg);
++ rv = -1;
++ goto out;
++ } else
++ pkcs11->id_len = len;
++ debug3_f("Setting CKA_ID = %s from PKCS#11 URI", arg);
++ break;
++ case pToken:
++ /* CK_TOKEN_INFO -> label */
++ charptr = &pkcs11->token;
++ parse_string:
++ if (*charptr != NULL) {
++ verbose_f("The %s already set in the PKCS#11 URI",
++ keywords[opcode].name);
++ rv = -1;
++ goto out;
++ }
++ percent_decode(arg, charptr);
++ debug3_f("Setting %s = %s from PKCS#11 URI",
++ 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;
++
++ default:
++ /* Unrecognized attribute in the URI path SHOULD be error */
++ verbose_f("Unknown part of path in PKCS#11 URI: %s", tok);
++ }
++ }
++
++ tok = strtok_r(NULL, "?", &saveptr1);
++ if (tok == NULL) {
++ goto out;
++ }
++ /* parse pk11-query (optional) */
++ for (str2 = tok; ; str2 = NULL) {
++ char *arg;
++ pkcs11uriOpCodes opcode;
++ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2);
++ if (tok == NULL)
++ break;
++ opcode = parse_token(tok);
++ if (opcode != pBadOption)
++ arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */
++
++ switch (opcode) {
++ case pModulePath:
++ /* module-path is PKCS11Provider */
++ if (pkcs11->module_path != NULL) {
++ verbose_f("Multiple module-path attributes are"
++ "not supported the PKCS#11 URI");
++ rv = -1;
++ goto out;
++ }
++ percent_decode(arg, &pkcs11->module_path);
++ debug3_f("Setting PKCS11Provider = %s from PKCS#11 URI",
++ pkcs11->module_path);
++ break;
++
++ case pPinValue:
++ /* pin-value */
++ if (pkcs11->pin != NULL) {
++ verbose_f("Multiple pin-value attributes are"
++ "not supported the PKCS#11 URI");
++ rv = -1;
++ goto out;
++ }
++ percent_decode(arg, &pkcs11->pin);
++ debug3_f("Setting PIN from PKCS#11 URI");
++ break;
++
++ default:
++ /* Unrecognized attribute in the URI query SHOULD be ignored */
++ verbose_f("Unknown part of query in PKCS#11 URI: %s", tok);
++ }
++ }
++out:
++ free(p);
++ return rv;
++}
++
++#endif /* ENABLE_PKCS11 */
+diff -up openssh-8.7p1/ssh-pkcs11-uri.h.pkcs11-uri openssh-8.7p1/ssh-pkcs11-uri.h
+--- openssh-8.7p1/ssh-pkcs11-uri.h.pkcs11-uri 2021-08-30 13:07:43.667700130 +0200
++++ openssh-8.7p1/ssh-pkcs11-uri.h 2021-08-30 13:07:43.667700130 +0200
+@@ -0,0 +1,42 @@
++/*
++ * Copyright (c) 2017 Red Hat
++ *
++ * Authors: Jakub Jelen <jjelen@redhat.com>
++ *
++ * 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;
++ char *pin; /* Only parsed, but not printed */
++};
++
++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/openssh-8.0p1-preserve-pam-errors.patch b/openssh-8.0p1-preserve-pam-errors.patch
new file mode 100644
index 0000000..dbdbe93
--- /dev/null
+++ b/openssh-8.0p1-preserve-pam-errors.patch
@@ -0,0 +1,44 @@
+diff -up openssh-8.0p1/auth-pam.c.preserve-pam-errors openssh-8.0p1/auth-pam.c
+--- openssh-8.0p1/auth-pam.c.preserve-pam-errors 2021-03-31 17:03:15.618592347 +0200
++++ openssh-8.0p1/auth-pam.c 2021-03-31 17:06:58.115220014 +0200
+@@ -511,7 +511,11 @@ sshpam_thread(void *ctxtp)
+ goto auth_fail;
+
+ if (!do_pam_account()) {
+- sshpam_err = PAM_ACCT_EXPIRED;
++ /* Preserve PAM_PERM_DENIED and PAM_USER_UNKNOWN.
++ * Backward compatibility for other errors. */
++ if (sshpam_err != PAM_PERM_DENIED
++ && sshpam_err != PAM_USER_UNKNOWN)
++ sshpam_err = PAM_ACCT_EXPIRED;
+ goto auth_fail;
+ }
+ if (sshpam_authctxt->force_pwchange) {
+@@ -568,8 +572,10 @@ sshpam_thread(void *ctxtp)
+ pam_strerror(sshpam_handle, sshpam_err))) != 0)
+ fatal("%s: buffer error: %s", __func__, ssh_err(r));
+ /* XXX - can't do much about an error here */
+- if (sshpam_err == PAM_ACCT_EXPIRED)
+- ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, buffer);
++ if (sshpam_err == PAM_PERM_DENIED
++ || sshpam_err == PAM_USER_UNKNOWN
++ || sshpam_err == PAM_ACCT_EXPIRED)
++ ssh_msg_send(ctxt->pam_csock, sshpam_err, buffer);
+ else if (sshpam_maxtries_reached)
+ ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, buffer);
+ else
+@@ -856,10 +862,12 @@ sshpam_query(void *ctx, char **name, cha
+ plen++;
+ free(msg);
+ break;
++ case PAM_USER_UNKNOWN:
++ case PAM_PERM_DENIED:
+ case PAM_ACCT_EXPIRED:
++ sshpam_account_status = 0;
++ /* FALLTHROUGH */
+ case PAM_MAXTRIES:
+- if (type == PAM_ACCT_EXPIRED)
+- sshpam_account_status = 0;
+ if (type == PAM_MAXTRIES)
+ sshpam_set_maxtries_reached(1);
+ /* FALLTHROUGH */
diff --git a/openssh-8.2p1-visibility.patch b/openssh-8.2p1-visibility.patch
new file mode 100644
index 0000000..89c35ef
--- /dev/null
+++ b/openssh-8.2p1-visibility.patch
@@ -0,0 +1,40 @@
+diff --git a/regress/misc/sk-dummy/sk-dummy.c b/regress/misc/sk-dummy/sk-dummy.c
+index dca158de..afdcb1d2 100644
+--- a/regress/misc/sk-dummy/sk-dummy.c
++++ b/regress/misc/sk-dummy/sk-dummy.c
+@@ -71,7 +71,7 @@ skdebug(const char *func, const char *fmt, ...)
+ #endif
+ }
+
+-uint32_t
++uint32_t __attribute__((visibility("default")))
+ sk_api_version(void)
+ {
+ return SSH_SK_VERSION_MAJOR;
+@@ -220,7 +220,7 @@ check_options(struct sk_option **options)
+ return 0;
+ }
+
+-int
++int __attribute__((visibility("default")))
+ sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
+ const char *application, uint8_t flags, const char *pin,
+ struct sk_option **options, struct sk_enroll_response **enroll_response)
+@@ -467,7 +467,7 @@ sig_ed25519(const uint8_t *message, size_t message_len,
+ return ret;
+ }
+
+-int
++int __attribute__((visibility("default")))
+ sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
+ const char *application, const uint8_t *key_handle, size_t key_handle_len,
+ uint8_t flags, const char *pin, struct sk_option **options,
+@@ -518,7 +518,7 @@ sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
+ return ret;
+ }
+
+-int
++int __attribute__((visibility("default")))
+ sk_load_resident_keys(const char *pin, struct sk_option **options,
+ struct sk_resident_key ***rks, size_t *nrks)
+ {
diff --git a/openssh-8.2p1-x11-without-ipv6.patch b/openssh-8.2p1-x11-without-ipv6.patch
new file mode 100644
index 0000000..8b83bc3
--- /dev/null
+++ b/openssh-8.2p1-x11-without-ipv6.patch
@@ -0,0 +1,30 @@
+diff --git a/channels.c b/channels.c
+--- a/channels.c
++++ b/channels.c
+@@ -3933,16 +3933,26 @@ x11_create_display_inet(int x11_display_
+ if (ai->ai_family == AF_INET6)
+ sock_set_v6only(sock);
+ if (x11_use_localhost)
+ set_reuseaddr(sock);
+ if (bind(sock, ai->ai_addr, ai->ai_addrlen) == -1) {
+ debug2_f("bind port %d: %.100s", port,
+ strerror(errno));
+ close(sock);
++
++ /* do not remove successfully opened
++ * sockets if the request failed because
++ * the protocol IPv4/6 is not available
++ * (e.g. IPv6 may be disabled while being
++ * supported)
++ */
++ if (EADDRNOTAVAIL == errno)
++ continue;
++
+ for (n = 0; n < num_socks; n++)
+ close(socks[n]);
+ num_socks = 0;
+ break;
+ }
+ socks[num_socks++] = sock;
+ if (num_socks == NUM_SOCKS)
+ break;
diff --git a/openssh-8.7p1-ibmca.patch b/openssh-8.7p1-ibmca.patch
new file mode 100644
index 0000000..2f2556e
--- /dev/null
+++ b/openssh-8.7p1-ibmca.patch
@@ -0,0 +1,12 @@
+Reference:https://src.fedoraproject.org/rpms/openssh/blob/rawhide/f/openssh-8.7p1-ibmca.patch
+--- openssh-8.7p1/openbsd-compat/bsd-closefrom.c.orig 2022-04-12 15:47:03.815044607 +0200
++++ openssh-8.7p1/openbsd-compat/bsd-closefrom.c 2022-04-12 15:48:12.464963511 +0200
+@@ -16,7 +16,7 @@
+
+ #include "includes.h"
+
+-#if !defined(HAVE_CLOSEFROM) || defined(BROKEN_CLOSEFROM)
++#if !defined(HAVE_CLOSEFROM) || defined(BROKEN_CLOSEFROM) || (defined __s390__)
+
+ #include <sys/types.h>
+ #include <unistd.h>
diff --git a/openssh-8.7p1-minrsabits.patch b/openssh-8.7p1-minrsabits.patch
new file mode 100644
index 0000000..2ed59a3
--- /dev/null
+++ b/openssh-8.7p1-minrsabits.patch
@@ -0,0 +1,24 @@
+diff --git a/readconf.c b/readconf.c
+index 7f26c680..42be690b 100644
+--- a/readconf.c
++++ b/readconf.c
+@@ -320,6 +320,7 @@ static struct {
+ { "securitykeyprovider", oSecurityKeyProvider },
+ { "knownhostscommand", oKnownHostsCommand },
+ { "requiredrsasize", oRequiredRSASize },
++ { "rsaminsize", oRequiredRSASize }, /* alias */
+ { "enableescapecommandline", oEnableEscapeCommandline },
+
+ { NULL, oBadOption }
+diff --git a/servconf.c b/servconf.c
+index 29df0463..423772b1 100644
+--- a/servconf.c
++++ b/servconf.c
+@@ -676,6 +680,7 @@ static struct {
+ { "casignaturealgorithms", sCASignatureAlgorithms, SSHCFG_ALL },
+ { "securitykeyprovider", sSecurityKeyProvider, SSHCFG_GLOBAL },
+ { "requiredrsasize", sRequiredRSASize, SSHCFG_ALL },
++ { "rsaminsize", sRequiredRSASize, SSHCFG_ALL }, /* alias */
+ { "channeltimeout", sChannelTimeout, SSHCFG_ALL },
+ { "unusedconnectiontimeout", sUnusedConnectionTimeout, SSHCFG_ALL },
+ { NULL, sBadOption, 0 }
diff --git a/openssh-8.7p1-negotiate-supported-algs.patch b/openssh-8.7p1-negotiate-supported-algs.patch
new file mode 100644
index 0000000..ee3637f
--- /dev/null
+++ b/openssh-8.7p1-negotiate-supported-algs.patch
@@ -0,0 +1,117 @@
+diff -up openssh-9.3p1/regress/hostkey-agent.sh.xxx openssh-9.3p1/regress/hostkey-agent.sh
+--- openssh-9.3p1/regress/hostkey-agent.sh.xxx 2023-05-29 18:15:56.311236887 +0200
++++ openssh-9.3p1/regress/hostkey-agent.sh 2023-05-29 18:16:07.598503551 +0200
+@@ -17,8 +17,21 @@ trace "make CA key"
+
+ ${SSHKEYGEN} -qt ed25519 -f $OBJ/agent-ca -N '' || fatal "ssh-keygen CA"
+
++PUBKEY_ACCEPTED_ALGOS=`$SSH -G "example.com" | \
++ grep -i "PubkeyAcceptedAlgorithms" | cut -d ' ' -f2- | tr "," "|"`
++SSH_ACCEPTED_KEYTYPES=`echo "$SSH_KEYTYPES" | egrep "$PUBKEY_ACCEPTED_ALGOS"`
++echo $PUBKEY_ACCEPTED_ALGOS | grep "rsa"
++r=$?
++if [ $r == 0 ]; then
++echo $SSH_ACCEPTED_KEYTYPES | grep "rsa"
++r=$?
++if [ $r -ne 0 ]; then
++SSH_ACCEPTED_KEYTYPES="$SSH_ACCEPTED_KEYTYPES ssh-rsa"
++fi
++fi
++
+ trace "load hostkeys"
+-for k in $SSH_KEYTYPES ; do
++for k in $SSH_ACCEPTED_KEYTYPES ; do
+ ${SSHKEYGEN} -qt $k -f $OBJ/agent-key.$k -N '' || fatal "ssh-keygen $k"
+ ${SSHKEYGEN} -s $OBJ/agent-ca -qh -n localhost-with-alias \
+ -I localhost-with-alias $OBJ/agent-key.$k.pub || \
+@@ -32,12 +48,16 @@ rm $OBJ/agent-ca # Don't need CA private
+
+ unset SSH_AUTH_SOCK
+
+-for k in $SSH_KEYTYPES ; do
++for k in $SSH_ACCEPTED_KEYTYPES ; do
+ verbose "key type $k"
++ hka=$k
++ if [ $k = "ssh-rsa" ]; then
++ hka="rsa-sha2-512"
++ fi
+ cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy
+- echo "HostKeyAlgorithms $k" >> $OBJ/sshd_proxy
++ echo "HostKeyAlgorithms $hka" >> $OBJ/sshd_proxy
+ echo "Hostkey $OBJ/agent-key.${k}" >> $OBJ/sshd_proxy
+- opts="-oHostKeyAlgorithms=$k -F $OBJ/ssh_proxy"
++ opts="-oHostKeyAlgorithms=$hka -F $OBJ/ssh_proxy"
+ ( printf 'localhost-with-alias,127.0.0.1,::1 ' ;
+ cat $OBJ/agent-key.$k.pub) > $OBJ/known_hosts
+ SSH_CONNECTION=`${SSH} $opts host 'echo $SSH_CONNECTION'`
+@@ -50,15 +70,16 @@ for k in $SSH_KEYTYPES ; do
+ done
+
+ SSH_CERTTYPES=`ssh -Q key-sig | grep 'cert-v01@openssh.com'`
++SSH_ACCEPTED_CERTTYPES=`echo "$SSH_CERTTYPES" | egrep "$PUBKEY_ACCEPTED_ALGOS"`
+
+ # Prepare sshd_proxy for certificates.
+ cp $OBJ/sshd_proxy.orig $OBJ/sshd_proxy
+ HOSTKEYALGS=""
+-for k in $SSH_CERTTYPES ; do
++for k in $SSH_ACCEPTED_CERTTYPES ; do
+ test -z "$HOSTKEYALGS" || HOSTKEYALGS="${HOSTKEYALGS},"
+ HOSTKEYALGS="${HOSTKEYALGS}${k}"
+ done
+-for k in $SSH_KEYTYPES ; do
++for k in $SSH_ACCEPTED_KEYTYPES ; do
+ echo "Hostkey $OBJ/agent-key.${k}.pub" >> $OBJ/sshd_proxy
+ echo "HostCertificate $OBJ/agent-key.${k}-cert.pub" >> $OBJ/sshd_proxy
+ test -f $OBJ/agent-key.${k}.pub || fatal "no $k key"
+@@ -70,7 +93,7 @@ echo "HostKeyAlgorithms $HOSTKEYALGS" >>
+ ( printf '@cert-authority localhost-with-alias ' ;
+ cat $OBJ/agent-ca.pub) > $OBJ/known_hosts
+
+-for k in $SSH_CERTTYPES ; do
++for k in $SSH_ACCEPTED_CERTTYPES ; do
+ verbose "cert type $k"
+ opts="-oHostKeyAlgorithms=$k -F $OBJ/ssh_proxy"
+ SSH_CONNECTION=`${SSH} $opts host 'echo $SSH_CONNECTION'`
+diff -up openssh-9.3p1/sshconnect2.c.xxx openssh-9.3p1/sshconnect2.c
+--- openssh-9.3p1/sshconnect2.c.xxx 2023-04-26 17:37:35.100827792 +0200
++++ openssh-9.3p1/sshconnect2.c 2023-04-26 17:50:31.860748877 +0200
+@@ -221,7 +221,7 @@ ssh_kex2(struct ssh *ssh, char *host, st
+ const struct ssh_conn_info *cinfo)
+ {
+ char *myproposal[PROPOSAL_MAX];
+- char *s, *all_key, *hkalgs = NULL;
++ char *s, *all_key, *hkalgs = NULL, *filtered_algs = NULL;
+ int r, use_known_hosts_order = 0;
+
+ #if defined(GSSAPI) && defined(WITH_OPENSSL)
+@@ -260,9 +260,21 @@ ssh_kex2(struct ssh *ssh, char *host, st
+ if (use_known_hosts_order)
+ hkalgs = order_hostkeyalgs(host, hostaddr, port, cinfo);
+
++ filtered_algs = hkalgs ? match_filter_allowlist(hkalgs, options.pubkey_accepted_algos)
++ : match_filter_allowlist(options.hostkeyalgorithms,
++ options.pubkey_accepted_algos);
++ if (filtered_algs == NULL) {
++ if (hkalgs)
++ fatal_f("No match between algorithms for %s (host %s) and pubkey accepted algorithms %s",
++ hkalgs, host, options.pubkey_accepted_algos);
++ else
++ fatal_f("No match between host key algorithms %s and pubkey accepted algorithms %s",
++ options.hostkeyalgorithms, options.pubkey_accepted_algos);
++ }
++
+ kex_proposal_populate_entries(ssh, myproposal, s, options.ciphers,
+ options.macs, compression_alg_list(options.compression),
+- hkalgs ? hkalgs : options.hostkeyalgorithms);
++ filtered_algs);
+
+ #if defined(GSSAPI) && defined(WITH_OPENSSL)
+ if (options.gss_keyex) {
+@@ -303,6 +315,7 @@ ssh_kex2(struct ssh *ssh, char *host, st
+ #endif
+
+ free(hkalgs);
++ free(filtered_algs);
+
+ /* start key exchange */
+ if ((r = kex_setup(ssh, myproposal)) != 0)
diff --git a/openssh-8.7p1-recursive-scp.patch b/openssh-8.7p1-recursive-scp.patch
new file mode 100644
index 0000000..f0d9b0f
--- /dev/null
+++ b/openssh-8.7p1-recursive-scp.patch
@@ -0,0 +1,181 @@
+diff -up openssh-8.7p1/scp.c.scp-sftpdirs openssh-8.7p1/scp.c
+--- openssh-8.7p1/scp.c.scp-sftpdirs 2022-02-07 12:31:07.407740407 +0100
++++ openssh-8.7p1/scp.c 2022-02-07 12:31:07.409740424 +0100
+@@ -1324,7 +1324,7 @@ source_sftp(int argc, char *src, char *t
+
+ if (src_is_dir && iamrecursive) {
+ if (upload_dir(conn, src, abs_dst, pflag,
+- SFTP_PROGRESS_ONLY, 0, 0, 1, 1) != 0) {
++ SFTP_PROGRESS_ONLY, 0, 0, 1, 1, 1) != 0) {
+ error("failed to upload directory %s to %s", src, targ);
+ errs = 1;
+ }
+diff -up openssh-8.7p1/sftp-client.c.scp-sftpdirs openssh-8.7p1/sftp-client.c
+--- openssh-8.7p1/sftp-client.c.scp-sftpdirs 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/sftp-client.c 2022-02-07 12:47:59.117516131 +0100
+@@ -971,7 +971,7 @@ do_fsetstat(struct sftp_conn *conn, cons
+
+ /* Implements both the realpath and expand-path operations */
+ static char *
+-do_realpath_expand(struct sftp_conn *conn, const char *path, int expand)
++do_realpath_expand(struct sftp_conn *conn, const char *path, int expand, int create_dir)
+ {
+ struct sshbuf *msg;
+ u_int expected_id, count, id;
+@@ -1033,11 +1033,43 @@ do_realpath_expand(struct sftp_conn *con
+ if ((r = sshbuf_get_u32(msg, &status)) != 0 ||
+ (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0)
+ fatal_fr(r, "parse status");
+- error("%s %s: %s", expand ? "expand" : "realpath",
+- path, *errmsg == '\0' ? fx2txt(status) : errmsg);
+- free(errmsg);
+- sshbuf_free(msg);
+- return NULL;
++ if ((status == SSH2_FX_NO_SUCH_FILE) && create_dir) {
++ memset(&a, '\0', sizeof(a));
++ if ((r = do_mkdir(conn, path, &a, 0)) != 0) {
++ sshbuf_free(msg);
++ return NULL;
++ }
++ debug2("Sending SSH2_FXP_REALPATH \"%s\" - create dir", path);
++ send_string_request(conn, id, SSH2_FXP_REALPATH,
++ path, strlen(path));
++
++ get_msg(conn, msg);
++ if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
++ (r = sshbuf_get_u32(msg, &id)) != 0)
++ fatal_fr(r, "parse");
++
++ if (id != expected_id)
++ fatal("ID mismatch (%u != %u)", id, expected_id);
++
++ if (type == SSH2_FXP_STATUS) {
++ free(errmsg);
++
++ if ((r = sshbuf_get_u32(msg, &status)) != 0 ||
++ (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0)
++ fatal_fr(r, "parse status");
++ error("%s %s: %s", expand ? "expand" : "realpath",
++ path, *errmsg == '\0' ? fx2txt(status) : errmsg);
++ free(errmsg);
++ sshbuf_free(msg);
++ return NULL;
++ }
++ } else {
++ error("%s %s: %s", expand ? "expand" : "realpath",
++ path, *errmsg == '\0' ? fx2txt(status) : errmsg);
++ free(errmsg);
++ sshbuf_free(msg);
++ return NULL;
++ }
+ } else if (type != SSH2_FXP_NAME)
+ fatal("Expected SSH2_FXP_NAME(%u) packet, got %u",
+ SSH2_FXP_NAME, type);
+@@ -1039,9 +1067,9 @@ do_realpath_expand(struct sftp_conn *con
+ }
+
+ char *
+-do_realpath(struct sftp_conn *conn, const char *path)
++do_realpath(struct sftp_conn *conn, const char *path, int create_dir)
+ {
+- return do_realpath_expand(conn, path, 0);
++ return do_realpath_expand(conn, path, 0, create_dir);
+ }
+
+ int
+@@ -1055,9 +1083,9 @@ do_expand_path(struct sftp_conn *conn, c
+ {
+ if (!can_expand_path(conn)) {
+ debug3_f("no server support, fallback to realpath");
+- return do_realpath_expand(conn, path, 0);
++ return do_realpath_expand(conn, path, 0, 0);
+ }
+- return do_realpath_expand(conn, path, 1);
++ return do_realpath_expand(conn, path, 1, 0);
+ }
+
+ int
+@@ -1807,7 +1835,7 @@ download_dir(struct sftp_conn *conn, con
+ char *src_canon;
+ int ret;
+
+- if ((src_canon = do_realpath(conn, src)) == NULL) {
++ if ((src_canon = do_realpath(conn, src, 0)) == NULL) {
+ error("download \"%s\": path canonicalization failed", src);
+ return -1;
+ }
+@@ -2115,12 +2143,12 @@ upload_dir_internal(struct sftp_conn *co
+ int
+ upload_dir(struct sftp_conn *conn, const char *src, const char *dst,
+ int preserve_flag, int print_flag, int resume, int fsync_flag,
+- int follow_link_flag, int inplace_flag)
++ int follow_link_flag, int inplace_flag, int create_dir)
+ {
+ char *dst_canon;
+ int ret;
+
+- if ((dst_canon = do_realpath(conn, dst)) == NULL) {
++ if ((dst_canon = do_realpath(conn, dst, create_dir)) == NULL) {
+ error("upload \"%s\": path canonicalization failed", dst);
+ return -1;
+ }
+@@ -2557,7 +2585,7 @@ crossload_dir(struct sftp_conn *from, st
+ char *from_path_canon;
+ int ret;
+
+- if ((from_path_canon = do_realpath(from, from_path)) == NULL) {
++ if ((from_path_canon = do_realpath(from, from_path, 0)) == NULL) {
+ error("crossload \"%s\": path canonicalization failed",
+ from_path);
+ return -1;
+diff -up openssh-8.7p1/sftp-client.h.scp-sftpdirs openssh-8.7p1/sftp-client.h
+--- openssh-8.7p1/sftp-client.h.scp-sftpdirs 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/sftp-client.h 2022-02-07 12:31:07.410740433 +0100
+@@ -111,7 +111,7 @@ int do_fsetstat(struct sftp_conn *, cons
+ int do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a);
+
+ /* Canonicalise 'path' - caller must free result */
+-char *do_realpath(struct sftp_conn *, const char *);
++char *do_realpath(struct sftp_conn *, const char *, int);
+
+ /* Canonicalisation with tilde expansion (requires server extension) */
+ char *do_expand_path(struct sftp_conn *, const char *);
+@@ -159,7 +159,7 @@ int do_upload(struct sftp_conn *, const
+ * times if 'pflag' is set
+ */
+ int upload_dir(struct sftp_conn *, const char *, const char *,
+- int, int, int, int, int, int);
++ int, int, int, int, int, int, int);
+
+ /*
+ * Download a 'from_path' from the 'from' connection and upload it to
+diff -up openssh-8.7p1/sftp.c.scp-sftpdirs openssh-8.7p1/sftp.c
+--- openssh-8.7p1/sftp.c.scp-sftpdirs 2021-08-20 06:03:49.000000000 +0200
++++ openssh-8.7p1/sftp.c 2022-02-07 12:31:07.411740442 +0100
+@@ -760,7 +760,7 @@ process_put(struct sftp_conn *conn, cons
+ if (globpath_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) {
+ if (upload_dir(conn, g.gl_pathv[i], abs_dst,
+ pflag || global_pflag, 1, resume,
+- fflag || global_fflag, 0, 0) == -1)
++ fflag || global_fflag, 0, 0, 0) == -1)
+ err = -1;
+ } else {
+ if (do_upload(conn, g.gl_pathv[i], abs_dst,
+@@ -1577,7 +1577,7 @@ parse_dispatch_command(struct sftp_conn
+ if (path1 == NULL || *path1 == '\0')
+ path1 = xstrdup(startdir);
+ path1 = make_absolute(path1, *pwd);
+- if ((tmp = do_realpath(conn, path1)) == NULL) {
++ if ((tmp = do_realpath(conn, path1, 0)) == NULL) {
+ err = 1;
+ break;
+ }
+@@ -2160,7 +2160,7 @@ interactive_loop(struct sftp_conn *conn,
+ }
+ #endif /* USE_LIBEDIT */
+
+- remote_path = do_realpath(conn, ".");
++ remote_path = do_realpath(conn, ".", 0);
+ if (remote_path == NULL)
+ fatal("Need cwd");
+ startdir = xstrdup(remote_path);
diff --git a/openssh-8.7p1-scp-kill-switch.patch b/openssh-8.7p1-scp-kill-switch.patch
new file mode 100644
index 0000000..161ab2d
--- /dev/null
+++ b/openssh-8.7p1-scp-kill-switch.patch
@@ -0,0 +1,46 @@
+diff -up openssh-8.7p1/pathnames.h.kill-scp openssh-8.7p1/pathnames.h
+--- openssh-8.7p1/pathnames.h.kill-scp 2021-09-16 11:37:57.240171687 +0200
++++ openssh-8.7p1/pathnames.h 2021-09-16 11:42:29.183427917 +0200
+@@ -42,6 +42,7 @@
+ #define _PATH_HOST_XMSS_KEY_FILE SSHDIR "/ssh_host_xmss_key"
+ #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key"
+ #define _PATH_DH_MODULI SSHDIR "/moduli"
++#define _PATH_SCP_KILL_SWITCH SSHDIR "/disable_scp"
+
+ #ifndef _PATH_SSH_PROGRAM
+ #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
+diff -up openssh-8.7p1/scp.1.kill-scp openssh-8.7p1/scp.1
+--- openssh-8.7p1/scp.1.kill-scp 2021-09-16 12:09:02.646714578 +0200
++++ openssh-8.7p1/scp.1 2021-09-16 12:26:49.978628226 +0200
+@@ -278,6 +278,13 @@ to print debugging messages about their
+ By default a 32KB buffer is used.
+ .El
+ .El
++.Pp
++Usage of SCP protocol can be blocked by creating a world-readable
++.Ar /etc/ssh/disable_scp
++file. If this file exists, when SCP protocol is in use (either remotely or
++via the
++.Fl O
++option), the program will exit.
+ .Sh EXIT STATUS
+ .Ex -std scp
+ .Sh SEE ALSO
+diff -up openssh-8.7p1/scp.c.kill-scp openssh-8.7p1/scp.c
+--- openssh-8.7p1/scp.c.kill-scp 2021-09-16 11:42:56.013650519 +0200
++++ openssh-8.7p1/scp.c 2021-09-16 11:53:03.249713836 +0200
+@@ -596,6 +596,14 @@ main(int argc, char **argv)
+ if (iamremote)
+ mode = MODE_SCP;
+
++ if (mode == MODE_SCP) {
++ FILE *f = fopen(_PATH_SCP_KILL_SWITCH, "r");
++ if (f != NULL) {
++ fclose(f);
++ fatal("SCP protocol is forbidden via %s", _PATH_SCP_KILL_SWITCH);
++ }
++ }
++
+ if ((pwd = getpwuid(userid = getuid())) == NULL)
+ fatal("unknown user %u", (u_int) userid);
+
diff --git a/openssh-8.7p1-ssh-manpage.patch b/openssh-8.7p1-ssh-manpage.patch
new file mode 100644
index 0000000..c7f6f1e
--- /dev/null
+++ b/openssh-8.7p1-ssh-manpage.patch
@@ -0,0 +1,53 @@
+diff --color -ru a/ssh.1 b/ssh.1
+--- a/ssh.1 2022-07-12 11:47:51.307295880 +0200
++++ b/ssh.1 2022-07-12 11:50:28.793363263 +0200
+@@ -493,6 +493,7 @@
+ .It AddressFamily
+ .It BatchMode
+ .It BindAddress
++.It BindInterface
+ .It CanonicalDomains
+ .It CanonicalizeFallbackLocal
+ .It CanonicalizeHostname
+@@ -510,6 +511,7 @@
+ .It ControlPath
+ .It ControlPersist
+ .It DynamicForward
++.It EnableSSHKeysign
+ .It EnableEscapeCommandline
+ .It EscapeChar
+ .It ExitOnForwardFailure
+@@ -538,6 +540,8 @@
+ .It IdentitiesOnly
+ .It IdentityAgent
+ .It IdentityFile
++.It IgnoreUnknown
++.It Include
+ .It IPQoS
+ .It KbdInteractiveAuthentication
+ .It KbdInteractiveDevices
+@@ -546,6 +550,7 @@
+ .It LocalCommand
+ .It LocalForward
+ .It LogLevel
++.It LogVerbose
+ .It MACs
+ .It Match
+ .It NoHostAuthenticationForLocalhost
+@@ -566,6 +571,8 @@
+ .It RemoteCommand
+ .It RemoteForward
+ .It RequestTTY
++.It RevokedHostKeys
++.It SecurityKeyProvider
+ .It RequiredRSASize
+ .It SendEnv
+ .It ServerAliveInterval
+@@ -575,6 +582,7 @@
+ .It StreamLocalBindMask
+ .It StreamLocalBindUnlink
+ .It StrictHostKeyChecking
++.It SyslogFacility
+ .It TCPKeepAlive
+ .It Tunnel
+ .It TunnelDevice
diff --git a/openssh-9.3p1-merged-openssl-evp.patch b/openssh-9.3p1-merged-openssl-evp.patch
new file mode 100644
index 0000000..d8c2ca3
--- /dev/null
+++ b/openssh-9.3p1-merged-openssl-evp.patch
@@ -0,0 +1,1228 @@
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/digest.h openssh-9.3p1-patched/digest.h
+--- openssh-9.3p1/digest.h 2023-03-15 22:28:19.000000000 +0100
++++ openssh-9.3p1-patched/digest.h 2023-06-06 15:52:25.602551466 +0200
+@@ -32,6 +32,12 @@
+ struct sshbuf;
+ struct ssh_digest_ctx;
+
++#ifdef WITH_OPENSSL
++#include <openssl/evp.h>
++/* Converts internal digest representation to the OpenSSL one */
++const EVP_MD *ssh_digest_to_md(int digest_type);
++#endif
++
+ /* Looks up a digest algorithm by name */
+ int ssh_digest_alg_by_name(const char *name);
+
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/digest-openssl.c openssh-9.3p1-patched/digest-openssl.c
+--- openssh-9.3p1/digest-openssl.c 2023-03-15 22:28:19.000000000 +0100
++++ openssh-9.3p1-patched/digest-openssl.c 2023-06-06 15:52:25.601551454 +0200
+@@ -64,6 +64,22 @@
+ { -1, NULL, 0, NULL },
+ };
+
++const EVP_MD *
++ssh_digest_to_md(int digest_type)
++{
++ switch (digest_type) {
++ case SSH_DIGEST_SHA1:
++ return EVP_sha1();
++ case SSH_DIGEST_SHA256:
++ return EVP_sha256();
++ case SSH_DIGEST_SHA384:
++ return EVP_sha384();
++ case SSH_DIGEST_SHA512:
++ return EVP_sha512();
++ }
++ return NULL;
++}
++
+ static const struct ssh_digest *
+ ssh_digest_by_alg(int alg)
+ {
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-dss.c openssh-9.3p1-patched/ssh-dss.c
+--- openssh-9.3p1/ssh-dss.c 2023-03-15 22:28:19.000000000 +0100
++++ openssh-9.3p1-patched/ssh-dss.c 2023-06-06 15:52:25.624551743 +0200
+@@ -32,6 +32,8 @@
+ #include <openssl/bn.h>
+ #include <openssl/dsa.h>
+ #include <openssl/evp.h>
++#include <openssl/core_names.h>
++#include <openssl/param_build.h>
+
+ #include <stdarg.h>
+ #include <string.h>
+@@ -261,11 +263,15 @@
+ const u_char *data, size_t datalen,
+ const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
+ {
++ EVP_PKEY *pkey = NULL;
+ DSA_SIG *sig = NULL;
+ const BIGNUM *sig_r, *sig_s;
+- u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN];
+- size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
++ u_char sigblob[SIGBLOB_LEN];
++ size_t rlen, slen;
++ int len;
+ struct sshbuf *b = NULL;
++ u_char *sigb = NULL;
++ const u_char *psig = NULL;
+ int ret = SSH_ERR_INVALID_ARGUMENT;
+
+ if (lenp != NULL)
+@@ -276,17 +282,23 @@
+ if (key == NULL || key->dsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_DSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+- if (dlen == 0)
+- return SSH_ERR_INTERNAL_ERROR;
+
+- if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
+- digest, sizeof(digest))) != 0)
++ if ((ret = ssh_create_evp_dss(key, &pkey)) != 0)
++ return ret;
++ ret = sshkey_calculate_signature(pkey, SSH_DIGEST_SHA1, &sigb, &len,
++ data, datalen);
++ EVP_PKEY_free(pkey);
++ if (ret < 0) {
+ goto out;
++ }
+
+- if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) {
++ psig = sigb;
++ if ((sig = d2i_DSA_SIG(NULL, &psig, len)) == NULL) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
++ free(sigb);
++ sigb = NULL;
+
+ DSA_SIG_get0(sig, &sig_r, &sig_s);
+ rlen = BN_num_bytes(sig_r);
+@@ -319,7 +331,7 @@
+ *lenp = len;
+ ret = 0;
+ out:
+- explicit_bzero(digest, sizeof(digest));
++ free(sigb);
+ DSA_SIG_free(sig);
+ sshbuf_free(b);
+ return ret;
+@@ -331,20 +343,20 @@
+ const u_char *data, size_t dlen, const char *alg, u_int compat,
+ struct sshkey_sig_details **detailsp)
+ {
++ EVP_PKEY *pkey = NULL;
+ DSA_SIG *dsig = NULL;
+ BIGNUM *sig_r = NULL, *sig_s = NULL;
+- u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL;
+- size_t len, hlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
++ u_char *sigblob = NULL;
++ size_t len, slen;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL;
+ char *ktype = NULL;
++ u_char *sigb = NULL, *psig = NULL;
+
+ if (key == NULL || key->dsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_DSA ||
+ sig == NULL || siglen == 0)
+ return SSH_ERR_INVALID_ARGUMENT;
+- if (hlen == 0)
+- return SSH_ERR_INTERNAL_ERROR;
+
+ /* fetch signature */
+ if ((b = sshbuf_from(sig, siglen)) == NULL)
+@@ -386,25 +398,28 @@
+ }
+ sig_r = sig_s = NULL; /* transferred */
+
+- /* sha1 the data */
+- if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, dlen,
+- digest, sizeof(digest))) != 0)
++ if ((slen = i2d_DSA_SIG(dsig, NULL)) == 0) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+-
+- switch (DSA_do_verify(digest, hlen, dsig, key->dsa)) {
+- case 1:
+- ret = 0;
+- break;
+- case 0:
+- ret = SSH_ERR_SIGNATURE_INVALID;
++ }
++ if ((sigb = malloc(slen)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+- default:
++ }
++ psig = sigb;
++ if ((slen = i2d_DSA_SIG(dsig, &psig)) == 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+
++ if ((ret = ssh_create_evp_dss(key, &pkey)) != 0)
++ goto out;
++ ret = sshkey_verify_signature(pkey, SSH_DIGEST_SHA1, data, dlen,
++ sigb, slen);
++ EVP_PKEY_free(pkey);
++
+ out:
+- explicit_bzero(digest, sizeof(digest));
++ free(sigb);
+ DSA_SIG_free(dsig);
+ BN_clear_free(sig_r);
+ BN_clear_free(sig_s);
+@@ -415,6 +430,65 @@
+ return ret;
+ }
+
++int
++ssh_create_evp_dss(const struct sshkey *k, EVP_PKEY **pkey)
++{
++ OSSL_PARAM_BLD *param_bld = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ const BIGNUM *p = NULL, *q = NULL, *g = NULL, *pub = NULL, *priv = NULL;
++ int ret = 0;
++
++ if (k == NULL)
++ return SSH_ERR_INVALID_ARGUMENT;
++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "DSA", NULL)) == NULL ||
++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ DSA_get0_pqg(k->dsa, &p, &q, &g);
++ DSA_get0_key(k->dsa, &pub, &priv);
++
++ if (p != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_P, p) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (q != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_Q, q) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (g != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_FFC_G, g) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (pub != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld,
++ OSSL_PKEY_PARAM_PUB_KEY,
++ pub) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (priv != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld,
++ OSSL_PKEY_PARAM_PRIV_KEY,
++ priv) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++out:
++ OSSL_PARAM_BLD_free(param_bld);
++ EVP_PKEY_CTX_free(ctx);
++ return ret;
++}
++
+ static const struct sshkey_impl_funcs sshkey_dss_funcs = {
+ /* .size = */ ssh_dss_size,
+ /* .alloc = */ ssh_dss_alloc,
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-ecdsa.c openssh-9.3p1-patched/ssh-ecdsa.c
+--- openssh-9.3p1/ssh-ecdsa.c 2023-03-15 22:28:19.000000000 +0100
++++ openssh-9.3p1-patched/ssh-ecdsa.c 2023-06-06 15:52:25.626551768 +0200
+@@ -34,6 +34,8 @@
+ #include <openssl/ec.h>
+ #include <openssl/ecdsa.h>
+ #include <openssl/evp.h>
++#include <openssl/core_names.h>
++#include <openssl/param_build.h>
+
+ #include <string.h>
+
+@@ -126,19 +128,29 @@
+ static int
+ ssh_ecdsa_generate(struct sshkey *k, int bits)
+ {
+- EC_KEY *private;
++ EVP_PKEY_CTX *ctx = NULL;
++ EVP_PKEY *res = NULL;
+
+ if ((k->ecdsa_nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
+ return SSH_ERR_KEY_LENGTH;
+- if ((private = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
++
++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+- if (EC_KEY_generate_key(private) != 1) {
+- EC_KEY_free(private);
++
++ if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_CTX_set_group_name(ctx, OBJ_nid2sn(k->ecdsa_nid)) <= 0
++ || EVP_PKEY_keygen(ctx, &res) <= 0) {
++ EVP_PKEY_CTX_free(ctx);
++ EVP_PKEY_free(res);
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ }
+- EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
+- k->ecdsa = private;
+- return 0;
++ /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/
++ k->ecdsa = EVP_PKEY_get1_EC_KEY(res);
++ if (k->ecdsa)
++ EC_KEY_set_asn1_flag(k->ecdsa, OPENSSL_EC_NAMED_CURVE);
++
++ EVP_PKEY_CTX_free(ctx);
++ EVP_PKEY_free(res);
++ return (k->ecdsa) ? 0 : SSH_ERR_LIBCRYPTO_ERROR;
+ }
+
+ static int
+@@ -228,11 +240,13 @@
+ const u_char *data, size_t dlen,
+ const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
+ {
++ EVP_PKEY *pkey = NULL;
+ ECDSA_SIG *esig = NULL;
++ unsigned char *sigb = NULL;
++ const unsigned char *psig;
+ const BIGNUM *sig_r, *sig_s;
+ int hash_alg;
+- u_char digest[SSH_DIGEST_MAX_LENGTH];
+- size_t len, hlen;
++ int len;
+ struct sshbuf *b = NULL, *bb = NULL;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+@@ -245,18 +259,33 @@
+ sshkey_type_plain(key->type) != KEY_ECDSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+- if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
+- (hlen = ssh_digest_bytes(hash_alg)) == 0)
++ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
+ return SSH_ERR_INTERNAL_ERROR;
+- if ((ret = ssh_digest_memory(hash_alg, data, dlen,
+- digest, sizeof(digest))) != 0)
++
++#ifdef ENABLE_PKCS11
++ if (is_ecdsa_pkcs11(key->ecdsa)) {
++ if ((pkey = EVP_PKEY_new()) == NULL ||
++ EVP_PKEY_set1_EC_KEY(pkey, key->ecdsa) != 1)
++ return SSH_ERR_ALLOC_FAIL;
++ } else {
++#endif
++ if ((ret = ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey)) != 0)
++ return ret;
++#ifdef ENABLE_PKCS11
++ }
++#endif
++ ret = sshkey_calculate_signature(pkey, hash_alg, &sigb, &len, data,
++ dlen);
++ EVP_PKEY_free(pkey);
++ if (ret < 0) {
+ goto out;
++ }
+
+- if ((esig = ECDSA_do_sign(digest, hlen, key->ecdsa)) == NULL) {
++ psig = sigb;
++ if (d2i_ECDSA_SIG(&esig, &psig, len) == NULL) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+-
+ if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+@@ -280,7 +309,7 @@
+ *lenp = len;
+ ret = 0;
+ out:
+- explicit_bzero(digest, sizeof(digest));
++ free(sigb);
+ sshbuf_free(b);
+ sshbuf_free(bb);
+ ECDSA_SIG_free(esig);
+@@ -293,22 +322,21 @@
+ const u_char *data, size_t dlen, const char *alg, u_int compat,
+ struct sshkey_sig_details **detailsp)
+ {
++ EVP_PKEY *pkey = NULL;
+ ECDSA_SIG *esig = NULL;
+ BIGNUM *sig_r = NULL, *sig_s = NULL;
+- int hash_alg;
+- u_char digest[SSH_DIGEST_MAX_LENGTH];
+- size_t hlen;
++ int hash_alg, len;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL, *sigbuf = NULL;
+ char *ktype = NULL;
++ unsigned char *sigb = NULL, *psig = NULL;
+
+ if (key == NULL || key->ecdsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_ECDSA ||
+ sig == NULL || siglen == 0)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+- if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
+- (hlen = ssh_digest_bytes(hash_alg)) == 0)
++ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1)
+ return SSH_ERR_INTERNAL_ERROR;
+
+ /* fetch signature */
+@@ -344,28 +372,33 @@
+ }
+ sig_r = sig_s = NULL; /* transferred */
+
+- if (sshbuf_len(sigbuf) != 0) {
+- ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
++ /* Figure out the length */
++ if ((len = i2d_ECDSA_SIG(esig, NULL)) == 0) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+- if ((ret = ssh_digest_memory(hash_alg, data, dlen,
+- digest, sizeof(digest))) != 0)
+- goto out;
+-
+- switch (ECDSA_do_verify(digest, hlen, esig, key->ecdsa)) {
+- case 1:
+- ret = 0;
+- break;
+- case 0:
+- ret = SSH_ERR_SIGNATURE_INVALID;
++ if ((sigb = malloc(len)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+- default:
++ }
++ psig = sigb;
++ if ((len = i2d_ECDSA_SIG(esig, &psig)) == 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+
++ if (sshbuf_len(sigbuf) != 0) {
++ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
++ goto out;
++ }
++
++ if (ssh_create_evp_ec(key->ecdsa, key->ecdsa_nid, &pkey) != 0)
++ goto out;
++ ret = sshkey_verify_signature(pkey, hash_alg, data, dlen, sigb, len);
++ EVP_PKEY_free(pkey);
++
+ out:
+- explicit_bzero(digest, sizeof(digest));
++ free(sigb);
+ sshbuf_free(sigbuf);
+ sshbuf_free(b);
+ ECDSA_SIG_free(esig);
+@@ -375,6 +408,79 @@
+ return ret;
+ }
+
++int
++ssh_create_evp_ec(EC_KEY *k, int ecdsa_nid, EVP_PKEY **pkey)
++{
++ OSSL_PARAM_BLD *param_bld = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ BN_CTX *bn_ctx = NULL;
++ uint8_t *pub_ser = NULL;
++ const char *group_name;
++ const EC_POINT *pub = NULL;
++ const BIGNUM *priv = NULL;
++ int ret = 0;
++
++ if (k == NULL)
++ return SSH_ERR_INVALID_ARGUMENT;
++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL)) == NULL ||
++ (param_bld = OSSL_PARAM_BLD_new()) == NULL ||
++ (bn_ctx = BN_CTX_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ if ((group_name = OSSL_EC_curve_nid2name(ecdsa_nid)) == NULL ||
++ OSSL_PARAM_BLD_push_utf8_string(param_bld,
++ OSSL_PKEY_PARAM_GROUP_NAME,
++ group_name,
++ strlen(group_name)) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if ((pub = EC_KEY_get0_public_key(k)) != NULL) {
++ const EC_GROUP *group;
++ size_t len;
++
++ group = EC_KEY_get0_group(k);
++ len = EC_POINT_point2oct(group, pub,
++ POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
++ if ((pub_ser = malloc(len)) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++ EC_POINT_point2oct(group,
++ pub,
++ POINT_CONVERSION_UNCOMPRESSED,
++ pub_ser,
++ len,
++ bn_ctx);
++ if (OSSL_PARAM_BLD_push_octet_string(param_bld,
++ OSSL_PKEY_PARAM_PUB_KEY,
++ pub_ser,
++ len) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ }
++ if ((priv = EC_KEY_get0_private_key(k)) != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld,
++ OSSL_PKEY_PARAM_PRIV_KEY, priv) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++out:
++ OSSL_PARAM_BLD_free(param_bld);
++ EVP_PKEY_CTX_free(ctx);
++ BN_CTX_free(bn_ctx);
++ free(pub_ser);
++ return ret;
++}
++
+ /* NB. not static; used by ECDSA-SK */
+ const struct sshkey_impl_funcs sshkey_ecdsa_funcs = {
+ /* .size = */ ssh_ecdsa_size,
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/sshkey.c openssh-9.3p1-patched/sshkey.c
+--- openssh-9.3p1/sshkey.c 2023-06-06 15:53:36.608444190 +0200
++++ openssh-9.3p1-patched/sshkey.c 2023-06-06 15:52:25.625551756 +0200
+@@ -34,6 +34,8 @@
+ #include <openssl/evp.h>
+ #include <openssl/err.h>
+ #include <openssl/pem.h>
++#include <openssl/core_names.h>
++#include <openssl/param_build.h>
+ #endif
+
+ #include "crypto_api.h"
+@@ -57,6 +59,7 @@
+ #define SSHKEY_INTERNAL
+ #include "sshkey.h"
+ #include "match.h"
++#include "log.h"
+ #include "ssh-sk.h"
+
+ #ifdef WITH_XMSS
+@@ -575,6 +577,86 @@
+ }
+
+ #ifdef WITH_OPENSSL
++int
++sshkey_calculate_signature(EVP_PKEY *pkey, int hash_alg, u_char **sigp,
++ int *lenp, const u_char *data, size_t datalen)
++{
++ EVP_MD_CTX *ctx = NULL;
++ u_char *sig = NULL;
++ int ret, slen;
++ size_t len;
++
++ if (sigp == NULL || lenp == NULL) {
++ return SSH_ERR_INVALID_ARGUMENT;
++ }
++
++ slen = EVP_PKEY_get_size(pkey);
++ if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
++ return SSH_ERR_INVALID_ARGUMENT;
++
++ len = slen;
++ if ((sig = malloc(slen)) == NULL) {
++ return SSH_ERR_ALLOC_FAIL;
++ }
++
++ if ((ctx = EVP_MD_CTX_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto error;
++ }
++ if (EVP_DigestSignInit(ctx, NULL, ssh_digest_to_md(hash_alg),
++ NULL, pkey) != 1 ||
++ EVP_DigestSignUpdate(ctx, data, datalen) != 1 ||
++ EVP_DigestSignFinal(ctx, sig, &len) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto error;
++ }
++
++ *sigp = sig;
++ *lenp = len;
++ /* Now owned by the caller */
++ sig = NULL;
++ ret = 0;
++
++error:
++ EVP_MD_CTX_free(ctx);
++ free(sig);
++ return ret;
++}
++
++int
++sshkey_verify_signature(EVP_PKEY *pkey, int hash_alg, const u_char *data,
++ size_t datalen, u_char *sigbuf, int siglen)
++{
++ EVP_MD_CTX *ctx = NULL;
++ int ret;
++
++ if ((ctx = EVP_MD_CTX_new()) == NULL) {
++ return SSH_ERR_ALLOC_FAIL;
++ }
++ if (EVP_DigestVerifyInit(ctx, NULL, ssh_digest_to_md(hash_alg),
++ NULL, pkey) != 1 ||
++ EVP_DigestVerifyUpdate(ctx, data, datalen) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto done;
++ }
++ ret = EVP_DigestVerifyFinal(ctx, sigbuf, siglen);
++ switch (ret) {
++ case 1:
++ ret = 0;
++ break;
++ case 0:
++ ret = SSH_ERR_SIGNATURE_INVALID;
++ break;
++ default:
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ break;
++ }
++
++done:
++ EVP_MD_CTX_free(ctx);
++ return ret;
++}
++
+ /* XXX: these are really begging for a table-driven approach */
+ int
+ sshkey_curve_name_to_nid(const char *name)
+@@ -3763,3 +3845,27 @@
+ return 0;
+ }
+ #endif /* WITH_XMSS */
++
++#ifdef WITH_OPENSSL
++EVP_PKEY *
++sshkey_create_evp(OSSL_PARAM_BLD *param_bld, EVP_PKEY_CTX *ctx)
++{
++ EVP_PKEY *ret = NULL;
++ OSSL_PARAM *params = NULL;
++ if (param_bld == NULL || ctx == NULL) {
++ debug2_f("param_bld or ctx is NULL");
++ return NULL;
++ }
++ if ((params = OSSL_PARAM_BLD_to_param(param_bld)) == NULL) {
++ debug2_f("Could not build param list");
++ return NULL;
++ }
++ if (EVP_PKEY_fromdata_init(ctx) != 1 ||
++ EVP_PKEY_fromdata(ctx, &ret, EVP_PKEY_KEYPAIR, params) != 1) {
++ debug2_f("EVP_PKEY_fromdata failed");
++ OSSL_PARAM_free(params);
++ return NULL;
++ }
++ return ret;
++}
++#endif /* WITH_OPENSSL */
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/sshkey.h openssh-9.3p1-patched/sshkey.h
+--- openssh-9.3p1/sshkey.h 2023-06-06 15:53:36.608444190 +0200
++++ openssh-9.3p1-patched/sshkey.h 2023-06-06 15:52:25.626551768 +0200
+@@ -31,6 +31,9 @@
+ #ifdef WITH_OPENSSL
+ #include <openssl/rsa.h>
+ #include <openssl/dsa.h>
++#include <openssl/evp.h>
++#include <openssl/param_build.h>
++#include <openssl/core_names.h>
+ # ifdef OPENSSL_HAS_ECC
+ # include <openssl/ec.h>
+ # include <openssl/ecdsa.h>
+@@ -268,6 +271,10 @@
+ const char *sshkey_ssh_name_plain(const struct sshkey *);
+ int sshkey_names_valid2(const char *, int);
+ char *sshkey_alg_list(int, int, int, char);
++int sshkey_calculate_signature(EVP_PKEY*, int, u_char **,
++ int *, const u_char *, size_t);
++int sshkey_verify_signature(EVP_PKEY *, int, const u_char *,
++ size_t, u_char *, int);
+
+ int sshkey_from_blob(const u_char *, size_t, struct sshkey **);
+ int sshkey_fromb(struct sshbuf *, struct sshkey **);
+@@ -324,6 +331,13 @@
+
+ void sshkey_sig_details_free(struct sshkey_sig_details *);
+
++#ifdef WITH_OPENSSL
++EVP_PKEY *sshkey_create_evp(OSSL_PARAM_BLD *, EVP_PKEY_CTX *);
++int ssh_create_evp_dss(const struct sshkey *, EVP_PKEY **);
++int ssh_create_evp_rsa(const struct sshkey *, EVP_PKEY **);
++int ssh_create_evp_ec(EC_KEY *, int, EVP_PKEY **);
++#endif /* WITH_OPENSSL */
++
+ #ifdef SSHKEY_INTERNAL
+ int sshkey_sk_fields_equal(const struct sshkey *a, const struct sshkey *b);
+ void sshkey_sk_cleanup(struct sshkey *k);
+@@ -338,6 +352,10 @@
+ #endif
+ #endif
+
++#ifdef ENABLE_PKCS11
++int pkcs11_get_ecdsa_idx(void);
++#endif
++
+ #if !defined(WITH_OPENSSL)
+ # undef RSA
+ # undef DSA
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-pkcs11.c openssh-9.3p1-patched/ssh-pkcs11.c
+--- openssh-9.3p1/ssh-pkcs11.c 2023-06-06 15:53:36.592443989 +0200
++++ openssh-9.3p1-patched/ssh-pkcs11.c 2023-06-06 15:52:25.626551768 +0200
+@@ -777,8 +777,24 @@
+
+ return (0);
+ }
++
++int
++is_ecdsa_pkcs11(EC_KEY *ecdsa)
++{
++ if (EC_KEY_get_ex_data(ecdsa, ec_key_idx) != NULL)
++ return 1;
++ return 0;
++}
+ #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */
+
++int
++is_rsa_pkcs11(RSA *rsa)
++{
++ if (RSA_get_ex_data(rsa, rsa_idx) != NULL)
++ return 1;
++ return 0;
++}
++
+ /* remove trailing spaces */
+ static void
+ rmspace(u_char *buf, size_t len)
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-pkcs11-client.c openssh-9.3p1-patched/ssh-pkcs11-client.c
+--- openssh-9.3p1/ssh-pkcs11-client.c 2023-06-06 15:53:36.591443976 +0200
++++ openssh-9.3p1-patched/ssh-pkcs11-client.c 2023-06-06 15:52:25.626551768 +0200
+@@ -225,8 +225,36 @@
+ static RSA_METHOD *helper_rsa;
+ #if defined(OPENSSL_HAS_ECC) && defined(HAVE_EC_KEY_METHOD_NEW)
+ static EC_KEY_METHOD *helper_ecdsa;
++
++int
++is_ecdsa_pkcs11(EC_KEY *ecdsa)
++{
++ const EC_KEY_METHOD *meth;
++ ECDSA_SIG *(*sign_sig)(const unsigned char *dgst, int dgstlen,
++ const BIGNUM *kinv, const BIGNUM *rp, EC_KEY *eckey) = NULL;
++
++ meth = EC_KEY_get_method(ecdsa);
++ EC_KEY_METHOD_get_sign(meth, NULL, NULL, &sign_sig);
++ if (sign_sig == ecdsa_do_sign)
++ return 1;
++ return 0;
++}
+ #endif /* OPENSSL_HAS_ECC && HAVE_EC_KEY_METHOD_NEW */
+
++int
++is_rsa_pkcs11(RSA *rsa)
++{
++ const RSA_METHOD *meth;
++ int (*priv_enc)(int flen, const unsigned char *from,
++ unsigned char *to, RSA *rsa, int padding) = NULL;
++
++ meth = RSA_get_method(rsa);
++ priv_enc = RSA_meth_get_priv_enc(meth);
++ if (priv_enc == rsa_encrypt)
++ return 1;
++ return 0;
++}
++
+ /* redirect private key crypto operations to the ssh-pkcs11-helper */
+ static void
+ wrap_key(struct sshkey *k)
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-pkcs11.h openssh-9.3p1-patched/ssh-pkcs11.h
+--- openssh-9.3p1/ssh-pkcs11.h 2023-06-06 15:53:36.592443989 +0200
++++ openssh-9.3p1-patched/ssh-pkcs11.h 2023-06-06 15:52:25.626551768 +0200
+@@ -39,6 +39,11 @@
+ u_int32_t *);
+ #endif
+
++#ifdef HAVE_EC_KEY_METHOD_NEW
++int is_ecdsa_pkcs11(EC_KEY *ecdsa);
++#endif
++int is_rsa_pkcs11(RSA *rsa);
++
+ #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11)
+ #undef ENABLE_PKCS11
+ #endif
+diff --color -ru -x regress -x autom4te.cache -x '*.o' -x '*.lo' -x Makefile -x config.status -x configure~ -x configure.ac openssh-9.3p1/ssh-rsa.c openssh-9.3p1-patched/ssh-rsa.c
+--- openssh-9.3p1/ssh-rsa.c 2023-03-15 22:28:19.000000000 +0100
++++ openssh-9.3p1-patched/ssh-rsa.c 2023-06-06 15:52:25.627551781 +0200
+@@ -23,6 +23,8 @@
+
+ #include <openssl/evp.h>
+ #include <openssl/err.h>
++#include <openssl/core_names.h>
++#include <openssl/param_build.h>
+
+ #include <stdarg.h>
+ #include <string.h>
+@@ -36,7 +38,7 @@
+
+ #include "openbsd-compat/openssl-compat.h"
+
+-static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
++static int openssh_RSA_verify(int, const u_char *, size_t, u_char *, size_t, EVP_PKEY *);
+
+ static u_int
+ ssh_rsa_size(const struct sshkey *key)
+@@ -131,27 +133,50 @@
+ static int
+ ssh_rsa_generate(struct sshkey *k, int bits)
+ {
+- RSA *private = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ EVP_PKEY *res = NULL;
+ BIGNUM *f4 = NULL;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
+ bits > SSHBUF_MAX_BIGNUM * 8)
+ return SSH_ERR_KEY_LENGTH;
+- if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
++
++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL
++ || (f4 = BN_new()) == NULL || !BN_set_word(f4, RSA_F4)) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+- if (!BN_set_word(f4, RSA_F4) ||
+- !RSA_generate_key_ex(private, bits, f4, NULL)) {
++
++ if (EVP_PKEY_keygen_init(ctx) <= 0) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++ if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0) {
++ ret = SSH_ERR_KEY_LENGTH;
++ goto out;
++ }
++
++ if (EVP_PKEY_CTX_set1_rsa_keygen_pubexp(ctx, f4) <= 0)
++ goto out;
++
++ if (EVP_PKEY_keygen(ctx, &res) <= 0) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++ /* This function is deprecated in OpenSSL 3.0 but OpenSSH doesn't worry about it*/
++ k->rsa = EVP_PKEY_get1_RSA(res);
++ if (k->rsa) {
++ ret = 0;
++ } else {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+- k->rsa = private;
+- private = NULL;
+- ret = 0;
+ out:
+- RSA_free(private);
++ EVP_PKEY_CTX_free(ctx);
++ EVP_PKEY_free(res);
+ BN_free(f4);
+ return ret;
+ }
+@@ -317,21 +342,6 @@
+ return -1;
+ }
+
+-static int
+-rsa_hash_alg_nid(int type)
+-{
+- switch (type) {
+- case SSH_DIGEST_SHA1:
+- return NID_sha1;
+- case SSH_DIGEST_SHA256:
+- return NID_sha256;
+- case SSH_DIGEST_SHA512:
+- return NID_sha512;
+- default:
+- return -1;
+- }
+-}
+-
+ int
+ ssh_rsa_complete_crt_parameters(struct sshkey *key, const BIGNUM *iqmp)
+ {
+@@ -393,11 +403,10 @@
+ const u_char *data, size_t datalen,
+ const char *alg, const char *sk_provider, const char *sk_pin, u_int compat)
+ {
+- const BIGNUM *rsa_n;
+- u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
+- size_t slen = 0;
+- u_int hlen, len;
+- int nid, hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
++ EVP_PKEY *pkey = NULL;
++ u_char *sig = NULL;
++ int len, slen = 0;
++ int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL;
+
+ if (lenp != NULL)
+@@ -409,33 +418,33 @@
+ hash_alg = SSH_DIGEST_SHA1;
+ else
+ hash_alg = rsa_hash_id_from_keyname(alg);
++
+ if (key == NULL || key->rsa == NULL || hash_alg == -1 ||
+ sshkey_type_plain(key->type) != KEY_RSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+- RSA_get0_key(key->rsa, &rsa_n, NULL, NULL);
+- if (BN_num_bits(rsa_n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
+- return SSH_ERR_KEY_LENGTH;
+ slen = RSA_size(key->rsa);
+- if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
+- return SSH_ERR_INVALID_ARGUMENT;
+-
+- /* hash the data */
+- nid = rsa_hash_alg_nid(hash_alg);
+- if ((hlen = ssh_digest_bytes(hash_alg)) == 0)
+- return SSH_ERR_INTERNAL_ERROR;
+- if ((ret = ssh_digest_memory(hash_alg, data, datalen,
+- digest, sizeof(digest))) != 0)
+- goto out;
++ if (RSA_bits(key->rsa) < SSH_RSA_MINIMUM_MODULUS_SIZE)
++ return SSH_ERR_KEY_LENGTH;
+
+- if ((sig = malloc(slen)) == NULL) {
+- ret = SSH_ERR_ALLOC_FAIL;
+- goto out;
++#ifdef ENABLE_PKCS11
++ if (is_rsa_pkcs11(key->rsa)) {
++ if ((pkey = EVP_PKEY_new()) == NULL ||
++ EVP_PKEY_set1_RSA(pkey, key->rsa) != 1)
++ return SSH_ERR_ALLOC_FAIL;
++ } else {
++#endif
++ if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0)
++ return ret;
++#ifdef ENABLE_PKCS11
+ }
+-
+- if (RSA_sign(nid, digest, hlen, sig, &len, key->rsa) != 1) {
+- ret = SSH_ERR_LIBCRYPTO_ERROR;
++#endif
++ ret = sshkey_calculate_signature(pkey, hash_alg, &sig, &len, data,
++ datalen);
++ EVP_PKEY_free(pkey);
++ if (ret < 0) {
+ goto out;
+ }
++
+ if (len < slen) {
+ size_t diff = slen - len;
+ memmove(sig + diff, sig, len);
+@@ -444,6 +453,7 @@
+ ret = SSH_ERR_INTERNAL_ERROR;
+ goto out;
+ }
++
+ /* encode signature */
+ if ((b = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+@@ -464,7 +474,6 @@
+ *lenp = len;
+ ret = 0;
+ out:
+- explicit_bzero(digest, sizeof(digest));
+ freezero(sig, slen);
+ sshbuf_free(b);
+ return ret;
+@@ -476,10 +485,10 @@
+ const u_char *data, size_t dlen, const char *alg, u_int compat,
+ struct sshkey_sig_details **detailsp)
+ {
+- const BIGNUM *rsa_n;
++ EVP_PKEY *pkey = NULL;
+ char *sigtype = NULL;
+ int hash_alg, want_alg, ret = SSH_ERR_INTERNAL_ERROR;
+- size_t len = 0, diff, modlen, hlen;
++ size_t len = 0, diff, modlen;
+ struct sshbuf *b = NULL;
+ u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
+
+@@ -487,8 +496,7 @@
+ sshkey_type_plain(key->type) != KEY_RSA ||
+ sig == NULL || siglen == 0)
+ return SSH_ERR_INVALID_ARGUMENT;
+- RSA_get0_key(key->rsa, &rsa_n, NULL, NULL);
+- if (BN_num_bits(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)
+@@ -540,16 +548,13 @@
+ explicit_bzero(sigblob, diff);
+ len = modlen;
+ }
+- if ((hlen = ssh_digest_bytes(hash_alg)) == 0) {
+- ret = SSH_ERR_INTERNAL_ERROR;
+- goto out;
+- }
+- if ((ret = ssh_digest_memory(hash_alg, data, dlen,
+- digest, sizeof(digest))) != 0)
++
++ if ((ret = ssh_create_evp_rsa(key, &pkey)) != 0)
+ goto out;
+
+- ret = openssh_RSA_verify(hash_alg, digest, hlen, sigblob, len,
+- key->rsa);
++ ret = openssh_RSA_verify(hash_alg, data, dlen, sigblob, len, pkey);
++ EVP_PKEY_free(pkey);
++
+ out:
+ freezero(sigblob, len);
+ free(sigtype);
+@@ -558,125 +563,110 @@
+ return ret;
+ }
+
+-/*
+- * See:
+- * http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/
+- * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1.asn
+- */
+-
+-/*
+- * id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+- * oiw(14) secsig(3) algorithms(2) 26 }
+- */
+-static const u_char id_sha1[] = {
+- 0x30, 0x21, /* type Sequence, length 0x21 (33) */
+- 0x30, 0x09, /* type Sequence, length 0x09 */
+- 0x06, 0x05, /* type OID, length 0x05 */
+- 0x2b, 0x0e, 0x03, 0x02, 0x1a, /* id-sha1 OID */
+- 0x05, 0x00, /* NULL */
+- 0x04, 0x14 /* Octet string, length 0x14 (20), followed by sha1 hash */
+-};
+-
+-/*
+- * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
+- * id-sha256 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
+- * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
+- * id-sha256(1) }
+- */
+-static const u_char id_sha256[] = {
+- 0x30, 0x31, /* type Sequence, length 0x31 (49) */
+- 0x30, 0x0d, /* type Sequence, length 0x0d (13) */
+- 0x06, 0x09, /* type OID, length 0x09 */
+- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, /* id-sha256 */
+- 0x05, 0x00, /* NULL */
+- 0x04, 0x20 /* Octet string, length 0x20 (32), followed by sha256 hash */
+-};
+-
+-/*
+- * See http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
+- * id-sha512 OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840)
+- * organization(1) gov(101) csor(3) nistAlgorithm(4) hashAlgs(2)
+- * id-sha256(3) }
+- */
+-static const u_char id_sha512[] = {
+- 0x30, 0x51, /* type Sequence, length 0x51 (81) */
+- 0x30, 0x0d, /* type Sequence, length 0x0d (13) */
+- 0x06, 0x09, /* type OID, length 0x09 */
+- 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, /* id-sha512 */
+- 0x05, 0x00, /* NULL */
+- 0x04, 0x40 /* Octet string, length 0x40 (64), followed by sha512 hash */
+-};
+-
+ static int
+-rsa_hash_alg_oid(int hash_alg, const u_char **oidp, size_t *oidlenp)
++openssh_RSA_verify(int hash_alg, const u_char *data, size_t datalen,
++ u_char *sigbuf, size_t siglen, EVP_PKEY *pkey)
+ {
+- switch (hash_alg) {
+- case SSH_DIGEST_SHA1:
+- *oidp = id_sha1;
+- *oidlenp = sizeof(id_sha1);
+- break;
+- case SSH_DIGEST_SHA256:
+- *oidp = id_sha256;
+- *oidlenp = sizeof(id_sha256);
+- break;
+- case SSH_DIGEST_SHA512:
+- *oidp = id_sha512;
+- *oidlenp = sizeof(id_sha512);
+- break;
+- default:
+- return SSH_ERR_INVALID_ARGUMENT;
+- }
+- return 0;
+-}
++ size_t rsasize = 0;
++ int ret;
+
+-static int
+-openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
+- u_char *sigbuf, size_t siglen, RSA *rsa)
+-{
+- size_t rsasize = 0, oidlen = 0, hlen = 0;
+- int ret, len, oidmatch, hashmatch;
+- const u_char *oid = NULL;
+- u_char *decrypted = NULL;
+-
+- if ((ret = rsa_hash_alg_oid(hash_alg, &oid, &oidlen)) != 0)
+- return ret;
+- ret = SSH_ERR_INTERNAL_ERROR;
+- hlen = ssh_digest_bytes(hash_alg);
+- if (hashlen != hlen) {
+- ret = SSH_ERR_INVALID_ARGUMENT;
+- goto done;
+- }
+- rsasize = RSA_size(rsa);
++ rsasize = EVP_PKEY_get_size(pkey);
+ if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
+ siglen == 0 || siglen > rsasize) {
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ goto done;
+ }
+- if ((decrypted = malloc(rsasize)) == NULL) {
+- ret = SSH_ERR_ALLOC_FAIL;
+- goto done;
+- }
+- if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
+- RSA_PKCS1_PADDING)) < 0) {
+- ret = SSH_ERR_LIBCRYPTO_ERROR;
+- goto done;
+- }
+- if (len < 0 || (size_t)len != hlen + oidlen) {
+- ret = SSH_ERR_INVALID_FORMAT;
+- goto done;
+- }
+- oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
+- hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
+- if (!oidmatch || !hashmatch) {
+- ret = SSH_ERR_SIGNATURE_INVALID;
+- goto done;
+- }
+- ret = 0;
++
++ ret = sshkey_verify_signature(pkey, hash_alg, data, datalen,
++ sigbuf, siglen);
++
+ done:
+- freezero(decrypted, rsasize);
+ return ret;
+ }
+
++int
++ssh_create_evp_rsa(const struct sshkey *k, EVP_PKEY **pkey)
++{
++ OSSL_PARAM_BLD *param_bld = NULL;
++ EVP_PKEY_CTX *ctx = NULL;
++ int ret = 0;
++ const BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
++ const BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
++
++ if (k == NULL)
++ return SSH_ERR_INVALID_ARGUMENT;
++ if ((ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL)) == NULL ||
++ (param_bld = OSSL_PARAM_BLD_new()) == NULL) {
++ ret = SSH_ERR_ALLOC_FAIL;
++ goto out;
++ }
++
++ RSA_get0_key(k->rsa, &n, &e, &d);
++ RSA_get0_factors(k->rsa, &p, &q);
++ RSA_get0_crt_params(k->rsa, &dmp1, &dmq1, &iqmp);
++
++ if (n != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_N, n) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (e != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_E, e) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (d != NULL &&
++ OSSL_PARAM_BLD_push_BN(param_bld, OSSL_PKEY_PARAM_RSA_D, d) != 1) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++ if ((*pkey = sshkey_create_evp(param_bld, ctx)) == NULL) {
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++ /* setting this to param_build makes the creation process fail */
++ if (p != NULL &&
++ EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, p) != 1) {
++ debug2_f("failed to add 'p' param");
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (q != NULL &&
++ EVP_PKEY_set_bn_param(*pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, q) != 1) {
++ debug2_f("failed to add 'q' param");
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (dmp1 != NULL &&
++ EVP_PKEY_set_bn_param(*pkey,
++ OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1) != 1) {
++ debug2_f("failed to add 'dmp1' param");
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (dmq1 != NULL &&
++ EVP_PKEY_set_bn_param(*pkey,
++ OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1) != 1) {
++ debug2_f("failed to add 'dmq1' param");
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++ if (iqmp != NULL &&
++ EVP_PKEY_set_bn_param(*pkey,
++ OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp) != 1) {
++ debug2_f("failed to add 'iqmp' param");
++ ret = SSH_ERR_LIBCRYPTO_ERROR;
++ goto out;
++ }
++
++out:
++ OSSL_PARAM_BLD_free(param_bld);
++ EVP_PKEY_CTX_free(ctx);
++ return ret;
++}
++
+ static const struct sshkey_impl_funcs sshkey_rsa_funcs = {
+ /* .size = */ ssh_rsa_size,
+ /* .alloc = */ ssh_rsa_alloc,
diff --git a/openssh-9.3p1-upstream-cve-2023-38408.patch b/openssh-9.3p1-upstream-cve-2023-38408.patch
new file mode 100644
index 0000000..e9ac2ae
--- /dev/null
+++ b/openssh-9.3p1-upstream-cve-2023-38408.patch
@@ -0,0 +1,130 @@
+diff --git a/ssh-agent.c b/ssh-agent.c
+index 618bb198..8ea831f4 100644
+diff -up openssh-9.3p1/ssh-agent.c.cve openssh-9.3p1/ssh-agent.c
+--- openssh-9.3p1/ssh-agent.c.cve 2023-07-21 15:38:13.237276580 +0200
++++ openssh-9.3p1/ssh-agent.c 2023-07-21 15:41:30.269943569 +0200
+@@ -169,6 +169,12 @@ char socket_dir[PATH_MAX];
+ /* Pattern-list of allowed PKCS#11/Security key paths */
+ static char *allowed_providers;
+
++/*
++ * Allows PKCS11 providers or SK keys that use non-internal providers to
++ * be added over a remote connection (identified by session-bind@openssh.com).
++ */
++static int remote_add_provider;
++
+ /* locking */
+ #define LOCK_SIZE 32
+ #define LOCK_SALT_SIZE 16
+@@ -1228,6 +1234,12 @@ process_add_identity(SocketEntry *e)
+ if (strcasecmp(sk_provider, "internal") == 0) {
+ debug_f("internal provider");
+ } else {
++ if (e->nsession_ids != 0 && !remote_add_provider) {
++ verbose("failed add of SK provider \"%.100s\": "
++ "remote addition of providers is disabled",
++ sk_provider);
++ goto out;
++ }
+ if (realpath(sk_provider, canonical_provider) == NULL) {
+ verbose("failed provider \"%.100s\": "
+ "realpath: %s", sk_provider,
+@@ -1368,7 +1380,7 @@ no_identities(SocketEntry *e)
+
+ #ifdef ENABLE_PKCS11
+ static char *
+-sanitize_pkcs11_provider(const char *provider)
++sanitize_pkcs11_provider(SocketEntry *e, const char *provider)
+ {
+ struct pkcs11_uri *uri = NULL;
+ char *sane_uri, *module_path = NULL; /* default path */
+@@ -1399,6 +1411,11 @@ sanitize_pkcs11_provider(const char *pro
+ module_path = strdup(provider); /* simple path */
+
+ if (module_path != NULL) { /* do not validate default NULL path in URI */
++ if (e->nsession_ids != 0 && !remote_add_provider) {
++ verbose("failed PKCS#11 add of \"%.100s\": remote addition of "
++ "providers is disabled", provider);
++ return NULL;
++ }
+ if (realpath(module_path, canonical_provider) == NULL) {
+ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s",
+ module_path, strerror(errno));
+@@ -1455,7 +1472,7 @@ process_add_smartcard_key(SocketEntry *e
+ goto send;
+ }
+
+- sane_uri = sanitize_pkcs11_provider(provider);
++ sane_uri = sanitize_pkcs11_provider(e, provider);
+ if (sane_uri == NULL)
+ goto send;
+
+@@ -1516,7 +1533,7 @@ process_remove_smartcard_key(SocketEntry
+ }
+ free(pin);
+
+- sane_uri = sanitize_pkcs11_provider(provider);
++ sane_uri = sanitize_pkcs11_provider(e, provider);
+ if (sane_uri == NULL)
+ goto send;
+
+@@ -2108,7 +2125,9 @@ main(int ac, char **av)
+ break;
+ case 'O':
+ if (strcmp(optarg, "no-restrict-websafe") == 0)
+- restrict_websafe = 0;
++ restrict_websafe = 0;
++ else if (strcmp(optarg, "allow-remote-pkcs11") == 0)
++ remote_add_provider = 1;
+ else
+ fatal("Unknown -O option");
+ break;
+diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
+index 6be647ec..ebddf6c3 100644
+--- a/ssh-pkcs11.c
++++ b/ssh-pkcs11.c
+@@ -1537,10 +1537,8 @@ pkcs11_register_provider(char *provider_id, char *pin,
+ 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;
+- }
++ if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL)
++ fatal("dlsym(C_GetFunctionList) failed: %s", dlerror());
+
+ p->module->handle = handle;
+ /* setup the pkcs11 callbacks */
+--- a/ssh-agent.1 2023-03-15 22:28:19.000000000 +0100
++++ b/ssh-agent.1 2023-07-19 21:39:17.981406432 +0200
+@@ -107,9 +107,27 @@
+ .It Fl O Ar option
+ Specify an option when starting
+ .Nm .
+-Currently only one option is supported:
++Currently two options are supported:
++.Cm allow-remote-pkcs11
++and
+ .Cm no-restrict-websafe .
+-This instructs
++.Pp
++The
++.Cm allow-remote-pkcs11
++option allows clients of a forwarded
++.Nm
++to load PKCS#11 or FIDO provider libraries.
++By default only local clients may perform this operation.
++Note that signalling that a
++.Nm
++client remote is performed by
++.Xr ssh 1 ,
++and use of other tools to forward access to the agent socket may circumvent
++this restriction.
++.Pp
++The
++.Cm no-restrict-websafe ,
++instructs
+ .Nm
+ to permit signatures using FIDO keys that might be web authentication
+ requests.
diff --git a/openssh-9.3p1.tar.gz.asc b/openssh-9.3p1.tar.gz.asc
new file mode 100644
index 0000000..13b4da9
--- /dev/null
+++ b/openssh-9.3p1.tar.gz.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAABCgAdFiEEcWi5g4FaXu9ZpK39Kj9BTnNgYLoFAmQSOZYACgkQKj9BTnNg
+YLrKJg//fSKjNlnb3l75ZwLoWhwpEZQp7poEq5qCCRNvu4dleuU1sMxNPl9/Ow1i
+iZVW67OGNjIsJ7FJmHNF3UOgkH50c6OHivmDaTywDtyCLZvUVmaSfOe0own8s8KB
+OV7czHqd9giHQlGWWTxg9eVAfOaqpzXugkzo7UoTVqEqJ3Ru/FQ4RGSIjTGzuM/0
+EC+JkKyO+0pP3mr4XfZdxsbYc9WVEG9ZIlT153y9I5MfiWM1SC/0gg4NLz025Xaa
+ment5c+BdhIwYjC2f5F/9s0J6+lFHiFBHLQVGx4qq/Tx3XGfP0xBcS1V9Mkhyjzf
+ZXj6acQ+T50H8p3OWZyrWn11YNtGjzkwuQWrj8Ue4NPFGqgPbANeH32yOiIWpIh0
+CtpGnRGQP1zF14hEAR5gKangTNCp/IVMBhIs4UL3zI6uS2yRLTGOWcgrnjJv26vg
+jb2WmL0AeqYLZw41pbq+zmVizhhg8qk7KPQQsFxnalSFHz35tnHN8oQD5TCDxqtu
+f/roTbZhW/nnlaMlEAnB09LO6e1nyDIcJ6hj0CK9cSgIn8pb1q9GdjYx5PNKwsoa
+NuD+bqlzF5krjiOHJh+vDw0GKFusflL46Dmry5a4K0vLUGBn6uAUPtuwMdBsLofU
+k3a4zBMlOCm6o3WqgAug4fSwCfYkJ9Dc+FaedGC1X4fys4lV/6k=
+=deVJ
+-----END PGP SIGNATURE-----
diff --git a/openssh-Add-sw64-architecture.patch b/openssh-Add-sw64-architecture.patch
new file mode 100644
index 0000000..33dfbb9
--- /dev/null
+++ b/openssh-Add-sw64-architecture.patch
@@ -0,0 +1,27 @@
+From 6075cb3ead4cf473d48d0022ec25540707111c10 Mon Sep 17 00:00:00 2001
+From: wzx <wuzx1226@qq.com>
+Date: Thu, 27 Oct 2022 17:19:00 +0800
+Subject: [PATCH] Add sw64 architecture
+
+Signed-off-by: wzx <wuzx1226@qq.com>
+---
+ configure.ac | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 307e974..612f458 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -931,6 +931,9 @@ main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+ aarch64*-*)
+ seccomp_audit_arch=AUDIT_ARCH_AARCH64
+ ;;
++ sw_64*-*)
++ seccomp_audit_arch=AUDIT_ARCH_SW_64
++ ;;
+ s390x-*)
+ seccomp_audit_arch=AUDIT_ARCH_S390X
+ ;;
+--
+2.33.0
+
diff --git a/openssh.spec b/openssh.spec
new file mode 100644
index 0000000..dc3003b
--- /dev/null
+++ b/openssh.spec
@@ -0,0 +1,745 @@
+%global gtk2 1
+%global pie 1
+
+# Add option to build without GTK2 for older platforms with only GTK+.
+# rpm -ba|--rebuild --define 'no_gtk2 1'
+%{?no_gtk2:%global gtk2 0}
+
+%global sshd_uid 74
+%global openssh_release 2.1
+
+Name: openssh
+Version: 9.3p1
+Release: %{openssh_release}
+URL: http://www.openssh.com/portable.html
+License: BSD
+Summary: An open source implementation of SSH protocol version 2
+
+Source0: https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz
+Source1: https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz.asc
+Source2: sshd.pam
+Source3: http://prdownloads.sourceforge.net/pamsshagentauth/pam_ssh_agent_auth/pam_ssh_agent_auth-0.10.4.tar.gz
+Source4: pam_ssh_agent-rmheaders
+Source5: ssh-keycat.pam
+Source6: sshd.sysconfig
+Source7: sshd@.service
+Source8: sshd.socket
+Source9: sshd.service
+Source10: sshd-keygen@.service
+Source11: sshd-keygen
+Source12: sshd.tmpfiles
+Source13: sshd-keygen.target
+Source14: ssh-agent.service
+Source15: ssh-agent.socket
+Source16: ssh-keygen-bash-completion.sh
+Patch0: openssh-6.7p1-coverity.patch
+Patch1: openssh-7.6p1-audit.patch
+Patch2: openssh-7.1p2-audit-race-condition.patch
+Patch3: pam_ssh_agent_auth-0.9.3-build.patch
+Patch4: pam_ssh_agent_auth-0.10.3-seteuid.patch
+Patch5: pam_ssh_agent_auth-0.9.2-visibility.patch
+Patch6: pam_ssh_agent_auth-0.9.3-agent_structure.patch
+Patch7: pam_ssh_agent_auth-0.10.2-compat.patch
+Patch8: pam_ssh_agent_auth-0.10.2-dereference.patch
+Patch9: pam_ssh_agent_auth-0.10.4-rsasha2.patch
+Patch10: pam_ssh_agent-configure-c99.patch
+Patch11: openssh-7.8p1-role-mls.patch
+Patch12: openssh-6.6p1-privsep-selinux.patch
+Patch14: openssh-6.6p1-keycat.patch
+Patch15: openssh-6.6p1-allow-ip-opts.patch
+Patch17: openssh-5.9p1-ipv6man.patch
+Patch18: openssh-5.8p2-sigpipe.patch
+Patch19: openssh-7.2p2-x11.patch
+Patch21: openssh-5.1p1-askpass-progress.patch
+Patch22: openssh-4.3p2-askpass-grab-info.patch
+Patch23: openssh-7.7p1.patch
+Patch24: openssh-7.8p1-UsePAM-warning.patch
+Patch28: openssh-8.0p1-gssapi-keyex.patch
+Patch29: openssh-6.6p1-force_krb.patch
+Patch30: openssh-6.6p1-GSSAPIEnablek5users.patch
+Patch31: openssh-7.7p1-gssapi-new-unique.patch
+Patch32: openssh-7.2p2-k5login_directory.patch
+Patch33: openssh-6.6p1-kuserok.patch
+Patch34: openssh-6.4p1-fromto-remote.patch
+Patch35: openssh-6.6.1p1-selinux-contexts.patch
+Patch36: openssh-6.6.1p1-log-in-chroot.patch
+Patch37: openssh-6.6.1p1-scp-non-existing-directory.patch
+Patch38: openssh-6.8p1-sshdT-output.patch
+Patch39: openssh-6.7p1-sftp-force-permission.patch
+Patch40: openssh-7.2p2-s390-closefrom.patch
+Patch41: openssh-7.3p1-x11-max-displays.patch
+Patch42: openssh-7.4p1-systemd.patch
+Patch43: openssh-7.6p1-cleanup-selinux.patch
+Patch44: openssh-7.5p1-sandbox.patch
+Patch45: openssh-8.0p1-pkcs11-uri.patch
+Patch46: openssh-7.8p1-scp-ipv6.patch
+Patch48: openssh-8.0p1-crypto-policies.patch
+Patch49: openssh-9.3p1-merged-openssl-evp.patch
+Patch50: openssh-8.0p1-openssl-kdf.patch
+Patch51: openssh-8.2p1-visibility.patch
+Patch52: openssh-8.2p1-x11-without-ipv6.patch
+Patch53: openssh-8.0p1-keygen-strip-doseol.patch
+Patch54: openssh-8.0p1-preserve-pam-errors.patch
+Patch55: openssh-8.7p1-scp-kill-switch.patch
+Patch56: openssh-8.7p1-recursive-scp.patch
+Patch57: openssh-8.7p1-minrsabits.patch
+Patch58: openssh-8.7p1-ibmca.patch
+Patch60: openssh-8.7p1-ssh-manpage.patch
+Patch61: openssh-8.7p1-negotiate-supported-algs.patch
+Patch65: openssh-9.3p1-upstream-cve-2023-38408.patch
+Patch66: bugfix-sftp-when-parse_user_host_path-empty-path-should-be-allowed.patch
+Patch67: bugfix-openssh-add-option-check-username-splash.patch
+Patch68: feature-openssh-7.4-hima-sftpserver-oom-and-fix.patch
+Patch69: bugfix-openssh-fix-sftpserver.patch
+Patch70: set-sshd-config.patch
+Patch71: feature-add-SMx-support.patch
+Patch72: add-loongarch.patch
+Patch73: openssh-Add-sw64-architecture.patch
+Patch74: add-strict-scp-check-for-CVE-2020-15778.patch
+Patch75: skip-scp-test-if-there-is-no-scp-on-remote-path-as-s.patch
+Patch77: set-ssh-config.patch
+
+Requires: /sbin/nologin
+Requires: libselinux >= 2.3-5 audit-libs >= 1.0.8
+Requires: openssh-server = %{version}-%{release}
+
+BuildRequires: gtk2-devel libX11-devel openldap-devel autoconf automake perl-interpreter perl-generators
+BuildRequires: zlib-devel audit-libs-devel >= 2.0.5 util-linux groff pam-devel
+BuildRequires: openssl3 perl-podlators systemd-devel gcc p11-kit-devel krb5-devel
+BuildRequires: libedit-devel ncurses-devel libselinux-devel >= 2.3-5 audit-libs >= 1.0.8 xauth gnupg2
+
+Recommends: p11-kit
+
+%package clients
+Summary: An open source SSH client applications
+Requires: openssh = %{version}-%{release}
+Requires: crypto-policies >= 20180306-1
+Requires: openssl3
+
+%package server
+Summary: An open source SSH server daemon
+Requires: openssh = %{version}-%{release}
+Requires(pre): shadow
+Requires: pam >= 1.0.1-3
+Requires: crypto-policies >= 20180306-1
+Requires: openssl3
+%{?systemd_requires}
+
+%package keycat
+Summary: A mls keycat backend for openssh
+Requires: openssh = %{version}-%{release}
+
+%package askpass
+Summary: A passphrase dialog for OpenSSH and X
+Requires: openssh = %{version}-%{release}
+
+%package -n pam_ssh_agent_auth
+Summary: PAM module for authentication with ssh-agent
+Version: 0.10.4
+Release: 4.%{openssh_release}
+License: BSD
+
+%description
+OpenSSH is the premier connectivity tool for remote login with the SSH protocol. \
+It encrypts all traffic to eliminate eavesdropping, connection hijacking, and \
+other attacks. In addition, OpenSSH provides a large suite of secure tunneling \
+capabilities, several authentication methods, and sophisticated configuration options.
+
+%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.
+
+%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 -n pam_ssh_agent_auth
+Provides PAM module for the use of authentication with 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.
+
+%package_help
+
+%prep
+%setup -q -a 3
+
+pushd pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4
+%patch3 -p2 -b .psaa-build
+%patch4 -p2 -b .psaa-seteuid
+%patch5 -p2 -b .psaa-visibility
+%patch7 -p2 -b .psaa-compat
+%patch6 -p2 -b .psaa-agent
+%patch8 -p2 -b .psaa-deref
+%patch9 -p2 -b .rsasha2
+%patch10 -p1 -b .psaa-configure-c99
+# Remove duplicate headers and library files
+rm -f $(cat %{SOURCE4})
+popd
+
+%patch11 -p1 -b .role-mls
+%patch12 -p1 -b .privsep-selinux
+%patch14 -p1 -b .keycat
+%patch15 -p1 -b .ip-opts
+%patch17 -p1 -b .ipv6man
+%patch18 -p1 -b .sigpipe
+%patch19 -p1 -b .x11
+%patch21 -p1 -b .progress
+%patch22 -p1 -b .grab-info
+%patch23 -p1
+%patch24 -p1 -b .log-usepam-no
+%patch28 -p1 -b .gsskex
+%patch29 -p1 -b .force_krb
+%patch31 -p1 -b .ccache_name
+%patch32 -p1 -b .k5login
+%patch33 -p1 -b .kuserok
+%patch34 -p1 -b .fromto-remote
+%patch35 -p1 -b .contexts
+%patch36 -p1 -b .log-in-chroot
+%patch37 -p1 -b .scp
+%patch30 -p1 -b .GSSAPIEnablek5users
+%patch38 -p1 -b .sshdt
+%patch39 -p1 -b .sftp-force-mode
+%patch40 -p1 -b .s390-dev
+%patch41 -p1 -b .x11max
+%patch42 -p1 -b .systemd
+%patch43 -p1 -b .refactor
+%patch44 -p1 -b .sandbox
+%patch45 -p1 -b .pkcs11-uri
+%patch46 -p1 -b .scp-ipv6
+%patch48 -p1 -b .crypto-policies
+%patch49 -p1 -b .openssl-evp
+%patch50 -p1 -b .openssl-kdf
+%patch51 -p1 -b .visibility
+%patch52 -p1 -b .x11-ipv6
+%patch53 -p1 -b .keygen-strip-doseol
+%patch54 -p1 -b .preserve-pam-errors
+%patch55 -p1 -b .kill-scp
+%patch56 -p1 -b .scp-sftpdirs
+%patch57 -p1 -b .minrsabits
+%patch58 -p1 -b .ibmca
+%patch60 -p1 -b .ssh-manpage
+%patch61 -p1 -b .negotiate-supported-algs
+%patch65 -p1 -b .cve-2023-38408
+%patch1 -p1 -b .audit
+%patch2 -p1 -b .audit-race
+%patch0 -p1 -b .coverity
+
+%patch66 -p1
+%patch67 -p1
+%patch68 -p1
+%patch69 -p1
+%patch70 -p1
+%patch71 -p1
+%patch72 -p1
+%patch73 -p1
+%patch74 -p1
+%patch75 -p1
+%patch77 -p1
+
+autoreconf
+pushd pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4
+autoreconf
+popd
+
+%build
+CFLAGS="$RPM_OPT_FLAGS -fvisibility=hidden"; export CFLAGS
+
+CFLAGS="$CFLAGS -Os"
+%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
+
+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
+
+%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 --disable-strip \
+ --without-zlib-version-check --with-ssl-engine --with-ipaddr-display \
+ --with-pie=no --without-hardening --with-systemd --with-default-pkcs11-provider=yes \
+ --with-pam --with-selinux --with-audit=linux --with-security-key-buildin=yes \
+%ifnarch riscv64 loongarch64 sw_64
+ --with-sandbox=seccomp_filter \
+%endif
+ --with-kerberos5${krb5_prefix:+=${krb5_prefix}} --with-libedit
+
+make
+gtk2=yes
+
+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
+
+pushd pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4
+LDFLAGS="$SAVE_LDFLAGS"
+%configure --with-selinux --libexecdir=/%{_libdir}/security --with-mantype=man \
+ --without-openssl-header-check
+make
+popd
+
+%check
+make tests
+
+%install
+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
+mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/bash_completion.d
+
+%make_install
+
+install -d $RPM_BUILD_ROOT/etc/pam.d/
+install -d $RPM_BUILD_ROOT/etc/sysconfig/
+install -d $RPM_BUILD_ROOT%{_libexecdir}/openssh
+install -m644 %{SOURCE2} $RPM_BUILD_ROOT/etc/pam.d/sshd
+install -m644 %{SOURCE5} $RPM_BUILD_ROOT/etc/pam.d/ssh-keycat
+install -m644 %{SOURCE6} $RPM_BUILD_ROOT/etc/sysconfig/sshd
+install -d -m755 $RPM_BUILD_ROOT/%{_unitdir}
+install -m644 %{SOURCE7} $RPM_BUILD_ROOT/%{_unitdir}/sshd@.service
+install -m644 %{SOURCE8} $RPM_BUILD_ROOT/%{_unitdir}/sshd.socket
+install -m644 %{SOURCE9} $RPM_BUILD_ROOT/%{_unitdir}/sshd.service
+install -m644 %{SOURCE10} $RPM_BUILD_ROOT/%{_unitdir}/sshd-keygen@.service
+install -m644 %{SOURCE13} $RPM_BUILD_ROOT/%{_unitdir}/sshd-keygen.target
+install -d -m755 $RPM_BUILD_ROOT/%{_userunitdir}
+install -m644 %{SOURCE14} $RPM_BUILD_ROOT/%{_userunitdir}/ssh-agent.service
+install -m644 %{SOURCE15} $RPM_BUILD_ROOT/%{_userunitdir}/ssh-agent.socket
+install -m744 %{SOURCE11} $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 %{SOURCE12} $RPM_BUILD_ROOT%{_tmpfilesdir}/%{name}.conf
+install contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass
+install -m644 %{SOURCE16} $RPM_BUILD_ROOT/etc/bash_completion.d/ssh-keygen-bash-completion.sh
+
+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/
+
+perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/*
+
+pushd pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4
+make install DESTDIR=$RPM_BUILD_ROOT
+popd
+
+%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
+
+%post clients
+%systemd_user_post ssh-agent.service
+%systemd_user_post ssh-agent.socket
+
+%preun clients
+%systemd_user_preun ssh-agent.service
+%systemd_user_preun ssh-agent.socket
+
+%files
+%license LICENCE
+%doc CREDITS README.platform
+%attr(0755,root,root) %dir %{_sysconfdir}/ssh
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli
+%attr(0755,root,root) %{_bindir}/ssh-keygen
+%attr(0755,root,root) %dir %{_libexecdir}/openssh
+%attr(2555,root,ssh_keys) %{_libexecdir}/openssh/ssh-keysign
+%attr(0644,root,root) %{_sysconfdir}/bash_completion.d/ssh-keygen-bash-completion.sh
+
+%files clients
+%attr(0755,root,root) %{_bindir}/ssh
+%attr(0755,root,root) %{_bindir}/scp
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config
+%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(0755,root,root) %{_libexecdir}/openssh/ssh-sk-helper
+%attr(0644,root,root) %{_userunitdir}/ssh-agent.service
+%attr(0644,root,root) %{_userunitdir}/ssh-agent.socket
+
+%files server
+%dir %attr(0711,root,root) %{_var}/empty/sshd
+%attr(0755,root,root) %{_sbindir}/sshd
+%attr(0755,root,root) %{_libexecdir}/openssh/sftp-server
+%attr(0755,root,root) %{_libexecdir}/openssh/sshd-keygen
+%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
+
+%files keycat
+%attr(0755,root,root) %{_libexecdir}/openssh/ssh-keycat
+%attr(0644,root,root) %config(noreplace) /etc/pam.d/ssh-keycat
+
+%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
+
+%files -n pam_ssh_agent_auth
+%license pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/OPENSSH_LICENSE
+%attr(0755,root,root) %{_libdir}/security/pam_ssh_agent_auth.so
+
+%files help
+%doc ChangeLog OVERVIEW PROTOCOL* README README.privsep README.tun README.dns TODO
+%doc HOWTO.ssh-keycat
+%attr(0644,root,root) %{_mandir}/man1/scp.1*
+%attr(0644,root,root) %{_mandir}/man1/ssh*.1*
+%attr(0644,root,root) %{_mandir}/man1/sftp.1*
+%attr(0644,root,root) %{_mandir}/man5/ssh*.5*
+%attr(0644,root,root) %{_mandir}/man5/moduli.5*
+%attr(0644,root,root) %{_mandir}/man8/ssh*.8*
+%attr(0644,root,root) %{_mandir}/man8/pam_ssh_agent_auth.8*
+%attr(0644,root,root) %{_mandir}/man8/sftp-server.8*
+
+%changelog
+* Mon Oct 02 2023 Funda Wang <fundawang@yeah.net> - 9.3p1-2.1
+- Try building with openssl3
+
+* Fri Aug 25 2023 renmingshuai<renmingshuai@huawei.com> - 9.3p1-2
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:use correct ssh-agent.socket name
+
+* Thu Jul 27 2023 renmingshuai<renmingshuai@huawei.com> - 9.3p1-1
+- Type:requirement
+- CVE:NA
+- SUG:NA
+- DESC:update to 9.3p1
+
+* Tue Jun 13 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-6
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:fix misspelling
+
+* Sat May 27 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-5
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:fix environment variable
+
+* Sat Mar 18 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-4
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:backport some upstreams patches and delete unused patches
+
+* Tue Feb 28 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-3
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:set default ssh_config
+
+* Mon Feb 06 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-2
+- Type:CVE
+- CVE:CVE-2023-25136
+- SUG:NA
+- DESC:fix CVE-2023-25136
+
+* Mon Jan 30 2023 renmingshuai<renmingshuai@huawei.com> - 9.1p1-1
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:update to openssh-9.1p1
+
+* Mon Jan 9 2023 renmingshuai <renmingshuai@huawei.com> - 8.8p1-17
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:fix possible NULL deref when built without FIDO
+
+* Tue Jan 3 2023 renmingshuai <renmingshuai@huawei.com> - 8.8p1-16
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:fix test failure and always make tests
+
+* Thu Dec 29 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-15
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:avoid integer overflow of auth attempts
+
+* Thu Dec 29 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-14
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:PubkeyAcceptedKeyTypes has been renamed to PubkeyAcceptedAlgorithms in openssh-8.5p1
+
+* Thu Dec 29 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-13
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:add strict scp check for CVE-2020-15778
+
+* Thu Dec 29 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-12
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:backport some upstream patches
+
+* Thu Dec 29 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-11
+- Type:requirement
+- CVE:NA
+- SUG:NA
+- DESC:add sw_64
+
+* Fri Dec 16 2022 renmingshuai <renmingshuai@huawei.com> - 8.8p1-10
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:Fix ssh-keygen -Y check novalidate requires name
+
+* Mon Nov 28 2022 zhaozhen <zhaozhen@loongson.cn> - 8.8p1-9
+- Type:feature
+- CVE:NA
+- SUG:NA
+- DESC:Add loongarch64 support
+
+* Mon Nov 28 2022 renmingshuai<renmingshuai@huawei.com> - 8.8p1-8
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:add better debugging
+
+* Wed Nov 2 2022 renmingshuai<renmingshuai@huawei.com> - 8.8p1-7
+- Type:requirement
+- CVE:NA
+- SUG:NA
+- DESC:add ssh-keygen bash completion
+
+* Thu Sep 01 2022 duyiwei<duyiwei@kylinos.cn> - 8.8P1-6
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:enable "include /etc/ssh/sshd_config.d/*.config" again
+
+* Fri Jul 29 2022 kircher<majun65@huawei.com> - 8.8p1-5
+- Type:bugfix
+- CVE:Na
+- SUG:NA
+- DESC:add SMx support in openssh
+
+* Thu May 05 2022 seuzw<930zhaowei@163.com> - 8.8p1-4
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:fix incorrect sftp-server binary path in /etc/ssh/sshd_config
+
+* Wed Mar 09 2022 duyiwei<duyiwei@kylinos.cn> - 8.8P1-3
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:enable "include /etc/ssh/sshd_config.d/*.config"
+
+* Mon Mar 07 2022 kircher<majun65@huawei.com> - 8.8P1-2
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:add sshd.tmpfiles
+
+* Thu Oct 28 2021 kircher<kircherlike@outlook.com> - 8.8P1-1
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:update to openssh-8.8p1
+
+* Fri Oct 8 2021 renmingshuai<renmingshuai@hauwei.com> - 8.2P1-15
+- Type:cves
+- CVE:CVE-2021-41617
+- SUG:NA
+- DESC:fix CVE-2021-41617
+
+* Sat Sep 18 2021 kircher<kircherlike@outlook.com> - 8.2P1-14
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:backport patch from github to fix NULL ref
+
+* Fri Jul 30 2021 kircher<majun65@huawei.com> - 8.2P1-13
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:remove debug message from sigchld handler
+
+* Tue Jul 20 2021 seuzw<930zhaowei@163.com> - 8.2P1-12
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:move closefrom to before first malloc
+
+* Fri Jul 09 2021 panchenbo<panchenbo@uniontech.com> - 8.2P1-11
+- fix pam_ssh_agent_auth.8.gz conflicts
+
+* Thu May 20 2021 seuzw<930zhaowei@163.com> - 8.2P1-10
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:add strict-scp-check for check command injection
+
+* Mon Jan 4 2021 chxssg<chxssg@qq.com> - 8.2P1-9
+- Type:cves
+- CVE:CVE-2020-14145
+- SUG:NA
+- DESC:fix CVE-2020-14145
+
+* Wed Nov 18 2020 gaihuiying<gaihuiying1@huawei.com> - 8.2P1-8
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:adjust pam_ssh_agent_auth release number
+
+* Tue Nov 17 2020 gaihuiying<gaihuiying1@huawei.com> - 8.2P1-7
+- Type:bugfix
+- CVE:NA
+- SUG:NA
+- DESC:keep pam_ssh_agent_auth change release number with openssh
+
+* Tue Sep 15 2020 liulong<liulong20@huawei.com> - 8.2P1-6
+- Type:cves
+- ID:CVE-2018-15919
+- SUG:NA
+- DESC:Fix CVE-2018-15919
+
+* Thu Jul 2 2020 zhouyihang<zhouyihang3@huawei.com> - 8.2P1-5
+- Type:cves
+- ID:CVE-2020-12062
+- SUG:NA
+- DESC:Fix CVE-2020-12062
+
+* Tue Jun 9 2020 openEuler Buildteam <buildteam@openeuler.org> - 8.2P1-4
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:add requires for openssh-server in openssh
+
+* Wed May 6 2020 openEuler Buildteam <buildteam@openeuler.org> - 8.2P1-3
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:fix update problem
+
+* Sat Apr 18 2020 openEuler Buildteam <buildteam@openeuler.org> - 8.2P1-2
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:fix pre problem
+
+* Thu Apr 16 2020 openEuler Buildteam <buildteam@openeuler.org> - 8.2P1-1
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:update to 8.2P1
+
+* Mon Mar 30 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-12
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:move sshd.service in %post server
+
+* Wed Mar 18 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-11
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:reduction of authority
+
+* Fri Mar 13 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-10
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:separate package
+
+* Thu Mar 5 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-9
+- Type:cves
+- ID:CVE-2018-15919
+- SUG:NA
+- DESC:Fix CVE-2018-15919
+
+* Thu Mar 5 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-8
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:debug3 to verbose in command line
+
+* Tue Jan 21 2020 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-7
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:add the patch for bugfix
+
+* Mon Dec 23 2019 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-6
+- Type:bugfix
+- ID:NA
+- SUG:NA
+- DESC:delete the patch
+
+* Sat Dec 21 2019 openEuler Buildteam <buildteam@openeuler.org> - 7.8P1-5
+- Type:cves
+- ID:NA
+- SUG:restart
+- DESC:fix cves
+
+* Fri Sep 20 2019 openEuler Buildteam <buildteam@openeuler.org> - 7.8p1-4
+- Package init
diff --git a/pam_ssh_agent-configure-c99.patch b/pam_ssh_agent-configure-c99.patch
new file mode 100644
index 0000000..bc40434
--- /dev/null
+++ b/pam_ssh_agent-configure-c99.patch
@@ -0,0 +1,249 @@
+configure.ac: Improve C99 compatibility
+
+Future compilers will not support implicit declarations and implicit
+ints by default. This means that configure probes which rely on them
+will fail unconditionally, without actually testing anything.
+
+The changes mostly mirror what has been implemented in the openssh
+repository, but had to be adapted somewhat because of drift between
+the two versions of configure.ac.
+
+Sam James has submitted similar fixes upstream:
+
+ <https://github.com/jbeverly/pam_ssh_agent_auth/pull/41>
+
+diff --git a/configure.ac b/configure.ac
+index 6496679..d927b62 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -500,10 +500,10 @@ int main(void) { exit(0); }
+ AC_DEFINE(HAVE_BUNDLE, 1, [Define if your system uses bundles instead of ELF shared objects])
+ AC_MSG_CHECKING(if we have working getaddrinfo)
+ AC_TRY_RUN([#include <mach-o/dyld.h>
+-main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
+- exit(0);
++int main(void) { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16))
++ return 0;
+ else
+- exit(1);
++ return 1;
+ }], [AC_MSG_RESULT(working)],
+ [AC_MSG_RESULT(buggy)
+ AC_DEFINE(BROKEN_GETADDRINFO, 1, [getaddrinfo is broken (if present)])],
+@@ -917,8 +917,8 @@ AC_SUBST(LDFLAGS_SHARED)
+ AC_MSG_CHECKING(compiler and flags for sanity)
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([
+-#include <stdio.h>
+-int main(){exit(0);}
++#include <stdlib.h>
++int main(void){exit(0);}
+ ])],
+ [ AC_MSG_RESULT(yes) ],
+ [
+@@ -951,9 +951,9 @@ int main(int argc, char **argv) {
+ strncpy(buf,"/etc", 32);
+ s = dirname(buf);
+ if (!s || strncmp(s, "/", 32) != 0) {
+- exit(1);
++ return 1;
+ } else {
+- exit(0);
++ return 0;
+ }
+ }
+ ]])],
+@@ -1102,7 +1102,7 @@ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <sys/types.h>
+ #include <dirent.h>
+-int main(void){struct dirent d;exit(sizeof(d.d_name)<=sizeof(char));}
++int main(void){struct dirent d;return sizeof(d.d_name)<=sizeof(char);}
+ ]])],
+ [AC_MSG_RESULT(yes)],
+ [
+@@ -1327,8 +1327,10 @@ AC_CHECK_FUNCS(setresuid, [
+ AC_MSG_CHECKING(if setresuid seems to work)
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
++#define _GNU_SOURCE
+ #include <stdlib.h>
+ #include <errno.h>
++#include <unistd.h>
+ int main(){errno=0; setresuid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0);}
+ ]])],
+ [AC_MSG_RESULT(yes)],
+@@ -1344,8 +1346,10 @@ AC_CHECK_FUNCS(setresgid, [
+ AC_MSG_CHECKING(if setresgid seems to work)
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
++#define _GNU_SOURCE
+ #include <stdlib.h>
+ #include <errno.h>
++#include <unistd.h>
+ int main(){errno=0; setresgid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0);}
+ ]])],
+ [AC_MSG_RESULT(yes)],
+@@ -1384,7 +1388,7 @@ if test "x$ac_cv_func_snprintf" = "xyes" ; then
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdio.h>
+-int main(void){char b[5];snprintf(b,5,"123456789");exit(b[4]!='\0');}
++int main(void){char b[5];snprintf(b,5,"123456789");return b[4]!='\0';}
+ ]])],
+ [AC_MSG_RESULT(yes)],
+ [
+@@ -1418,7 +1422,7 @@ int x_snprintf(char *str,size_t count,const char *fmt,...)
+ int main(void)
+ {
+ char x[1];
+- exit(x_snprintf(x, 1, "%s %d", "hello", 12345) == 11 ? 0 : 1);
++ return x_snprintf(x, 1, "%s %d", "hello", 12345) == 11 ? 0 : 1;
+ } ]])],
+ [AC_MSG_RESULT(yes)],
+ [
+@@ -1467,7 +1471,8 @@ AC_MSG_CHECKING([for (overly) strict mkstemp])
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdlib.h>
+-main() { char template[]="conftest.mkstemp-test";
++#include <unistd.h>
++int main(void) { char template[]="conftest.mkstemp-test";
+ if (mkstemp(template) == -1)
+ exit(1);
+ unlink(template); exit(0);
+@@ -1492,10 +1497,14 @@ if test ! -z "$check_for_openpty_ctty_bug"; then
+ AC_MSG_CHECKING(if openpty correctly handles controlling tty)
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
++#include <stdlib.h>
+ #include <stdio.h>
+ #include <sys/fcntl.h>
+ #include <sys/types.h>
+ #include <sys/wait.h>
++#ifdef HAVE_PTY_H
++#include <pty.h>
++#endif
+
+ int
+ main()
+@@ -1543,6 +1552,7 @@ if test "x$ac_cv_func_getaddrinfo" = "xyes" && \
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <stdio.h>
++#include <stdlib.h>
+ #include <sys/socket.h>
+ #include <netdb.h>
+ #include <errno.h>
+@@ -1748,6 +1758,7 @@ AC_TRY_LINK_FUNC(RAND_add, AC_DEFINE(HAVE_OPENSSL, 1,
+ AC_MSG_CHECKING([OpenSSL header version])
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
++#include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <openssl/opensslv.h>
+@@ -1794,12 +1805,12 @@ int main(void) {
+
+ fd = fopen(DATA,"w");
+ if(fd == NULL)
+- exit(1);
++ return 1;
+
+ if ((rc = fprintf(fd ,"%x (%s)\n", SSLeay(), SSLeay_version(SSLEAY_VERSION))) <0)
+- exit(1);
++ return 1;
+
+- exit(0);
++ return 0;
+ }
+ ]])],
+ [
+@@ -1829,7 +1840,7 @@ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+ #include <string.h>
+ #include <openssl/opensslv.h>
+-int main(void) { exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1); }
++int main(void) { return SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1; }
+ ]])],
+ [
+ AC_MSG_RESULT(yes)
+@@ -2598,7 +2609,7 @@ dnl test snprintf (broken on SCO w/gcc)
+ #include <stdio.h>
+ #include <string.h>
+ #ifdef HAVE_SNPRINTF
+-main()
++int main(void)
+ {
+ char buf[50];
+ char expected_out[50];
+@@ -2611,11 +2622,11 @@ main()
+ strcpy(expected_out, "9223372036854775807");
+ snprintf(buf, mazsize, "%lld", num);
+ if(strcmp(buf, expected_out) != 0)
+- exit(1);
+- exit(0);
++ return 1;
++ return 0;
+ }
+ #else
+-main() { exit(0); }
++int main(void) { return 0; }
+ #endif
+ ]])], [ true ], [ AC_DEFINE(BROKEN_SNPRINTF) ],
+ AC_MSG_WARN([cross compiling: Assuming working snprintf()])
+@@ -2746,11 +2757,11 @@ AC_CACHE_CHECK([for msg_accrights field in struct msghdr],
+ int main() {
+ #ifdef msg_accrights
+ #error "msg_accrights is a macro"
+-exit(1);
++return 1;
+ #endif
+ struct msghdr m;
+ m.msg_accrights = 0;
+-exit(0);
++return 0;
+ }
+ ])],
+ [ ac_cv_have_accrights_in_msghdr="yes" ],
+@@ -2773,11 +2784,11 @@ AC_CACHE_CHECK([for msg_control field in struct msghdr],
+ int main() {
+ #ifdef msg_control
+ #error "msg_control is a macro"
+-exit(1);
++return 1;
+ #endif
+ struct msghdr m;
+ m.msg_control = 0;
+-exit(0);
++return 0;
+ }
+ ])],
+ [ ac_cv_have_control_in_msghdr="yes" ],
+@@ -2791,7 +2802,7 @@ if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then
+ fi
+
+ AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [
+- AC_TRY_LINK([],
++ AC_TRY_LINK([#include <stdio.h>],
+ [ extern char *__progname; printf("%s", __progname); ],
+ [ ac_cv_libc_defines___progname="yes" ],
+ [ ac_cv_libc_defines___progname="no" ]
+@@ -2871,7 +2882,7 @@ if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then
+ fi
+
+ AC_CACHE_CHECK([if libc defines sys_errlist], ac_cv_libc_defines_sys_errlist, [
+- AC_TRY_LINK([],
++ AC_TRY_LINK([#include <stdio.h>],
+ [ extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]);],
+ [ ac_cv_libc_defines_sys_errlist="yes" ],
+ [ ac_cv_libc_defines_sys_errlist="no" ]
+@@ -2884,7 +2895,7 @@ fi
+
+
+ AC_CACHE_CHECK([if libc defines sys_nerr], ac_cv_libc_defines_sys_nerr, [
+- AC_TRY_LINK([],
++ AC_TRY_LINK([#include <stdio.h>],
+ [ extern int sys_nerr; printf("%i", sys_nerr);],
+ [ ac_cv_libc_defines_sys_nerr="yes" ],
+ [ ac_cv_libc_defines_sys_nerr="no" ]
diff --git a/pam_ssh_agent-rmheaders b/pam_ssh_agent-rmheaders
new file mode 100644
index 0000000..ab5899f
--- /dev/null
+++ b/pam_ssh_agent-rmheaders
@@ -0,0 +1,36 @@
+authfd.c
+authfd.h
+atomicio.c
+atomicio.h
+bufaux.c
+bufbn.c
+buffer.h
+buffer.c
+cleanup.c
+cipher.h
+compat.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/pam_ssh_agent_auth-0.10.2-compat.patch b/pam_ssh_agent_auth-0.10.2-compat.patch
new file mode 100644
index 0000000..0822b61
--- /dev/null
+++ b/pam_ssh_agent_auth-0.10.2-compat.patch
@@ -0,0 +1,992 @@
+diff -up openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/get_command_line.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/get_command_line.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/get_command_line.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/get_command_line.c 2020-09-23 10:52:16.424001475 +0200
+@@ -27,6 +27,7 @@
+ * or implied, of Jamie Beverly.
+ */
+
++#include <stdlib.h>
+ #include <stdio.h>
+ #include <errno.h>
+ #include <string.h>
+@@ -66,8 +67,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;
+@@ -106,9 +107,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-pam_ssh_agent_auth-0.10.4/identity.h.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/identity.h
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/identity.h.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/identity.h 2020-09-23 10:52:16.424001475 +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 <stdio.h>
+
+@@ -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-pam_ssh_agent_auth-0.10.4/iterate_ssh_agent_keys.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/iterate_ssh_agent_keys.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/iterate_ssh_agent_keys.c.psaa-compat 2020-09-23 10:52:16.421001434 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/iterate_ssh_agent_keys.c 2020-09-23 10:52:16.424001475 +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 <stdio.h>
+ #include <openssl/evp.h>
+@@ -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 +120,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;
+@@ -144,7 +151,8 @@ pamsshagentauth_session_id2_gen(Buffer *
+ }
+ 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 +169,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);
++ }
++ /* 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));
+ }
+- /* 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("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 +290,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,9 +300,9 @@ 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;
+@@ -299,31 +312,30 @@ pamsshagentauth_find_authorized_keys(con
+ 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-pam_ssh_agent_auth-0.10.4/pam_ssh_agent_auth.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_ssh_agent_auth.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_ssh_agent_auth.c.psaa-compat 2020-09-23 10:52:16.423001461 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_ssh_agent_auth.c 2020-09-23 10:53:10.631727657 +0200
+@@ -106,7 +106,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--) {
+@@ -132,11 +132,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);
+@@ -151,12 +151,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);
+@@ -165,11 +165,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;
+ }
+
+@@ -179,8 +179,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");
+ }
+
+ /*
+@@ -189,7 +189,7 @@ 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);
+
+ /*
+ * Attempt to read data from the sshd if we're being called as an auth agent.
+@@ -197,10 +197,10 @@ pam_sm_authenticate(pam_handle_t * pamh,
+ const char* ssh_user_auth = pam_getenv(pamh, "SSH_AUTH_INFO_0");
+ int sshd_service = strncasecmp(servicename, sshd_service_name, sizeof(sshd_service_name) - 1);
+ if (sshd_service == 0 && ssh_user_auth != NULL) {
+- pamsshagentauth_verbose("Got SSH_AUTH_INFO_0: `%.20s...'", ssh_user_auth);
++ verbose("Got SSH_AUTH_INFO_0: `%.20s...'", ssh_user_auth);
+ if (userauth_pubkey_from_pam(ruser, ssh_user_auth) > 0) {
+ retval = PAM_SUCCESS;
+- pamsshagentauth_logit("Authenticated (sshd): `%s' as `%s' using %s", ruser, user, authorized_keys_file);
++ logit("Authenticated (sshd): `%s' as `%s' using %s", ruser, user, authorized_keys_file);
+ goto cleanexit;
+ }
+ }
+@@ -208,13 +208,13 @@ pam_sm_authenticate(pam_handle_t * pamh,
+ * 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 (agent): `%s' as `%s' using %s", ruser, user, authorized_keys_file);
++ logit("Authenticated (agent): `%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-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.c 2020-09-23 10:52:16.424001475 +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-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.h.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.h
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.h.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_authorized_keys.h 2020-09-23 10:52:16.424001475 +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-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.c 2020-09-23 10:52:16.424001475 +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 <unistd.h>
+
+ /* 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-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.h.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.h
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.h.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/pam_user_key_allowed2.h 2020-09-23 10:52:16.424001475 +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-pam_ssh_agent_auth-0.10.4/secure_filename.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/secure_filename.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/secure_filename.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/secure_filename.c 2020-09-23 10:52:16.424001475 +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-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c 2020-09-23 10:52:16.424001475 +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 +49,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;
+- int authenticated = 0;
++ size_t blen = 0, slen = 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, NULL) == 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-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.h.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.h
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.h.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.h 2020-09-23 10:52:16.424001475 +0200
+@@ -31,7 +31,7 @@
+ #ifndef _USERAUTH_PUBKEY_FROM_ID_H
+ #define _USERAUTH_PUBKEY_FROM_ID_H
+
+-#include <identity.h>
+-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-pam_ssh_agent_auth-0.10.4/uuencode.c.psaa-compat openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/uuencode.c
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/uuencode.c.psaa-compat 2019-07-08 18:36:13.000000000 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/uuencode.c 2020-09-23 10:52:16.424001475 +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);
+ }
+--- openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_pam.c.compat 2020-09-23 11:32:30.783695267 +0200
++++ openssh/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_pam.c 2020-09-23 11:33:21.383389036 +0200
+@@ -33,7 +33,8 @@
+ #include <string.h>
+
+ #include "defines.h"
+-#include "key.h"
++#include <includes.h>
++#include "sshkey.h"
+ #include "log.h"
+
+ #include "pam_user_authorized_keys.h"
+@@ -42,28 +42,28 @@
+ int authenticated = 0;
+ const char method[] = "publickey ";
+
+- char* ai = pamsshagentauth_xstrdup(ssh_auth_info);
++ char* ai = xstrdup(ssh_auth_info);
+ char* saveptr;
+
+ char* auth_line = strtok_r(ai, "\n", &saveptr);
+ while (auth_line != NULL) {
+ if (strncmp(auth_line, method, sizeof(method) - 1) == 0) {
+ char* key_str = auth_line + sizeof(method) - 1;
+- Key* key = pamsshagentauth_key_new(KEY_UNSPEC);
++ struct sshkey* key = sshkey_new(KEY_UNSPEC);
+ if (key == NULL) {
+ continue;
+ }
+- int r = pamsshagentauth_key_read(key, &key_str);
++ int r = sshkey_read(key, &key_str);
+ if (r == 1) {
+ if (pam_user_key_allowed(ruser, key)) {
+ authenticated = 1;
+- pamsshagentauth_key_free(key);
++ sshkey_free(key);
+ break;
+ }
+ } else {
+- pamsshagentauth_verbose("Failed to create key for %s: %d", auth_line, r);
++ verbose("Failed to create key for %s: %d", auth_line, r);
+ }
+- pamsshagentauth_key_free(key);
++ sshkey_free(key);
+ }
+ auth_line = strtok_r(NULL, "\n", &saveptr);
+ }
diff --git a/pam_ssh_agent_auth-0.10.2-dereference.patch b/pam_ssh_agent_auth-0.10.2-dereference.patch
new file mode 100644
index 0000000..bf49c37
--- /dev/null
+++ b/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/pam_ssh_agent_auth-0.10.3-seteuid.patch b/pam_ssh_agent_auth-0.10.3-seteuid.patch
new file mode 100644
index 0000000..be1f8e5
--- /dev/null
+++ b/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/pam_ssh_agent_auth-0.10.4-rsasha2.patch b/pam_ssh_agent_auth-0.10.4-rsasha2.patch
new file mode 100644
index 0000000..c8815bb
--- /dev/null
+++ b/pam_ssh_agent_auth-0.10.4-rsasha2.patch
@@ -0,0 +1,19 @@
+diff -up openssh-8.7p1/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c.rsasha2 openssh-8.7p1/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c
+--- openssh-8.7p1/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c.rsasha2 2022-07-15 15:08:12.865585410 +0200
++++ openssh-8.7p1/pam_ssh_agent_auth-pam_ssh_agent_auth-0.10.4/userauth_pubkey_from_id.c 2022-07-15 15:16:25.164282372 +0200
+@@ -87,8 +87,13 @@ 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->fd, id->key, &sig, &slen, sshbuf_ptr(b), sshbuf_len(b), NULL, 0) != 0)
+- goto user_auth_clean_exit;
++ if (sshkey_type_plain(id->key->type) == KEY_RSA
++ && ssh_agent_sign(id->ac->fd, id->key, &sig, &slen, sshbuf_ptr(b), sshbuf_len(b), "rsa-sha2-256", 0) == 0) {
++ /* Do nothing */
++ } else {
++ 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 */
+ if (sshkey_verify(id->key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), NULL, 0, NULL) == 0)
diff --git a/pam_ssh_agent_auth-0.9.2-visibility.patch b/pam_ssh_agent_auth-0.9.2-visibility.patch
new file mode 100644
index 0000000..aea068d
--- /dev/null
+++ b/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/pam_ssh_agent_auth-0.9.3-agent_structure.patch b/pam_ssh_agent_auth-0.9.3-agent_structure.patch
new file mode 100644
index 0000000..1f2c02c
--- /dev/null
+++ b/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 <stdio.h>
+ #include <openssl/evp.h>
+ #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/pam_ssh_agent_auth-0.9.3-build.patch b/pam_ssh_agent_auth-0.9.3-build.patch
new file mode 100644
index 0000000..4018c4d
--- /dev/null
+++ b/pam_ssh_agent_auth-0.9.3-build.patch
@@ -0,0 +1,198 @@
+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 <openssl/evp.h>
+ #include "ssh2.h"
+ #include "misc.h"
++#include "ssh.h"
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <unistd.h>
++#include <stdlib.h>
++#include <errno.h>
++#include <fcntl.h>
+
+ #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,8 +61,8 @@ INSTALL=@INSTALL@
+ PERL=@PERL@
+ SED=@SED@
+ ENT=@ENT@
+-LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@
+-LDFLAGS_SHARED = @LDFLAGS_SHARED@
++LDFLAGS=-L.. -L../openbsd-compat/ @LDFLAGS@
++LDFLAGS_SHARED =-Wl,-z,defs @LDFLAGS_SHARED@
+ EXEEXT=@EXEEXT@
+
+ INSTALL_SSH_PRNG_CMDS=@INSTALL_SSH_PRNG_CMDS@
+@@ -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 userauth_pubkey_from_pam.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 userauth_pubkey_from_pam.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 ../ssh-sk-client.o
++ $(LD) $(LDFLAGS_SHARED) -o $@ $(PAM_SSH_AGENT_AUTH_OBJS) ../ssh-sk-client.o $(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/set-ssh-config.patch b/set-ssh-config.patch
new file mode 100644
index 0000000..4899679
--- /dev/null
+++ b/set-ssh-config.patch
@@ -0,0 +1,30 @@
+From 8b8319aac379d9d6f75577507e87a97a8aa8aadc Mon Sep 17 00:00:00 2001
+From: renmingshuai <renmingshuai@huawei.com>
+Date: Mon, 5 Sep 2022 10:33:02 +0800
+Subject: [PATCH] set-ssh-config
+
+---
+ ssh_config | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/ssh_config b/ssh_config
+index df22e2f..46b0987 100644
+--- a/ssh_config
++++ b/ssh_config
+@@ -48,4 +48,13 @@
+ # file under /etc/ssh/ssh_config.d/ which will be automatically
+ # included below. For more information, see manual page for
+ # update-crypto-policies(8) and ssh_config(5).
++Match final all
++ Include /etc/crypto-policies/back-ends/openssh.config
++ GSSAPIAuthentication yes
++ ForwardX11Trusted yes
++ 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
++
+ Include /etc/ssh/ssh_config.d/*.conf
+--
+1.8.3.1
+
diff --git a/set-sshd-config.patch b/set-sshd-config.patch
new file mode 100644
index 0000000..2915ce9
--- /dev/null
+++ b/set-sshd-config.patch
@@ -0,0 +1,97 @@
+From ca0b2bcd17a2c0e1682b8125960ac81e08d0f6dd Mon Sep 17 00:00:00 2001
+From: kircher <kircherlike@outlook.com>
+Date: Wed, 27 Oct 2021 16:51:41 +0800
+Subject: [PATCH] set
+
+---
+ sshd_config | 32 +++++++++++++++++++-------------
+ 1 file changed, 19 insertions(+), 13 deletions(-)
+
+Index: b/sshd_config
+===================================================================
+--- a/sshd_config
++++ b/sshd_config
+@@ -23,21 +23,22 @@
+ #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
+
+ # Logging
+ #SyslogFacility AUTH
++SyslogFacility AUTHPRIV
+ #LogLevel INFO
+
+ # Authentication:
+
+ #LoginGraceTime 2m
+-#PermitRootLogin prohibit-password
++PermitRootLogin yes
+ #StrictModes yes
+ #MaxAuthTries 6
+ #MaxSessions 10
+@@ -62,11 +63,11 @@ AuthorizedKeysFile .ssh/authorized_keys
+ #IgnoreRhosts yes
+
+ # To disable tunneled clear text passwords, change to no here!
+-#PasswordAuthentication yes
++PasswordAuthentication yes
+ #PermitEmptyPasswords no
+
+ # Change to no to disable s/key passwords
+-#KbdInteractiveAuthentication yes
++KbdInteractiveAuthentication no
+
+ # Kerberos options
+ #KerberosAuthentication no
+@@ -76,8 +77,8 @@ AuthorizedKeysFile .ssh/authorized_keys
+ #KerberosUseKuserok yes
+
+ # GSSAPI options
+-#GSSAPIAuthentication no
+-#GSSAPICleanupCredentials yes
++GSSAPIAuthentication yes
++GSSAPICleanupCredentials no
+ #GSSAPIStrictAcceptorCheck yes
+ #GSSAPIKeyExchange no
+ #GSSAPIEnablek5users no
+@@ -93,16 +94,16 @@ AuthorizedKeysFile .ssh/authorized_keys
+ # and KbdInteractiveAuthentication to 'no'.
+ # WARNING: 'UsePAM no' is not supported in openEuler and may cause several
+ # problems.
+-#UsePAM no
++UsePAM yes
+
+ #AllowAgentForwarding yes
+ #AllowTcpForwarding yes
+ #GatewayPorts no
+-#X11Forwarding no
++X11Forwarding yes
+ #X11DisplayOffset 10
+ #X11UseLocalhost yes
+ #PermitTTY yes
+-#PrintMotd yes
++PrintMotd no
+ #PrintLastLog yes
+ #TCPKeepAlive yes
+ #PermitUserEnvironment no
+@@ -119,6 +120,11 @@ AuthorizedKeysFile .ssh/authorized_keys
+ # no default banner path
+ #Banner none
+
++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/skip-scp-test-if-there-is-no-scp-on-remote-path-as-s.patch b/skip-scp-test-if-there-is-no-scp-on-remote-path-as-s.patch
new file mode 100644
index 0000000..ebb9a53
--- /dev/null
+++ b/skip-scp-test-if-there-is-no-scp-on-remote-path-as-s.patch
@@ -0,0 +1,38 @@
+From e32af8efd8d36f8349761615b73cb3174e52134a Mon Sep 17 00:00:00 2001
+From: chengyechun <chengyechun1@huawei.com>
+Date: Thu, 5 Jan 2023 22:45:10 +0800
+Subject: [PATCH] skip scp test if there is no scp on remote path as scp3.sh
+ did
+
+---
+ regress/multiplex.sh | 14 +++++++++-----
+ 1 file changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/regress/multiplex.sh b/regress/multiplex.sh
+index 4744fa3..8ab0f59 100644
+--- a/regress/multiplex.sh
++++ b/regress/multiplex.sh
+@@ -67,11 +67,15 @@ echo "get ${DATA} ${COPY}" | \
+ test -f ${COPY} || fail "sftp: failed copy ${DATA}"
+ cmp ${DATA} ${COPY} || fail "sftp: corrupted copy of ${DATA}"
+
+-rm -f ${COPY}
+-trace "scp transfer over multiplexed connection and check result"
+-${SCP} -S ${SSH} -F $OBJ/ssh_config -oControlPath=$CTL otherhost:${DATA} ${COPY} >>$TEST_REGRESS_LOGFILE 2>&1
+-test -f ${COPY} || fail "scp: failed copy ${DATA}"
+-cmp ${DATA} ${COPY} || fail "scp: corrupted copy of ${DATA}"
++$SSH -F $OBJ/ssh_proxy somehost \
++ 'IFS=":"; for i in $PATH;do [ -x "$i/scp" ] && exit 0; done; exit 1'
++if [ $? -eq 0 ]; then
++ rm -f ${COPY}
++ trace "scp transfer over multiplexed connection and check result"
++ ${SCP} -S ${SSH} -F $OBJ/ssh_config -oControlPath=$CTL otherhost:${DATA} ${COPY} >>$TEST_REGRESS_LOGFILE 2>&1
++ test -f ${COPY} || fail "scp: failed copy ${DATA}"
++ cmp ${DATA} ${COPY} || fail "scp: corrupted copy of ${DATA}"
++fi
+
+ rm -f ${COPY}
+ verbose "test $tid: forward"
+--
+2.23.0
+
diff --git a/sources b/sources
new file mode 100644
index 0000000..6a7fb53
--- /dev/null
+++ b/sources
@@ -0,0 +1,2 @@
+3430d5e6e71419e28f440a42563cb553 openssh-9.3p1.tar.gz
+42f87c6cce9bcf8cfd46ed4605b779f5 pam_ssh_agent_auth-0.10.4.tar.gz
diff --git a/ssh-agent.service b/ssh-agent.service
new file mode 100644
index 0000000..311f91d
--- /dev/null
+++ b/ssh-agent.service
@@ -0,0 +1,19 @@
+# Requires SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"
+# set in environment, handled for example in plasma via
+# /etc/xdg/plasma-workspace/env/ssh-agent.sh
+[Unit]
+ConditionEnvironment=!SSH_AGENT_PID
+Description=OpenSSH key agent
+Documentation=man:ssh-agent(1) man:ssh-add(1) man:ssh(1)
+Requires=ssh-agent.socket
+
+[Service]
+Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
+ExecStartPre=/usr/bin/rm -f $SSH_AUTH_SOCK
+ExecStart=/usr/bin/ssh-agent -a $SSH_AUTH_SOCK
+PassEnvironment=SSH_AGENT_PID
+SuccessExitStatus=2
+Type=forking
+
+[Install]
+Also=ssh-agent.socket
diff --git a/ssh-agent.socket b/ssh-agent.socket
new file mode 100644
index 0000000..d589cbc
--- /dev/null
+++ b/ssh-agent.socket
@@ -0,0 +1,14 @@
+[Unit]
+Description=OpenSSH key agent
+Documentation=man:ssh-agent(1) man:ssh-add(1) man:ssh(1)
+
+[Socket]
+ListenStream=%t/ssh-agent.socket
+Service=ssh-agent.service
+Priority=6
+Backlog=5
+SocketMode=0600
+DirectoryMode=0700
+
+[Install]
+WantedBy=sockets.target
diff --git a/ssh-keycat.pam b/ssh-keycat.pam
new file mode 100644
index 0000000..d7a3f67
--- /dev/null
+++ b/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/ssh-keygen-bash-completion.sh b/ssh-keygen-bash-completion.sh
new file mode 100644
index 0000000..f601174
--- /dev/null
+++ b/ssh-keygen-bash-completion.sh
@@ -0,0 +1,63 @@
+# ssh-keygen(1) completion -*- shell-script -*-
+
+_ssh_keygen()
+{
+ local cur prev words cword
+ _init_completion -n = || return
+
+ case $prev in
+ -*[abCIJjMNnrPSVWz])
+ return
+ ;;
+ -*E)
+ COMPREPLY=( $(compgen -W 'md5 sha256' -- "$cur") )
+ return
+ ;;
+ -*[FR])
+ # TODO: trim this down to actual entries in known hosts files
+ _known_hosts_real -- "$cur"
+ return
+ ;;
+ -*D)
+ _filedir so
+ return
+ ;;
+ -*[fGKsT])
+ _filedir
+ return
+ ;;
+ -*m)
+ COMPREPLY=( $(compgen -W 'PEM PKCS8 RFC4716' -- "$cur") )
+ return
+ ;;
+ -*O)
+ if [[ $cur != *=* ]]; then
+ COMPREPLY=( $(compgen -W 'clear force-command=
+ no-agent-forwarding no-port-forwarding no-pty no-user-rc
+ no-x11-forwarding permit-agent-forwarding
+ permit-port-forwarding permit-pty permit-user-rc
+ permit-x11-forwarding source-address=' -- "$cur") )
+ [[ $COMPREPLY == *= ]] && compopt -o nospace
+ fi
+ return
+ ;;
+ -*t)
+ local protocols=$(_xfunc ssh _ssh_query "$1" protocol-version)
+ local types='dsa ecdsa ed25519 rsa sm2'
+ if [[ $protocols == *1* ]]; then
+ types+=' rsa1'
+ fi
+ COMPREPLY=( $(compgen -W "$types" -- "$cur") )
+ return
+ ;;
+ esac
+
+ if [[ $cur == -* ]]; then
+ local opts=$(_parse_usage "$1" "-?")
+ [[ -z "$opts" ]] && opts=$(_parse_help "$1" "-?") # OpenSSH < 7
+ COMPREPLY=( $(compgen -W "$opts" -- "$cur") )
+ fi
+} &&
+complete -F _ssh_keygen ssh-keygen
+
+# ex: filetype=sh
diff --git a/sshd-keygen b/sshd-keygen
new file mode 100644
index 0000000..efd876c
--- /dev/null
+++ b/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 400 $KEY
+/usr/bin/chmod 400 $KEY.pub
+if [[ -x /usr/sbin/restorecon ]]; then
+ /usr/sbin/restorecon $KEY{,.pub}
+fi
+
+exit 0
diff --git a/sshd-keygen.target b/sshd-keygen.target
new file mode 100644
index 0000000..9efb4e2
--- /dev/null
+++ b/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/sshd-keygen@.service b/sshd-keygen@.service
new file mode 100644
index 0000000..f27f729
--- /dev/null
+++ b/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/sshd.pam b/sshd.pam
new file mode 100644
index 0000000..780f62e
--- /dev/null
+++ b/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/sshd.service b/sshd.service
new file mode 100644
index 0000000..e8afb86
--- /dev/null
+++ b/sshd.service
@@ -0,0 +1,17 @@
+[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/sysconfig/sshd
+ExecStart=/usr/sbin/sshd -D $OPTIONS
+ExecReload=/bin/kill -HUP $MAINPID
+KillMode=process
+Restart=on-failure
+RestartSec=42s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/sshd.socket b/sshd.socket
new file mode 100644
index 0000000..caa50c4
--- /dev/null
+++ b/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/sshd.sysconfig b/sshd.sysconfig
new file mode 100644
index 0000000..a217ce7
--- /dev/null
+++ b/sshd.sysconfig
@@ -0,0 +1,7 @@
+# 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.
diff --git a/sshd.tmpfiles b/sshd.tmpfiles
new file mode 100644
index 0000000..c35a2b8
--- /dev/null
+++ b/sshd.tmpfiles
@@ -0,0 +1 @@
+d /var/empty/sshd 711 root root -
diff --git a/sshd@.service b/sshd@.service
new file mode 100644
index 0000000..196c555
--- /dev/null
+++ b/sshd@.service
@@ -0,0 +1,10 @@
+[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/sysconfig/sshd
+ExecStart=-/usr/sbin/sshd -i $OPTIONS
+StandardInput=socket