diff options
84 files changed, 22836 insertions, 0 deletions
@@ -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&#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 + @@ -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 |