diff --git a/.gitignore b/.gitignore index 8244f14..747c78e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ pam_ssh_agent_auth-0.9.2.tar.bz2 /openssh-7.8p1.tar.gz.asc /openssh-7.9p1.tar.gz /openssh-7.9p1.tar.gz.asc +/openssh-8.0p1.tar.gz +/openssh-8.0p1.tar.gz.asc diff --git a/openssh-6.1p1-gssapi-canohost.patch b/openssh-6.1p1-gssapi-canohost.patch deleted file mode 100644 index 3e6c9cc..0000000 --- a/openssh-6.1p1-gssapi-canohost.patch +++ /dev/null @@ -1,21 +0,0 @@ -diff -up openssh-6.1p1/sshconnect2.c.canohost openssh-6.1p1/sshconnect2.c ---- openssh-6.1p1/sshconnect2.c.canohost 2012-10-30 10:52:59.593301692 +0100 -+++ openssh-6.1p1/sshconnect2.c 2012-10-30 11:01:12.870301632 +0100 -@@ -699,12 +699,15 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int r, ok = 0; -- const char *gss_host; -+ const char *gss_host = NULL; - - if (options.gss_server_identity) - gss_host = options.gss_server_identity; -- else if (options.gss_trust_dns) -+ else if (options.gss_trust_dns) { - gss_host = get_canonical_hostname(active_state, 1); -+ if (strcmp(gss_host, "UNKNOWN") == 0) -+ gss_host = authctxt->host; -+ } - else - gss_host = authctxt->host; - diff --git a/openssh-6.2p1-vendor.patch b/openssh-6.2p1-vendor.patch deleted file mode 100644 index 1af5e9d..0000000 --- a/openssh-6.2p1-vendor.patch +++ /dev/null @@ -1,142 +0,0 @@ -diff -up openssh-7.4p1/configure.ac.vendor openssh-7.4p1/configure.ac ---- openssh-7.4p1/configure.ac.vendor 2016-12-23 13:34:51.681253844 +0100 -+++ openssh-7.4p1/configure.ac 2016-12-23 13:34:51.694253847 +0100 -@@ -4930,6 +4930,12 @@ AC_ARG_WITH([lastlog], - fi - ] - ) -+AC_ARG_ENABLE(vendor-patchlevel, -+ [ --enable-vendor-patchlevel=TAG specify a vendor patch level], -+ [AC_DEFINE_UNQUOTED(SSH_VENDOR_PATCHLEVEL,[SSH_RELEASE "-" "$enableval"],[Define to your vendor patch level, if it has been modified from the upstream source release.]) -+ SSH_VENDOR_PATCHLEVEL="$enableval"], -+ [AC_DEFINE(SSH_VENDOR_PATCHLEVEL,SSH_RELEASE,[Define to your vendor patch level, if it has been modified from the upstream source release.]) -+ SSH_VENDOR_PATCHLEVEL=none]) - - dnl lastlog, [uw]tmpx? detection - dnl NOTE: set the paths in the platform section to avoid the -@@ -5194,6 +5200,7 @@ echo " Translate v4 in v6 hack - echo " BSD Auth support: $BSD_AUTH_MSG" - echo " Random number source: $RAND_MSG" - echo " Privsep sandbox style: $SANDBOX_STYLE" -+echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" - - echo "" - -diff -up openssh-7.4p1/servconf.c.vendor openssh-7.4p1/servconf.c ---- openssh-7.4p1/servconf.c.vendor 2016-12-19 05:59:41.000000000 +0100 -+++ openssh-7.4p1/servconf.c 2016-12-23 13:36:07.555268628 +0100 -@@ -143,6 +143,7 @@ initialize_server_options(ServerOptions - options->max_authtries = -1; - options->max_sessions = -1; - options->banner = NULL; -+ options->show_patchlevel = -1; - options->use_dns = -1; - options->client_alive_interval = -1; - options->client_alive_count_max = -1; -@@ -325,6 +326,8 @@ fill_default_server_options(ServerOption - options->ip_qos_bulk = IPTOS_DSCP_CS1; - if (options->version_addendum == NULL) - options->version_addendum = xstrdup(""); -+ if (options->show_patchlevel == -1) -+ options->show_patchlevel = 0; - if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) - options->fwd_opts.streamlocal_bind_mask = 0177; - if (options->fwd_opts.streamlocal_bind_unlink == -1) -@@ -402,7 +405,7 @@ typedef enum { - sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, - sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedKeyTypes, - sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, -- sBanner, sUseDNS, sHostbasedAuthentication, -+ sBanner, sShowPatchLevel, sUseDNS, sHostbasedAuthentication, - sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, -@@ -528,6 +531,7 @@ static struct { - { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, - { "maxsessions", sMaxSessions, SSHCFG_ALL }, - { "banner", sBanner, SSHCFG_ALL }, -+ { "showpatchlevel", sShowPatchLevel, SSHCFG_GLOBAL }, - { "usedns", sUseDNS, SSHCFG_GLOBAL }, - { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, - { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, -@@ -1369,6 +1373,10 @@ process_server_config_line(ServerOptions - intptr = &options->disable_forwarding; - goto parse_flag; - -+ case sShowPatchLevel: -+ intptr = &options->show_patchlevel; -+ goto parse_flag; -+ - case sAllowUsers: - while ((arg = strdelim(&cp)) && *arg != '\0') { - if (match_user(NULL, NULL, NULL, arg) == -1) -@@ -2269,6 +2277,7 @@ dump_config(ServerOptions *o) - dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); - dump_cfg_fmtint(sCompression, o->compression); - dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); -+ dump_cfg_fmtint(sShowPatchLevel, o->show_patchlevel); - dump_cfg_fmtint(sUseDNS, o->use_dns); - dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); - dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); -diff -up openssh-7.4p1/servconf.h.vendor openssh-7.4p1/servconf.h ---- openssh-7.4p1/servconf.h.vendor 2016-12-19 05:59:41.000000000 +0100 -+++ openssh-7.4p1/servconf.h 2016-12-23 13:34:51.694253847 +0100 -@@ -149,6 +149,7 @@ typedef struct { - int max_authtries; - int max_sessions; - char *banner; /* SSH-2 banner message */ -+ int show_patchlevel; /* Show vendor patch level to clients */ - int use_dns; - int client_alive_interval; /* - * poke the client this often to -diff -up openssh-7.4p1/sshd_config.5.vendor openssh-7.4p1/sshd_config.5 ---- openssh-7.4p1/sshd_config.5.vendor 2016-12-23 13:34:51.695253847 +0100 -+++ openssh-7.4p1/sshd_config.5 2016-12-23 13:37:17.482282253 +0100 -@@ -1334,6 +1334,13 @@ an OpenSSH Key Revocation List (KRL) as - .Cm AcceptEnv - or - .Cm PermitUserEnvironment . -+.It Cm ShowPatchLevel -+Specifies whether -+.Nm sshd -+will display the patch level of the binary in the identification string. -+The patch level is set at compile-time. -+The default is -+.Dq no . - .It Cm StreamLocalBindMask - Sets the octal file creation mode mask - .Pq umask -diff -up openssh-7.4p1/sshd_config.vendor openssh-7.4p1/sshd_config ---- openssh-7.4p1/sshd_config.vendor 2016-12-23 13:34:51.690253846 +0100 -+++ openssh-7.4p1/sshd_config 2016-12-23 13:34:51.695253847 +0100 -@@ -105,6 +105,7 @@ X11Forwarding yes - #Compression delayed - #ClientAliveInterval 0 - #ClientAliveCountMax 3 -+#ShowPatchLevel no - #UseDNS no - #PidFile /var/run/sshd.pid - #MaxStartups 10:30:100 -diff -up openssh-7.4p1/sshd.c.vendor openssh-7.4p1/sshd.c ---- openssh-7.4p1/sshd.c.vendor 2016-12-23 13:34:51.682253844 +0100 -+++ openssh-7.4p1/sshd.c 2016-12-23 13:38:32.434296856 +0100 -@@ -367,7 +367,8 @@ sshd_exchange_identification(struct ssh - char remote_version[256]; /* Must be at least as big as buf. */ - - xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s\r\n", -- PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, -+ PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, -+ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, - *options.version_addendum == '\0' ? "" : " ", - options.version_addendum); - -@@ -1650,7 +1651,8 @@ main(int ac, char **av) - exit(1); - } - -- debug("sshd version %s, %s", SSH_VERSION, -+ debug("sshd version %s, %s", -+ (options.show_patchlevel == 1) ? SSH_VENDOR_PATCHLEVEL : SSH_VERSION, - #ifdef WITH_OPENSSL - SSLeay_version(SSLEAY_VERSION) - #else diff --git a/openssh-6.6.1p1-log-in-chroot.patch b/openssh-6.6.1p1-log-in-chroot.patch index 7f822ab..b009d99 100644 --- a/openssh-6.6.1p1-log-in-chroot.patch +++ b/openssh-6.6.1p1-log-in-chroot.patch @@ -46,7 +46,7 @@ diff -up openssh-7.4p1/monitor.c.log-in-chroot openssh-7.4p1/monitor.c + pmonitor->m_state = "preauth"; + - authctxt = _authctxt; + authctxt = (Authctxt *)ssh->authctxt; memset(authctxt, 0, sizeof(*authctxt)); ssh->authctxt = authctxt; @@ -405,6 +407,8 @@ monitor_child_postauth(struct monitor *p @@ -113,7 +113,7 @@ diff -up openssh-7.4p1/monitor.h.log-in-chroot openssh-7.4p1/monitor.h +void monitor_reinit(struct monitor *, const char *); struct Authctxt; - void monitor_child_preauth(struct Authctxt *, struct monitor *); + void monitor_child_preauth(struct ssh *, struct monitor *); diff -up openssh-7.4p1/session.c.log-in-chroot openssh-7.4p1/session.c --- openssh-7.4p1/session.c.log-in-chroot 2016-12-23 15:14:33.319168086 +0100 +++ openssh-7.4p1/session.c 2016-12-23 15:18:18.742211853 +0100 diff --git a/openssh-6.6p1-GSSAPIEnablek5users.patch b/openssh-6.6p1-GSSAPIEnablek5users.patch index c33bdac..d943f41 100644 --- a/openssh-6.6p1-GSSAPIEnablek5users.patch +++ b/openssh-6.6p1-GSSAPIEnablek5users.patch @@ -22,15 +22,15 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c --- openssh-7.4p1/servconf.c.GSSAPIEnablek5users 2016-12-23 15:18:40.615216100 +0100 +++ openssh-7.4p1/servconf.c 2016-12-23 15:35:36.354401156 +0100 @@ -168,6 +168,7 @@ initialize_server_options(ServerOptions - options->gss_strict_acceptor = -1; options->gss_store_rekey = -1; + options->gss_kex_algorithms = NULL; options->use_kuserok = -1; + options->enable_k5users = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; @@ -345,6 +346,8 @@ fill_default_server_options(ServerOption - options->gss_store_rekey = 0; + #endif if (options->use_kuserok == -1) options->use_kuserok = 1; + if (options->enable_k5users == -1) @@ -44,20 +44,22 @@ diff -up openssh-7.4p1/servconf.c.GSSAPIEnablek5users openssh-7.4p1/servconf.c sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, - sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, + sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, -@@ -497,12 +500,14 @@ static struct { - { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, +@@ -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 }, diff --git a/openssh-6.6p1-kuserok.patch b/openssh-6.6p1-kuserok.patch index 4b681ff..56a6950 100644 --- a/openssh-6.6p1-kuserok.patch +++ b/openssh-6.6p1-kuserok.patch @@ -176,17 +176,17 @@ diff -up openssh-7.4p1/servconf.c.kuserok openssh-7.4p1/servconf.c --- openssh-7.4p1/servconf.c.kuserok 2016-12-23 14:36:07.630465944 +0100 +++ openssh-7.4p1/servconf.c 2016-12-23 15:11:52.278133344 +0100 @@ -116,6 +116,7 @@ initialize_server_options(ServerOptions - options->gss_cleanup_creds = -1; options->gss_strict_acceptor = -1; options->gss_store_rekey = -1; + options->gss_kex_algorithms = NULL; + options->use_kuserok = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; @@ -278,6 +279,8 @@ fill_default_server_options(ServerOption - options->gss_strict_acceptor = 1; - if (options->gss_store_rekey == -1) - options->gss_store_rekey = 0; + if (options->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) diff --git a/openssh-6.7p1-coverity.patch b/openssh-6.7p1-coverity.patch index 0d238dd..15d489d 100644 --- a/openssh-6.7p1-coverity.patch +++ b/openssh-6.7p1-coverity.patch @@ -20,7 +20,7 @@ diff -up openssh-7.4p1/monitor.c.coverity openssh-7.4p1/monitor.c --- openssh-7.4p1/monitor.c.coverity 2016-12-23 16:40:26.888788688 +0100 +++ openssh-7.4p1/monitor.c 2016-12-23 16:40:26.900788691 +0100 @@ -411,7 +411,7 @@ monitor_child_preauth(Authctxt *_authctx - mm_get_keystate(pmonitor); + mm_get_keystate(ssh, pmonitor); /* Drain any buffered messages from the child */ - while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) @@ -124,14 +124,14 @@ diff -up openssh-7.4p1/serverloop.c.coverity openssh-7.4p1/serverloop.c } @@ -518,7 +518,7 @@ server_request_tun(void) + debug("%s: invalid tun", __func__); + goto done; } - - tun = packet_get_int(); - if (auth_opts->force_tun_device != -1) { + if (auth_opts->force_tun_device >= 0) { - if (tun != SSH_TUNID_ANY && auth_opts->force_tun_device != tun) + if (tun != SSH_TUNID_ANY && + auth_opts->force_tun_device != (int)tun) goto done; - tun = auth_opts->force_tun_device; diff -up openssh-7.4p1/sftp.c.coverity openssh-7.4p1/sftp.c --- openssh-7.4p1/sftp.c.coverity 2016-12-19 05:59:41.000000000 +0100 +++ openssh-7.4p1/sftp.c 2016-12-23 16:40:26.903788691 +0100 @@ -163,7 +163,7 @@ diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c +++ openssh-7.4p1/sshd.c 2016-12-23 16:40:26.904788692 +0100 @@ -691,8 +691,10 @@ privsep_preauth(Authctxt *authctxt) - privsep_preauth_child(); + privsep_preauth_child(ssh); setproctitle("%s", "[net]"); - if (box != NULL) + if (box != NULL) { @@ -174,8 +174,8 @@ diff -up openssh-7.4p1/sshd.c.coverity openssh-7.4p1/sshd.c return 0; } @@ -1386,6 +1388,9 @@ server_accept_loop(int *sock_in, int *so - if (num_listen_socks < 0) - break; + explicit_bzero(rnd, sizeof(rnd)); + } } + + if (fdset != NULL) diff --git a/openssh-6.7p1-kdf-cavs.patch b/openssh-6.7p1-kdf-cavs.patch index 181267c..a5beb8c 100644 --- a/openssh-6.7p1-kdf-cavs.patch +++ b/openssh-6.7p1-kdf-cavs.patch @@ -40,7 +40,7 @@ diff -up openssh-6.8p1/Makefile.in.kdf-cavs openssh-6.8p1/Makefile.in diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c --- openssh-6.8p1/ssh-cavs.c.kdf-cavs 2015-03-18 11:23:46.348049354 +0100 +++ openssh-6.8p1/ssh-cavs.c 2015-03-18 11:23:46.348049354 +0100 -@@ -0,0 +1,377 @@ +@@ -0,0 +1,387 @@ +/* + * Copyright (C) 2015, Stephan Mueller + * @@ -208,6 +208,7 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c +{ + int ret = 0; + struct kex kex; ++ struct sshbuf *Kb = NULL; + BIGNUM *Kbn = NULL; + int mode = 0; + struct newkeys *ctoskeys; @@ -222,10 +223,17 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c + Kbn = BN_new(); + BN_bin2bn(test->K, test->Klen, Kbn); + if (!Kbn) { -+ printf("cannot convert K into BIGNUM\n"); ++ printf("cannot convert K into bignum\n"); + ret = 1; + goto out; + } ++ Kb = sshbuf_new(); ++ if (!Kb) { ++ printf("cannot convert K into sshbuf\n"); ++ ret = 1; ++ goto out; ++ } ++ sshbuf_put_bignum2(Kb, Kbn); + + kex.session_id = test->session_id; + kex.session_id_len = test->session_id_len; @@ -285,7 +293,7 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c + goto out; + } + ssh->kex = &kex; -+ kex_derive_keys_bn(ssh, test->H, test->Hlen, Kbn); ++ kex_derive_keys(ssh, test->H, test->Hlen, Kb); + + ctoskeys = kex.newkeys[0]; + stockeys = kex.newkeys[1]; @@ -321,6 +329,8 @@ diff -up openssh-6.8p1/ssh-cavs.c.kdf-cavs openssh-6.8p1/ssh-cavs.c +out: + if (Kbn) + BN_free(Kbn); ++ if (Kb) ++ sshbuf_free(Kb); + if (ssh) + ssh_packet_close(ssh); + return ret; diff --git a/openssh-6.8p1-sshdT-output.patch b/openssh-6.8p1-sshdT-output.patch index 7a843e7..ac9169a 100644 --- a/openssh-6.8p1-sshdT-output.patch +++ b/openssh-6.8p1-sshdT-output.patch @@ -10,18 +10,3 @@ diff -up openssh/servconf.c.sshdt openssh/servconf.c dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); -diff -up openssh/ssh.1.sshdt openssh/ssh.1 ---- openssh/ssh.1.sshdt 2015-06-24 11:42:19.565102807 +0200 -+++ openssh/ssh.1 2015-06-24 11:42:29.042078701 +0200 -@@ -441,7 +441,11 @@ For full details of the options listed b - .It GatewayPorts - .It GlobalKnownHostsFile - .It GSSAPIAuthentication -+.It GSSAPIKeyExchange -+.It GSSAPIClientIdentity - .It GSSAPIDelegateCredentials -+.It GSSAPIRenewalForcesRekey -+.It GSSAPITrustDNS - .It HashKnownHosts - .It Host - .It HostbasedAuthentication diff --git a/openssh-7.0p1-gssKexAlgorithms.patch b/openssh-7.0p1-gssKexAlgorithms.patch deleted file mode 100644 index 8bd2d4e..0000000 --- a/openssh-7.0p1-gssKexAlgorithms.patch +++ /dev/null @@ -1,431 +0,0 @@ -diff -up openssh-7.0p1/gss-genr.c.gsskexalg openssh-7.0p1/gss-genr.c ---- openssh-7.0p1/gss-genr.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 -+++ openssh-7.0p1/gss-genr.c 2015-08-19 12:28:38.078518839 +0200 -@@ -78,7 +78,8 @@ ssh_gssapi_oid_table_ok() { - */ - - char * --ssh_gssapi_client_mechanisms(const char *host, const char *client) { -+ssh_gssapi_client_mechanisms(const char *host, const char *client, -+ const char *kex) { - gss_OID_set gss_supported; - OM_uint32 min_status; - -@@ -86,12 +87,12 @@ ssh_gssapi_client_mechanisms(const char - return NULL; - - return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, -- host, client)); -+ host, client, kex)); - } - - char * - ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, -- const char *host, const char *client) { -+ const char *host, const char *client, const char *kex) { - struct sshbuf *buf; - size_t i; - int oidpos, enclen, r; -@@ -100,6 +101,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - char deroid[2]; - const EVP_MD *evp_md = EVP_md5(); - EVP_MD_CTX *md; -+ char *s, *cp, *p; - - if (gss_enc2oid != NULL) { - for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -@@ -113,6 +115,7 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - - md = EVP_MD_CTX_new(); - oidpos = 0; -+ s = cp = xstrdup(kex); - for (i = 0; i < gss_supported->count; i++) { - if (gss_supported->elements[i].length < 128 && - (*check)(NULL, &(gss_supported->elements[i]), host, client)) { -@@ -131,28 +134,25 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_sup - enclen = __b64_ntop(digest, EVP_MD_size(evp_md), - encoded, EVP_MD_size(evp_md) * 2); - -- if (oidpos != 0) -- if ((r = sshbuf_put_u8(buf, ',')) != 0) -- fatal("%s: buffer error: %s", __func__, ssh_err(r)); -- -- if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, -- sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0 || -- (r = sshbuf_put_u8(buf, ',')) != 0 || -- (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, -- sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0 || -- (r = sshbuf_put_u8(buf, ',')) != 0 || -- (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, -- sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 || -- (r = sshbuf_put(buf, encoded, enclen)) != 0) -- fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ cp = strncpy(s, kex, strlen(kex)); -+ for ((p = strsep(&cp, ",")); p && *p != '\0'; -+ (p = strsep(&cp, ","))) { -+ if (sshbuf_len(buf) != 0) -+ if ((r = sshbuf_put_u8(buf, ',')) != 0) -+ fatal("%s: buffer error: %s", -+ __func__, ssh_err(r)); -+ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0) -+ fatal("%s: buffer error: %s", -+ __func__, ssh_err(r)); -+ } - - gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); - gss_enc2oid[oidpos].encoded = encoded; - oidpos++; - } - } -+ free(s); - EVP_MD_CTX_free(md); - gss_enc2oid[oidpos].oid = NULL; - gss_enc2oid[oidpos].encoded = NULL; -diff -up openssh-7.0p1/gss-serv.c.gsskexalg openssh-7.0p1/gss-serv.c ---- openssh-7.0p1/gss-serv.c.gsskexalg 2015-08-19 12:28:38.024518959 +0200 -+++ openssh-7.0p1/gss-serv.c 2015-08-19 12:28:38.078518839 +0200 -@@ -149,7 +149,8 @@ ssh_gssapi_server_mechanisms() { - if (supported_oids == NULL) - ssh_gssapi_prepare_supported_oids(); - return (ssh_gssapi_kex_mechs(supported_oids, -- &ssh_gssapi_server_check_mech, NULL, NULL)); -+ &ssh_gssapi_server_check_mech, NULL, NULL, -+ options.gss_kex_algorithms)); - } - - /* Unprivileged */ -diff -up openssh-7.0p1/kex.c.gsskexalg openssh-7.0p1/kex.c ---- openssh-7.0p1/kex.c.gsskexalg 2015-08-19 12:28:38.078518839 +0200 -+++ openssh-7.0p1/kex.c 2015-08-19 12:30:13.249306371 +0200 -@@ -50,6 +50,7 @@ - #include "misc.h" - #include "dispatch.h" - #include "monitor.h" -+#include "xmalloc.h" - - #include "ssherr.h" - #include "sshbuf.h" -@@ -232,6 +232,29 @@ kex_assemble_names(const char *def, char - return r; - } - -+/* Validate GSS KEX method name list */ -+int -+gss_kex_names_valid(const char *names) -+{ -+ char *s, *cp, *p; -+ -+ if (names == NULL || *names == '\0') -+ return 0; -+ s = cp = xstrdup(names); -+ for ((p = strsep(&cp, ",")); p && *p != '\0'; -+ (p = strsep(&cp, ","))) { -+ if (strncmp(p, "gss-", 4) != 0 -+ || kex_alg_by_name(p) == NULL) { -+ error("Unsupported KEX algorithm \"%.100s\"", p); -+ free(s); -+ return 0; -+ } -+ } -+ debug3("gss kex names ok: [%s]", names); -+ free(s); -+ return 1; -+} -+ - /* put algorithm proposal into buffer */ - int - kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) -diff -up openssh-7.0p1/kex.h.gsskexalg openssh-7.0p1/kex.h ---- openssh-7.0p1/kex.h.gsskexalg 2015-08-19 12:28:38.078518839 +0200 -+++ openssh-7.0p1/kex.h 2015-08-19 12:30:52.404218958 +0200 -@@ -173,6 +173,7 @@ int kex_names_valid(const char *); - char *kex_alg_list(char); - char *kex_names_cat(const char *, const char *); - int kex_assemble_names(char **, const char *, const char *); -+int gss_kex_names_valid(const char *); - - int kex_new(struct ssh *, char *[PROPOSAL_MAX], struct kex **); - int kex_setup(struct ssh *, char *[PROPOSAL_MAX]); -diff -up openssh-7.0p1/readconf.c.gsskexalg openssh-7.0p1/readconf.c ---- openssh-7.0p1/readconf.c.gsskexalg 2015-08-19 12:28:38.026518955 +0200 -+++ openssh-7.0p1/readconf.c 2015-08-19 12:31:28.333138747 +0200 -@@ -61,6 +61,7 @@ - #include "uidswap.h" - #include "myproposal.h" - #include "digest.h" -+#include "ssh-gss.h" - - /* Format of the configuration file: - -@@ -148,7 +149,7 @@ typedef enum { - oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, - oAddressFamily, oGssAuthentication, oGssDelegateCreds, - oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, -- oGssServerIdentity, -+ oGssServerIdentity, oGssKexAlgorithms, - oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, - oHashKnownHosts, -@@ -200,6 +201,7 @@ static struct { - { "gssapiclientidentity", oGssClientIdentity }, - { "gssapiserveridentity", oGssServerIdentity }, - { "gssapirenewalforcesrekey", oGssRenewalRekey }, -+ { "gssapikexalgorithms", oGssKexAlgorithms }, - # else - { "gssapiauthentication", oUnsupported }, - { "gssapikeyexchange", oUnsupported }, -@@ -207,6 +209,7 @@ static struct { - { "gssapitrustdns", oUnsupported }, - { "gssapiclientidentity", oUnsupported }, - { "gssapirenewalforcesrekey", oUnsupported }, -+ { "gssapikexalgorithms", oUnsupported }, - #endif - #ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, -@@ -929,6 +932,18 @@ parse_time: - intptr = &options->gss_renewal_rekey; - goto parse_flag; - -+ case oGssKexAlgorithms: -+ arg = strdelim(&s); -+ if (!arg || *arg == '\0') -+ fatal("%.200s line %d: Missing argument.", -+ filename, linenum); -+ if (!gss_kex_names_valid(arg)) -+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", -+ filename, linenum, arg ? arg : ""); -+ if (*activep && options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = xstrdup(arg); -+ break; -+ - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; -@@ -1638,6 +1653,7 @@ initialize_options(Options * options) - options->gss_renewal_rekey = -1; - options->gss_client_identity = NULL; - options->gss_server_identity = NULL; -+ options->gss_kex_algorithms = NULL; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->kbd_interactive_devices = NULL; -@@ -1773,6 +1789,10 @@ fill_default_options(Options * options) - options->gss_trust_dns = 0; - if (options->gss_renewal_rekey == -1) - options->gss_renewal_rekey = 0; -+#ifdef GSSAPI -+ if (options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); -+#endif - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -@@ -2651,6 +2671,8 @@ dump_client_config(Options *o, const cha - dump_cfg_string(oGssClientIdentity, o->gss_client_identity); - dump_cfg_string(oGssServerIdentity, o->gss_client_identity); - dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); -+ dump_cfg_string(oGssKexAlgorithms, o->gss_kex_algorithms ? -+ o->gss_kex_algorithms : GSS_KEX_DEFAULT_KEX); - #endif /* GSSAPI */ - dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); - dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); -diff -up openssh-7.9p1/readconf.h.gsskexalg openssh-7.9p1/readconf.h ---- openssh-7.9p1/readconf.h.gsskexalg 2018-11-14 09:20:06.616350574 +0100 -+++ openssh-7.9p1/readconf.h 2018-11-14 09:20:06.647350828 +0100 -@@ -46,6 +46,7 @@ typedef struct { - int gss_renewal_rekey; /* Credential renewal forces rekey */ - char *gss_client_identity; /* Principal to initiate GSSAPI with */ - char *gss_server_identity; /* GSSAPI target principal */ -+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ - int password_authentication; /* Try password - * authentication. */ - int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ -diff -up openssh-7.0p1/servconf.c.gsskexalg openssh-7.0p1/servconf.c ---- openssh-7.0p1/servconf.c.gsskexalg 2015-08-19 12:28:38.074518847 +0200 -+++ openssh-7.0p1/servconf.c 2015-08-19 12:33:13.599902732 +0200 -@@ -57,6 +57,7 @@ - #include "auth.h" - #include "myproposal.h" - #include "digest.h" -+#include "ssh-gss.h" - - static void add_listen_addr(ServerOptions *, const char *, - const char *, int); -@@ -121,6 +122,7 @@ initialize_server_options(ServerOptions - options->gss_cleanup_creds = -1; - options->gss_strict_acceptor = -1; - options->gss_store_rekey = -1; -+ options->gss_kex_algorithms = NULL; - options->use_kuserok = -1; - options->enable_k5users = -1; - options->password_authentication = -1; -@@ -288,6 +290,10 @@ fill_default_server_options(ServerOption - options->gss_strict_acceptor = 1; - if (options->gss_store_rekey == -1) - options->gss_store_rekey = 0; -+#ifdef GSSAPI -+ if (options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = strdup(GSS_KEX_DEFAULT_KEX); -+#endif - if (options->use_kuserok == -1) - options->use_kuserok = 1; - if (options->enable_k5users == -1) -@@ -427,7 +431,7 @@ typedef enum { - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssEnablek5users, sGssStrictAcceptor, -- sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, -+ sGssKeyEx, sGssStoreRekey, sGssKexAlgorithms, sAcceptEnv, sSetEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, -@@ -506,6 +510,7 @@ static struct { - { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, - { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, - { "gssapienablek5users", sGssEnablek5users, SSHCFG_ALL }, -+ { "gssapikexalgorithms", sGssKexAlgorithms, SSHCFG_GLOBAL }, - #else - { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, - { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, -@@ -513,6 +518,7 @@ static struct { - { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, - { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, - { "gssapienablek5users", sUnsupported, SSHCFG_ALL }, -+ { "gssapikexalgorithms", sUnsupported, SSHCFG_GLOBAL }, - #endif - { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, - { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, -@@ -1273,6 +1279,18 @@ process_server_config_line(ServerOptions - intptr = &options->gss_store_rekey; - goto parse_flag; - -+ case sGssKexAlgorithms: -+ arg = strdelim(&cp); -+ if (!arg || *arg == '\0') -+ fatal("%.200s line %d: Missing argument.", -+ filename, linenum); -+ if (!gss_kex_names_valid(arg)) -+ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", -+ filename, linenum, arg ? arg : ""); -+ if (*activep && options->gss_kex_algorithms == NULL) -+ options->gss_kex_algorithms = xstrdup(arg); -+ break; -+ - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; -@@ -2304,6 +2322,7 @@ dump_config(ServerOptions *o) - dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); - dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); - dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); -+ dump_cfg_string(sGssKexAlgorithms, o->gss_kex_algorithms); - #endif - dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); - dump_cfg_fmtint(sKbdInteractiveAuthentication, -diff -up openssh-7.0p1/servconf.h.gsskexalg openssh-7.0p1/servconf.h ---- openssh-7.0p1/servconf.h.gsskexalg 2015-08-19 12:28:38.080518834 +0200 -+++ openssh-7.0p1/servconf.h 2015-08-19 12:34:46.328693944 +0200 -@@ -122,6 +122,7 @@ typedef struct { - int gss_cleanup_creds; /* If true, destroy cred cache on logout */ - int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ - int gss_store_rekey; -+ char *gss_kex_algorithms; /* GSSAPI kex methods to be offered by client. */ - int password_authentication; /* If true, permit password - * authentication. */ - int kbd_interactive_authentication; /* If true, permit */ -diff -up openssh-7.0p1/ssh.1.gsskexalg openssh-7.0p1/ssh.1 ---- openssh-7.0p1/ssh.1.gsskexalg 2015-08-19 12:28:38.081518832 +0200 -+++ openssh-7.0p1/ssh.1 2015-08-19 12:35:31.741591692 +0200 -@@ -496,6 +496,7 @@ For full details of the options listed b - .It GSSAPIDelegateCredentials - .It GSSAPIRenewalForcesRekey - .It GSSAPITrustDNS -+.It GSSAPIKexAlgorithms - .It HashKnownHosts - .It Host - .It HostbasedAuthentication -diff -up openssh-7.0p1/ssh_config.5.gsskexalg openssh-7.0p1/ssh_config.5 ---- openssh-7.0p1/ssh_config.5.gsskexalg 2015-08-19 12:28:38.028518950 +0200 -+++ openssh-7.0p1/ssh_config.5 2015-08-19 12:28:38.082518830 +0200 -@@ -786,6 +786,18 @@ command line will be passed untouched to - command line will be passed untouched to the GSSAPI library. - The default is - .Dq no . -+.It Cm GSSAPIKexAlgorithms -+The list of key exchange algorithms that are offered for GSSAPI -+key exchange. Possible values are -+.Bd -literal -offset 3n -+gss-gex-sha1-, -+gss-group1-sha1-, -+gss-group14-sha1- -+.Ed -+.Pp -+The default is -+.Dq gss-gex-sha1-,gss-group14-sha1- . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff -up openssh-7.0p1/sshconnect2.c.gsskexalg openssh-7.0p1/sshconnect2.c ---- openssh-7.0p1/sshconnect2.c.gsskexalg 2015-08-19 12:28:38.045518912 +0200 -+++ openssh-7.0p1/sshconnect2.c 2015-08-19 12:28:38.081518832 +0200 -@@ -179,7 +179,8 @@ ssh_kex2(char *host, struct sockaddr *ho - else - gss_host = host; - -- gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ gss = ssh_gssapi_client_mechanisms(gss_host, -+ options.gss_client_identity, options.gss_kex_algorithms); - if (gss) { - debug("Offering GSSAPI proposal: %s", gss); - xasprintf(&options.kex_algorithms, ---- openssh-7.1p1/sshd_config.5.gsskexalg 2015-12-10 15:32:48.105418092 +0100 -+++ openssh-7.1p1/sshd_config.5 2015-12-10 15:33:47.771279548 +0100 -@@ -663,6 +663,18 @@ or updated credentials from a compatible - For this to work - .Cm GSSAPIKeyExchange - needs to be enabled in the server and also used by the client. -+.It Cm GSSAPIKexAlgorithms -+The list of key exchange algorithms that are accepted by GSSAPI -+key exchange. Possible values are -+.Bd -literal -offset 3n -+gss-gex-sha1-, -+gss-group1-sha1-, -+gss-group14-sha1- -+.Ed -+.Pp -+The default is -+.Dq gss-gex-sha1-,gss-group14-sha1- . -+This option only applies to protocol version 2 connections using GSSAPI. - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. -diff -up openssh-7.0p1/ssh-gss.h.gsskexalg openssh-7.0p1/ssh-gss.h ---- openssh-7.0p1/ssh-gss.h.gsskexalg 2015-08-19 12:28:38.031518944 +0200 -+++ openssh-7.0p1/ssh-gss.h 2015-08-19 12:28:38.081518832 +0200 -@@ -76,6 +76,10 @@ extern char **k5users_allowed_cmds; - #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - -+#define GSS_KEX_DEFAULT_KEX \ -+ KEX_GSS_GEX_SHA1_ID "," \ -+ KEX_GSS_GRP14_SHA1_ID -+ - typedef struct { - char *envvar; - char *envval; -@@ -147,9 +151,9 @@ int ssh_gssapi_credentials_updated(Gssct - /* In the server */ - typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, - const char *); --char *ssh_gssapi_client_mechanisms(const char *, const char *); -+char *ssh_gssapi_client_mechanisms(const char *, const char *, const char *); - char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, -- const char *); -+ const char *, const char *); - gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); - int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, - const char *); diff --git a/openssh-7.1p1-gssapi-documentation.patch b/openssh-7.1p1-gssapi-documentation.patch deleted file mode 100644 index db689d4..0000000 --- a/openssh-7.1p1-gssapi-documentation.patch +++ /dev/null @@ -1,52 +0,0 @@ -diff -up openssh-7.4p1/ssh_config.5.gss-docs openssh-7.4p1/ssh_config.5 ---- openssh-7.4p1/ssh_config.5.gss-docs 2016-12-23 14:28:34.051714486 +0100 -+++ openssh-7.4p1/ssh_config.5 2016-12-23 14:34:24.568522417 +0100 -@@ -765,10 +765,19 @@ The default is - If set to - .Dq yes - then renewal of the client's GSSAPI credentials will force the rekeying of the --ssh connection. With a compatible server, this can delegate the renewed -+ssh connection. With a compatible server, this will delegate the renewed - credentials to a session on the server. -+.Pp -+Checks are made to ensure that credentials are only propagated when the new -+credentials match the old ones on the originating client and where the -+receiving server still has the old set in its cache. -+.Pp - The default is - .Dq no . -+.Pp -+For this to work -+.Cm GSSAPIKeyExchange -+needs to be enabled in the server and also used by the client. - .It Cm GSSAPIServerIdentity - If set, specifies the GSSAPI server identity that ssh should expect when - connecting to the server. The default is unset, which means that the -@@ -776,9 +785,11 @@ expected GSSAPI server identity will be - hostname. - .It Cm GSSAPITrustDns - Set to --.Dq yes to indicate that the DNS is trusted to securely canonicalize -+.Dq yes -+to indicate that the DNS is trusted to securely canonicalize - the name of the host being connected to. If --.Dq no, the hostname entered on the -+.Dq no , -+the hostname entered on the - command line will be passed untouched to the GSSAPI library. - The default is - .Dq no . -diff -up openssh-7.4p1/sshd_config.5.gss-docs openssh-7.4p1/sshd_config.5 ---- openssh-7.4p1/sshd_config.5.gss-docs 2016-12-23 14:28:34.043714490 +0100 -+++ openssh-7.4p1/sshd_config.5 2016-12-23 14:28:34.051714486 +0100 -@@ -652,6 +652,10 @@ Controls whether the user's GSSAPI crede - successful connection rekeying. This option can be used to accepted renewed - or updated credentials from a compatible client. The default is - .Dq no . -+.Pp -+For this to work -+.Cm GSSAPIKeyExchange -+needs to be enabled in the server and also used by the client. - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. diff --git a/openssh-7.1p2-audit-race-condition.patch b/openssh-7.1p2-audit-race-condition.patch index c4536ff..9c9a680 100644 --- a/openssh-7.1p2-audit-race-condition.patch +++ b/openssh-7.1p2-audit-race-condition.patch @@ -56,9 +56,9 @@ diff -up openssh-7.4p1/monitor_wrap.h.audit-race openssh-7.4p1/monitor_wrap.h --- openssh-7.4p1/monitor_wrap.h.audit-race 2016-12-23 16:35:52.694685771 +0100 +++ openssh-7.4p1/monitor_wrap.h 2016-12-23 16:35:52.698685772 +0100 @@ -83,6 +83,8 @@ void mm_audit_unsupported_body(int); - void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); - void mm_audit_session_key_free_body(int, pid_t, uid_t); - void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); + 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 @@ -82,7 +82,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c return 1; } -+void child_destory_sensitive_data(); ++void child_destory_sensitive_data(struct ssh *ssh); + #define USE_PIPES 1 /* @@ -91,7 +91,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c close(err[0]); #endif -+ child_destory_sensitive_data(); ++ child_destory_sensitive_data(ssh); + /* Do processing for the child (exec command etc). */ do_child(ssh, s, command); @@ -101,7 +101,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c close(ttyfd); + /* Do this early, so we will not block large MOTDs */ -+ child_destory_sensitive_data(); ++ child_destory_sensitive_data(ssh); + /* record login, etc. similar to login(1) */ #ifndef HAVE_OSF_SIA @@ -109,7 +109,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c @@ -717,6 +728,8 @@ do_exec(Session *s, const char *command) } if (s->command != NULL && s->ptyfd == -1) - s->command_handle = PRIVSEP(audit_run_command(s->command)); + s->command_handle = PRIVSEP(audit_run_command(ssh, s->command)); + if (pipe(paudit) < 0) + fatal("pipe: %s", strerror(errno)); #endif @@ -141,7 +141,7 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c } +void -+child_destory_sensitive_data() ++child_destory_sensitive_data(struct ssh *ssh) +{ +#ifdef SSH_AUDIT_EVENTS + int pparent = paudit[1]; @@ -152,15 +152,15 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c +#endif + + /* remove hostkey from the child's memory */ -+ destroy_sensitive_data(use_privsep); ++ 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(0, 1); ++ packet_destroy_all(ssh, 0, 1); + /* XXX this will clean the rest but should not audit anymore */ -+ /* packet_clear_keys(); */ ++ /* packet_clear_keys(ssh); */ + +#ifdef SSH_AUDIT_EVENTS + /* Notify parent that we are done */ @@ -172,15 +172,15 @@ diff -up openssh-7.4p1/session.c.audit-race openssh-7.4p1/session.c * 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 - pw = s->pw; - } + + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); - /* remove hostkey from the child's memory */ -- destroy_sensitive_data(1); -- packet_clear_keys(); +- 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(0, 1); +- packet_destroy_all(ssh, 0, 1); - /* Force a password change */ if (s->authctxt->force_pwchange) { diff --git a/openssh-7.2p2-k5login_directory.patch b/openssh-7.2p2-k5login_directory.patch index 09369aa..600117f 100644 --- a/openssh-7.2p2-k5login_directory.patch +++ b/openssh-7.2p2-k5login_directory.patch @@ -2,10 +2,11 @@ diff --git a/auth-krb5.c b/auth-krb5.c index 2b02a04..19b9364 100644 --- a/auth-krb5.c +++ b/auth-krb5.c -@@ -375,6 +375,22 @@ cleanup: - return -1; +@@ -375,5 +375,21 @@ cleanup: + return (krb5_cc_resolve(ctx, ccname, ccache)); + } } - ++ +/* + * Reads k5login_directory option from the krb5.conf + */ @@ -21,10 +22,8 @@ index 2b02a04..19b9364 100644 + return profile_get_string(p, "libdefaults", "k5login_directory", NULL, NULL, + k5login_directory); +} -+ - krb5_error_code - ssh_krb5_get_cctemplate(krb5_context ctx, char **ccname) { - profile_t p; + #endif /* !HEIMDAL */ + #endif /* KRB5 */ diff --git a/auth.h b/auth.h index f9d191c..c432d2f 100644 --- a/auth.h diff --git a/openssh-7.5p1-gssapi-kex-with-ec.patch b/openssh-7.5p1-gssapi-kex-with-ec.patch deleted file mode 100644 index 5f2763a..0000000 --- a/openssh-7.5p1-gssapi-kex-with-ec.patch +++ /dev/null @@ -1,1377 +0,0 @@ -From 6ff8f667f792052fd47689c3e421fcd6ddca1cd0 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Fri, 25 Aug 2017 19:15:48 +0200 -Subject: [PATCH 1/3] GSSAPI Key exchange methods with DH and SHA2 - ---- - gss-genr.c | 10 ++++++++++ - kex.c | 2 ++ - kex.h | 2 ++ - kexgssc.c | 6 ++++++ - kexgsss.c | 6 ++++++ - monitor.c | 2 ++ - regress/kextype.sh | 4 +++- - regress/rekey.sh | 8 ++++++-- - ssh-gss.h | 2 ++ - ssh_config.5 | 4 +++- - sshconnect2.c | 2 ++ - sshd.c | 2 ++ - sshd_config.5 | 4 +++- - 13 files changed, 49 insertions(+), 5 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index dc63682d..c6eff3d7 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -183,6 +183,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; - break; -+ case KEX_GSS_GRP14_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; -+ break; -+ case KEX_GSS_GRP16_SHA512: -+ if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; -+ break; - case KEX_GSS_GEX_SHA1: - if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) - return GSS_C_NO_OID; -diff --git a/kex.c b/kex.c -index 63e028fa..e798fecb 100644 ---- a/kex.c -+++ b/kex.c -@@ -112,6 +112,8 @@ static const struct kexalg kexalgs[] = { - { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, -+ { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, - #endif - { NULL, -1, -1, -1}, - }; -diff --git a/kex.h b/kex.h -index 8a2b37c5..f27958ae 100644 ---- a/kex.h -+++ b/kex.h -@@ -102,6 +102,8 @@ enum kex_exchange { - #ifdef GSSAPI - KEX_GSS_GRP1_SHA1, - KEX_GSS_GRP14_SHA1, -+ KEX_GSS_GRP14_SHA256, -+ KEX_GSS_GRP16_SHA512, - KEX_GSS_GEX_SHA1, - #endif - KEX_MAX -diff --git a/kexgssc.c b/kexgssc.c -index 132df8b5..ed23f06d 100644 ---- a/kexgssc.c -+++ b/kexgssc.c -@@ -88,8 +88,12 @@ kexgss_client(struct ssh *ssh) { - dh = dh_new_group1(); - break; - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: - dh = dh_new_group14(); - break; -+ case KEX_GSS_GRP16_SHA512: -+ dh = dh_new_group16(); -+ break; - case KEX_GSS_GEX_SHA1: - debug("Doing group exchange\n"); - nbits = dh_estimate(ssh->kex->we_need * 8); -@@ -272,6 +276,8 @@ kexgss_client(struct ssh *ssh) { - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: -+ case KEX_GSS_GRP16_SHA512: - kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, - ssh->kex->server_version_string, - sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -diff --git a/kexgsss.c b/kexgsss.c -index 82a715cc..b7da8823 100644 ---- a/kexgsss.c -+++ b/kexgsss.c -@@ -104,8 +104,12 @@ kexgss_server(struct ssh *ssh) - dh = dh_new_group1(); - break; - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: - dh = dh_new_group14(); - break; -+ case KEX_GSS_GRP16_SHA512: -+ dh = dh_new_group16(); -+ break; - case KEX_GSS_GEX_SHA1: - debug("Doing group exchange"); - packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); -@@ -223,6 +227,8 @@ kexgss_server(struct ssh *ssh) - switch (ssh->kex->kex_type) { - case KEX_GSS_GRP1_SHA1: - case KEX_GSS_GRP14_SHA1: -+ case KEX_GSS_GRP14_SHA256: -+ case KEX_GSS_GRP16_SHA512: - kex_dh_hash(ssh->kex->hash_alg, - ssh->kex->client_version_string, ssh->kex->server_version_string, - sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -diff --git a/monitor.c b/monitor.c -index 17046936..d6bc7ac7 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -1648,6 +1648,8 @@ monitor_apply_keystate(struct monitor *pmonitor) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; - } - #endif -diff --git a/regress/kextype.sh b/regress/kextype.sh -index 780362ca..45f4f16d 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -14,7 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -- if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then -+ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -+ $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ -+ $k = "gss-group16-sha512-" ]; then - continue - fi - verbose "kex $k" -diff --git a/regress/rekey.sh b/regress/rekey.sh -index 9fbe9b38..a2921bef 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -38,7 +38,9 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -- if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then -+ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -+ $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ -+ $i = "gss-group16-sha512-" ]; then - continue - fi - opts="$opts KexAlgorithms=$i" -@@ -59,7 +61,9 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -- if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then -+ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -+ $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ -+ $kex = "gss-group16-sha512-" ]; then - continue - fi - verbose "client rekey $c $kex" -diff --git a/ssh-gss.h b/ssh-gss.h -index 6b6adb2b..7bf8d75e 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -70,6 +70,8 @@ - #define SSH2_MSG_KEXGSS_GROUP 41 - #define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" - #define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" -+#define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" -+#define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - - #define GSS_KEX_DEFAULT_KEX \ -diff --git a/ssh_config.5 b/ssh_config.5 -index 6b24649e..3d6da510 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -760,7 +760,9 @@ key exchange. Possible values are - .Bd -literal -offset 3n - gss-gex-sha1-, - gss-group1-sha1-, --gss-group14-sha1- -+gss-group14-sha1-, -+gss-group14-sha256-, -+gss-group16-sha512- - .Ed - .Pp - The default is -diff --git a/sshconnect2.c b/sshconnect2.c -index 8db98293..5d6b8be0 100644 ---- a/sshconnect2.c -+++ b/sshconnect2.c -@@ -253,6 +253,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; - } - #endif -diff --git a/sshd.c b/sshd.c -index 895df26f..e4c879a2 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -2244,6 +2244,8 @@ do_ssh2_kex(void) - if (options.gss_keyex) { - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; - kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; -+ kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; - } - #endif -diff --git a/sshd_config.5 b/sshd_config.5 -index bf81f6af..0793418b 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -675,7 +675,9 @@ key exchange. Possible values are - .Bd -literal -offset 3n - gss-gex-sha1-, - gss-group1-sha1-, --gss-group14-sha1- -+gss-group14-sha1-, -+gss-group14-sha256-, -+gss-group16-sha512- - .Ed - .Pp - The default is --- -2.13.5 - - -From 7d56144903fc625c33da7fabf103f4f6bba4d43a Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 29 Aug 2017 15:32:14 +0200 -Subject: [PATCH 2/3] GSSAPI Key exchange using ECDH and SHA2 - ---- - gss-genr.c | 10 ++ - kex.c | 3 + - kex.h | 4 + - kexgssc.c | 392 ++++++++++++++++++++++++++++++++++++++++++++++++++++- - kexgsss.c | 333 +++++++++++++++++++++++++++++++++++++++++++++ - monitor.c | 5 +- - regress/kextype.sh | 1 + - regress/rekey.sh | 2 + - ssh-gss.h | 2 + - ssh_config.5 | 4 +- - sshconnect2.c | 2 + - sshd.c | 2 + - sshd_config.5 | 4 +- - 13 files changed, 754 insertions(+), 10 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index c6eff3d7..22040244 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -198,6 +198,16 @@ ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - return GSS_C_NO_OID; - name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; - break; -+ case KEX_GSS_NISTP256_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; -+ break; - default: - return GSS_C_NO_OID; - } -diff --git a/kex.c b/kex.c -index e798fecb..bdeeada9 100644 ---- a/kex.c -+++ b/kex.c -@@ -114,6 +114,9 @@ static const struct kexalg kexalgs[] = { - { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, - { KEX_GSS_GRP14_SHA256_ID, KEX_GSS_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, - { KEX_GSS_GRP16_SHA512_ID, KEX_GSS_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, -+ { KEX_GSS_NISTP256_SHA256_ID, KEX_GSS_NISTP256_SHA256, -+ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, -+ { KEX_GSS_C25519_SHA256_ID, KEX_GSS_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - #endif - { NULL, -1, -1, -1}, - }; -diff --git a/kex.h b/kex.h -index f27958ae..7def8561 100644 ---- a/kex.h -+++ b/kex.h -@@ -105,6 +105,8 @@ enum kex_exchange { - KEX_GSS_GRP14_SHA256, - KEX_GSS_GRP16_SHA512, - KEX_GSS_GEX_SHA1, -+ KEX_GSS_NISTP256_SHA256, -+ KEX_GSS_C25519_SHA256, - #endif - KEX_MAX - }; -@@ -211,6 +213,8 @@ int kexecdh_server(struct ssh *); - int kexc25519_client(struct ssh *); - int kexc25519_server(struct ssh *); - #ifdef GSSAPI -+int kexecgss_client(struct ssh *); -+int kexecgss_server(struct ssh *); - int kexgss_client(struct ssh *); - int kexgss_server(struct ssh *); - #endif -diff --git a/kexgssc.c b/kexgssc.c -index ed23f06d..bdb3109a 100644 ---- a/kexgssc.c -+++ b/kexgssc.c -@@ -43,6 +43,7 @@ - #include "packet.h" - #include "dh.h" - #include "digest.h" -+#include "ssherr.h" - - #include "ssh-gss.h" - -@@ -52,7 +53,7 @@ kexgss_client(struct ssh *ssh) { - gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; - Gssctxt *ctxt; - OM_uint32 maj_status, min_status, ret_flags; -- u_int klen, kout, slen = 0, strlen; -+ u_int klen, kout, slen = 0, packet_len; - DH *dh; - BIGNUM *dh_server_pub = NULL; - BIGNUM *shared_secret = NULL; -@@ -201,20 +202,20 @@ kexgss_client(struct ssh *ssh) { - debug("Received GSSAPI_CONTINUE"); - if (maj_status == GSS_S_COMPLETE) - fatal("GSSAPI Continue received from server when complete"); -- recv_tok.value = packet_get_string(&strlen); -- recv_tok.length = strlen; -+ recv_tok.value = packet_get_string(&packet_len); -+ recv_tok.length = packet_len; - break; - case SSH2_MSG_KEXGSS_COMPLETE: - debug("Received GSSAPI_COMPLETE"); - packet_get_bignum2(dh_server_pub); -- msg_tok.value = packet_get_string(&strlen); -- msg_tok.length = strlen; -+ msg_tok.value = packet_get_string(&packet_len); -+ msg_tok.length = packet_len; - - /* Is there a token included? */ - if (packet_get_char()) { - recv_tok.value= -- packet_get_string(&strlen); -- recv_tok.length = strlen; -+ packet_get_string(&packet_len); -+ recv_tok.length = packet_len; - /* If we're already complete - protocol error */ - if (maj_status == GSS_S_COMPLETE) - packet_disconnect("Protocol error: received token when complete"); -@@ -344,4 +345,382 @@ kexgss_client(struct ssh *ssh) { - return kex_send_newkeys(ssh); - } - -+int -+kexecgss_client(struct ssh *ssh) { -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; -+ Gssctxt *ctxt; -+ OM_uint32 maj_status, min_status, ret_flags; -+ u_int klen = 0, slen = 0, packet_len; -+ u_char *server_pub = NULL; -+ u_int server_pub_len = 0; -+ BIGNUM *shared_secret = NULL; -+ u_char *kbuf = NULL; -+ u_char *serverhostkey = NULL; -+ u_char *empty = ""; -+ char *msg; -+ char *lang; -+ int type = 0; -+ int first = 1; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ const EC_GROUP *group = NULL; -+ const EC_POINT *public_key; -+ struct sshbuf *Q_C = NULL; -+ struct kex *kex = ssh->kex; -+ EC_POINT *server_public = NULL; -+ struct sshbuf *c25519_shared_secret = NULL; -+ int r; -+ -+ /* Initialise our GSSAPI world */ -+ ssh_gssapi_build_ctx(&ctxt); -+ if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type) -+ == GSS_C_NO_OID) -+ fatal("Couldn't identify host exchange"); -+ -+ if (ssh_gssapi_import_name(ctxt, kex->gss_host)) -+ fatal("Couldn't import hostname"); -+ -+ if (kex->gss_client && -+ ssh_gssapi_client_identity(ctxt, kex->gss_client)) -+ fatal("Couldn't acquire client credentials"); -+ -+ if ((Q_C = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((kex->ec_client_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (EC_KEY_generate_key(kex->ec_client_key) != 1) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ group = EC_KEY_get0_group(kex->ec_client_key); -+ public_key = EC_KEY_get0_public_key(kex->ec_client_key); -+#ifdef DEBUG_KEXECDH -+ fputs("client private key:\n", stderr); -+ sshkey_dump_ec_key(kex->ec_client_key); -+#endif -+ -+ sshbuf_put_ec(Q_C, public_key, group); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kexc25519_keygen(kex->c25519_client_key, kex->c25519_client_pubkey); -+#ifdef DEBUG_KEXECDH -+ dump_digest("client private key:", kex->c25519_client_key, -+ sizeof(kex->c25519_client_key)); -+#endif -+ -+ if ((r = sshbuf_put_string(Q_C, kex->c25519_client_pubkey, -+ sizeof(kex->c25519_client_pubkey))) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ token_ptr = GSS_C_NO_BUFFER; -+ -+ do { -+ /* Step 2 - call GSS_Init_sec_context() */ -+ debug("Calling gss_init_sec_context"); -+ -+ maj_status = ssh_gssapi_init_ctx(ctxt, -+ kex->gss_deleg_creds, token_ptr, &send_tok, -+ &ret_flags); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length != 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ fatal("gss_init_context failed"); -+ } -+ -+ /* If we've got an old receive buffer get rid of it */ -+ if (token_ptr != GSS_C_NO_BUFFER) -+ free(recv_tok.value); -+ -+ if (maj_status == GSS_S_COMPLETE) { -+ /* If mutual state flag is not true, kex fails */ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual authentication failed"); -+ -+ /* If integ avail flag is not true kex fails */ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity check failed"); -+ } -+ -+ /* -+ * If we have data to send, then the last message that we -+ * received cannot have been a 'complete'. -+ */ -+ if (send_tok.length != 0) { -+ if (first) { -+ const u_char * ptr; -+ size_t len; -+ -+ packet_start(SSH2_MSG_KEXGSS_INIT); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ sshbuf_get_string_direct(Q_C, &ptr, &len); -+ packet_put_string(ptr, len); -+ first = 0; -+ } else { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ -+ /* If we've sent them data, they should reply */ -+ do { -+ type = packet_read(); -+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { -+ debug("Received KEXGSS_HOSTKEY"); -+ if (serverhostkey) -+ fatal("Server host key received more than once"); -+ serverhostkey = -+ packet_get_string(&slen); -+ } -+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); -+ -+ switch (type) { -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ debug("Received GSSAPI_CONTINUE"); -+ if (maj_status == GSS_S_COMPLETE) -+ fatal("GSSAPI Continue received from server when complete"); -+ recv_tok.value = packet_get_string(&packet_len); -+ recv_tok.length = packet_len; -+ break; -+ case SSH2_MSG_KEXGSS_COMPLETE: -+ debug("Received GSSAPI_COMPLETE"); -+ server_pub = packet_get_string(&server_pub_len); -+ msg_tok.value = packet_get_string(&packet_len); -+ msg_tok.length = packet_len; -+ -+ /* Is there a token included? */ -+ if (packet_get_char()) { -+ recv_tok.value= -+ packet_get_string(&packet_len); -+ recv_tok.length = packet_len; -+ /* If we're already complete - protocol error */ -+ if (maj_status == GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: received token when complete"); -+ } else { -+ /* No token included */ -+ if (maj_status != GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: did not receive final token"); -+ } -+ break; -+ case SSH2_MSG_KEXGSS_ERROR: -+ debug("Received Error"); -+ maj_status = packet_get_int(); -+ min_status = packet_get_int(); -+ msg = packet_get_string(NULL); -+ lang = packet_get_string(NULL); -+ fatal("GSSAPI Error: \n%.400s",msg); -+ default: -+ packet_disconnect("Protocol error: didn't expect packet type %d", -+ type); -+ } -+ token_ptr = &recv_tok; -+ } else { -+ /* No data, and not complete */ -+ if (maj_status != GSS_S_COMPLETE) -+ fatal("Not complete, and no token output"); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ /* -+ * We _must_ have received a COMPLETE message in reply from the -+ * server, which will have set dh_server_pub and msg_tok -+ */ -+ -+ if (type != SSH2_MSG_KEXGSS_COMPLETE) -+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); -+ -+ /* 7. C verifies that the key Q_S is valid */ -+ /* 8. C computes shared secret */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (server_pub_len != 65) -+ fatal("The received NIST-P256 key did not match" -+ "expected length (expected 65, got %d)", server_pub_len); -+ -+ if (server_pub[0] != POINT_CONVERSION_UNCOMPRESSED) -+ fatal("The received NIST-P256 key does not have first octet 0x04"); -+ -+ if ((server_public = EC_POINT_new(group)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ if (!EC_POINT_oct2point(group, server_public, server_pub, -+ server_pub_len, NULL)) -+ fatal("Can not decode received NIST-P256 client key"); -+#ifdef DEBUG_KEXECDH -+ fputs("server public key:\n", stderr); -+ sshkey_dump_ec_point(group, server_public); -+#endif -+ -+ if (sshkey_ec_validate_public(group, server_public) != 0) { -+ sshpkt_disconnect(ssh, "invalid client public key"); -+ r = SSH_ERR_MESSAGE_INCOMPLETE; -+ goto out; -+ } -+ -+ if (!EC_POINT_is_on_curve(group, server_public, NULL)) -+ fatal("Received NIST-P256 client key is not on curve"); -+ -+ /* Calculate shared_secret */ -+ klen = (EC_GROUP_get_degree(group) + 7) / 8; -+ if ((kbuf = malloc(klen)) == NULL || -+ (shared_secret = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (ECDH_compute_key(kbuf, klen, server_public, -+ kex->ec_client_key, NULL) != (int)klen || -+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+#ifdef DEBUG_KEXECDH -+ dump_digest("shared secret", kbuf, klen); -+#endif -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (server_pub_len != 32) -+ fatal("The received curve25519 key did not match" -+ "expected length (expected 32, got %d)", server_pub_len); -+ -+ if (server_pub[server_pub_len-1] & 0x80) -+ fatal("The received key has MSB of last octet set!"); -+#ifdef DEBUG_KEXECDH -+ dump_digest("server public key:", server_pub, CURVE25519_SIZE); -+#endif -+ -+ /* generate shared secret */ -+ if ((c25519_shared_secret = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if ((r = kexc25519_shared_key(kex->c25519_client_key, -+ server_pub, c25519_shared_secret)) < 0) -+ goto out; -+ -+ /* if all octets of the shared secret are zero octets, -+ * is already checked in kexc25519_shared_key() */ -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ hashlen = sizeof(hash); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ kex_ecdh_hash( -+ kex->hash_alg, -+ group, -+ kex->client_version_string, -+ kex->server_version_string, -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ EC_KEY_get0_public_key(kex->ec_client_key), -+ server_public, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kex_c25519_hash( -+ kex->hash_alg, -+ kex->client_version_string, kex->server_version_string, -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ kex->c25519_client_pubkey, server_pub, -+ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ /* Verify that the hash matches the MIC we just got. */ -+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) -+ packet_disconnect("Hash's MIC didn't verify"); -+ -+ free(msg_tok.value); -+ -+ /* save session id */ -+ if (kex->session_id == NULL) { -+ kex->session_id_len = hashlen; -+ kex->session_id = xmalloc(kex->session_id_len); -+ memcpy(kex->session_id, hash, kex->session_id_len); -+ } -+ -+ if (kex->gss_deleg_creds) -+ ssh_gssapi_credentials_updated(ctxt); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ /* Finally derive the keys and send them */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) -+ goto out; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) -+ goto out; -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ r = kex_send_newkeys(ssh); -+out: -+ free(serverhostkey); -+ explicit_bzero(hash, sizeof(hash)); -+ sshbuf_free(Q_C); -+ if (server_pub) -+ free(server_pub); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (kex->ec_client_key) { -+ EC_KEY_free(kex->ec_client_key); -+ kex->ec_client_key = NULL; -+ } -+ if (server_public) -+ EC_POINT_clear_free(server_public); -+ if (kbuf) { -+ explicit_bzero(kbuf, klen); -+ free(kbuf); -+ } -+ if (shared_secret) -+ BN_clear_free(shared_secret); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ explicit_bzero(kex->c25519_client_key, sizeof(kex->c25519_client_key)); -+ sshbuf_free(c25519_shared_secret); -+ break; -+ } -+ return r; -+} - #endif /* GSSAPI */ -diff --git a/kexgsss.c b/kexgsss.c -index b7da8823..a7c42803 100644 ---- a/kexgsss.c -+++ b/kexgsss.c -@@ -46,6 +46,7 @@ - #include "servconf.h" - #include "ssh-gss.h" - #include "digest.h" -+#include "ssherr.h" - - extern ServerOptions options; - -@@ -303,4 +304,338 @@ kexgss_server(struct ssh *ssh) - ssh_gssapi_rekey_creds(); - return 0; - } -+ -+int -+kexecgss_server(struct ssh *ssh) -+{ -+ OM_uint32 maj_status, min_status; -+ -+ /* -+ * Some GSSAPI implementations use the input value of ret_flags (an -+ * output variable) as a means of triggering mechanism specific -+ * features. Initializing it to zero avoids inadvertently -+ * activating this non-standard behaviour. -+ */ -+ -+ OM_uint32 ret_flags = 0; -+ gss_buffer_desc gssbuf, recv_tok, msg_tok; -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ Gssctxt *ctxt = NULL; -+ u_int slen, klen = 0; -+ u_char *kbuf = NULL; -+ BIGNUM *shared_secret = NULL; -+ int type = 0; -+ gss_OID oid; -+ char *mechs; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ u_char *client_pub = NULL; -+ u_int client_pub_len = 0; -+ const EC_GROUP *group = NULL; -+ EC_POINT *client_public = NULL; -+ EC_KEY *server_key = NULL; -+ const EC_POINT *public_key; -+ u_char c25519_server_key[CURVE25519_SIZE]; -+ u_char c25519_server_pubkey[CURVE25519_SIZE]; -+ struct sshbuf *c25519_shared_secret = NULL; -+ struct sshbuf *Q_S; -+ struct kex *kex = ssh->kex; -+ int r; -+ -+ /* Initialise GSSAPI */ -+ -+ /* If we're rekeying, privsep means that some of the private structures -+ * in the GSSAPI code are no longer available. This kludges them back -+ * into life -+ */ -+ if (!ssh_gssapi_oid_table_ok()) -+ if ((mechs = ssh_gssapi_server_mechanisms())) -+ free(mechs); -+ -+ debug2("%s: Identifying %s", __func__, kex->name); -+ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); -+ if (oid == GSS_C_NO_OID) -+ fatal("Unknown gssapi mechanism"); -+ -+ debug2("%s: Acquiring credentials", __func__); -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) -+ fatal("Unable to acquire credentials for the server"); -+ -+ if ((Q_S = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ /* 5. S generates an ephemeral key pair (do the allocations early) */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((server_key = EC_KEY_new_by_curve_name(kex->ec_nid)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (EC_KEY_generate_key(server_key) != 1) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ group = EC_KEY_get0_group(server_key); -+ public_key = EC_KEY_get0_public_key(server_key); -+ -+ sshbuf_put_ec(Q_S, public_key, group); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kexc25519_keygen(c25519_server_key, c25519_server_pubkey); -+#ifdef DEBUG_KEXECDH -+ dump_digest("server private key:", c25519_server_key, -+ sizeof(c25519_server_key)); -+#endif -+ if ((r = sshbuf_put_string(Q_S, c25519_server_pubkey, -+ sizeof(c25519_server_pubkey))) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ do { -+ debug("Wait SSH2_MSG_GSSAPI_INIT"); -+ type = packet_read(); -+ switch(type) { -+ case SSH2_MSG_KEXGSS_INIT: -+ if (client_pub != NULL) -+ fatal("Received KEXGSS_INIT after initialising"); -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ -+ client_pub = packet_get_string(&client_pub_len); -+ -+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ -+ break; -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ break; -+ default: -+ packet_disconnect( -+ "Protocol error: didn't expect packet type %d", -+ type); -+ } -+ -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); -+ -+ free(recv_tok.value); -+ -+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) -+ fatal("Zero length token output when incomplete"); -+ -+ if (client_pub == NULL) -+ fatal("No client public key"); -+ -+ if (maj_status & GSS_S_CONTINUE_NEEDED) { -+ debug("Sending GSSAPI_CONTINUE"); -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length > 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ } -+ fatal("accept_ctx died"); -+ } -+ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual Authentication flag wasn't set"); -+ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity flag wasn't set"); -+ -+ /* 3. S verifies that the (client) key is valid */ -+ /* calculate shared secret */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (client_pub_len != 65) -+ fatal("The received NIST-P256 key did not match" -+ "expected length (expected 65, got %d)", client_pub_len); -+ -+ if (client_pub[0] != POINT_CONVERSION_UNCOMPRESSED) -+ fatal("The received NIST-P256 key does not have first octet 0x04"); -+ -+ if ((client_public = EC_POINT_new(group)) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ -+ if (!EC_POINT_oct2point(group, client_public, client_pub, -+ client_pub_len, NULL)) -+ fatal("Can not decode received NIST-P256 client key"); -+ -+ if (sshkey_ec_validate_public(group, client_public) != 0) { -+ sshpkt_disconnect(ssh, "invalid client public key"); -+ r = SSH_ERR_MESSAGE_INCOMPLETE; -+ goto out; -+ } -+ -+ if (!EC_POINT_is_on_curve(group, client_public, NULL)) -+ fatal("Received NIST-P256 client key is not on curve"); -+ -+ /* Calculate shared_secret */ -+ klen = (EC_GROUP_get_degree(group) + 7) / 8; -+ if ((kbuf = malloc(klen)) == NULL || -+ (shared_secret = BN_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if (ECDH_compute_key(kbuf, klen, client_public, -+ server_key, NULL) != (int)klen || -+ BN_bin2bn(kbuf, klen, shared_secret) == NULL) { -+ r = SSH_ERR_LIBCRYPTO_ERROR; -+ goto out; -+ } -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if (client_pub_len != 32) -+ fatal("The received curve25519 key did not match" -+ "expected length (expected 32, got %d)", client_pub_len); -+ -+ if (client_pub[client_pub_len-1] & 0x80) -+ fatal("The received key has MSB of last octet set!"); -+ -+ /* generate shared secret */ -+ if ((c25519_shared_secret = sshbuf_new()) == NULL) { -+ r = SSH_ERR_ALLOC_FAIL; -+ goto out; -+ } -+ if ((r = kexc25519_shared_key(c25519_server_key, -+ client_pub, c25519_shared_secret)) < 0) -+ goto out; -+ -+ /* if all octets of the shared secret are zero octets, -+ * is already checked in kexc25519_shared_key() */ -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ hashlen = sizeof(hash); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ kex_ecdh_hash( -+ kex->hash_alg, -+ group, -+ kex->client_version_string, -+ kex->server_version_string, -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ NULL, 0, -+ client_public, -+ EC_KEY_get0_public_key(server_key), -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ kex_c25519_hash( -+ kex->hash_alg, -+ kex->client_version_string, kex->server_version_string, -+ sshbuf_ptr(kex->peer), sshbuf_len(kex->peer), -+ sshbuf_ptr(kex->my), sshbuf_len(kex->my), -+ NULL, 0, -+ client_pub, c25519_server_pubkey, -+ sshbuf_ptr(c25519_shared_secret), sshbuf_len(c25519_shared_secret), -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ -+ if (kex->session_id == NULL) { -+ kex->session_id_len = hashlen; -+ kex->session_id = xmalloc(kex->session_id_len); -+ memcpy(kex->session_id, hash, kex->session_id_len); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) -+ fatal("Couldn't get MIC"); -+ -+ packet_start(SSH2_MSG_KEXGSS_COMPLETE); -+ { -+ const u_char *ptr; -+ size_t len; -+ if ((r = sshbuf_get_string_direct(Q_S, &ptr, &len)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ packet_put_string(ptr, len); -+ } -+ packet_put_string(msg_tok.value, msg_tok.length); -+ -+ if (send_tok.length != 0) { -+ packet_put_char(1); /* true */ -+ packet_put_string(send_tok.value, send_tok.length); -+ } else { -+ packet_put_char(0); /* false */ -+ } -+ packet_send(); -+ -+ gss_release_buffer(&min_status, &send_tok); -+ gss_release_buffer(&min_status, &msg_tok); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ /* Finally derive the keys and send them */ -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) != 0) -+ goto out; -+ break; -+ case KEX_GSS_C25519_SHA256: -+ if ((r = kex_derive_keys(ssh, hash, hashlen, c25519_shared_secret)) != 0) -+ goto out; -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); -+ } -+ if ((r = kex_send_newkeys(ssh)) != 0) -+ goto out; -+ -+ /* If this was a rekey, then save out any delegated credentials we -+ * just exchanged. */ -+ if (options.gss_store_rekey) -+ ssh_gssapi_rekey_creds(); -+out: -+ explicit_bzero(hash, sizeof(hash)); -+ if (Q_S) -+ sshbuf_free(Q_S); -+ if (client_pub) -+ free(client_pub); -+ switch (kex->kex_type) { -+ case KEX_GSS_NISTP256_SHA256: -+ if (server_key) -+ EC_KEY_free(server_key); -+ if (kbuf) { -+ explicit_bzero(kbuf, klen); -+ free(kbuf); -+ } -+ if (shared_secret) -+ BN_clear_free(shared_secret); -+ break; -+ case KEX_GSS_C25519_SHA256: -+ explicit_bzero(c25519_server_key, sizeof(c25519_server_key)); -+ sshbuf_free(c25519_shared_secret); -+ break; -+ } -+ return r; -+} - #endif /* GSSAPI */ -diff --git a/monitor.c b/monitor.c -index d6bc7ac7..b11616c8 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -1651,6 +1651,8 @@ monitor_apply_keystate(struct monitor *pmonitor) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; - } - #endif - kex->load_host_public_key=&get_hostkey_public_by_type; -@@ -1867,7 +1869,8 @@ mm_answer_gss_sign(int socket, Buffer *m) - - if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); -- if (data.length != 20) -+ /* Lengths of SHA-1, SHA-256 and SHA-512 hashes that are used */ -+ if (data.length != 20 && data.length != 32 && data.length != 64) - fatal("%s: data length incorrect: %d", __func__, - (int) data.length); - -diff --git a/regress/kextype.sh b/regress/kextype.sh -index 45f4f16d..d5b4a713 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -15,6 +15,7 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do - if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -+ $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ - $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ - $k = "gss-group16-sha512-" ]; then - continue -diff --git a/regress/rekey.sh b/regress/rekey.sh -index a2921bef..b118c6c8 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -39,6 +39,7 @@ increase_datafile_size 300 - opts="" - for i in `${SSH} -Q kex`; do - if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -+ $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ - $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ - $i = "gss-group16-sha512-" ]; then - continue -@@ -62,6 +63,7 @@ if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do - if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -+ $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ - $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ - $kex = "gss-group16-sha512-" ]; then - continue -diff --git a/ssh-gss.h b/ssh-gss.h -index 7bf8d75e..1f73721d 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -73,6 +73,8 @@ - #define KEX_GSS_GRP14_SHA256_ID "gss-group14-sha256-" - #define KEX_GSS_GRP16_SHA512_ID "gss-group16-sha512-" - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" -+#define KEX_GSS_NISTP256_SHA256_ID "gss-nistp256-sha256-" -+#define KEX_GSS_C25519_SHA256_ID "gss-curve25519-sha256-" - - #define GSS_KEX_DEFAULT_KEX \ - KEX_GSS_GEX_SHA1_ID "," \ -diff --git a/ssh_config.5 b/ssh_config.5 -index 3d6da510..1dc29bf1 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -762,7 +762,9 @@ gss-gex-sha1-, - gss-group1-sha1-, - gss-group14-sha1-, - gss-group14-sha256-, --gss-group16-sha512- -+gss-group16-sha512-, -+gss-nistp256-sha256-, -+gss-curve25519-sha256- - .Ed - .Pp - The default is -diff --git a/sshconnect2.c b/sshconnect2.c -index 5d6b8be0..280ae5a6 100644 ---- a/sshconnect2.c -+++ b/sshconnect2.c -@@ -256,6 +256,8 @@ ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_client; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_client; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_client; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_client; - } - #endif - kex->kex[KEX_C25519_SHA256] = kexc25519_client; -diff --git a/sshd.c b/sshd.c -index e4c879a2..a35735d8 100644 ---- a/sshd.c -+++ b/sshd.c -@@ -2247,6 +2247,8 @@ do_ssh2_kex(void) - kex->kex[KEX_GSS_GRP14_SHA256] = kexgss_server; - kex->kex[KEX_GSS_GRP16_SHA512] = kexgss_server; - kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_NISTP256_SHA256] = kexecgss_server; -+ kex->kex[KEX_GSS_C25519_SHA256] = kexecgss_server; - } - #endif - kex->server = 1; -diff --git a/sshd_config.5 b/sshd_config.5 -index 0793418b..888316bf 100644 ---- a/sshd_config.5 -+++ b/sshd_config.5 -@@ -677,7 +677,9 @@ gss-gex-sha1-, - gss-group1-sha1-, - gss-group14-sha1-, - gss-group14-sha256-, --gss-group16-sha512- -+gss-group16-sha512-, -+gss-nistp256-sha256-, -+gss-curve25519-sha256- - .Ed - .Pp - The default is --- -2.13.5 - - -From 0431695660d5eb1dd1169d42a1624c75a92aa5d2 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Wed, 30 Aug 2017 15:30:51 +0200 -Subject: [PATCH 3/3] Simplify rough edges of GSSAPI Kex - ---- - gss-genr.c | 53 +++++++++++++++++------------------------------------ - regress/kextype.sh | 10 ++++------ - regress/rekey.sh | 20 ++++++++------------ - 3 files changed, 29 insertions(+), 54 deletions(-) - -diff --git a/gss-genr.c b/gss-genr.c -index 22040244..c671be31 100644 ---- a/gss-genr.c -+++ b/gss-genr.c -@@ -171,47 +171,28 @@ ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, - gss_OID - ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { - int i = 0; -- -- switch (kex_type) { -- case KEX_GSS_GRP1_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; -- break; -- case KEX_GSS_GRP14_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; -- break; -- case KEX_GSS_GRP14_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP14_SHA256_ID) - 1; -- break; -- case KEX_GSS_GRP16_SHA512: -- if (strlen(name) < sizeof(KEX_GSS_GRP16_SHA512_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GRP16_SHA512_ID) - 1; -- break; -- case KEX_GSS_GEX_SHA1: -- if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; -- break; -- case KEX_GSS_NISTP256_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_NISTP256_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_NISTP256_SHA256_ID) - 1; -- break; -- case KEX_GSS_C25519_SHA256: -- if (strlen(name) < sizeof(KEX_GSS_C25519_SHA256_ID)) -- return GSS_C_NO_OID; -- name += sizeof(KEX_GSS_C25519_SHA256_ID) - 1; -+ -+#define SKIP_KEX_NAME(type) \ -+ case type: \ -+ if (strlen(name) < sizeof(type##_ID)) \ -+ return GSS_C_NO_OID; \ -+ name += sizeof(type##_ID) - 1; \ - break; -+ -+ switch (kex_type) { -+ SKIP_KEX_NAME(KEX_GSS_GRP1_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_GRP14_SHA256) -+ SKIP_KEX_NAME(KEX_GSS_GRP16_SHA512) -+ SKIP_KEX_NAME(KEX_GSS_GEX_SHA1) -+ SKIP_KEX_NAME(KEX_GSS_NISTP256_SHA256) -+ SKIP_KEX_NAME(KEX_GSS_C25519_SHA256) - default: - return GSS_C_NO_OID; - } - -+#undef SKIP_KEX_NAME -+ - while (gss_enc2oid[i].encoded != NULL && - strcmp(name, gss_enc2oid[i].encoded) != 0) - i++; -diff --git a/regress/kextype.sh b/regress/kextype.sh -index d5b4a713..6b4af28a 100644 ---- a/regress/kextype.sh -+++ b/regress/kextype.sh -@@ -14,12 +14,10 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/sshd_proxy - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -- if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o \ -- $k = "gss-nistp256-sha256-" -o $k = "gss-curve25519-sha256-" -o \ -- $k = "gss-group14-sha1-" -o $k = "gss-group14-sha256-" -o \ -- $k = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $k in -+ gss-* ) continue ;; -+ esac - verbose "kex $k" - for i in $tries; do - ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true -diff --git a/regress/rekey.sh b/regress/rekey.sh -index b118c6c8..d6a8742f 100644 ---- a/regress/rekey.sh -+++ b/regress/rekey.sh -@@ -38,12 +38,10 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -- if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o \ -- $i = "gss-nistp256-sha256-" -o $i = "gss-curve25519-sha256-" -o \ -- $i = "gss-group14-sha1-" -o $i = "gss-group14-sha256-" -o \ -- $i = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $i in -+ gss-* ) continue ;; -+ esac - opts="$opts KexAlgorithms=$i" - done - for i in `${SSH} -Q cipher`; do -@@ -62,12 +60,10 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -- if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o \ -- $kex = "gss-nistp256-sha256-" -o $kex = "gss-curve25519-sha256-" -o \ -- $kex = "gss-group14-sha1-" -o $kex = "gss-group14-sha256-" -o \ -- $kex = "gss-group16-sha512-" ]; then -- continue -- fi -+ # ignore GSSAPI key exchange mechanisms (all of them start with gss-) -+ case $kex in -+ gss-* ) continue ;; -+ esac - verbose "client rekey $c $kex" - ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c - done --- -2.13.5 - diff --git a/openssh-7.6p1-audit.patch b/openssh-7.6p1-audit.patch index feb170f..01e509e 100644 --- a/openssh-7.6p1-audit.patch +++ b/openssh-7.6p1-audit.patch @@ -1,83 +1,80 @@ -diff -up openssh-7.6p1/audit-bsm.c.audit openssh-7.6p1/audit-bsm.c ---- openssh-7.6p1/audit-bsm.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit-bsm.c 2017-10-04 17:18:32.834505048 +0200 -@@ -373,10 +373,23 @@ audit_connection_from(const char *host, +diff -up openssh/audit-bsm.c.audit openssh/audit-bsm.c +--- openssh/audit-bsm.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit-bsm.c 2019-04-03 17:02:20.713886041 +0200 +@@ -372,13 +372,26 @@ audit_connection_from(const char *host, #endif } --void +int - audit_run_command(const char *command) - { - /* not implemented */ -+ return 0; -+} -+ -+void -+audit_end_command(int handle, const char *command) ++audit_run_command(struct ssh *ssh, const char *command) +{ + /* not implemented */ ++ return 0; +} + -+void -+audit_count_session_open(void) -+{ -+ /* not necessary */ + void +-audit_run_command(const char *command) ++audit_end_command(struct ssh *ssh, int handle, const char *command) + { + /* not implemented */ } void -@@ -391,6 +404,12 @@ audit_session_close(struct logininfo *li ++audit_count_session_open(void) ++{ ++ /* not necessary */ ++} ++ ++void + audit_session_open(struct logininfo *li) + { + /* not implemented */ +@@ -390,6 +403,12 @@ audit_session_close(struct logininfo *li /* not implemented */ } +int -+audit_keyusage(int host_user, char *fp, int rv) ++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv) +{ + /* not implemented */ +} + void - audit_event(ssh_audit_event_t event) + audit_event(struct ssh *ssh, ssh_audit_event_t event) { -@@ -452,4 +471,34 @@ audit_event(ssh_audit_event_t event) +@@ -451,4 +470,28 @@ audit_event(struct ssh *ssh, ssh_audit_e debug("%s: unhandled event %d", __func__, event); } } + +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ + /* not implemented */ +} + +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, uid_t uid) ++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(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh * ssh, int ctos, pid_t pid, uid_t uid) +{ + /* not implemented */ +} + +void -+audit_destroy_sensitive_data(const char *fp) -+{ -+ /* not implemented */ -+} -+ -+void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + /* not implemented */ +} #endif /* BSM */ -diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c ---- openssh-7.6p1/audit.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit.c 2017-10-04 17:18:32.834505048 +0200 -@@ -34,6 +35,12 @@ +diff -up openssh/audit.c.audit openssh/audit.c +--- openssh/audit.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit.c 2019-04-03 17:02:20.713886041 +0200 +@@ -34,6 +34,12 @@ #include "log.h" #include "hostfile.h" #include "auth.h" @@ -119,38 +116,38 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c } +void -+audit_key(int host_user, int *rv, const struct sshkey *key) ++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(host_user, fp, (*rv == 0)) == 0) ++ if (audit_keyusage(ssh, host_user, fp, (*rv == 0)) == 0) + *rv = -SSH_ERR_INTERNAL_ERROR; + free(fp); +} + +void -+audit_unsupported(int what) ++audit_unsupported(struct ssh *ssh, int what) +{ -+ PRIVSEP(audit_unsupported_body(what)); ++ PRIVSEP(audit_unsupported_body(ssh, what)); +} + +void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs) +{ -+ PRIVSEP(audit_kex_body(ctos, enc, mac, comp, pfs, getpid(), getuid())); ++ PRIVSEP(audit_kex_body(ssh, ctos, enc, mac, comp, pfs, getpid(), getuid())); +} + +void -+audit_session_key_free(int ctos) ++audit_session_key_free(struct ssh *ssh, int ctos) +{ -+ PRIVSEP(audit_session_key_free_body(ctos, getpid(), getuid())); ++ PRIVSEP(audit_session_key_free_body(ssh, ctos, getpid(), getuid())); +} + # ifndef CUSTOM_SSH_AUDIT_EVENTS /* * Null implementations of audit functions. -@@ -138,6 +173,17 @@ audit_event(ssh_audit_event_t event) +@@ -138,6 +171,17 @@ audit_event(struct ssh *ssh, ssh_audit_e } /* @@ -168,7 +165,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c * Called when a user session is started. Argument is the tty allocated to * the session, or NULL if no tty was allocated. * -@@ -172,13 +218,82 @@ audit_session_close(struct logininfo *li +@@ -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 @@ -177,8 +174,9 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * audit_end_command. */ -void +-audit_run_command(const char *command) +int - audit_run_command(const char *command) ++audit_run_command(struct ssh *ssh, const char *command) { debug("audit run command euid %d user %s command '%.200s'", geteuid(), audit_username(), command); @@ -192,7 +190,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * the corresponding audit_run_command. + */ +void -+audit_end_command(int handle, const char *command) ++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); @@ -204,7 +202,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * Type is the key type, len is the key length(byte) and fp is the fingerprint of the key. + */ +int -+audit_keyusage(int host_user, char *fp, int rv) ++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(), @@ -215,7 +213,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called when the protocol negotiation fails. + */ +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ + debug("audit unsupported protocol euid %d type %d", geteuid(), what); +} @@ -224,7 +222,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on succesfull protocol negotiation. + */ +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, char *pfs, pid_t pid, ++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", @@ -236,7 +234,7 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on succesfull session key discard + */ +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++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); @@ -246,25 +244,25 @@ diff -up openssh-7.6p1/audit.c.audit openssh-7.6p1/audit.c + * This will be called on destroy private part of the server key + */ +void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++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-7.6p1/audit.h.audit openssh-7.6p1/audit.h ---- openssh-7.6p1/audit.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit.h 2017-10-04 17:18:32.834505048 +0200 +diff -up openssh/audit.h.audit openssh/audit.h +--- openssh/audit.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit.h 2019-04-03 17:02:20.713886041 +0200 @@ -26,6 +26,7 @@ # define _SSH_AUDIT_H #include "loginrec.h" +#include "sshkey.h" - enum ssh_audit_event_type { - SSH_LOGIN_EXCEED_MAXTRIES, -@@ -43,13 +44,32 @@ enum ssh_audit_event_type { + struct ssh; + +@@ -45,13 +46,32 @@ enum ssh_audit_event_type { SSH_CONNECTION_ABANDON, /* closed without completing auth */ SSH_AUDIT_UNKNOWN }; @@ -279,28 +277,28 @@ diff -up openssh-7.6p1/audit.h.audit openssh-7.6p1/audit.h +int listening_for_clients(void); + void audit_connection_from(const char *, int); - void audit_event(ssh_audit_event_t); + 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(const char *); -+void audit_end_command(int, 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(int, char *, int); -+void audit_key(int, int *, const struct sshkey *); -+void audit_unsupported(int); -+void audit_kex(int, char *, char *, char *, char *); -+void audit_unsupported_body(int); -+void audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void audit_session_key_free(int ctos); -+void audit_session_key_free_body(int ctos, pid_t, uid_t); -+void audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++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-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c ---- openssh-7.6p1/audit-linux.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/audit-linux.c 2017-10-04 17:18:32.835505053 +0200 +diff -up openssh/audit-linux.c.audit openssh/audit-linux.c +--- openssh/audit-linux.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/audit-linux.c 2019-04-03 17:02:20.713886041 +0200 @@ -33,27 +33,40 @@ #include "log.h" @@ -414,7 +412,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +int -+audit_keyusage(int host_user, char *fp, int rv) ++audit_keyusage(struct ssh *ssh, int host_user, char *fp, int rv) +{ + char buf[AUDIT_LOG_SIZE]; + int audit_fd, rc, saved_errno; @@ -429,12 +427,12 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + } + snprintf(buf, sizeof(buf), "%s_auth grantors=auth-key", host_user ? "pubkey" : "hostbased"); + rc = audit_log_acct_message(audit_fd, AUDIT_USER_AUTH, NULL, -+ buf, audit_username(), -1, NULL, ssh_remote_ipaddr(active_state), NULL, rv); ++ 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(active_state), NULL, rv); ++ ssh_remote_ipaddr(ssh), NULL, rv); +out: + saved_errno = errno; + audit_close(audit_fd); @@ -448,34 +446,34 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c /* Below is the sshd audit API code */ void -@@ -76,24 +177,55 @@ audit_connection_from(const char *host, +@@ -76,49 +176,210 @@ audit_connection_from(const char *host, /* not implemented */ } --void +int - audit_run_command(const char *command) - { -- /* not implemented */ ++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(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_LOGIN); + linux_audit_user_logxxx(the_authctxt->pw->pw_uid, NULL, -+ ssh_remote_ipaddr(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_START); + return 0; +} + -+void -+audit_end_command(int handle, const char *command) -+{ + 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(active_state), ++ 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(active_state), ++ ssh_remote_ipaddr(ssh), + "ssh", 1, AUDIT_USER_LOGOUT); +} + @@ -510,9 +508,8 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c } void -@@ -102,25 +231,155 @@ audit_event(ssh_audit_event_t event) - struct ssh *ssh = active_state; /* XXX */ - + audit_event(struct ssh *ssh, ssh_audit_event_t event) + { switch(event) { - case SSH_AUTH_SUCCESS: - case SSH_CONNECTION_CLOSE: @@ -563,7 +560,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c } + +void -+audit_unsupported_body(int what) ++audit_unsupported_body(struct ssh *ssh, int what) +{ +#ifdef AUDIT_CRYPTO_SESSION + char buf[AUDIT_LOG_SIZE]; @@ -572,15 +569,15 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + int audit_fd; + + snprintf(buf, sizeof(buf), "op=unsupported-%s direction=? cipher=? ksize=? rport=%d laddr=%s lport=%d ", -+ name[what], ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), -+ ssh_local_port(active_state)); ++ 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(active_state), NULL, 0); ++ buf, NULL, ssh_remote_ipaddr(ssh), NULL, 0); + audit_close(audit_fd); +#endif +} @@ -588,7 +585,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +const static char *direction[] = { "from-server", "from-client", "both" }; + +void -+audit_kex_body(int ctos, char *enc, char *mac, char *compress, ++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 @@ -600,7 +597,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + snprintf(buf, sizeof(buf), "op=start direction=%s cipher=%s ksize=%d mac=%s pfs=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", + direction[ctos], enc, cipher ? 8 * cipher->key_len : 0, mac, pfs, + (intmax_t)pid, (intmax_t)uid, -+ ssh_remote_port(active_state), (s = get_local_ipaddr(packet_get_connection_in())), ssh_local_port(active_state)); ++ 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) { @@ -611,7 +608,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + fatal("cannot open audit"); /* Must prevent login */ + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_SESSION, -+ buf, NULL, ssh_remote_ipaddr(active_state), NULL, 1); ++ 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))) @@ -620,7 +617,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++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; @@ -628,9 +625,9 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + + snprintf(buf, sizeof(buf), "op=destroy kind=session fp=? direction=%s spid=%jd suid=%jd rport=%d laddr=%s lport=%d ", + direction[ctos], (intmax_t)pid, (intmax_t)uid, -+ ssh_remote_port(active_state), -+ (s = get_local_ipaddr(packet_get_connection_in())), -+ ssh_local_port(active_state)); ++ 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) { @@ -640,7 +637,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + return; + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, -+ buf, NULL, ssh_remote_ipaddr(active_state), NULL, 1); ++ 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))) @@ -648,7 +645,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c +} + +void -+audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++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; @@ -664,7 +661,7 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + } + audit_ok = audit_log_user_message(audit_fd, AUDIT_CRYPTO_KEY_USER, + buf, NULL, -+ listening_for_clients() ? NULL : ssh_remote_ipaddr(active_state), ++ 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 */ @@ -672,10 +669,10 @@ diff -up openssh-7.6p1/audit-linux.c.audit openssh-7.6p1/audit-linux.c + error("cannot write into audit"); +} #endif /* USE_LINUX_AUDIT */ -diff -up openssh-7.6p1/auditstub.c.audit openssh-7.6p1/auditstub.c ---- openssh-7.6p1/auditstub.c.audit 2017-10-04 17:18:32.835505053 +0200 -+++ openssh-7.6p1/auditstub.c 2017-10-04 17:18:32.835505053 +0200 -@@ -0,0 +1,50 @@ +diff -up openssh/auditstub.c.audit openssh/auditstub.c +--- openssh/auditstub.c.audit 2019-04-03 17:02:20.714886050 +0200 ++++ openssh/auditstub.c 2019-04-03 17:02:20.714886050 +0200 +@@ -0,0 +1,52 @@ +/* $Id: auditstub.c,v 1.1 jfch Exp $ */ + +/* @@ -707,95 +704,97 @@ diff -up openssh-7.6p1/auditstub.c.audit openssh-7.6p1/auditstub.c + +#include + ++struct ssh; ++ +void -+audit_unsupported(int n) ++audit_unsupported(struct ssh *ssh, int n) +{ +} + +void -+audit_kex(int ctos, char *enc, char *mac, char *comp, char *pfs) ++audit_kex(struct ssh *ssh, int ctos, char *enc, char *mac, char *comp, char *pfs) +{ +} + +void -+audit_session_key_free(int ctos) ++audit_session_key_free(struct ssh *ssh, int ctos) +{ +} + +void -+audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid) +{ +} -diff -up openssh-7.6p1/auth2.c.audit openssh-7.6p1/auth2.c ---- openssh-7.6p1/auth2.c.audit 2017-10-04 17:18:32.746504598 +0200 -+++ openssh-7.6p1/auth2.c 2017-10-04 17:18:32.835505053 +0200 -@@ -255,9 +255,6 @@ input_userauth_request(int type, u_int32 +diff -up openssh/auth2.c.audit openssh/auth2.c +--- openssh/auth2.c.audit 2019-04-03 17:02:20.651885453 +0200 ++++ openssh/auth2.c 2019-04-03 17:02:20.714886050 +0200 +@@ -303,9 +303,6 @@ input_userauth_request(int type, u_int32 } else { /* Invalid user, fake password information */ authctxt->pw = fakepw(); -#ifdef SSH_AUDIT_EVENTS -- PRIVSEP(audit_event(SSH_INVALID_USER)); +- PRIVSEP(audit_event(ssh, SSH_INVALID_USER)); -#endif } #ifdef USE_PAM if (options.use_pam) -diff -up openssh-7.6p1/auth2-hostbased.c.audit openssh-7.6p1/auth2-hostbased.c ---- openssh-7.6p1/auth2-hostbased.c.audit 2017-10-04 17:18:32.683504276 +0200 -+++ openssh-7.6p1/auth2-hostbased.c 2017-10-04 17:18:32.835505053 +0200 -@@ -152,7 +152,7 @@ userauth_hostbased(struct ssh *ssh) - /* test for allowed key and correct signature */ +diff -up openssh/auth2-hostbased.c.audit openssh/auth2-hostbased.c +--- openssh/auth2-hostbased.c.audit 2019-04-03 17:02:20.612885083 +0200 ++++ openssh/auth2-hostbased.c 2019-04-03 17:02:20.714886050 +0200 +@@ -158,7 +158,7 @@ userauth_hostbased(struct ssh *ssh) authenticated = 0; - if (PRIVSEP(hostbased_key_allowed(authctxt->pw, cuser, chost, key)) && + if (PRIVSEP(hostbased_key_allowed(ssh, authctxt->pw, cuser, + chost, key)) && - PRIVSEP(sshkey_verify(key, sig, slen, -+ PRIVSEP(hostbased_key_verify(key, sig, slen, ++ PRIVSEP(hostbased_key_verify(ssh, key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), pkalg, ssh->compat)) == 0) authenticated = 1; -@@ -169,6 +169,19 @@ done: +@@ -175,6 +175,19 @@ done: return authenticated; } +int -+hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, -+ const u_char *data, size_t datalen, const char *pkalg, u_int compat) ++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) +{ + int rv; + + rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat); +#ifdef SSH_AUDIT_EVENTS -+ audit_key(0, &rv, key); ++ audit_key(ssh, 0, &rv, key); +#endif + return rv; +} + /* return 1 if given hostkey is allowed */ int - hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, -diff -up openssh-7.6p1/auth2-pubkey.c.audit openssh-7.6p1/auth2-pubkey.c ---- openssh-7.6p1/auth2-pubkey.c.audit 2017-10-04 17:18:32.828505018 +0200 -+++ openssh-7.6p1/auth2-pubkey.c 2017-10-04 17:18:32.835505053 +0200 -@@ -206,7 +206,7 @@ userauth_pubkey(struct ssh *ssh) + hostbased_key_allowed(struct ssh *ssh, struct passwd *pw, +diff -up openssh/auth2-pubkey.c.audit openssh/auth2-pubkey.c +--- openssh/auth2-pubkey.c.audit 2019-04-03 17:02:20.691885832 +0200 ++++ openssh/auth2-pubkey.c 2019-04-03 17:02:20.714886050 +0200 +@@ -219,7 +219,7 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && - PRIVSEP(sshkey_verify(key, sig, slen, -+ PRIVSEP(user_key_verify(key, sig, slen, ++ PRIVSEP(user_key_verify(ssh, key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), (ssh->compat & SSH_BUG_SIGTYPE) == 0 ? pkalg : NULL, ssh->compat)) == 0) { -@@ -250,6 +250,19 @@ done: +@@ -278,6 +278,19 @@ done: return authenticated; } +int -+user_key_verify(const struct sshkey *key, const u_char *sig, size_t slen, -+ const u_char *data, size_t datalen, const char *pkalg, u_int compat) ++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) +{ + int rv; + + rv = sshkey_verify(key, sig, slen, data, datalen, pkalg, compat); +#ifdef SSH_AUDIT_EVENTS -+ audit_key(1, &rv, key); ++ audit_key(ssh, 1, &rv, key); +#endif + return rv; +} @@ -803,52 +802,52 @@ diff -up openssh-7.6p1/auth2-pubkey.c.audit openssh-7.6p1/auth2-pubkey.c static int match_principals_option(const char *principal_list, struct sshkey_cert *cert) { -diff -up openssh-7.6p1/auth.c.audit openssh-7.6p1/auth.c ---- openssh-7.6p1/auth.c.audit 2017-10-04 17:18:32.746504598 +0200 -+++ openssh-7.6p1/auth.c 2017-10-04 17:18:32.835505053 +0200 -@@ -360,7 +360,7 @@ auth_log(Authctxt *authctxt, int authent +diff -up openssh/auth.c.audit openssh/auth.c +--- openssh/auth.c.audit 2019-04-03 17:02:20.691885832 +0200 ++++ openssh/auth.c 2019-04-03 17:02:20.714886050 +0200 +@@ -366,7 +366,7 @@ auth_log(struct ssh *ssh, int authentica # endif #endif #ifdef SSH_AUDIT_EVENTS - if (authenticated == 0 && !authctxt->postponed) + if (authenticated == 0 && !authctxt->postponed && !partial) - audit_event(audit_classify_auth(method)); + audit_event(ssh, audit_classify_auth(method)); #endif } -@@ -599,9 +599,6 @@ getpwnamallow(const char *user) - record_failed_login(user, +@@ -592,9 +592,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_INVALID_USER); +- audit_event(ssh, SSH_INVALID_USER); -#endif /* SSH_AUDIT_EVENTS */ return (NULL); } - if (!allowed_user(pw)) -diff -up openssh-7.6p1/auth.h.audit openssh-7.6p1/auth.h ---- openssh-7.6p1/auth.h.audit 2017-10-04 17:18:32.768504711 +0200 -+++ openssh-7.6p1/auth.h 2017-10-04 17:18:32.836505059 +0200 -@@ -198,6 +198,8 @@ struct passwd * getpwnamallow(const char + if (!allowed_user(ssh, pw)) +diff -up openssh/auth.h.audit openssh/auth.h +--- openssh/auth.h.audit 2019-04-03 17:02:20.692885842 +0200 ++++ openssh/auth.h 2019-04-03 17:02:20.714886050 +0200 +@@ -195,6 +195,8 @@ struct passwd * getpwnamallow(struct ssh char *expand_authorized_keys(const char *, struct passwd *pw); char *authorized_principals_file(struct passwd *); -+int user_key_verify(const struct sshkey *, const u_char *, size_t, ++int user_key_verify(struct ssh *, const struct sshkey *, const u_char *, size_t, + const u_char *, size_t, const char *, u_int); FILE *auth_openkeyfile(const char *, struct passwd *, int); FILE *auth_openprincipals(const char *, struct passwd *, int); -@@ -217,6 +218,8 @@ struct sshkey *get_hostkey_private_by_ty +@@ -214,6 +216,8 @@ struct sshkey *get_hostkey_private_by_ty int get_hostkey_index(struct sshkey *, int, struct ssh *); - int sshd_hostkey_sign(struct sshkey *, struct sshkey *, u_char **, - size_t *, const u_char *, size_t, const char *, u_int); -+int hostbased_key_verify(const struct sshkey *, const u_char *, size_t, + 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); /* Key / cert options linkage to auth layer */ const struct sshauthopt *auth_options(struct ssh *); -diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c ---- openssh-7.6p1/cipher.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/cipher.c 2017-10-04 17:18:32.836505059 +0200 +diff -up openssh/cipher.c.audit openssh/cipher.c +--- openssh/cipher.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/cipher.c 2019-04-03 17:02:20.714886050 +0200 @@ -61,25 +61,6 @@ struct sshcipher_ctx { const struct sshcipher *cipher; }; @@ -875,7 +874,7 @@ diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c static const struct sshcipher ciphers[] = { #ifdef WITH_OPENSSL #ifndef OPENSSL_NO_DES -@@ -409,7 +409,7 @@ cipher_get_length(struct sshcipher_ctx * +@@ -410,7 +391,7 @@ cipher_get_length(struct sshcipher_ctx * void cipher_free(struct sshcipher_ctx *cc) { @@ -884,9 +883,9 @@ diff -up openssh-7.6p1/cipher.c.audit openssh-7.6p1/cipher.c return; if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); -diff -up openssh-7.6p1/cipher.h.audit openssh-7.6p1/cipher.h ---- openssh-7.6p1/cipher.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/cipher.h 2017-10-04 17:18:32.836505059 +0200 +diff -up openssh/cipher.h.audit openssh/cipher.h +--- openssh/cipher.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/cipher.h 2019-04-03 17:02:20.714886050 +0200 @@ -45,7 +45,25 @@ #define CIPHER_ENCRYPT 1 #define CIPHER_DECRYPT 0 @@ -914,10 +913,10 @@ diff -up openssh-7.6p1/cipher.h.audit openssh-7.6p1/cipher.h struct sshcipher_ctx; const struct sshcipher *cipher_by_name(const char *); -diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c ---- openssh-7.6p1/kex.c.audit 2017-10-04 17:18:32.822504987 +0200 -+++ openssh-7.6p1/kex.c 2017-10-04 17:18:32.836505059 +0200 -@@ -54,6 +54,7 @@ +diff -up openssh/kex.c.audit openssh/kex.c +--- openssh/kex.c.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/kex.c 2019-04-03 17:02:20.715886060 +0200 +@@ -60,6 +60,7 @@ #include "ssherr.h" #include "sshbuf.h" #include "digest.h" @@ -925,64 +924,91 @@ diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c #ifdef GSSAPI #include "ssh-gss.h" -@@ -692,8 +693,12 @@ choose_enc(struct sshenc *enc, char *cli +@@ -758,12 +759,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_AUDIT_UNSUPPORTED_CIPHER); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_CIPHER); +#endif return SSH_ERR_NO_CIPHER_ALG_MATCH; + } if ((enc->cipher = cipher_by_name(name)) == NULL) { free(name); return SSH_ERR_INTERNAL_ERROR; -@@ -713,8 +718,12 @@ choose_mac(struct ssh *ssh, struct sshma +@@ -783,8 +788,12 @@ choose_mac(struct ssh *ssh, struct sshma { char *name = match_list(client, server, NULL); - if (name == NULL) + if (name == NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_unsupported(SSH_AUDIT_UNSUPPORTED_MAC); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_MAC); +#endif return SSH_ERR_NO_MAC_ALG_MATCH; + } if (mac_setup(mac, name) < 0) { free(name); return SSH_ERR_INTERNAL_ERROR; -@@ -733,8 +742,12 @@ choose_comp(struct sshcomp *comp, char * +@@ -796,12 +805,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_AUDIT_UNSUPPORTED_COMPRESSION); ++ audit_unsupported(ssh, SSH_AUDIT_UNSUPPORTED_COMPRESSION); +#endif return SSH_ERR_NO_COMPRESS_ALG_MATCH; + } if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { -@@ -904,6 +917,10 @@ kex_choose_conf(struct ssh *ssh) +@@ -933,7 +946,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; +@@ -948,7 +961,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; +@@ -971,6 +984,10 @@ kex_choose_conf(struct ssh *ssh) dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); + debug("kex: %s need=%d dh_need=%d", kex->name, need, dh_need); +#ifdef SSH_AUDIT_EVENTS -+ audit_kex(mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); ++ audit_kex(ssh, mode, newkeys->enc.name, newkeys->mac.name, newkeys->comp.name, kex->name); +#endif } /* XXX need runden? */ kex->we_need = need; -@@ -1037,3 +1054,33 @@ dump_digest(char *msg, u_char *digest, i - sshbuf_dump_data(digest, len, stderr); +@@ -1129,6 +1146,36 @@ dump_digest(const char *msg, const u_cha } #endif -+ + +static void +enc_destroy(struct sshenc *enc) +{ @@ -1012,22 +1038,26 @@ diff -up openssh-7.6p1/kex.c.audit openssh-7.6p1/kex.c + mac_destroy(&newkeys->mac); + memset(&newkeys->comp, 0, sizeof(newkeys->comp)); +} -diff -up openssh-7.6p1/kex.h.audit openssh-7.6p1/kex.h ---- openssh-7.6p1/kex.h.audit 2017-10-04 17:18:32.822504987 +0200 -+++ openssh-7.6p1/kex.h 2017-10-04 17:18:32.836505059 +0200 -@@ -219,6 +219,8 @@ int kexgss_client(struct ssh *); ++ + /* + * 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/kex.h.audit openssh/kex.h +--- openssh/kex.h.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/kex.h 2019-04-03 17:02:20.715886060 +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_hash(int, const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, - const BIGNUM *, const BIGNUM *, const BIGNUM *, u_char *, size_t *); -diff -up openssh-7.6p1/mac.c.audit openssh-7.6p1/mac.c ---- openssh-7.6p1/mac.c.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/mac.c 2017-10-04 17:18:32.836505059 +0200 -@@ -242,6 +242,20 @@ mac_clear(struct sshmac *mac) + int kex_dh_keypair(struct kex *); + int kex_dh_enc(struct kex *, const struct sshbuf *, struct sshbuf **, + struct sshbuf **); +diff -up openssh/mac.c.audit openssh/mac.c +--- openssh/mac.c.audit 2019-04-03 17:02:20.652885462 +0200 ++++ openssh/mac.c 2019-04-03 17:02:20.715886060 +0200 +@@ -243,6 +243,20 @@ mac_clear(struct sshmac *mac) mac->umac_ctx = NULL; } @@ -1048,9 +1078,9 @@ diff -up openssh-7.6p1/mac.c.audit openssh-7.6p1/mac.c /* XXX copied from ciphers_valid */ #define MAC_SEP "," int -diff -up openssh-7.6p1/mac.h.audit openssh-7.6p1/mac.h ---- openssh-7.6p1/mac.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/mac.h 2017-10-04 17:18:32.837505064 +0200 +diff -up openssh/mac.h.audit openssh/mac.h +--- openssh/mac.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/mac.h 2019-04-03 17:02:20.715886060 +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); @@ -1058,23 +1088,23 @@ diff -up openssh-7.6p1/mac.h.audit openssh-7.6p1/mac.h +void mac_destroy(struct sshmac *); #endif /* SSHMAC_H */ -diff -up openssh-7.6p1/Makefile.in.audit openssh-7.6p1/Makefile.in ---- openssh-7.6p1/Makefile.in.audit 2017-10-04 17:18:32.749504614 +0200 -+++ openssh-7.6p1/Makefile.in 2017-10-04 17:18:32.837505064 +0200 -@@ -100,7 +100,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ - kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ - kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ +diff -up openssh/Makefile.in.audit openssh/Makefile.in +--- openssh/Makefile.in.audit 2019-04-03 17:02:20.705885965 +0200 ++++ openssh/Makefile.in 2019-04-03 17:02:20.715886060 +0200 +@@ -109,7 +109,8 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kexgexc.o kexgexs.o \ + sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ + kexgssc.o \ - platform-pledge.o platform-tracing.o platform-misc.o + platform-pledge.o platform-tracing.o platform-misc.o \ + auditstub.o + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ - sshconnect.o sshconnect2.o mux.o -diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c ---- openssh-7.6p1/monitor.c.audit 2017-10-04 17:18:32.824504997 +0200 -+++ openssh-7.6p1/monitor.c 2017-10-04 17:18:32.837505064 +0200 -@@ -102,6 +102,7 @@ +diff -up openssh/monitor.c.audit openssh/monitor.c +--- openssh/monitor.c.audit 2019-04-03 17:02:20.674885671 +0200 ++++ openssh/monitor.c 2019-04-03 17:03:17.201421405 +0200 +@@ -93,6 +93,7 @@ #include "compat.h" #include "ssh2.h" #include "authfd.h" @@ -1082,28 +1112,28 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #include "match.h" #include "ssherr.h" -@@ -117,6 +118,8 @@ extern Buffer auth_debug; +@@ -107,6 +108,8 @@ extern u_char session_id[]; extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; /* XXX move to permanent ssh->authctxt? */ -+extern void destroy_sensitive_data(int); ++extern void destroy_sensitive_data(struct ssh *, int); + /* State exported from the child */ static struct sshbuf *child_state; -@@ -167,6 +170,11 @@ int mm_answer_gss_updatecreds(int, Buffe +@@ -157,6 +160,11 @@ int mm_answer_gss_updatecreds(struct ssh #ifdef SSH_AUDIT_EVENTS - int mm_answer_audit_event(int, struct sshbuf *); - int mm_answer_audit_command(int, struct sshbuf *); -+int mm_answer_audit_end_command(int, struct sshbuf *); -+int mm_answer_audit_unsupported_body(int, struct sshbuf *); -+int mm_answer_audit_kex_body(int, struct sshbuf *); -+int mm_answer_audit_session_key_free_body(int, struct sshbuf *); -+int mm_answer_audit_server_key_free(int, struct sshbuf *); + 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 int monitor_read_log(struct monitor *); -@@ -222,6 +230,10 @@ struct mon_table mon_dispatch_proto20[] + 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}, @@ -1114,7 +1144,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, -@@ -260,6 +272,11 @@ struct mon_table mon_dispatch_postauth20 +@@ -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}, @@ -1126,7 +1156,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c #endif {0, 0, NULL} }; -@@ -1396,8 +1413,10 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1445,8 +1462,10 @@ mm_answer_keyverify(struct ssh *ssh, int char *sigalg; size_t signaturelen, datalen, bloblen; int r, ret, valid_data = 0, encoded_ret; @@ -1138,7 +1168,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c (r = sshbuf_get_string(m, &signature, &signaturelen)) != 0 || (r = sshbuf_get_string(m, &data, &datalen)) != 0 || (r = sshbuf_get_cstring(m, &sigalg, NULL)) != 0) -@@ -1405,6 +1424,8 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1455,6 +1474,8 @@ mm_answer_keyverify(struct ssh *ssh, int if (hostbased_cuser == NULL || hostbased_chost == NULL || !monitor_allowed_key(blob, bloblen)) fatal("%s: bad key, not previously allowed", __func__); @@ -1147,18 +1177,18 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c /* Empty signature algorithm means NULL. */ if (*sigalg == '\0') { -@@ -1414,21 +1435,24 @@ mm_answer_keyverify(int sock, struct ssh +@@ -1470,21 +1491,24 @@ mm_answer_keyverify(struct ssh *ssh, int case MM_USERKEY: valid_data = monitor_valid_userblob(data, datalen); auth_method = "publickey"; -+ ret = user_key_verify(key, signature, signaturelen, data, -+ datalen, sigalg, active_state->compat); ++ ret = user_key_verify(ssh, key, signature, signaturelen, data, ++ datalen, sigalg, ssh->compat); break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); -+ ret = hostbased_key_verify(key, signature, signaturelen, data, -+ datalen, sigalg, active_state->compat); ++ ret = hostbased_key_verify(ssh, key, signature, signaturelen, data, ++ datalen, sigalg, ssh->compat); auth_method = "hostbased"; break; default: @@ -1170,33 +1200,59 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c fatal("%s: bad signature data blob", __func__); - ret = sshkey_verify(key, signature, signaturelen, data, datalen, -- sigalg, active_state->compat); +- sigalg, ssh->compat); debug3("%s: %s %p signature %s", __func__, auth_method, key, (ret == 0) ? "verified" : "unverified"); auth2_record_key(authctxt, ret == 0, key); -@@ -1485,6 +1509,12 @@ mm_session_close(Session *s) +@@ -1536,13 +1560,19 @@ mm_record_login(struct ssh *ssh, Session + } + + static void +-mm_session_close(Session *s) ++mm_session_close(struct ssh *ssh, Session *s) + { + debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid); + if (s->ttyfd != -1) { debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); session_pty_cleanup2(s); } +#ifdef SSH_AUDIT_EVENTS + if (s->command != NULL) { + debug3("%s: command %d", __func__, s->command_handle); -+ session_end_command2(s); ++ session_end_command2(ssh, s); + } +#endif session_unused(s->self); } -@@ -1588,6 +1618,8 @@ mm_answer_term(int sock, Buffer *req) +@@ -1609,7 +1639,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("%s: buffer error: %s", __func__, ssh_err(r)); + mm_request_send(sock, MONITOR_ANS_PTY, m); +@@ -1628,7 +1658,7 @@ mm_answer_pty_cleanup(struct ssh *ssh, i + if ((r = sshbuf_get_cstring(m, &tty, NULL)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if ((s = session_by_tty(tty)) != NULL) +- mm_session_close(s); ++ mm_session_close(ssh, s); + sshbuf_reset(m); + free(tty); + return (0); +@@ -1650,6 +1680,8 @@ mm_answer_term(struct ssh *ssh, int sock sshpam_cleanup(); #endif -+ destroy_sensitive_data(0); ++ destroy_sensitive_data(ssh, 0); + while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); -@@ -1630,12 +1662,47 @@ mm_answer_audit_command(int socket, Buff +@@ -1696,12 +1728,47 @@ mm_answer_audit_command(struct ssh *ssh, { char *cmd; int r; @@ -1213,7 +1269,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: error allocating a session", __func__); + s->command = cmd; +#ifdef SSH_AUDIT_EVENTS -+ s->command_handle = audit_run_command(cmd); ++ s->command_handle = audit_run_command(ssh, cmd); +#endif + + sshbuf_reset(m); @@ -1225,7 +1281,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_end_command(int socket, struct sshbuf *m) ++mm_answer_audit_end_command(struct ssh *ssh, int socket, struct sshbuf *m) +{ + int handle, r; + size_t len; @@ -1241,19 +1297,19 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + if (s == NULL || s->ttyfd != -1 || s->command == NULL || + strcmp(s->command, cmd) != 0) + fatal("%s: invalid handle", __func__); -+ mm_session_close(s); ++ mm_session_close(ssh, s); free(cmd); return (0); } -@@ -1702,6 +1768,7 @@ monitor_apply_keystate(struct monitor *p +@@ -1767,6 +1834,7 @@ monitor_apply_keystate(struct ssh *ssh, void - mm_get_keystate(struct monitor *pmonitor) + mm_get_keystate(struct ssh *ssh, struct monitor *pmonitor) { + struct sshbuf *m; debug3("%s: Waiting for new keys", __func__); if ((child_state = sshbuf_new()) == NULL) -@@ -1709,6 +1776,19 @@ mm_get_keystate(struct monitor *pmonitor +@@ -1774,6 +1842,19 @@ mm_get_keystate(struct ssh *ssh, struct mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, child_state); debug3("%s: GOT new keys", __func__); @@ -1262,7 +1318,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + m = sshbuf_new(); + mm_request_receive_expect(pmonitor->m_sendfd, + MONITOR_REQ_AUDIT_SESSION_KEY_FREE, m); -+ mm_answer_audit_session_key_free_body(pmonitor->m_sendfd, m); ++ mm_answer_audit_session_key_free_body(ssh, pmonitor->m_sendfd, m); + sshbuf_free(m); +#endif + @@ -1273,20 +1329,20 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c } -@@ -1976,3 +2056,102 @@ mm_answer_gss_updatecreds(int socket, Bu +@@ -2066,3 +2147,102 @@ mm_answer_gss_updatecreds(struct ssh *ss #endif /* GSSAPI */ +#ifdef SSH_AUDIT_EVENTS +int -+mm_answer_audit_unsupported_body(int sock, struct sshbuf *m) ++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("%s: buffer error: %s", __func__, ssh_err(r)); + -+ audit_unsupported_body(what); ++ audit_unsupported_body(ssh, what); + + sshbuf_reset(m); + @@ -1295,7 +1351,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_kex_body(int sock, struct sshbuf *m) ++mm_answer_audit_kex_body(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int ctos, r; + char *cipher, *mac, *compress, *pfs; @@ -1315,7 +1371,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (pid_t) tmp; + -+ audit_kex_body(ctos, cipher, mac, compress, pfs, pid, uid); ++ audit_kex_body(ssh, ctos, cipher, mac, compress, pfs, pid, uid); + + free(cipher); + free(mac); @@ -1328,7 +1384,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_session_key_free_body(int sock, struct sshbuf *m) ++mm_answer_audit_session_key_free_body(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int ctos, r; + u_int64_t tmp; @@ -1343,7 +1399,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (uid_t) tmp; + -+ audit_session_key_free_body(ctos, pid, uid); ++ audit_session_key_free_body(ssh, ctos, pid, uid); + + sshbuf_reset(m); + @@ -1352,7 +1408,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c +} + +int -+mm_answer_audit_server_key_free(int sock, struct sshbuf *m) ++mm_answer_audit_server_key_free(struct ssh *ssh, int sock, struct sshbuf *m) +{ + size_t len, r; + char *fp; @@ -1368,7 +1424,7 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + uid = (uid_t) tmp; + -+ audit_destroy_sensitive_data(fp, pid, uid); ++ audit_destroy_sensitive_data(ssh, fp, pid, uid); + + free(fp); + sshbuf_reset(m); @@ -1376,10 +1432,10 @@ diff -up openssh-7.6p1/monitor.c.audit openssh-7.6p1/monitor.c + return 0; +} +#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.6p1/monitor.h.audit openssh-7.6p1/monitor.h ---- openssh-7.6p1/monitor.h.audit 2017-10-04 17:18:32.781504777 +0200 -+++ openssh-7.6p1/monitor.h 2017-10-04 17:18:32.837505064 +0200 -@@ -69,7 +69,13 @@ enum monitor_reqtype { +diff -up openssh/monitor.h.audit openssh/monitor.h +--- openssh/monitor.h.audit 2019-04-03 17:02:20.674885671 +0200 ++++ openssh/monitor.h 2019-04-03 17:02:20.715886060 +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, @@ -1390,14 +1446,14 @@ diff -up openssh-7.6p1/monitor.h.audit openssh-7.6p1/monitor.h + MONITOR_REQ_AUDIT_UNSUPPORTED = 118, MONITOR_ANS_AUDIT_UNSUPPORTED = 119, + MONITOR_REQ_AUDIT_KEX = 120, MONITOR_ANS_AUDIT_KEX = 121, + MONITOR_REQ_AUDIT_SESSION_KEY_FREE = 122, MONITOR_ANS_AUDIT_SESSION_KEY_FREE = 123, -+ MONITOR_REQ_AUDIT_SERVER_KEY_FREE = 124 - - }; - -diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c ---- openssh-7.6p1/monitor_wrap.c.audit 2017-10-04 17:18:32.750504619 +0200 -+++ openssh-7.6p1/monitor_wrap.c 2017-10-04 17:18:32.838505069 +0200 -@@ -463,7 +463,7 @@ mm_key_allowed(enum mm_keytype type, con ++ 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/monitor_wrap.c.audit openssh/monitor_wrap.c +--- openssh/monitor_wrap.c.audit 2019-04-03 17:02:20.653885472 +0200 ++++ openssh/monitor_wrap.c 2019-04-03 17:02:20.716886069 +0200 +@@ -513,7 +513,7 @@ mm_key_allowed(enum mm_keytype type, con */ int @@ -1406,7 +1462,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c const u_char *data, size_t datalen, const char *sigalg, u_int compat) { struct sshbuf *m; -@@ -478,7 +478,8 @@ mm_sshkey_verify(const struct sshkey *ke +@@ -525,7 +525,8 @@ mm_sshkey_verify(const struct sshkey *ke if ((m = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); @@ -1416,34 +1472,35 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c (r = sshbuf_put_string(m, sig, siglen)) != 0 || (r = sshbuf_put_string(m, data, datalen)) != 0 || (r = sshbuf_put_cstring(m, sigalg == NULL ? "" : sigalg)) != 0) -@@ -497,6 +498,20 @@ mm_sshkey_verify(const struct sshkey *ke +@@ -547,6 +548,20 @@ mm_sshkey_verify(const struct sshkey *ke return 0; } +int -+mm_hostbased_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++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) +{ + return mm_sshkey_verify(MM_HOSTKEY, key, sig, siglen, data, datalen, pkalg, compat); +} + +int -+mm_user_key_verify(const struct sshkey *key, const u_char *sig, size_t siglen, ++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) +{ + return mm_sshkey_verify(MM_USERKEY, key, sig, siglen, data, datalen, pkalg, compat); +} + void - mm_send_keystate(struct monitor *monitor) + mm_send_keystate(struct ssh *ssh, struct monitor *monitor) { -@@ -874,11 +889,12 @@ mm_audit_event(ssh_audit_event_t event) +@@ -900,11 +915,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(const char *command) ++mm_audit_run_command(struct ssh *ssh, const char *command) { struct sshbuf *m; int r; @@ -1451,7 +1508,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c debug3("%s entering command %s", __func__, command); -@@ -885,6 +901,30 @@ mm_audit_run_command(const char *command +@@ -914,6 +930,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); @@ -1465,7 +1522,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_end_command(int handle, const char *command) ++mm_audit_end_command(struct ssh *ssh, int handle, const char *command) +{ + int r; + struct sshbuf *m; @@ -1482,13 +1539,13 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c sshbuf_free(m); } #endif /* SSH_AUDIT_EVENTS */ -@@ -1020,3 +1056,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc - return (ok); +@@ -1074,3 +1114,83 @@ mm_ssh_gssapi_update_creds(ssh_gssapi_cc } + #endif /* GSSAPI */ +#ifdef SSH_AUDIT_EVENTS +void -+mm_audit_unsupported_body(int what) ++mm_audit_unsupported_body(struct ssh *ssh, int what) +{ + int r; + struct sshbuf *m; @@ -1506,7 +1563,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_kex_body(int ctos, char *cipher, char *mac, char *compress, char *fps, pid_t pid, ++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; @@ -1531,7 +1588,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_session_key_free_body(int ctos, pid_t pid, uid_t uid) ++mm_audit_session_key_free_body(struct ssh *ssh, int ctos, pid_t pid, uid_t uid) +{ + int r; + struct sshbuf *m; @@ -1550,7 +1607,7 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c +} + +void -+mm_audit_destroy_sensitive_data(const char *fp, pid_t pid, uid_t uid) ++mm_audit_destroy_sensitive_data(struct ssh *ssh, const char *fp, pid_t pid, uid_t uid) +{ + int r; + struct sshbuf *m; @@ -1566,38 +1623,38 @@ diff -up openssh-7.6p1/monitor_wrap.c.audit openssh-7.6p1/monitor_wrap.c + sshbuf_free(m); +} +#endif /* SSH_AUDIT_EVENTS */ -diff -up openssh-7.6p1/monitor_wrap.h.audit openssh-7.6p1/monitor_wrap.h ---- openssh-7.6p1/monitor_wrap.h.audit 2017-10-04 17:18:32.750504619 +0200 -+++ openssh-7.6p1/monitor_wrap.h 2017-10-04 17:18:32.838505069 +0200 -@@ -53,7 +53,9 @@ int mm_key_allowed(enum mm_keytype, cons +diff -up openssh/monitor_wrap.h.audit openssh/monitor_wrap.h +--- openssh/monitor_wrap.h.audit 2019-04-03 17:02:20.653885472 +0200 ++++ openssh/monitor_wrap.h 2019-04-03 17:02:20.716886069 +0200 +@@ -57,7 +57,9 @@ int mm_user_key_allowed(struct ssh *, st struct sshauthopt **); - int mm_hostbased_key_allowed(struct passwd *, const char *, + 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(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); -+int mm_user_key_verify(const struct sshkey *, const u_char *, size_t, ++int mm_user_key_verify(struct ssh*, const struct sshkey *, const u_char *, size_t, const u_char *, size_t, const char *, u_int); #ifdef GSSAPI -@@ -78,7 +80,12 @@ void mm_sshpam_free_ctx(void *); +@@ -82,7 +84,12 @@ void mm_sshpam_free_ctx(void *); #ifdef SSH_AUDIT_EVENTS #include "audit.h" - void mm_audit_event(ssh_audit_event_t); + void mm_audit_event(struct ssh *, ssh_audit_event_t); -void mm_audit_run_command(const char *); -+int mm_audit_run_command(const char *); -+void mm_audit_end_command(int, const char *); -+void mm_audit_unsupported_body(int); -+void mm_audit_kex_body(int, char *, char *, char *, char *, pid_t, uid_t); -+void mm_audit_session_key_free_body(int, pid_t, uid_t); -+void mm_audit_destroy_sensitive_data(const char *, pid_t, uid_t); ++int mm_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-7.6p1/packet.c.audit openssh-7.6p1/packet.c ---- openssh-7.6p1/packet.c.audit 2017-10-04 17:18:32.672504220 +0200 -+++ openssh-7.6p1/packet.c 2017-10-04 17:25:48.141741390 +0200 -@@ -67,6 +67,7 @@ +diff -up openssh/packet.c.audit openssh/packet.c +--- openssh/packet.c.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/packet.c 2019-04-03 17:02:20.716886069 +0200 +@@ -77,6 +77,7 @@ #include #include "xmalloc.h" @@ -1605,7 +1662,7 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c #include "crc32.h" #include "compat.h" #include "ssh2.h" -@@ -502,6 +503,13 @@ ssh_packet_get_connection_out(struct ssh +@@ -510,6 +511,13 @@ ssh_packet_get_connection_out(struct ssh return ssh->state->connection_out; } @@ -1619,7 +1676,7 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. -@@ -566,22 +574,19 @@ ssh_packet_close_internal(struct ssh *ss +@@ -587,22 +595,19 @@ ssh_packet_close_internal(struct ssh *ss { struct session_state *state = ssh->state; u_int mode; @@ -1642,18 +1699,18 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c + state->output = NULL; sshbuf_free(state->outgoing_packet); + state->outgoing_packet = NULL; - sshbuf_free(state->incoming_packet); + sshbuf_free(state->incoming_packet); + state->incoming_packet = NULL; for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(state->newkeys[mode]); /* current keys */ state->newkeys[mode] = NULL; -@@ -615,8 +616,18 @@ ssh_packet_close_internal(struct ssh *ss +@@ -636,8 +641,18 @@ ssh_packet_close_internal(struct ssh *ss } cipher_free(state->send_context); cipher_free(state->receive_context); + if (had_keys && state->server_side) { + /* Assuming this is called only from privsep child */ -+ audit_session_key_free(MODE_MAX); ++ audit_session_key_free(ssh, MODE_MAX); + } state->send_context = state->receive_context = NULL; if (do_close) { @@ -1666,15 +1723,15 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c free(ssh->local_ipaddr); ssh->local_ipaddr = NULL; free(ssh->remote_ipaddr); -@@ -854,6 +863,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod - (unsigned long long)state->p_read.blocks, +@@ -864,6 +879,7 @@ ssh_set_newkeys(struct ssh *ssh, int mod (unsigned long long)state->p_send.bytes, (unsigned long long)state->p_send.blocks); -+ audit_session_key_free(mode); - cipher_free(*ccp); - *ccp = NULL; kex_free_newkeys(state->newkeys[mode]); -@@ -2135,6 +2145,72 @@ ssh_packet_get_output(struct ssh *ssh) ++ audit_session_key_free(ssh, mode); + state->newkeys[mode] = NULL; + } + /* note that both bytes and the seqnr are not reset */ +@@ -2167,6 +2183,71 @@ ssh_packet_get_output(struct ssh *ssh) return (void *)ssh->state->output; } @@ -1728,18 +1785,17 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c +} + +void -+packet_destroy_all(int audit_it, int privsep) ++packet_destroy_all(struct ssh *ssh, int audit_it, int privsep) +{ + if (audit_it) -+ audit_it = (active_state != NULL && packet_state_has_keys(active_state->state)); -+ if (active_state != NULL) -+ packet_destroy_state(active_state->state); ++ 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(MODE_MAX); ++ audit_session_key_free(ssh, MODE_MAX); + else -+ audit_session_key_free_body(MODE_MAX, getpid(), getuid()); ++ audit_session_key_free_body(ssh, MODE_MAX, getpid(), getuid()); +#endif + } +} @@ -1747,28 +1803,28 @@ diff -up openssh-7.6p1/packet.c.audit openssh-7.6p1/packet.c /* Reset after_authentication and reset compression in post-auth privsep */ static int ssh_packet_set_postauth(struct ssh *ssh) -diff -up openssh-7.6p1/packet.h.audit openssh-7.6p1/packet.h ---- openssh-7.6p1/packet.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/packet.h 2017-10-04 17:18:32.838505069 +0200 -@@ -217,4 +217,5 @@ extern struct ssh *active_state; +diff -up openssh/packet.h.audit openssh/packet.h +--- openssh/packet.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/packet.h 2019-04-03 17:02:20.716886069 +0200 +@@ -217,4 +217,5 @@ const u_char *sshpkt_ptr(struct ssh *, s # undef EC_POINT #endif -+void packet_destroy_all(int, int); ++void packet_destroy_all(struct ssh *, int, int); #endif /* PACKET_H */ -diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c ---- openssh-7.6p1/session.c.audit 2017-10-04 17:18:32.812504936 +0200 -+++ openssh-7.6p1/session.c 2017-10-04 17:18:32.839505074 +0200 -@@ -138,7 +138,7 @@ extern char *__progname; +diff -up openssh/session.c.audit openssh/session.c +--- openssh/session.c.audit 2019-04-03 17:02:20.712886031 +0200 ++++ openssh/session.c 2019-04-03 17:02:20.716886069 +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(int); ++extern void destroy_sensitive_data(struct ssh *, int); extern struct sshbuf *loginmsg; extern struct sshauthopt *auth_opts; - char *tun_fwd_ifnames; /* serverloop.c */ -@@ -605,6 +605,14 @@ do_exec_pty(struct ssh *ssh, Session *s, + extern char *tun_fwd_ifnames; /* serverloop.c */ +@@ -648,6 +648,14 @@ do_exec_pty(struct ssh *ssh, Session *s, /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); @@ -1782,8 +1838,8 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c + /* Enter interactive session. */ s->ptymaster = ptymaster; - packet_set_interactive(1, -@@ -724,15 +732,19 @@ do_exec(struct ssh *ssh, Session *s, con + ssh_packet_set_interactive(ssh, 1, +@@ -740,15 +748,19 @@ do_exec(struct ssh *ssh, Session *s, con s->self); #ifdef SSH_AUDIT_EVENTS @@ -1801,24 +1857,24 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c + s->command = xstrdup(shell); } + if (s->command != NULL && s->ptyfd == -1) -+ s->command_handle = PRIVSEP(audit_run_command(s->command)); ++ s->command_handle = PRIVSEP(audit_run_command(ssh, s->command)); #endif if (s->ttyfd != -1) ret = do_exec_pty(ssh, s, command); -@@ -1499,8 +1511,11 @@ do_child(struct ssh *ssh, Session *s, co - } +@@ -1556,8 +1568,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(1); - packet_clear_keys(); ++ 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(0, 1); ++ packet_destroy_all(ssh, 0, 1); /* Force a password change */ if (s->authctxt->force_pwchange) { -@@ -1714,6 +1729,9 @@ session_unused(int id) +@@ -1769,6 +1784,9 @@ session_unused(int id) sessions[id].ttyfd = -1; sessions[id].ptymaster = -1; sessions[id].x11_chanids = NULL; @@ -1828,7 +1884,7 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c sessions[id].next_unused = sessions_first_unused; sessions_first_unused = id; } -@@ -1796,6 +1814,19 @@ session_open(Authctxt *authctxt, int cha +@@ -1851,6 +1869,19 @@ session_open(Authctxt *authctxt, int cha } Session * @@ -1848,17 +1904,17 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c session_by_tty(char *tty) { int i; -@@ -2307,6 +2338,32 @@ session_exit_message(struct ssh *ssh, Se +@@ -2461,6 +2492,32 @@ session_exit_message(struct ssh *ssh, Se chan_write_failed(ssh, c); } +#ifdef SSH_AUDIT_EVENTS +void -+session_end_command2(Session *s) ++session_end_command2(struct ssh *ssh, Session *s) +{ + if (s->command != NULL) { + if (s->command_handle != -1) -+ audit_end_command(s->command_handle, s->command); ++ audit_end_command(ssh, s->command_handle, s->command); + free(s->command); + s->command = NULL; + s->command_handle = -1; @@ -1866,11 +1922,11 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c +} + +static void -+session_end_command(Session *s) ++session_end_command(struct ssh *ssh, Session *s) +{ + if (s->command != NULL) { + if (s->command_handle != -1) -+ PRIVSEP(audit_end_command(s->command_handle, s->command)); ++ PRIVSEP(audit_end_command(ssh, s->command_handle, s->command)); + free(s->command); + s->command = NULL; + s->command_handle = -1; @@ -1881,34 +1937,51 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c void session_close(struct ssh *ssh, Session *s) { -@@ -2320,6 +2377,10 @@ session_close(struct ssh *ssh, Session * +@@ -2474,6 +2531,10 @@ session_close(struct ssh *ssh, Session * if (s->ttyfd != -1) session_pty_cleanup(s); +#ifdef SSH_AUDIT_EVENTS + if (s->command) -+ session_end_command(s); ++ session_end_command(ssh, s); +#endif free(s->term); free(s->display); free(s->x11_chanids); -@@ -2528,6 +2589,15 @@ do_authenticated2(struct ssh *ssh, Authc +@@ -2549,14 +2610,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); + } +@@ -2683,6 +2744,15 @@ do_authenticated2(struct ssh *ssh, Authc server_loop2(ssh, authctxt); } +static void -+do_cleanup_one_session(Session *s) ++do_cleanup_one_session(struct ssh *ssh, Session *s) +{ + session_pty_cleanup2(s); +#ifdef SSH_AUDIT_EVENTS -+ session_end_command2(s); ++ session_end_command2(ssh, s); +#endif +} + void do_cleanup(struct ssh *ssh, Authctxt *authctxt) { -@@ -2585,7 +2655,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au +@@ -2746,7 +2816,7 @@ do_cleanup(struct ssh *ssh, Authctxt *au * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) @@ -1917,10 +1990,10 @@ diff -up openssh-7.6p1/session.c.audit openssh-7.6p1/session.c } /* Return a name for the remote host that fits inside utmp_size */ -diff -up openssh-7.6p1/session.h.audit openssh-7.6p1/session.h ---- openssh-7.6p1/session.h.audit 2017-10-02 21:34:26.000000000 +0200 -+++ openssh-7.6p1/session.h 2017-10-04 17:18:32.839505074 +0200 -@@ -60,6 +60,12 @@ struct Session { +diff -up openssh/session.h.audit openssh/session.h +--- openssh/session.h.audit 2019-03-27 23:26:14.000000000 +0100 ++++ openssh/session.h 2019-04-03 17:02:20.717886079 +0200 +@@ -61,6 +61,12 @@ struct Session { char *name; char *val; } *env; @@ -1933,20 +2006,23 @@ diff -up openssh-7.6p1/session.h.audit openssh-7.6p1/session.h }; void do_authenticated(struct ssh *, Authctxt *); -@@ -72,8 +78,10 @@ void session_close_by_pid(struct ssh *s +@@ -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, void *); - void session_destroy_all(struct ssh *, void (*)(Session *)); +-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(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-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c ---- openssh-7.6p1/sshd.c.audit 2017-10-04 17:18:32.830505028 +0200 -+++ openssh-7.6p1/sshd.c 2017-10-04 17:18:32.839505074 +0200 +diff -up openssh/sshd.c.audit openssh/sshd.c +--- openssh/sshd.c.audit 2019-04-03 17:02:20.692885842 +0200 ++++ openssh/sshd.c 2019-04-03 17:02:20.717886079 +0200 @@ -122,6 +122,7 @@ #include "ssh-gss.h" #endif @@ -1955,16 +2031,18 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c #include "ssh-sandbox.h" #include "auth-options.h" #include "version.h" -@@ -248,7 +249,7 @@ Buffer loginmsg; +@@ -261,8 +262,8 @@ struct sshbuf *loginmsg; struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ -void destroy_sensitive_data(void); -+void destroy_sensitive_data(int); - void demote_sensitive_data(void); - static void do_ssh2_kex(void); +-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 *); -@@ -265,6 +266,15 @@ close_listen_socks(void) + /* +@@ -278,6 +279,15 @@ close_listen_socks(void) num_listen_socks = -1; } @@ -1980,8 +2058,8 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c static void close_startup_pipes(void) { -@@ -475,18 +485,45 @@ sshd_exchange_identification(struct ssh - } +@@ -380,18 +390,45 @@ grace_alarm_handler(int sig) + ssh_remote_port(the_active_state)); } -/* Destroy the host and server keys. They will no longer be needed. */ @@ -1991,7 +2069,7 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + */ void -destroy_sensitive_data(void) -+destroy_sensitive_data(int privsep) ++destroy_sensitive_data(struct ssh *ssh, int privsep) { u_int i; +#ifdef SSH_AUDIT_EVENTS @@ -2014,10 +2092,10 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + if (fp != NULL) { +#ifdef SSH_AUDIT_EVENTS + if (privsep) -+ PRIVSEP(audit_destroy_sensitive_data(fp, ++ PRIVSEP(audit_destroy_sensitive_data(ssh, fp, + pid, uid)); + else -+ audit_destroy_sensitive_data(fp, ++ audit_destroy_sensitive_data(ssh, fp, + pid, uid); +#endif + free(fp); @@ -2029,7 +2107,13 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c sshkey_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } -@@ -499,16 +536,34 @@ demote_sensitive_data(void) +@@ -400,14 +437,26 @@ 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; @@ -2051,20 +2135,38 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c if ((r = sshkey_from_private( sensitive_data.host_keys[i], &tmp)) != 0) fatal("could not demote host %s key: %s", - sshkey_type(sensitive_data.host_keys[i]), +@@ -415,6 +464,12 @@ demote_sensitive_data(void) ssh_err(r)); sshkey_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; + if (fp != NULL) { +#ifdef SSH_AUDIT_EVENTS -+ audit_destroy_sensitive_data(fp, pid, uid); ++ audit_destroy_sensitive_data(ssh, fp, pid, uid); +#endif + free(fp); + } } /* Certs do not need demotion */ } -@@ -587,7 +642,7 @@ privsep_preauth(Authctxt *authctxt) +@@ -442,7 +497,7 @@ reseed_prngs(void) + } + + static void +-privsep_preauth_child(void) ++privsep_preauth_child(struct ssh *ssh) + { + gid_t gidset[1]; + +@@ -457,7 +512,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(); +@@ -496,7 +551,7 @@ privsep_preauth(struct ssh *ssh) if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); @@ -2073,28 +2175,64 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { -@@ -1162,6 +1217,7 @@ server_accept_loop(int *sock_in, int *so +@@ -542,7 +597,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); +@@ -594,7 +649,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(); + +@@ -1057,7 +1112,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) + { + fd_set *fdset; + int i, j, ret, maxfd; +@@ -1112,6 +1167,7 @@ server_accept_loop(int *sock_in, int *so if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); -+ destroy_sensitive_data(0); ++ destroy_sensitive_data(ssh, 0); close_listen_socks(); if (options.pid_file != NULL) unlink(options.pid_file); -@@ -2165,6 +2221,9 @@ main(int ac, char **av) +@@ -1978,7 +2034,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); + } + +@@ -2222,6 +2278,9 @@ main(int ac, char **av) do_authenticated(ssh, authctxt); /* The connection has been terminated. */ -+ packet_destroy_all(1, 1); -+ destroy_sensitive_data(1); ++ packet_destroy_all(ssh, 1, 1); ++ destroy_sensitive_data(ssh, 1); + - packet_get_bytes(&ibytes, &obytes); + ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); -@@ -2344,6 +2403,15 @@ void +@@ -2401,6 +2460,15 @@ do_ssh2_kex(struct ssh *ssh) + void cleanup_exit(int i) { - struct ssh *ssh = active_state; /* XXX */ + static int in_cleanup = 0; + int is_privsep_child; + @@ -2104,29 +2242,31 @@ diff -up openssh-7.6p1/sshd.c.audit openssh-7.6p1/sshd.c + if (in_cleanup) + _exit(i); + in_cleanup = 1; - - if (the_authctxt) { - do_cleanup(ssh, the_authctxt); -@@ -2356,9 +2424,14 @@ cleanup_exit(int i) + if (the_active_state != NULL && the_authctxt != NULL) { + do_cleanup(the_active_state, the_authctxt); + if (use_privsep && privsep_is_preauth && +@@ -2414,9 +2482,16 @@ cleanup_exit(int i) pmonitor->m_pid, strerror(errno)); } } + is_privsep_child = use_privsep && pmonitor != NULL && pmonitor->m_pid == 0; -+ if (sensitive_data.host_keys != NULL) -+ destroy_sensitive_data(is_privsep_child); -+ packet_destroy_all(1, is_privsep_child); ++ 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 (!use_privsep || mm_is_monitor()) -+ if ((the_authctxt == NULL || !the_authctxt->authenticated) && +- 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(SSH_CONNECTION_ABANDON); + audit_event(the_active_state, SSH_CONNECTION_ABANDON); #endif _exit(i); -diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c ---- openssh-7.6p1/sshkey.c.audit 2017-10-04 17:18:32.758504660 +0200 -+++ openssh-7.6p1/sshkey.c 2017-10-04 17:18:32.839505074 +0200 -@@ -295,6 +295,38 @@ sshkey_type_is_valid_ca(int type) +diff -up openssh/sshkey.c.audit openssh/sshkey.c +--- openssh/sshkey.c.audit 2019-04-03 17:02:20.657885510 +0200 ++++ openssh/sshkey.c 2019-04-03 17:02:20.718886088 +0200 +@@ -331,6 +331,38 @@ sshkey_type_is_valid_ca(int type) } int @@ -2165,10 +2305,10 @@ diff -up openssh-7.6p1/sshkey.c.audit openssh-7.6p1/sshkey.c sshkey_is_cert(const struct sshkey *k) { if (k == NULL) -diff -up openssh-7.6p1/sshkey.h.audit openssh-7.6p1/sshkey.h ---- openssh-7.6p1/sshkey.h.audit 2017-10-04 17:18:32.758504660 +0200 -+++ openssh-7.6p1/sshkey.h 2017-10-04 17:18:32.840505079 +0200 -@@ -133,6 +133,7 @@ u_int sshkey_size(const struct sshkey +diff -up openssh/sshkey.h.audit openssh/sshkey.h +--- openssh/sshkey.h.audit 2019-04-03 17:02:20.657885510 +0200 ++++ openssh/sshkey.h 2019-04-03 17:02:20.718886088 +0200 +@@ -148,6 +148,7 @@ u_int sshkey_size(const struct sshkey int sshkey_generate(int type, u_int bits, struct sshkey **keyp); int sshkey_from_private(const struct sshkey *, struct sshkey **); int sshkey_type_from_name(const char *); diff --git a/openssh-7.6p1-cleanup-selinux.patch b/openssh-7.6p1-cleanup-selinux.patch index 3b5001a..08cd349 100644 --- a/openssh-7.6p1-cleanup-selinux.patch +++ b/openssh-7.6p1-cleanup-selinux.patch @@ -1,6 +1,6 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c ---- openssh/auth2-pubkey.c.refactor 2017-09-27 13:10:19.556830609 +0200 -+++ openssh/auth2-pubkey.c 2017-09-27 13:10:19.677831274 +0200 +--- 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 @@ extern ServerOptions options; extern u_char *session_id2; @@ -11,7 +11,7 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c static char * format_key(const struct sshkey *key) -@@ -432,7 +435,8 @@ match_principals_command(struct passwd * +@@ -511,7 +514,8 @@ match_principals_command(struct ssh *ssh if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command, ac, av, &f, @@ -21,7 +21,7 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c goto out; uid_swapped = 1; -@@ -762,7 +766,8 @@ user_key_command_allowed2(struct passwd +@@ -981,7 +985,8 @@ user_key_command_allowed2(struct ssh *ss if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command, ac, av, &f, @@ -32,9 +32,9 @@ diff -up openssh/auth2-pubkey.c.refactor openssh/auth2-pubkey.c uid_swapped = 1; diff -up openssh/auth.c.refactor openssh/auth.c ---- openssh/auth.c.refactor 2017-09-27 13:10:19.640831071 +0200 -+++ openssh/auth.c 2017-09-27 13:10:19.678831279 +0200 -@@ -1435,7 +1435,8 @@ argv_assemble(int argc, char **argv) +--- openssh/auth.c.refactor 2019-04-04 13:19:12.235821686 +0200 ++++ openssh/auth.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, struct passwd *pw, const char *command, @@ -44,7 +44,7 @@ diff -up openssh/auth.c.refactor openssh/auth.c { FILE *f = NULL; struct stat st; -@@ -1551,7 +1552,7 @@ subprocess(const char *tag, struct passw +@@ -872,7 +873,7 @@ subprocess(const char *tag, struct passw } #ifdef WITH_SELINUX @@ -54,9 +54,9 @@ diff -up openssh/auth.c.refactor openssh/auth.c strerror(errno)); _exit(127); diff -up openssh/auth.h.refactor openssh/auth.h ---- openssh/auth.h.refactor 2017-09-25 01:48:10.000000000 +0200 -+++ openssh/auth.h 2017-09-27 13:10:19.678831279 +0200 -@@ -144,7 +144,7 @@ int exited_cleanly(pid_t, const char *, +--- openssh/auth.h.refactor 2019-04-04 13:19:12.251821839 +0200 ++++ openssh/auth.h 2019-04-04 13:19:12.276822078 +0200 +@@ -235,7 +235,7 @@ struct passwd *fakepw(void); #define SSH_SUBPROCESS_STDOUT_CAPTURE (1<<1) /* Redirect stdout */ #define SSH_SUBPROCESS_STDERR_DISCARD (1<<2) /* Discard stderr */ pid_t subprocess(const char *, struct passwd *, @@ -66,8 +66,8 @@ diff -up openssh/auth.h.refactor openssh/auth.h int sys_auth_passwd(struct ssh *, const char *); diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/port-linux.h ---- openssh/openbsd-compat/port-linux.h.refactor 2017-09-27 13:10:19.634831038 +0200 -+++ openssh/openbsd-compat/port-linux.h 2017-09-27 13:10:54.954025248 +0200 +--- 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); @@ -80,9 +80,9 @@ diff -up openssh/openbsd-compat/port-linux.h.refactor openssh/openbsd-compat/por #endif diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compat/port-linux-sshd.c ---- openssh/openbsd-compat/port-linux-sshd.c.refactor 2017-09-27 13:10:19.634831038 +0200 -+++ openssh/openbsd-compat/port-linux-sshd.c 2017-09-27 13:12:06.811420371 +0200 -@@ -48,11 +48,6 @@ +--- 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 #endif @@ -94,7 +94,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa /* Wrapper around is_selinux_enabled() to log its return value once only */ int sshd_selinux_enabled(void) -@@ -222,7 +217,8 @@ get_user_context(const char *sename, con +@@ -223,7 +218,8 @@ get_user_context(const char *sename, con } static void @@ -104,7 +104,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { *role = NULL; *level = NULL; -@@ -240,8 +236,8 @@ ssh_selinux_get_role_level(char **role, +@@ -241,8 +237,8 @@ ssh_selinux_get_role_level(char **role, /* Return the default security context for the given username */ static int @@ -115,7 +115,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { char *sename, *lvl; char *role; -@@ -249,7 +245,7 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -250,7 +246,7 @@ sshd_selinux_getctxbyname(char *pwname, int r = 0; context_t con = NULL; @@ -124,7 +124,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa #ifdef HAVE_GETSEUSERBYNAME if ((r=getseuserbyname(pwname, &sename, &lvl)) != 0) { -@@ -271,7 +267,7 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -272,7 +268,7 @@ sshd_selinux_getctxbyname(char *pwname, if (r == 0) { /* If launched from xinetd, we must use current level */ @@ -133,7 +133,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa security_context_t sshdsc=NULL; if (getcon_raw(&sshdsc) < 0) -@@ -332,7 +328,8 @@ sshd_selinux_getctxbyname(char *pwname, +@@ -333,7 +329,8 @@ sshd_selinux_getctxbyname(char *pwname, /* Setup environment variables for pam_selinux */ static int @@ -143,7 +143,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa { const char *reqlvl; char *role; -@@ -341,11 +338,11 @@ sshd_selinux_setup_variables(int(*set_it +@@ -342,11 +339,11 @@ sshd_selinux_setup_variables(int(*set_it debug3("%s: setting execution context", __func__); @@ -157,7 +157,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa use_current = "1"; } else { use_current = ""; -@@ -361,9 +358,10 @@ sshd_selinux_setup_variables(int(*set_it +@@ -362,9 +359,10 @@ sshd_selinux_setup_variables(int(*set_it } static int @@ -170,7 +170,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa } static int -@@ -373,25 +371,28 @@ do_setenv(char *name, const char *value) +@@ -374,25 +372,28 @@ do_setenv(char *name, const char *value) } int @@ -204,7 +204,7 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa switch (security_getenforce()) { case -1: fatal("%s: security_getenforce() failed", __func__); -@@ -409,7 +410,7 @@ sshd_selinux_setup_exec_context(char *pw +@@ -410,7 +411,7 @@ sshd_selinux_setup_exec_context(char *pw debug3("%s: setting execution context", __func__); @@ -214,9 +214,9 @@ diff -up openssh/openbsd-compat/port-linux-sshd.c.refactor openssh/openbsd-compa r = setexeccon(user_ctx); if (r < 0) { diff -up openssh/platform.c.refactor openssh/platform.c ---- openssh/platform.c.refactor 2017-09-27 13:10:19.574830708 +0200 -+++ openssh/platform.c 2017-09-27 13:11:45.475303050 +0200 -@@ -33,6 +33,9 @@ +--- 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; @@ -226,7 +226,7 @@ diff -up openssh/platform.c.refactor openssh/platform.c void platform_pre_listen(void) -@@ -184,7 +187,9 @@ platform_setusercontext_post_groups(stru +@@ -183,7 +186,9 @@ platform_setusercontext_post_groups(stru } #endif /* HAVE_SETPCRED */ #ifdef WITH_SELINUX @@ -238,9 +238,27 @@ diff -up openssh/platform.c.refactor openssh/platform.c } diff -up openssh/sshd.c.refactor openssh/sshd.c ---- openssh/sshd.c.refactor 2017-09-27 13:10:19.674831257 +0200 -+++ openssh/sshd.c 2017-09-27 13:12:01.635391909 +0200 -@@ -2135,7 +2135,9 @@ main(int ac, char **av) +--- 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 diff --git a/openssh-7.6p1-pkcs11-ecdsa.patch b/openssh-7.6p1-pkcs11-ecdsa.patch deleted file mode 100644 index 68fe97b..0000000 --- a/openssh-7.6p1-pkcs11-ecdsa.patch +++ /dev/null @@ -1,794 +0,0 @@ -diff -up openssh/ssh-pkcs11-client.c.pkcs11-ecdsa openssh/ssh-pkcs11-client.c ---- openssh/ssh-pkcs11-client.c.pkcs11-ecdsa 2018-10-12 14:05:55.020656974 +0200 -+++ openssh/ssh-pkcs11-client.c 2018-10-12 14:05:55.023656999 +0200 -@@ -31,6 +31,15 @@ - #include - - #include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif - - #include "openbsd-compat/openssl-compat.h" - -@@ -155,9 +164,9 @@ pkcs11_rsa_private_encrypt(int flen, con - return (ret); - } - --/* redirect the private key encrypt operation to the ssh-pkcs11-helper */ -+/* redirect the RSA private key encrypt operation to the ssh-pkcs11-helper */ - static int --wrap_key(RSA *rsa) -+wrap_rsa_key(RSA *rsa) - { - static RSA_METHOD *helper_rsa; - -@@ -170,6 +179,88 @@ wrap_key(RSA *rsa) - return (0); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static ECDSA_SIG * -+pkcs11_ecdsa_private_sign(const unsigned char *from, int flen, -+ const BIGNUM *inv, const BIGNUM *rp, EC_KEY * ecdsa) -+{ -+ struct sshkey *key = NULL; -+ u_char *blob, *signature = NULL; -+ size_t blen, slen = 0; -+ struct sshbuf *msg = NULL; -+ ECDSA_SIG *ret = NULL; -+ BIGNUM *r = NULL, *s = NULL; -+ int rv; -+ -+ if ((key = sshkey_new(KEY_ECDSA)) == NULL) -+ fatal("%s: sshkey_new failed", __func__); -+ key->ecdsa = ecdsa; -+ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(ecdsa); -+ if (sshkey_to_blob(key, &blob, &blen) == 0) -+ goto out; -+ if ((msg = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ if ((rv = sshbuf_put_u8(msg, SSH2_AGENTC_SIGN_REQUEST)) != 0 || -+ (rv = sshbuf_put_string(msg, blob, blen)) != 0 || -+ (rv = sshbuf_put_string(msg, from, flen)) != 0 || -+ (rv = sshbuf_put_u32(msg, 0)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); -+ free(blob); -+ send_msg(msg); -+ sshbuf_reset(msg); -+ -+ if (recv_msg(msg) == SSH2_AGENT_SIGN_RESPONSE) { -+ if ((rv = sshbuf_get_string(msg, &signature, &slen)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(rv)); -+ if (slen <= (size_t)ECDSA_size(ecdsa)) { -+ int nlen = slen / 2; -+ ret = ECDSA_SIG_new(); -+ r = BN_new(); -+ s = BN_new(); -+ BN_bin2bn(&signature[0], nlen, r); -+ BN_bin2bn(&signature[nlen], nlen, s); -+ ECDSA_SIG_set0(ret, r, s); -+ } -+ free(signature); -+ } -+out: -+ sshkey_free(key); -+ sshbuf_free(msg); -+ return (ret); -+} -+ -+/* redirect the ECDSA private key encrypt operation to the ssh-pkcs11-helper */ -+static int -+wrap_ecdsa_key(EC_KEY *ecdsa) { -+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ static EC_KEY_METHOD *helper_ecdsa = NULL; -+ if (helper_ecdsa == NULL) { -+ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); -+ helper_ecdsa = EC_KEY_METHOD_new(def); -+ EC_KEY_METHOD_set_sign(helper_ecdsa, NULL, NULL, pkcs11_ecdsa_private_sign); -+ } -+ EC_KEY_set_method(ecdsa, helper_ecdsa); -+#else -+ static ECDSA_METHOD *helper_ecdsa = NULL; -+ if(helper_ecdsa == NULL) { -+ const ECDSA_METHOD *def = ECDSA_get_default_method(); -+# ifdef ECDSA_F_ECDSA_METHOD_NEW -+ helper_ecdsa = ECDSA_METHOD_new((ECDSA_METHOD *)def); -+ ECDSA_METHOD_set_name(helper_ecdsa, "ssh-pkcs11-helper-ecdsa"); -+ ECDSA_METHOD_set_sign(helper_ecdsa, pkcs11_ecdsa_private_sign); -+# else -+ helper_ecdsa = xcalloc(1, sizeof(*helper_ecdsa)); -+ memcpy(helper_ecdsa, def, sizeof(*helper_ecdsa)); -+ helper_ecdsa->name = "ssh-pkcs11-helper-ecdsa"; -+ helper_ecdsa->ecdsa_do_sign = pkcs11_ecdsa_private_sign; -+# endif -+ } -+ ECDSA_set_method(ecdsa, helper_ecdsa); -+#endif -+ return (0); -+} -+#endif -+ - static int - pkcs11_start_helper(void) - { -@@ -238,7 +329,15 @@ pkcs11_add_provider(char *name, char *pi - __func__, ssh_err(r)); - if ((r = sshkey_from_blob(blob, blen, &k)) != 0) - fatal("%s: bad key: %s", __func__, ssh_err(r)); -- wrap_key(k->rsa); -+ if(k->type == KEY_RSA) { -+ wrap_rsa_key(k->rsa); -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if(k->type == KEY_ECDSA) { -+ wrap_ecdsa_key(k->ecdsa); -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } else { -+ /* Unsupported type */ -+ } - (*keysp)[i] = k; - free(blob); - } -diff -up openssh/ssh-pkcs11.c.pkcs11-ecdsa openssh/ssh-pkcs11.c ---- openssh/ssh-pkcs11.c.pkcs11-ecdsa 2018-10-12 14:05:55.021656982 +0200 -+++ openssh/ssh-pkcs11.c 2018-10-12 14:11:54.292636679 +0200 -@@ -33,6 +33,16 @@ - #include "openbsd-compat/openssl-compat.h" - - #include -+#include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif - - #define CRYPTOKI_COMPAT - #include "pkcs11.h" -@@ -74,6 +84,7 @@ TAILQ_HEAD(, pkcs11_provider) pkcs11_pro - struct pkcs11_key { - struct pkcs11_provider *provider; - CK_ULONG slotidx; -+ CK_ULONG key_type; - int (*orig_finish)(RSA *rsa); - RSA_METHOD *rsa_method; - char *keyid; -@@ -82,6 +93,9 @@ struct pkcs11_key { - }; - - int pkcs11_interactive = 0; -+#ifdef ENABLE_PKCS11_ECDSA -+static int pkcs11_key_idx = -1; -+#endif /* ENABLE_PKCS11_ECDSA */ - - /* - * This can't be in the ssh-pkcs11-uri, becase we can not depend on -@@ -345,6 +359,40 @@ pkcs11_find(struct pkcs11_provider *p, C - return (ret); - } - -+int pkcs11_login(struct pkcs11_key *k11, CK_FUNCTION_LIST *f, struct pkcs11_slotinfo *si) { -+ char *pin = NULL, prompt[1024]; -+ CK_RV rv; -+ if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { -+ if (!pkcs11_interactive) { -+ error("need pin entry%s", (si->token.flags & -+ CKF_PROTECTED_AUTHENTICATION_PATH) ? -+ " on reader keypad" : ""); -+ return (-1); -+ } -+ if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) -+ verbose("Deferring PIN entry to reader keypad."); -+ else { -+ snprintf(prompt, sizeof(prompt), -+ "Enter PIN for '%s': ", si->token.label); -+ pin = read_passphrase(prompt, RP_ALLOW_EOF); -+ if (pin == NULL) -+ return (-1); /* bail out */ -+ } -+ rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, -+ (pin != NULL) ? strlen(pin) : 0); -+ if (pin != NULL) { -+ explicit_bzero(pin, strlen(pin)); -+ free(pin); -+ } -+ if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { -+ error("C_Login failed: %lu", rv); -+ return (-1); -+ } -+ si->logged_in = 1; -+ } -+ return 0; -+} -+ - /* openssl callback doing the actual signing operation */ - static int - pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, -@@ -366,7 +414,6 @@ pkcs11_rsa_private_encrypt(int flen, con - {CKA_ID, NULL, 0}, - {CKA_SIGN, NULL, sizeof(true_val) } - }; -- char *pin = NULL, prompt[1024]; - int rval = -1; - - key_filter[0].pValue = &private_key_class; -@@ -383,33 +430,8 @@ pkcs11_rsa_private_encrypt(int flen, con - } - f = k11->provider->module->function_list; - si = &k11->provider->module->slotinfo[k11->slotidx]; -- if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { -- if (!pkcs11_interactive) { -- error("need pin entry%s", (si->token.flags & -- CKF_PROTECTED_AUTHENTICATION_PATH) ? -- " on reader keypad" : ""); -- return (-1); -- } -- if (si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH) -- verbose("Deferring PIN entry to reader keypad."); -- else { -- snprintf(prompt, sizeof(prompt), -- "Enter PIN for '%s': ", si->token.label); -- pin = read_passphrase(prompt, RP_ALLOW_EOF); -- if (pin == NULL) -- return (-1); /* bail out */ -- } -- rv = f->C_Login(si->session, CKU_USER, (u_char *)pin, -- (pin != NULL) ? strlen(pin) : 0); -- if (pin != NULL) { -- explicit_bzero(pin, strlen(pin)); -- free(pin); -- } -- if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { -- error("C_Login failed: %lu", rv); -- return (-1); -- } -- si->logged_in = 1; -+ if(pkcs11_login(k11, f, si)) { -+ return (-1); - } - key_filter[1].pValue = k11->keyid; - key_filter[1].ulValueLen = k11->keyid_len; -@@ -447,6 +469,7 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - const RSA_METHOD *def = RSA_get_default_method(); - - k11 = xcalloc(1, sizeof(*k11)); -+ k11->key_type = CKK_RSA; - k11->provider = provider; - provider->refcount++; /* provider referenced by RSA key */ - k11->slotidx = slotidx; -@@ -477,6 +500,184 @@ pkcs11_rsa_wrap(struct pkcs11_provider * - return (0); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static ECDSA_SIG *pkcs11_ecdsa_sign(const unsigned char *dgst, int dgst_len, -+ const BIGNUM *inv, const BIGNUM *rp, -+ EC_KEY *ecdsa) { -+ struct pkcs11_key *k11; -+ struct pkcs11_slotinfo *si; -+ CK_FUNCTION_LIST *f; -+ CK_OBJECT_HANDLE obj; -+ CK_ULONG tlen = 0; -+ CK_RV rv; -+ CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY; -+ CK_BBOOL true_val = CK_TRUE; -+ CK_MECHANISM mech = { -+ CKM_ECDSA, NULL_PTR, 0 -+ }; -+ CK_ATTRIBUTE key_filter[] = { -+ {CKA_CLASS, NULL, sizeof(private_key_class) }, -+ {CKA_ID, NULL, 0}, -+ {CKA_SIGN, NULL, sizeof(true_val) } -+ }; -+ ECDSA_SIG *rval = NULL; -+ key_filter[0].pValue = &private_key_class; -+ key_filter[2].pValue = &true_val; -+ -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ if ((k11 = (struct pkcs11_key *)EC_KEY_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { -+ error("EC_KEY_get_ex_data failed for ecdsa %p", ecdsa); -+ #else -+ if ((k11 = (struct pkcs11_key *)ECDSA_get_ex_data(ecdsa, pkcs11_key_idx)) == NULL) { -+ error("ECDSA_get_ex_data failed for ecdsa %p", ecdsa); -+ #endif -+ return NULL; -+ } -+ if (!k11->provider || !k11->provider->valid) { -+ error("no pkcs11 (valid) provider for ecdsa %p", ecdsa); -+ return NULL; -+ } -+ f = k11->provider->module->function_list; -+ si = &k11->provider->module->slotinfo[k11->slotidx]; -+ if(pkcs11_login(k11, f, si)) { -+ return NULL; -+ } -+ key_filter[1].pValue = k11->keyid; -+ key_filter[1].ulValueLen = k11->keyid_len; -+ /* try to find object w/CKA_SIGN first, retry w/o */ -+ if (pkcs11_find(k11->provider, k11->slotidx, key_filter, 3, &obj) < 0 && -+ pkcs11_find(k11->provider, k11->slotidx, key_filter, 2, &obj) < 0) { -+ error("cannot find private key"); -+ } else if ((rv = f->C_SignInit(si->session, &mech, obj)) != CKR_OK) { -+ error("C_SignInit failed: %lu", rv); -+ } else { -+ CK_BYTE_PTR buf = NULL; -+ BIGNUM *r = NULL, *s = NULL; -+ int nlen; -+ /* Make a call to C_Sign to find out the size of the signature */ -+ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, NULL, &tlen); -+ if (rv != CKR_OK) { -+ error("C_Sign failed: %lu", rv); -+ return NULL; -+ } -+ if ((buf = xmalloc(tlen)) == NULL) { -+ error("failure to allocate signature buffer"); -+ return NULL; -+ } -+ rv = f->C_Sign(si->session, (CK_BYTE *)dgst, dgst_len, buf, &tlen); -+ if (rv != CKR_OK) { -+ error("C_Sign failed: %lu", rv); -+ } -+ -+ if ((rval = ECDSA_SIG_new()) == NULL || -+ (r = BN_new()) == NULL || -+ (s = BN_new()) == NULL) { -+ error("failure to allocate ECDSA signature"); -+ } else { -+ /* -+ * ECDSA signature is 2 large integers of same size returned -+ * concatenated by PKCS#11, we separate them to create an -+ * ECDSA_SIG for OpenSSL. -+ */ -+ nlen = tlen / 2; -+ BN_bin2bn(&buf[0], nlen, r); -+ BN_bin2bn(&buf[nlen], nlen, s); -+ ECDSA_SIG_set0(rval, r, s); -+ } -+ free(buf); -+ } -+ return (rval); -+} -+ -+#if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+static EC_KEY_METHOD *get_pkcs11_ecdsa_method(void) { -+ static EC_KEY_METHOD *pkcs11_ecdsa_method = NULL; -+ if(pkcs11_key_idx == -1) { -+ pkcs11_key_idx = EC_KEY_get_ex_new_index(0, NULL, NULL, NULL, 0); -+ } -+ if (pkcs11_ecdsa_method == NULL) { -+ const EC_KEY_METHOD *def = EC_KEY_get_default_method(); -+ pkcs11_ecdsa_method = EC_KEY_METHOD_new(def); -+ EC_KEY_METHOD_set_sign(pkcs11_ecdsa_method, NULL, NULL, pkcs11_ecdsa_sign); -+ } -+#else -+static ECDSA_METHOD *get_pkcs11_ecdsa_method(void) { -+ static ECDSA_METHOD *pkcs11_ecdsa_method = NULL; -+ if(pkcs11_key_idx == -1) { -+ pkcs11_key_idx = ECDSA_get_ex_new_index(0, NULL, NULL, NULL, 0); -+ } -+ if(pkcs11_ecdsa_method == NULL) { -+ const ECDSA_METHOD *def = ECDSA_get_default_method(); -+ #ifdef ECDSA_F_ECDSA_METHOD_NEW -+ pkcs11_ecdsa_method = ECDSA_METHOD_new((ECDSA_METHOD *)def); -+ ECDSA_METHOD_set_name(pkcs11_ecdsa_method, "pkcs11"); -+ ECDSA_METHOD_set_sign(pkcs11_ecdsa_method, pkcs11_ecdsa_sign); -+ #else -+ pkcs11_ecdsa_method = xcalloc(1, sizeof(*pkcs11_ecdsa_method)); -+ memcpy(pkcs11_ecdsa_method, def, sizeof(*pkcs11_ecdsa_method)); -+ pkcs11_ecdsa_method->name = "pkcs11"; -+ pkcs11_ecdsa_method->ecdsa_do_sign = pkcs11_ecdsa_sign; -+ #endif -+ } -+#endif -+ return pkcs11_ecdsa_method; -+} -+ -+static int -+pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, -+ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, EC_KEY *ecdsa) -+{ -+ struct pkcs11_key *k11; -+ k11 = xcalloc(1, sizeof(*k11)); -+ k11->key_type = CKK_EC; -+ k11->provider = provider; -+ provider->refcount++; /* provider referenced by ECDSA key */ -+ k11->slotidx = slotidx; -+ /* identify key object on smartcard */ -+ k11->keyid_len = keyid_attrib->ulValueLen; -+ if (k11->keyid_len > 0) { -+ k11->keyid = xmalloc(k11->keyid_len); -+ memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); -+ } -+ if (label_attrib->ulValueLen > 0 ) { -+ k11->label = xmalloc(label_attrib->ulValueLen+1); -+ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); -+ k11->label[label_attrib->ulValueLen] = 0; -+ } -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ EC_KEY_set_method(ecdsa, get_pkcs11_ecdsa_method()); -+ EC_KEY_set_ex_data(ecdsa, pkcs11_key_idx, k11); -+ #else -+ ECDSA_set_method(ecdsa, get_pkcs11_ecdsa_method()); -+ ECDSA_set_ex_data(ecdsa, pkcs11_key_idx, k11); -+ #endif -+ return (0); -+} -+#endif /* ENABLE_PKCS11_ECDSA */ -+ -+int pkcs11_del_key(struct sshkey *key) { -+#ifdef ENABLE_PKCS11_ECDSA -+ if(key->type == KEY_ECDSA) { -+ struct pkcs11_key *k11 = (struct pkcs11_key *) -+ #if (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+ EC_KEY_get_ex_data(key->ecdsa, pkcs11_key_idx); -+ #else -+ ECDSA_get_ex_data(key->ecdsa, pkcs11_key_idx); -+ #endif -+ if (k11 == NULL) { -+ error("EC_KEY_get_ex_data failed for ecdsa %p", key->ecdsa); -+ } else { -+ if (k11->provider) -+ pkcs11_provider_unref(k11->provider); -+ free(k11->keyid); -+ free(k11); -+ } -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ -+ sshkey_free(key); -+ return (0); -+} -+ - /* remove trailing spaces */ - static void - rmspace(u_char *buf, size_t len) -@@ -544,11 +745,13 @@ static int - pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, - struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) - { -- size_t filter_size = 1; -+ size_t filter_size = 2; -+ CK_KEY_TYPE pubkey_type = CKK_RSA; - CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE pubkey_filter[] = { - { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_KEY_TYPE, NULL, sizeof(pubkey_type) }, - { CKA_ID, NULL, 0 }, - { CKA_LABEL, NULL, 0 } - }; -@@ -569,37 +772,72 @@ pkcs11_fetch_keys(struct pkcs11_provider - { CKA_SUBJECT, NULL, 0 }, - { CKA_VALUE, NULL, 0 } - }; -+#ifdef ENABLE_PKCS11_ECDSA -+ CK_KEY_TYPE ecdsa_type = CKK_EC; -+ CK_ATTRIBUTE ecdsa_filter[] = { -+ { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_KEY_TYPE, NULL, sizeof(ecdsa_type) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } -+ }; -+ CK_ATTRIBUTE ecdsa_attribs[] = { -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, -+ { CKA_EC_PARAMS, NULL, 0 }, -+ { CKA_EC_POINT, NULL, 0 } -+ }; -+ ecdsa_filter[0].pValue = &pubkey_class; -+ ecdsa_filter[1].pValue = &ecdsa_type; -+#endif /* ENABLE_PKCS11_ECDSA */ - pubkey_filter[0].pValue = &pubkey_class; -+ pubkey_filter[1].pValue = &pubkey_type; - cert_filter[0].pValue = &cert_class; - - if (uri->id != NULL) { - pubkey_filter[filter_size].pValue = uri->id; - pubkey_filter[filter_size].ulValueLen = uri->id_len; -- cert_filter[filter_size].pValue = uri->id; -- cert_filter[filter_size].ulValueLen = uri->id_len; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa_filter[filter_size].pValue = uri->id; -+ ecdsa_filter[filter_size].ulValueLen = uri->id_len; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ cert_filter[filter_size-1].pValue = uri->id; -+ cert_filter[filter_size-1].ulValueLen = uri->id_len; - filter_size++; - } - if (uri->object != NULL) { - pubkey_filter[filter_size].pValue = uri->object; - pubkey_filter[filter_size].ulValueLen = strlen(uri->object); - pubkey_filter[filter_size].type = CKA_LABEL; -- cert_filter[filter_size].pValue = uri->object; -- cert_filter[filter_size].ulValueLen = strlen(uri->object); -- cert_filter[filter_size].type = CKA_LABEL; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa_filter[filter_size].pValue = uri->object; -+ ecdsa_filter[filter_size].ulValueLen = strlen(uri->object); -+ ecdsa_filter[filter_size].type = CKA_LABEL; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ cert_filter[filter_size-1].pValue = uri->object; -+ cert_filter[filter_size-1].ulValueLen = strlen(uri->object); -+ cert_filter[filter_size-1].type = CKA_LABEL; - filter_size++; - } - - if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, - pubkey_attribs, keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+#ifdef ENABLE_PKCS11_ECDSA -+ pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size, -+ ecdsa_attribs, keysp, nkeys) < 0|| -+#endif /* ENABLE_PKCS11_ECDSA */ -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1, - cert_attribs, keysp, nkeys) < 0) - return (-1); - if (*nkeys == 0) { - /* Try once more without the label filter */ - filter_size--; - if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, - pubkey_attribs, keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+#ifdef ENABLE_PKCS11_ECDSA -+ pkcs11_fetch_keys_filter(p, slotidx, ecdsa_filter, filter_size, -+ ecdsa_attribs, keysp, nkeys) < 0|| -+#endif /* ENABLE_PKCS11_ECDSA */ -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size - 1, - cert_attribs, keysp, nkeys) < 0) - return (-1); - } -@@ -624,8 +858,13 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], - struct sshkey ***keysp, int *nkeys) - { -- struct sshkey *key; -+ struct sshkey *key = NULL; - RSA *rsa; -+#ifdef ENABLE_PKCS11_ECDSA -+ EC_KEY *ecdsa; -+#else -+ void *ecdsa; -+#endif /* ENABLE_PKCS11_ECDSA */ - X509 *x509; - EVP_PKEY *evp = NULL; - int i; -@@ -678,6 +917,9 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - * or ID, label, subject and value for certificates. - */ - rsa = NULL; -+#ifdef ENABLE_PKCS11_ECDSA -+ ecdsa = NULL; -+#endif /* ENABLE_PKCS11_ECDSA */ - if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); -@@ -700,6 +942,45 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - BN_free(rsa_n); - BN_free(rsa_e); - } -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if (attribs[2].type == CKA_EC_PARAMS ) { -+ if ((ecdsa = EC_KEY_new()) == NULL) { -+ error("EC_KEY_new failed"); -+ } else { -+ const unsigned char *ptr1 = attribs[2].pValue; -+ const unsigned char *ptr2 = attribs[3].pValue; -+ CK_ULONG len1 = attribs[2].ulValueLen; -+ CK_ULONG len2 = attribs[3].ulValueLen; -+ ASN1_OCTET_STRING *point = NULL; -+ -+ /* -+ * CKA_EC_PARAMS contains the curve parameters of the key -+ * either referenced as an OID or directly with all values. -+ * CKA_EC_POINT contains the point (public key) on the curve. -+ * The point is should be returned inside a DER-encoded -+ * ASN.1 OCTET STRING value (but some implementation). -+ */ -+ if ((point = d2i_ASN1_OCTET_STRING(NULL, &ptr2, len2))) { -+ /* Pointing to OCTET STRING content */ -+ ptr2 = point->data; -+ len2 = point->length; -+ } else { -+ /* No OCTET STRING */ -+ ptr2 = attribs[3].pValue; -+ } -+ -+ if((d2i_ECParameters(&ecdsa, &ptr1, len1) == NULL) || -+ (o2i_ECPublicKey(&ecdsa, &ptr2, len2) == NULL)) { -+ EC_KEY_free(ecdsa); -+ ecdsa = NULL; -+ error("EC public key parsing failed"); -+ } -+ -+ if(point) { -+ ASN1_OCTET_STRING_free(point); -+ } -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ - } else { - cp = attribs[3].pValue; - if ((x509 = X509_new()) == NULL) { -@@ -707,13 +988,28 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) - == NULL) { - error("d2i_X509 failed"); -- } else if ((evp = X509_get_pubkey(x509)) == NULL || -- EVP_PKEY_base_id(evp) != EVP_PKEY_RSA || -- EVP_PKEY_get0_RSA(evp) == NULL) { -- debug("X509_get_pubkey failed or no rsa"); -- } else if ((rsa = RSAPublicKey_dup( -- EVP_PKEY_get0_RSA(evp))) == NULL) { -- error("RSAPublicKey_dup"); -+ } else if ((evp = X509_get_pubkey(x509)) == NULL) { -+ debug("X509_get_pubkey failed"); -+ } else { -+ switch (EVP_PKEY_base_id(evp)) { -+ case EVP_PKEY_RSA: -+ if (EVP_PKEY_get0_RSA(evp) == NULL) -+ debug("Missing RSA key"); -+ else if ((rsa = RSAPublicKey_dup( -+ EVP_PKEY_get0_RSA(evp))) == NULL) -+ error("RSAPublicKey_dup failed"); -+ break; -+ case EVP_PKEY_EC: -+ if (EVP_PKEY_get0_EC_KEY(evp) == NULL) -+ debug("Missing ECDSA key"); -+ else if ((ecdsa = EC_KEY_dup( -+ EVP_PKEY_get0_EC_KEY(evp))) == NULL) -+ error("EC_KEY_dup failed"); -+ break; -+ default: -+ debug("not a RSA or ECDSA key"); -+ break; -+ } - } - X509_free(x509); - EVP_PKEY_free(evp); -@@ -725,6 +1021,18 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - key->rsa = rsa; - key->type = KEY_RSA; - key->flags |= SSHKEY_FLAG_EXT; -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if (ecdsa && -+ pkcs11_ecdsa_wrap(p, slotidx, &attribs[0], &attribs[1], ecdsa) == 0) { -+ if ((key = sshkey_new(KEY_UNSPEC)) == NULL) -+ fatal("sshkey_new failed"); -+ key->ecdsa = ecdsa; -+ key->ecdsa_nid = sshkey_ecdsa_key_to_nid(key->ecdsa); -+ key->type = KEY_ECDSA; -+ key->flags |= SSHKEY_FLAG_EXT; -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } -+ if (key) { - if (pkcs11_key_included(keysp, nkeys, key)) { - sshkey_free(key); - } else { -@@ -737,6 +1044,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - } else if (rsa) { - RSA_free(rsa); -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if (ecdsa) { -+ EC_KEY_free(ecdsa); -+#endif /* ENABLE_PKCS11_ECDSA */ - } - for (i = 0; i < nattribs; i++) - free(attribs[i].pValue); -diff -up openssh/ssh-pkcs11-helper.c.pkcs11-ecdsa openssh/ssh-pkcs11-helper.c ---- openssh/ssh-pkcs11-helper.c.pkcs11-ecdsa 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-pkcs11-helper.c 2018-10-12 14:05:55.023656999 +0200 -@@ -24,6 +24,17 @@ - - #include "openbsd-compat/sys-queue.h" - -+#include -+#ifdef OPENSSL_HAS_ECC -+#include -+#if ((defined(LIBRESSL_VERSION_NUMBER) && \ -+ (LIBRESSL_VERSION_NUMBER >= 0x20010002L))) || \ -+ (defined(ECDSA_F_ECDSA_METHOD_NEW)) || \ -+ (OPENSSL_VERSION_NUMBER >= 0x00010100L) -+#define ENABLE_PKCS11_ECDSA 1 -+#endif -+#endif -+ - #include - #include - #include -@@ -77,7 +88,7 @@ del_keys_by_name(char *name) - if (!strcmp(ki->providername, name)) { - TAILQ_REMOVE(&pkcs11_keylist, ki, next); - free(ki->providername); -- sshkey_free(ki->key); -+ pkcs11_del_key(ki->key); - free(ki); - } - } -@@ -172,6 +183,20 @@ process_del(void) - sshbuf_free(msg); - } - -+#ifdef ENABLE_PKCS11_ECDSA -+static u_int EC_KEY_order_size(EC_KEY *key) -+{ -+ const EC_GROUP *group = EC_KEY_get0_group(key); -+ BIGNUM *order = BN_new(); -+ u_int nbytes = 0; -+ if ((group != NULL) && (order != NULL) && EC_GROUP_get_order(group, order, NULL)) { -+ nbytes = BN_num_bytes(order); -+ } -+ BN_clear_free(order); -+ return nbytes; -+} -+#endif /* ENABLE_PKCS11_ECDSA */ -+ - static void - process_sign(void) - { -@@ -192,14 +217,38 @@ process_sign(void) - else { - if ((found = lookup_key(key)) != NULL) { - #ifdef WITH_OPENSSL -- int ret; -- -- slen = RSA_size(key->rsa); -- signature = xmalloc(slen); -- if ((ret = RSA_private_encrypt(dlen, data, signature, -- found->rsa, RSA_PKCS1_PADDING)) != -1) { -- slen = ret; -- ok = 0; -+ if(found->type == KEY_RSA) { -+ int ret; -+ slen = RSA_size(key->rsa); -+ signature = xmalloc(slen); -+ if ((ret = RSA_private_encrypt(dlen, data, signature, -+ found->rsa, RSA_PKCS1_PADDING)) != -1) { -+ slen = ret; -+ ok = 0; -+ } -+#ifdef ENABLE_PKCS11_ECDSA -+ } else if(found->type == KEY_ECDSA) { -+ ECDSA_SIG *sig; -+ const BIGNUM *r = NULL, *s = NULL; -+ if ((sig = ECDSA_do_sign(data, dlen, found->ecdsa)) != NULL) { -+ /* PKCS11 2.3.1 recommends both r and s to have the order size for -+ backward compatiblity */ -+ ECDSA_SIG_get0(sig, &r, &s); -+ u_int o_len = EC_KEY_order_size(found->ecdsa); -+ u_int r_len = BN_num_bytes(r); -+ u_int s_len = BN_num_bytes(s); -+ if (o_len > 0 && r_len <= o_len && s_len <= o_len) { -+ signature = xcalloc(2, o_len); -+ BN_bn2bin(r, signature + o_len - r_len); -+ BN_bn2bin(s, signature + (2 * o_len) - s_len); -+ slen = 2 * o_len; -+ ok = 0; -+ } -+ ECDSA_SIG_free(sig); -+ } -+#endif /* ENABLE_PKCS11_ECDSA */ -+ } else { -+ /* Unsupported type */ - } - #endif /* WITH_OPENSSL */ - } -diff -up openssh/ssh-pkcs11.h.pkcs11-ecdsa openssh/ssh-pkcs11.h ---- openssh/ssh-pkcs11.h.pkcs11-ecdsa 2018-10-12 14:05:55.021656982 +0200 -+++ openssh/ssh-pkcs11.h 2018-10-12 14:05:55.023656999 +0200 -@@ -20,6 +20,7 @@ - int pkcs11_init(int); - void pkcs11_terminate(void); - int pkcs11_add_provider(char *, char *, struct sshkey ***); -+int pkcs11_del_key(struct sshkey *); - int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); - int pkcs11_del_provider(char *); - int pkcs11_uri_write(const struct sshkey *, FILE *); diff --git a/openssh-7.6p1-pkcs11-uri.patch b/openssh-7.6p1-pkcs11-uri.patch deleted file mode 100644 index 708f016..0000000 --- a/openssh-7.6p1-pkcs11-uri.patch +++ /dev/null @@ -1,4760 +0,0 @@ -diff -up openssh/authfd.c.pkcs11-uri openssh/authfd.c ---- openssh/authfd.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/authfd.c 2018-10-12 13:52:55.450191401 +0200 -@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identit - if (idl->comments != NULL) - free(idl->comments[i]); - } -+ free(idl->keys); -+ free(idl->comments); - free(idl); - } - -diff -up openssh/configure.ac.pkcs11-uri openssh/configure.ac ---- openssh/configure.ac.pkcs11-uri 2018-10-12 13:52:55.430191235 +0200 -+++ openssh/configure.ac 2018-10-12 13:52:55.451191409 +0200 -@@ -1987,12 +1987,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 - ] - ) -@@ -2008,6 +2010,40 @@ if test "x$openssl" = "xyes" && test "x$ - ) - fi - -+# Check whether we have a p11-kit, we got default provider on command line -+DEFAULT_PKCS11_PROVIDER_MSG="no" -+AC_ARG_WITH([default-pkcs11-provider], -+ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], -+ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then -+ if test "x$withval" = "xyes" ; then -+ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) -+ if test "x$PKGCONFIG" != "xno"; then -+ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) -+ if "$PKGCONFIG" "p11-kit-1"; then -+ AC_MSG_RESULT([yes]) -+ use_pkgconfig_for_p11kit=yes -+ else -+ AC_MSG_RESULT([no]) -+ fi -+ fi -+ else -+ PKCS11_PATH="${withval}" -+ fi -+ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then -+ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` -+ fi -+ AC_CHECK_FILE("$PKCS11_PATH", -+ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) -+ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" -+ ], -+ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] -+ ) -+ else -+ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) -+ fi ] -+) -+ -+ - # IRIX has a const char return value for gai_strerror() - AC_CHECK_FUNCS([gai_strerror], [ - AC_DEFINE([HAVE_GAI_STRERROR]) -@@ -5458,6 +5494,7 @@ echo " BSD Auth support - echo " Random number source: $RAND_MSG" - echo " Privsep sandbox style: $SANDBOX_STYLE" - echo " Vendor patch level: $SSH_VENDOR_PATCHLEVEL" -+echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" - - echo "" - -diff -up openssh/Makefile.in.pkcs11-uri openssh/Makefile.in ---- openssh/Makefile.in.pkcs11-uri 2018-10-12 13:52:55.366190704 +0200 -+++ openssh/Makefile.in 2018-10-12 13:52:55.450191401 +0200 -@@ -102,7 +102,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ - kexgssc.o \ - msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ -- ssh-pkcs11.o smult_curve25519_ref.o \ -+ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ - poly1305.o chacha.o cipher-chachapoly.o \ - ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ - sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ -@@ -270,6 +270,8 @@ clean: regressclean - rm -f regress/unittests/match/test_match$(EXEEXT) - rm -f regress/unittests/utf8/*.o - rm -f regress/unittests/utf8/test_utf8$(EXEEXT) -+ rm -f regress/unittests/pkcs11/*.o -+ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) - rm -f regress/misc/kexfuzz/*.o - rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) - (cd openbsd-compat && $(MAKE) clean) -@@ -300,6 +302,8 @@ distclean: regressclean - rm -f regress/unittests/match/test_match - rm -f regress/unittests/utf8/*.o - rm -f regress/unittests/utf8/test_utf8 -+ rm -f regress/unittests/pkcs11/*.o -+ rm -f regress/unittests/pkcs11/test_pkcs11 - rm -f regress/misc/kexfuzz/*.o - rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) - (cd openbsd-compat && $(MAKE) distclean) -@@ -484,6 +488,7 @@ regress-prep: - $(MKDIR_P) `pwd`/regress/unittests/kex - $(MKDIR_P) `pwd`/regress/unittests/match - $(MKDIR_P) `pwd`/regress/unittests/utf8 -+ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 - $(MKDIR_P) `pwd`/regress/misc/kexfuzz - [ -f `pwd`/regress/Makefile ] || \ - ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile -@@ -502,6 +507,11 @@ regress/netcat$(EXEEXT): $(srcdir)/regre - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ - $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) - -+regress/soft-pkcs11.so: $(srcdir)/regress/soft-pkcs11.c $(REGRESSLIBS) -+ $(CC) $(CFLAGS) $(CPPFLAGS) -fpic -c $(srcdir)/regress/soft-pkcs11.c \ -+ -o $(srcdir)/regress/soft-pkcs11.o -+ $(CC) -shared -o $@ $(srcdir)/regress/soft-pkcs11.o -+ - regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) - $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ - $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -@@ -607,6 +617,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT - regress/unittests/test_helper/libtest_helper.a \ - -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) - -+UNITTESTS_TEST_PKCS11_OBJS=\ -+ regress/unittests/pkcs11/tests.o -+ -+regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ -+ ${UNITTESTS_TEST_PKCS11_OBJS} \ -+ regress/unittests/test_helper/libtest_helper.a libssh.a -+ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ -+ regress/unittests/test_helper/libtest_helper.a \ -+ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) -+ - MISC_KEX_FUZZ_OBJS=\ - regress/misc/kexfuzz/kexfuzz.o - -@@ -617,6 +637,7 @@ regress/misc/kexfuzz/kexfuzz$(EXEEXT): $ - regress-binaries: regress/modpipe$(EXEEXT) \ - regress/setuid-allowed$(EXEEXT) \ - regress/netcat$(EXEEXT) \ -+ regress/soft-pkcs11.so \ - regress/check-perm$(EXEEXT) \ - regress/mkdtemp$(EXEEXT) \ - regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ -@@ -627,6 +648,7 @@ regress-binaries: regress/modpipe$(EXEEX - regress/unittests/kex/test_kex$(EXEEXT) \ - regress/unittests/match/test_match$(EXEEXT) \ - regress/unittests/utf8/test_utf8$(EXEEXT) \ -+ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ - regress/misc/kexfuzz/kexfuzz$(EXEEXT) - - tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) -diff -up openssh/regress/agent-pkcs11.sh.pkcs11-uri openssh/regress/agent-pkcs11.sh ---- openssh/regress/agent-pkcs11.sh.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/regress/agent-pkcs11.sh 2018-10-12 13:52:55.451191409 +0200 -@@ -4,10 +4,17 @@ - tid="pkcs11 agent test" - - TEST_SSH_PIN="" --TEST_SSH_PKCS11=/usr/local/lib/soft-pkcs11.so.0.0 -+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so - - test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" - -+# requires ssh-agent built with correct path to ssh-pkcs11-helper -+# otherwise it fails to start the helper -+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" -+if [ $? -ne 0 ]; then -+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" -+fi -+ - # setup environment for soft-pkcs11 token - SOFTPKCS11RC=$OBJ/pkcs11.info - export SOFTPKCS11RC -@@ -23,7 +30,7 @@ notty() { - } - - trace "start agent" --eval `${SSHAGENT} -s` > /dev/null -+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null - r=$? - if [ $r -ne 0 ]; then - fail "could not start ssh-agent: exit code $r" -@@ -60,7 +67,7 @@ else - fi - - trace "remove pkcs11 keys" -- echo ${TEST_SSH_PIN} | notty ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 -+ ${SSHADD} -e ${TEST_SSH_PKCS11} > /dev/null 2>&1 - r=$? - if [ $r -ne 0 ]; then - fail "ssh-add -e failed: exit code $r" -diff -up openssh/regress/locl.h.pkcs11-uri openssh/regress/locl.h ---- openssh/regress/locl.h.pkcs11-uri 2018-10-12 13:52:55.451191409 +0200 -+++ openssh/regress/locl.h 2018-10-12 13:52:55.451191409 +0200 -@@ -0,0 +1,78 @@ -+/* -+ * Copyright (c) 2004, Stockholms universitet -+ * (Stockholm University, Stockholm Sweden) -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. Neither the name of the university nor the names of its contributors -+ * may be used to endorse or promote products derived from this software -+ * without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+/* $Id: locl.h,v 1.5 2005/08/28 15:30:31 lha Exp $ */ -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#include -+#include -+#define _GNU_SOURCE -+#include -+#include -+#include -+#include -+ -+#include "../pkcs11.h" -+ -+#define OPENSSL_ASN1_MALLOC_ENCODE(T, B, BL, S, R) \ -+{ \ -+ unsigned char *p; \ -+ (BL) = i2d_##T((S), NULL); \ -+ if ((BL) <= 0) { \ -+ (R) = EINVAL; \ -+ } else { \ -+ (B) = malloc((BL)); \ -+ if ((B) == NULL) { \ -+ (R) = ENOMEM; \ -+ } else { \ -+ p = (B); \ -+ (R) = 0; \ -+ (BL) = i2d_##T((S), &p); \ -+ if ((BL) <= 0) { \ -+ free((B)); \ -+ (R) = EINVAL; \ -+ } \ -+ } \ -+ } \ -+} -diff -up openssh/regress/Makefile.pkcs11-uri openssh/regress/Makefile ---- openssh/regress/Makefile.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/regress/Makefile 2018-10-12 13:52:55.451191409 +0200 -@@ -109,9 +109,11 @@ 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 \ -+ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ -+ rsa-agent-cert.pub rsa.pub \ - rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ -- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ -+ soft-pkcs11.so soft-pkcs11.o pkcs11*.crt pkcs11*.key \ -+ pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ - scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ - sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ - ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ -@@ -226,6 +228,7 @@ unit: - V="" ; \ - test "x${USE_VALGRIND}" = "x" || \ - V=${.CURDIR}/valgrind-unit.sh ; \ -+ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ - $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ - $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ - -d ${.CURDIR}/unittests/sshkey/testdata ; \ -diff -up openssh/regress/pkcs11.sh.pkcs11-uri openssh/regress/pkcs11.sh ---- openssh/regress/pkcs11.sh.pkcs11-uri 2018-10-12 13:52:55.451191409 +0200 -+++ openssh/regress/pkcs11.sh 2018-10-12 13:52:55.451191409 +0200 -@@ -0,0 +1,300 @@ -+# -+# Copyright (c) 2017 Red Hat -+# -+# Authors: Jakub Jelen -+# -+# Permission to use, copy, modify, and distribute this software for any -+# purpose with or without fee is hereby granted, provided that the above -+# copyright notice and this permission notice appear in all copies. -+# -+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ -+tid="pkcs11 tests with soft token" -+ -+TEST_SSH_PIN="" -+TEST_SSH_PKCS11=$OBJ/soft-pkcs11.so -+ -+test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" -+ -+# requires ssh-agent built with correct path to ssh-pkcs11-helper -+# otherwise it fails to start the helper -+strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" -+if [ $? -ne 0 ]; then -+ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" -+fi -+ -+# setup environment for soft-pkcs11 token -+SOFTPKCS11RC=$OBJ/pkcs11.info -+rm -f $SOFTPKCS11RC -+export SOFTPKCS11RC -+# prevent ssh-agent from calling ssh-askpass -+SSH_ASKPASS=/usr/bin/true -+export SSH_ASKPASS -+unset DISPLAY -+ -+# start command w/o tty, so ssh accepts pin from stdin (from agent-pkcs11.sh) -+notty() { -+ perl -e 'use POSIX; POSIX::setsid(); -+ if (fork) { wait; exit($? >> 8); } else { exec(@ARGV) }' "$@" -+} -+ -+create_key() { -+ ID=$1 -+ LABEL=$2 -+ rm -f $OBJ/pkcs11-${ID}.key $OBJ/pkcs11-${ID}.crt -+ openssl genrsa -out $OBJ/pkcs11-${ID}.key 2048 > /dev/null 2>&1 -+ chmod 600 $OBJ/pkcs11-${ID}.key -+ openssl req -key $OBJ/pkcs11-${ID}.key -new -x509 \ -+ -out $OBJ/pkcs11-${ID}.crt -text -subj '/CN=pkcs11 test' >/dev/null -+ printf "${ID}\t${LABEL}\t$OBJ/pkcs11-${ID}.crt\t$OBJ/pkcs11-${ID}.key\n" \ -+ >> $SOFTPKCS11RC -+} -+ -+trace "Create a key pairs on soft token" -+ID1="02" -+ID2="04" -+create_key "$ID1" "SSH RSA Key" -+create_key "$ID2" "SSH RSA Key 2" -+ -+trace "List the keys in the ssh-keygen with PKCS#11 URIs" -+${SSHKEYGEN} -D ${TEST_SSH_PKCS11} > $OBJ/token_keys -+if [ $? -ne 0 ]; then -+ fail "keygen fails to enumerate keys on PKCS#11 token" -+fi -+grep "pkcs11:" $OBJ/token_keys > /dev/null -+if [ $? -ne 0 ]; then -+ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" -+fi -+tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER -+ -+ -+trace "Simple connect with ssh (without PKCS#11 URI)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ -+ -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with pkcs11 failed (exit code $r)" -+fi -+ -+ -+trace "Connect with PKCS#11 URI" -+trace " (second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (first key should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+trace "Connect with various filtering options in PKCS#11 URI" -+trace " (by object label, second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (by object label, first key should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+trace " (by token label, second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI failed (exit code $r)" -+fi -+ -+trace " (by wrong token label, should fail)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI succeeded (should fail)" -+fi -+ -+ -+ -+ -+trace "Test PKCS#11 URI specification in configuration files" -+echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ -+ >> $OBJ/ssh_proxy -+trace " (second key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" -+fi -+ -+trace " (first key should fail)" -+head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -eq 5 ]; then -+ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" -+fi -+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy -+ -+trace "Test PKCS#11 URI specification in configuration files with bogus spaces" -+echo "IdentityFile \" pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} \"" \ -+ >> $OBJ/ssh_proxy -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ -+ "(exit code $r)" -+fi -+sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy -+ -+ -+trace "Combination of PKCS11Provider and PKCS11URI on commandline" -+trace " (first key should succeed)" -+echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ -+ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 -+r=$? -+if [ $r -ne 5 ]; then -+ fail "ssh connect with PKCS#11 URI and provider combination" \ -+ "failed (exit code $r)" -+fi -+ -+trace "Regress: Missing provider in PKCS11URI option" -+${SSH} -F $OBJ/ssh_proxy \ -+ -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 -+r=$? -+if [ $r -eq 139 ]; then -+ fail "ssh connect with missing provider_id from configuration option" \ -+ "crashed (exit code $r)" -+fi -+ -+ -+trace "SSH Agent can work with PKCS#11 URI" -+trace "start the agent" -+eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null -+ -+r=$? -+if [ $r -ne 0 ]; then -+ fail "could not start ssh-agent: exit code $r" -+else -+ trace "add whole provider to agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with whole provider: exit code $r" -+ fi -+ -+ trace " pkcs11 list via agent (all keys)" -+ ${SSHADD} -l > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -l failed with whole provider: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (all keys)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with whole provider (exit code $r)" -+ fi -+ -+ trace " remove pkcs11 keys (all keys)" -+ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with whole provider: exit code $r" -+ fi -+ -+ trace "add only first key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with first key: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (first key)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with first key (exit code $r)" -+ fi -+ -+ trace " remove first pkcs11 key" -+ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with first key: exit code $r" -+ fi -+ -+ trace "add only second key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with second key: exit code $r" -+ fi -+ -+ trace " pkcs11 connect via agent (second key should fail)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -eq 5 ]; then -+ fail "ssh connect passed without key (should fail)" -+ fi -+ -+ trace "add also the first key to the agent" -+ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ -+ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add failed with first key: exit code $r" -+ fi -+ -+ trace " remove second pkcs11 key" -+ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -ne 0 ]; then -+ fail "ssh-add -d failed with second key: exit code $r" -+ fi -+ -+ trace " remove already-removed pkcs11 key should fail" -+ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ -+ > /dev/null 2>&1 -+ r=$? -+ if [ $r -eq 0 ]; then -+ fail "ssh-add -d passed with non-existing key (should fail)" -+ fi -+ -+ trace " pkcs11 connect via agent (the first key should be still usable)" -+ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 -+ r=$? -+ if [ $r -ne 5 ]; then -+ fail "ssh connect failed with first key (after removing second): exit code $r" -+ fi -+ -+ trace "kill agent" -+ ${SSHAGENT} -k > /dev/null -+fi -+ -+rm -rf $OBJ/.tokens $OBJ/token_keys -diff -up openssh/regress/soft-pkcs11.c.pkcs11-uri openssh/regress/soft-pkcs11.c ---- openssh/regress/soft-pkcs11.c.pkcs11-uri 2018-10-12 13:52:55.452191417 +0200 -+++ openssh/regress/soft-pkcs11.c 2018-10-12 13:52:55.452191417 +0200 -@@ -0,0 +1,2058 @@ -+/* -+ * Copyright (c) 2004-2006, Stockholms universitet -+ * (Stockholm University, Stockholm Sweden) -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * 3. Neither the name of the university nor the names of its contributors -+ * may be used to endorse or promote products derived from this software -+ * without specific prior written permission. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -+ * POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "locl.h" -+ -+/* RCSID("$Id: main.c,v 1.24 2006/01/11 12:42:53 lha Exp $"); */ -+ -+#define OBJECT_ID_MASK 0xfff -+#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK) -+#define OBJECT_ID(obj) HANDLE_OBJECT_ID((obj)->object_handle) -+ -+#if OPENSSL_VERSION_NUMBER < 0x10100000L -+ #define RSA_PKCS1_SSLeay RSA_PKCS1_OpenSSL -+#endif -+ -+struct st_attr { -+ CK_ATTRIBUTE attribute; -+ int secret; -+}; -+ -+struct st_object { -+ CK_OBJECT_HANDLE object_handle; -+ struct st_attr *attrs; -+ int num_attributes; -+ enum { -+ STO_T_CERTIFICATE, -+ STO_T_PRIVATE_KEY, -+ STO_T_PUBLIC_KEY -+ } type; -+ union { -+ X509 *cert; -+ EVP_PKEY *public_key; -+ struct { -+ const char *file; -+ EVP_PKEY *key; -+ X509 *cert; -+ } private_key; -+ } u; -+}; -+ -+static struct soft_token { -+ CK_VOID_PTR application; -+ CK_NOTIFY notify; -+ struct { -+ struct st_object **objs; -+ int num_objs; -+ } object; -+ struct { -+ int hardware_slot; -+ int app_error_fatal; -+ int login_done; -+ } flags; -+ int open_sessions; -+ struct session_state { -+ CK_SESSION_HANDLE session_handle; -+ -+ struct { -+ CK_ATTRIBUTE *attributes; -+ CK_ULONG num_attributes; -+ int next_object; -+ } find; -+ -+ int encrypt_object; -+ CK_MECHANISM_PTR encrypt_mechanism; -+ int decrypt_object; -+ CK_MECHANISM_PTR decrypt_mechanism; -+ int sign_object; -+ CK_MECHANISM_PTR sign_mechanism; -+ int verify_object; -+ CK_MECHANISM_PTR verify_mechanism; -+ int digest_object; -+ } state[10]; -+#define MAX_NUM_SESSION (sizeof(soft_token.state)/sizeof(soft_token.state[0])) -+ FILE *logfile; -+} soft_token; -+ -+static void -+application_error(const char *fmt, ...) -+{ -+ va_list ap; -+ va_start(ap, fmt); -+ vprintf(fmt, ap); -+ va_end(ap); -+ if (soft_token.flags.app_error_fatal) -+ abort(); -+} -+ -+static void -+st_logf(const char *fmt, ...) -+{ -+ va_list ap; -+ if (soft_token.logfile == NULL) -+ return; -+ va_start(ap, fmt); -+ vfprintf(soft_token.logfile, fmt, ap); -+ va_end(ap); -+ fflush(soft_token.logfile); -+} -+ -+static void -+snprintf_fill(char *str, size_t size, char fillchar, const char *fmt, ...) -+{ -+ int len; -+ va_list ap; -+ len = vsnprintf(str, size, fmt, ap); -+ va_end(ap); -+ if (len < 0 || len > (int) size) -+ return; -+ while(len < (int) size) -+ str[len++] = fillchar; -+} -+ -+#ifndef TEST_APP -+#define printf error_use_st_logf -+#endif -+ -+#define VERIFY_SESSION_HANDLE(s, state) \ -+{ \ -+ CK_RV ret; \ -+ ret = verify_session_handle(s, state); \ -+ if (ret != CKR_OK) { \ -+ /* return CKR_OK */; \ -+ } \ -+} -+ -+static CK_RV -+verify_session_handle(CK_SESSION_HANDLE hSession, -+ struct session_state **state) -+{ -+ size_t i; -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++){ -+ if (soft_token.state[i].session_handle == hSession) -+ break; -+ } -+ if (i == MAX_NUM_SESSION) { -+ application_error("use of invalid handle: 0x%08lx\n", -+ (unsigned long)hSession); -+ return CKR_SESSION_HANDLE_INVALID; -+ } -+ if (state) -+ *state = &soft_token.state[i]; -+ return CKR_OK; -+} -+ -+static CK_RV -+object_handle_to_object(CK_OBJECT_HANDLE handle, -+ struct st_object **object) -+{ -+ int i = HANDLE_OBJECT_ID(handle); -+ -+ *object = NULL; -+ if (i >= soft_token.object.num_objs) -+ return CKR_ARGUMENTS_BAD; -+ if (soft_token.object.objs[i] == NULL) -+ return CKR_ARGUMENTS_BAD; -+ if (soft_token.object.objs[i]->object_handle != handle) -+ return CKR_ARGUMENTS_BAD; -+ *object = soft_token.object.objs[i]; -+ return CKR_OK; -+} -+ -+static int -+attributes_match(const struct st_object *obj, -+ const CK_ATTRIBUTE *attributes, -+ CK_ULONG num_attributes) -+{ -+ CK_ULONG i; -+ int j; -+ st_logf("attributes_match: %ld\n", (unsigned long)OBJECT_ID(obj)); -+ -+ for (i = 0; i < num_attributes; i++) { -+ int match = 0; -+ for (j = 0; j < obj->num_attributes; j++) { -+ if (attributes[i].type == obj->attrs[j].attribute.type && -+ attributes[i].ulValueLen == obj->attrs[j].attribute.ulValueLen && -+ memcmp(attributes[i].pValue, obj->attrs[j].attribute.pValue, -+ attributes[i].ulValueLen) == 0) { -+ match = 1; -+ break; -+ } -+ } -+ if (match == 0) { -+ st_logf("type %d attribute have no match\n", attributes[i].type); -+ return 0; -+ } -+ } -+ st_logf("attribute matches\n"); -+ return 1; -+} -+ -+static void -+print_attributes(const CK_ATTRIBUTE *attributes, -+ CK_ULONG num_attributes) -+{ -+ CK_ULONG i; -+ -+ st_logf("find objects: attrs: %lu\n", (unsigned long)num_attributes); -+ -+ for (i = 0; i < num_attributes; i++) { -+ st_logf(" type: "); -+ switch (attributes[i].type) { -+ case CKA_TOKEN: { -+ CK_BBOOL *ck_true; -+ if (attributes[i].ulValueLen != sizeof(CK_BBOOL)) { -+ application_error("token attribute wrong length\n"); -+ break; -+ } -+ ck_true = attributes[i].pValue; -+ st_logf("token: %s", *ck_true ? "TRUE" : "FALSE"); -+ break; -+ } -+ case CKA_CLASS: { -+ CK_OBJECT_CLASS *class; -+ if (attributes[i].ulValueLen != sizeof(CK_ULONG)) { -+ application_error("class attribute wrong length\n"); -+ break; -+ } -+ class = attributes[i].pValue; -+ st_logf("class "); -+ switch (*class) { -+ case CKO_CERTIFICATE: -+ st_logf("certificate"); -+ break; -+ case CKO_PUBLIC_KEY: -+ st_logf("public key"); -+ break; -+ case CKO_PRIVATE_KEY: -+ st_logf("private key"); -+ break; -+ case CKO_SECRET_KEY: -+ st_logf("secret key"); -+ break; -+ case CKO_DOMAIN_PARAMETERS: -+ st_logf("domain parameters"); -+ break; -+ default: -+ st_logf("[class %lx]", (long unsigned)*class); -+ break; -+ } -+ break; -+ } -+ case CKA_PRIVATE: -+ st_logf("private"); -+ break; -+ case CKA_LABEL: -+ st_logf("label"); -+ break; -+ case CKA_APPLICATION: -+ st_logf("application"); -+ break; -+ case CKA_VALUE: -+ st_logf("value"); -+ break; -+ case CKA_ID: -+ st_logf("id"); -+ break; -+ default: -+ st_logf("[unknown 0x%08lx]", (unsigned long)attributes[i].type); -+ break; -+ } -+ st_logf("\n"); -+ } -+} -+ -+static struct st_object * -+add_st_object(void) -+{ -+ struct st_object *o, **objs; -+ int i; -+ -+ o = malloc(sizeof(*o)); -+ if (o == NULL) -+ return NULL; -+ memset(o, 0, sizeof(*o)); -+ o->attrs = NULL; -+ o->num_attributes = 0; -+ -+ for (i = 0; i < soft_token.object.num_objs; i++) { -+ if (soft_token.object.objs == NULL) { -+ soft_token.object.objs[i] = o; -+ break; -+ } -+ } -+ if (i == soft_token.object.num_objs) { -+ objs = realloc(soft_token.object.objs, -+ (soft_token.object.num_objs + 1) * sizeof(soft_token.object.objs[0])); -+ if (objs == NULL) { -+ free(o); -+ return NULL; -+ } -+ soft_token.object.objs = objs; -+ soft_token.object.objs[soft_token.object.num_objs++] = o; -+ } -+ soft_token.object.objs[i]->object_handle = -+ (random() & (~OBJECT_ID_MASK)) | i; -+ -+ return o; -+} -+ -+static CK_RV -+add_object_attribute(struct st_object *o, -+ int secret, -+ CK_ATTRIBUTE_TYPE type, -+ CK_VOID_PTR pValue, -+ CK_ULONG ulValueLen) -+{ -+ struct st_attr *a; -+ int i; -+ -+ i = o->num_attributes; -+ a = realloc(o->attrs, (i + 1) * sizeof(o->attrs[0])); -+ if (a == NULL) -+ return CKR_DEVICE_MEMORY; -+ o->attrs = a; -+ o->attrs[i].secret = secret; -+ o->attrs[i].attribute.type = type; -+ o->attrs[i].attribute.pValue = malloc(ulValueLen); -+ if (o->attrs[i].attribute.pValue == NULL && ulValueLen != 0) -+ return CKR_DEVICE_MEMORY; -+ memcpy(o->attrs[i].attribute.pValue, pValue, ulValueLen); -+ o->attrs[i].attribute.ulValueLen = ulValueLen; -+ o->num_attributes++; -+ -+ return CKR_OK; -+} -+ -+static CK_RV -+add_pubkey_info(struct st_object *o, CK_KEY_TYPE key_type, EVP_PKEY *key) -+{ -+ switch (key_type) { -+ case CKK_RSA: { -+ CK_BYTE *modulus = NULL; -+ size_t modulus_len = 0; -+ CK_ULONG modulus_bits = 0; -+ CK_BYTE *exponent = NULL; -+ size_t exponent_len = 0; -+ RSA* rsa = NULL; -+ const BIGNUM *n = NULL, *e = NULL; -+ -+ rsa = EVP_PKEY_get0_RSA(key); -+ RSA_get0_key(rsa, &n, &e, NULL); -+ -+ modulus_bits = BN_num_bits(n); -+ -+ modulus_len = BN_num_bytes(n); -+ modulus = malloc(modulus_len); -+ BN_bn2bin(n, modulus); -+ -+ exponent_len = BN_num_bytes(e); -+ exponent = malloc(exponent_len); -+ BN_bn2bin(e, exponent); -+ -+ add_object_attribute(o, 0, CKA_MODULUS, modulus, modulus_len); -+ add_object_attribute(o, 0, CKA_MODULUS_BITS, -+ &modulus_bits, sizeof(modulus_bits)); -+ add_object_attribute(o, 0, CKA_PUBLIC_EXPONENT, -+ exponent, exponent_len); -+ -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ -+ free(modulus); -+ free(exponent); -+ } -+ default: -+ /* XXX */ -+ break; -+ } -+ return CKR_OK; -+} -+ -+ -+static int -+pem_callback(char *buf, int num, int w, void *key) -+{ -+ return -1; -+} -+ -+ -+static CK_RV -+add_certificate(char *label, -+ const char *cert_file, -+ const char *private_key_file, -+ char *id, -+ int anchor) -+{ -+ struct st_object *o = NULL; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_BBOOL bool_false = CK_FALSE; -+ CK_OBJECT_CLASS c; -+ CK_CERTIFICATE_TYPE cert_type = CKC_X_509; -+ CK_KEY_TYPE key_type; -+ CK_MECHANISM_TYPE mech_type; -+ void *cert_data = NULL; -+ size_t cert_length; -+ void *subject_data = NULL; -+ size_t subject_length; -+ void *issuer_data = NULL; -+ size_t issuer_length; -+ void *serial_data = NULL; -+ size_t serial_length; -+ CK_RV ret = CKR_GENERAL_ERROR; -+ X509 *cert; -+ EVP_PKEY *public_key; -+ -+ size_t id_len = strlen(id); -+ -+ { -+ FILE *f; -+ -+ f = fopen(cert_file, "r"); -+ if (f == NULL) { -+ st_logf("failed to open file %s\n", cert_file); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ cert = PEM_read_X509(f, NULL, NULL, NULL); -+ fclose(f); -+ if (cert == NULL) { -+ st_logf("failed reading PEM cert\n"); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509, cert_data, cert_length, cert, ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, issuer_data, issuer_length, -+ X509_get_issuer_name(cert), ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(X509_NAME, subject_data, subject_length, -+ X509_get_subject_name(cert), ret); -+ if (ret) -+ goto out; -+ -+ OPENSSL_ASN1_MALLOC_ENCODE(ASN1_INTEGER, serial_data, serial_length, -+ X509_get_serialNumber(cert), ret); -+ if (ret) -+ goto out; -+ -+ } -+ -+ st_logf("done parsing, adding to internal structure\n"); -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_CERTIFICATE; -+ o->u.cert = cert; -+ public_key = X509_get_pubkey(o->u.cert); -+ -+ switch (EVP_PKEY_base_id(public_key)) { -+ case EVP_PKEY_RSA: -+ key_type = CKK_RSA; -+ break; -+ case EVP_PKEY_DSA: -+ key_type = CKK_DSA; -+ break; -+ default: -+ /* XXX */ -+ break; -+ } -+ -+ c = CKO_CERTIFICATE; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_CERTIFICATE_TYPE, &cert_type, sizeof(cert_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_ISSUER, issuer_data, issuer_length); -+ add_object_attribute(o, 0, CKA_SERIAL_NUMBER, serial_data, serial_length); -+ add_object_attribute(o, 0, CKA_VALUE, cert_data, cert_length); -+ if (anchor) -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); -+ else -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_false, sizeof(bool_false)); -+ -+ st_logf("add cert ok: %lx\n", (unsigned long)OBJECT_ID(o)); -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_PUBLIC_KEY; -+ o->u.public_key = public_key; -+ -+ c = CKO_PUBLIC_KEY; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); -+ mech_type = CKM_RSA_X_509; -+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_ENCRYPT, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_VERIFY, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_VERIFY_RECOVER, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_WRAP, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_TRUSTED, &bool_true, sizeof(bool_true)); -+ -+ add_pubkey_info(o, key_type, public_key); -+ -+ st_logf("add key ok: %lx\n", (unsigned long)OBJECT_ID(o)); -+ -+ if (private_key_file) { -+ CK_FLAGS flags; -+ FILE *f; -+ -+ o = add_st_object(); -+ if (o == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ o->type = STO_T_PRIVATE_KEY; -+ o->u.private_key.file = strdup(private_key_file); -+ o->u.private_key.key = NULL; -+ -+ o->u.private_key.cert = cert; -+ -+ c = CKO_PRIVATE_KEY; -+ add_object_attribute(o, 0, CKA_CLASS, &c, sizeof(c)); -+ add_object_attribute(o, 0, CKA_TOKEN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_PRIVATE, &bool_true, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_MODIFIABLE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LABEL, label, strlen(label)); -+ -+ add_object_attribute(o, 0, CKA_KEY_TYPE, &key_type, sizeof(key_type)); -+ add_object_attribute(o, 0, CKA_ID, id, id_len); -+ add_object_attribute(o, 0, CKA_START_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_END_DATE, "", 1); /* XXX */ -+ add_object_attribute(o, 0, CKA_DERIVE, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_LOCAL, &bool_false, sizeof(bool_false)); -+ mech_type = CKM_RSA_X_509; -+ add_object_attribute(o, 0, CKA_KEY_GEN_MECHANISM, &mech_type, sizeof(mech_type)); -+ -+ add_object_attribute(o, 0, CKA_SUBJECT, subject_data, subject_length); -+ add_object_attribute(o, 0, CKA_SENSITIVE, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SECONDARY_AUTH, &bool_false, sizeof(bool_true)); -+ flags = 0; -+ add_object_attribute(o, 0, CKA_AUTH_PIN_FLAGS, &flags, sizeof(flags)); -+ -+ add_object_attribute(o, 0, CKA_DECRYPT, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SIGN, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_SIGN_RECOVER, &bool_false, sizeof(bool_false)); -+ add_object_attribute(o, 0, CKA_UNWRAP, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_EXTRACTABLE, &bool_true, sizeof(bool_true)); -+ add_object_attribute(o, 0, CKA_NEVER_EXTRACTABLE, &bool_false, sizeof(bool_false)); -+ -+ add_pubkey_info(o, key_type, public_key); -+ -+ f = fopen(private_key_file, "r"); -+ if (f == NULL) { -+ st_logf("failed to open private key\n"); -+ return CKR_GENERAL_ERROR; -+ } -+ -+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, pem_callback, NULL); -+ fclose(f); -+ if (o->u.private_key.key == NULL) { -+ st_logf("failed to read private key a startup\n"); -+ /* don't bother with this failure for now, -+ fix it at C_Login time */; -+ } else { -+ /* XXX verify keytype */ -+ -+ if (key_type == CKK_RSA) { -+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ } -+ -+ if (X509_check_private_key(cert, o->u.private_key.key) != 1) { -+ EVP_PKEY_free(o->u.private_key.key); -+ o->u.private_key.key = NULL; -+ st_logf("private key doesn't verify\n"); -+ } else { -+ st_logf("private key usable\n"); -+ soft_token.flags.login_done = 1; -+ } -+ } -+ } -+ -+ ret = CKR_OK; -+ out: -+ if (ret != CKR_OK) { -+ st_logf("something went wrong when adding cert!\n"); -+ -+ /* XXX wack o */; -+ } -+ free(cert_data); -+ free(serial_data); -+ free(issuer_data); -+ free(subject_data); -+ -+ return ret; -+} -+ -+static void -+find_object_final(struct session_state *state) -+{ -+ if (state->find.attributes) { -+ CK_ULONG i; -+ -+ for (i = 0; i < state->find.num_attributes; i++) { -+ if (state->find.attributes[i].pValue) -+ free(state->find.attributes[i].pValue); -+ } -+ free(state->find.attributes); -+ state->find.attributes = NULL; -+ state->find.num_attributes = 0; -+ state->find.next_object = -1; -+ } -+} -+ -+static void -+reset_crypto_state(struct session_state *state) -+{ -+ state->encrypt_object = -1; -+ if (state->encrypt_mechanism) -+ free(state->encrypt_mechanism); -+ state->encrypt_mechanism = NULL_PTR; -+ state->decrypt_object = -1; -+ if (state->decrypt_mechanism) -+ free(state->decrypt_mechanism); -+ state->decrypt_mechanism = NULL_PTR; -+ state->sign_object = -1; -+ if (state->sign_mechanism) -+ free(state->sign_mechanism); -+ state->sign_mechanism = NULL_PTR; -+ state->verify_object = -1; -+ if (state->verify_mechanism) -+ free(state->verify_mechanism); -+ state->verify_mechanism = NULL_PTR; -+ state->digest_object = -1; -+} -+ -+static void -+close_session(struct session_state *state) -+{ -+ if (state->find.attributes) { -+ application_error("application didn't do C_FindObjectsFinal\n"); -+ find_object_final(state); -+ } -+ -+ state->session_handle = CK_INVALID_HANDLE; -+ soft_token.application = NULL_PTR; -+ soft_token.notify = NULL_PTR; -+ reset_crypto_state(state); -+} -+ -+static const char * -+has_session(void) -+{ -+ return soft_token.open_sessions > 0 ? "yes" : "no"; -+} -+ -+static void -+read_conf_file(const char *fn) -+{ -+ char buf[1024], *cert, *key, *id, *label, *s, *p; -+ int anchor; -+ FILE *f; -+ -+ f = fopen(fn, "r"); -+ if (f == NULL) { -+ st_logf("can't open configuration file %s\n", fn); -+ return; -+ } -+ -+ while(fgets(buf, sizeof(buf), f) != NULL) { -+ buf[strcspn(buf, "\n")] = '\0'; -+ -+ anchor = 0; -+ -+ st_logf("line: %s\n", buf); -+ -+ p = buf; -+ while (isspace(*p)) -+ p++; -+ if (*p == '#') -+ continue; -+ while (isspace(*p)) -+ p++; -+ -+ s = NULL; -+ id = strtok_r(p, "\t", &s); -+ if (id == NULL) -+ continue; -+ label = strtok_r(NULL, "\t", &s); -+ if (label == NULL) -+ continue; -+ cert = strtok_r(NULL, "\t", &s); -+ if (cert == NULL) -+ continue; -+ key = strtok_r(NULL, "\t", &s); -+ -+ /* XXX */ -+ if (strcmp(id, "anchor") == 0) { -+ id = "\x00\x00"; -+ anchor = 1; -+ } -+ -+ st_logf("adding: %s\n", label); -+ -+ add_certificate(label, cert, key, id, anchor); -+ } -+} -+ -+static CK_RV -+func_not_supported(void) -+{ -+ st_logf("function not supported\n"); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_Initialize(CK_VOID_PTR a) -+{ -+ CK_C_INITIALIZE_ARGS_PTR args = a; -+ st_logf("Initialize\n"); -+ size_t i; -+ -+ OpenSSL_add_all_algorithms(); -+ ERR_load_crypto_strings(); -+ -+ srandom(getpid() ^ time(NULL)); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) { -+ soft_token.state[i].session_handle = CK_INVALID_HANDLE; -+ soft_token.state[i].find.attributes = NULL; -+ soft_token.state[i].find.num_attributes = 0; -+ soft_token.state[i].find.next_object = -1; -+ reset_crypto_state(&soft_token.state[i]); -+ } -+ -+ soft_token.flags.hardware_slot = 1; -+ soft_token.flags.app_error_fatal = 0; -+ soft_token.flags.login_done = 0; -+ -+ soft_token.object.objs = NULL; -+ soft_token.object.num_objs = 0; -+ -+ soft_token.logfile = NULL; -+#if 1 -+// soft_token.logfile = stdout; -+#endif -+#if 0 -+ soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a"); -+#endif -+ -+ if (a != NULL_PTR) { -+ st_logf("\tCreateMutex:\t%p\n", args->CreateMutex); -+ st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex); -+ st_logf("\tLockMutext\t%p\n", args->LockMutex); -+ st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex); -+ st_logf("\tFlags\t%04x\n", (unsigned int)args->flags); -+ } -+ -+ { -+ char *fn = NULL, *home = NULL; -+ -+ if (getuid() == geteuid()) { -+ fn = getenv("SOFTPKCS11RC"); -+ if (fn) -+ fn = strdup(fn); -+ home = getenv("HOME"); -+ } -+ if (fn == NULL && home == NULL) { -+ struct passwd *pw = getpwuid(getuid()); -+ if(pw != NULL) -+ home = pw->pw_dir; -+ } -+ if (fn == NULL) { -+ if (home) -+ asprintf(&fn, "%s/.soft-token.rc", home); -+ else -+ fn = strdup("/etc/soft-token.rc"); -+ } -+ -+ read_conf_file(fn); -+ free(fn); -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Finalize(CK_VOID_PTR args) -+{ -+ size_t i; -+ -+ st_logf("Finalize\n"); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) { -+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) { -+ application_error("application finalized without " -+ "closing session\n"); -+ close_session(&soft_token.state[i]); -+ } -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetInfo(CK_INFO_PTR args) -+{ -+ st_logf("GetInfo\n"); -+ -+ memset(args, 17, sizeof(*args)); -+ args->cryptokiVersion.major = 2; -+ args->cryptokiVersion.minor = 10; -+ snprintf_fill((char *)args->manufacturerID, -+ sizeof(args->manufacturerID), -+ ' ', -+ "SoftToken"); -+ snprintf_fill((char *)args->libraryDescription, -+ sizeof(args->libraryDescription), ' ', -+ "SoftToken"); -+ args->libraryVersion.major = 1; -+ args->libraryVersion.minor = 8; -+ -+ return CKR_OK; -+} -+ -+extern CK_FUNCTION_LIST funcs; -+ -+CK_RV -+C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) -+{ -+ *ppFunctionList = &funcs; -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSlotList(CK_BBOOL tokenPresent, -+ CK_SLOT_ID_PTR pSlotList, -+ CK_ULONG_PTR pulCount) -+{ -+ st_logf("GetSlotList: %s\n", -+ tokenPresent ? "tokenPresent" : "token not Present"); -+ if (pSlotList) -+ pSlotList[0] = 1; -+ *pulCount = 1; -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSlotInfo(CK_SLOT_ID slotID, -+ CK_SLOT_INFO_PTR pInfo) -+{ -+ st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session()); -+ -+ memset(pInfo, 18, sizeof(*pInfo)); -+ -+ if (slotID != 1) -+ return CKR_ARGUMENTS_BAD; -+ -+ snprintf_fill((char *)pInfo->slotDescription, -+ sizeof(pInfo->slotDescription), -+ ' ', -+ "SoftToken (slot)"); -+ snprintf_fill((char *)pInfo->manufacturerID, -+ sizeof(pInfo->manufacturerID), -+ ' ', -+ "SoftToken (slot)"); -+ pInfo->flags = CKF_TOKEN_PRESENT; -+ if (soft_token.flags.hardware_slot) -+ pInfo->flags |= CKF_HW_SLOT; -+ pInfo->hardwareVersion.major = 1; -+ pInfo->hardwareVersion.minor = 0; -+ pInfo->firmwareVersion.major = 1; -+ pInfo->firmwareVersion.minor = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetTokenInfo(CK_SLOT_ID slotID, -+ CK_TOKEN_INFO_PTR pInfo) -+{ -+ st_logf("GetTokenInfo: %s\n", has_session()); -+ -+ memset(pInfo, 19, sizeof(*pInfo)); -+ -+ snprintf_fill((char *)pInfo->label, -+ sizeof(pInfo->label), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->manufacturerID, -+ sizeof(pInfo->manufacturerID), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->model, -+ sizeof(pInfo->model), -+ ' ', -+ "SoftToken (token)"); -+ snprintf_fill((char *)pInfo->serialNumber, -+ sizeof(pInfo->serialNumber), -+ ' ', -+ "4711"); -+ pInfo->flags = -+ CKF_TOKEN_INITIALIZED | -+ CKF_USER_PIN_INITIALIZED; -+ -+ if (soft_token.flags.login_done == 0) -+ pInfo->flags |= CKF_LOGIN_REQUIRED; -+ -+ /* CFK_RNG | -+ CKF_RESTORE_KEY_NOT_NEEDED | -+ */ -+ pInfo->ulMaxSessionCount = MAX_NUM_SESSION; -+ pInfo->ulSessionCount = soft_token.open_sessions; -+ pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION; -+ pInfo->ulRwSessionCount = soft_token.open_sessions; -+ pInfo->ulMaxPinLen = 1024; -+ pInfo->ulMinPinLen = 0; -+ pInfo->ulTotalPublicMemory = 4711; -+ pInfo->ulFreePublicMemory = 4712; -+ pInfo->ulTotalPrivateMemory = 4713; -+ pInfo->ulFreePrivateMemory = 4714; -+ pInfo->hardwareVersion.major = 2; -+ pInfo->hardwareVersion.minor = 0; -+ pInfo->firmwareVersion.major = 2; -+ pInfo->firmwareVersion.minor = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetMechanismList(CK_SLOT_ID slotID, -+ CK_MECHANISM_TYPE_PTR pMechanismList, -+ CK_ULONG_PTR pulCount) -+{ -+ st_logf("GetMechanismList\n"); -+ -+ *pulCount = 2; -+ if (pMechanismList == NULL_PTR) -+ return CKR_OK; -+ pMechanismList[0] = CKM_RSA_X_509; -+ pMechanismList[1] = CKM_RSA_PKCS; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetMechanismInfo(CK_SLOT_ID slotID, -+ CK_MECHANISM_TYPE type, -+ CK_MECHANISM_INFO_PTR pInfo) -+{ -+ st_logf("GetMechanismInfo: slot %d type: %d\n", -+ (int)slotID, (int)type); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_InitToken(CK_SLOT_ID slotID, -+ CK_UTF8CHAR_PTR pPin, -+ CK_ULONG ulPinLen, -+ CK_UTF8CHAR_PTR pLabel) -+{ -+ st_logf("InitToken: slot %d\n", (int)slotID); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_OpenSession(CK_SLOT_ID slotID, -+ CK_FLAGS flags, -+ CK_VOID_PTR pApplication, -+ CK_NOTIFY Notify, -+ CK_SESSION_HANDLE_PTR phSession) -+{ -+ size_t i; -+ -+ st_logf("OpenSession: slot: %d\n", (int)slotID); -+ -+ if (soft_token.open_sessions == MAX_NUM_SESSION) -+ return CKR_SESSION_COUNT; -+ -+ soft_token.application = pApplication; -+ soft_token.notify = Notify; -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) -+ if (soft_token.state[i].session_handle == CK_INVALID_HANDLE) -+ break; -+ if (i == MAX_NUM_SESSION) -+ abort(); -+ -+ soft_token.open_sessions++; -+ -+ soft_token.state[i].session_handle = -+ (CK_SESSION_HANDLE)(random() & 0xfffff); -+ *phSession = soft_token.state[i].session_handle; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_CloseSession(CK_SESSION_HANDLE hSession) -+{ -+ struct session_state *state; -+ st_logf("CloseSession\n"); -+ -+ if (verify_session_handle(hSession, &state) != CKR_OK) -+ application_error("closed session not open"); -+ else -+ close_session(state); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_CloseAllSessions(CK_SLOT_ID slotID) -+{ -+ size_t i; -+ -+ st_logf("CloseAllSessions\n"); -+ -+ for (i = 0; i < MAX_NUM_SESSION; i++) -+ if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) -+ close_session(&soft_token.state[i]); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_GetSessionInfo(CK_SESSION_HANDLE hSession, -+ CK_SESSION_INFO_PTR pInfo) -+{ -+ st_logf("GetSessionInfo\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ -+ memset(pInfo, 20, sizeof(*pInfo)); -+ -+ pInfo->slotID = 1; -+ if (soft_token.flags.login_done) -+ pInfo->state = CKS_RO_USER_FUNCTIONS; -+ else -+ pInfo->state = CKS_RO_PUBLIC_SESSION; -+ pInfo->flags = CKF_SERIAL_SESSION; -+ pInfo->ulDeviceError = 0; -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Login(CK_SESSION_HANDLE hSession, -+ CK_USER_TYPE userType, -+ CK_UTF8CHAR_PTR pPin, -+ CK_ULONG ulPinLen) -+{ -+ char *pin = NULL; -+ int i; -+ -+ st_logf("Login\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ -+ if (pPin != NULL_PTR) { -+ asprintf(&pin, "%.*s", (int)ulPinLen, pPin); -+ st_logf("type: %d password: %s\n", (int)userType, pin); -+ } -+ -+ for (i = 0; i < soft_token.object.num_objs; i++) { -+ struct st_object *o = soft_token.object.objs[i]; -+ FILE *f; -+ -+ if (o->type != STO_T_PRIVATE_KEY) -+ continue; -+ -+ if (o->u.private_key.key) -+ continue; -+ -+ f = fopen(o->u.private_key.file, "r"); -+ if (f == NULL) { -+ st_logf("can't open private file: %s\n", o->u.private_key.file); -+ continue; -+ } -+ -+ o->u.private_key.key = PEM_read_PrivateKey(f, NULL, NULL, pin); -+ fclose(f); -+ if (o->u.private_key.key == NULL) { -+ st_logf("failed to read key: %s error: %s\n", -+ o->u.private_key.file, -+ ERR_error_string(ERR_get_error(), NULL)); -+ /* just ignore failure */; -+ continue; -+ } -+ -+ /* XXX check keytype */ -+ RSA *rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ RSA_set_method(rsa, RSA_PKCS1_OpenSSL()); -+ -+ if (X509_check_private_key(o->u.private_key.cert, o->u.private_key.key) != 1) { -+ EVP_PKEY_free(o->u.private_key.key); -+ o->u.private_key.key = NULL; -+ st_logf("private key %s doesn't verify\n", o->u.private_key.file); -+ continue; -+ } -+ -+ soft_token.flags.login_done = 1; -+ } -+ free(pin); -+ -+ return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT; -+} -+ -+CK_RV -+C_Logout(CK_SESSION_HANDLE hSession) -+{ -+ st_logf("Logout\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GetObjectSize(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE hObject, -+ CK_ULONG_PTR pulSize) -+{ -+ st_logf("GetObjectSize\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GetAttributeValue(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE hObject, -+ CK_ATTRIBUTE_PTR pTemplate, -+ CK_ULONG ulCount) -+{ -+ struct session_state *state; -+ struct st_object *obj; -+ CK_ULONG i; -+ CK_RV ret; -+ int j; -+ -+ st_logf("GetAttributeValue: %lx\n", -+ (unsigned long)HANDLE_OBJECT_ID(hObject)); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) { -+ st_logf("object not found: %lx\n", -+ (unsigned long)HANDLE_OBJECT_ID(hObject)); -+ return ret; -+ } -+ -+ ret = CKR_OK; -+ for (i = 0; i < ulCount; i++) { -+ st_logf(" getting 0x%08lx\n", (unsigned long)pTemplate[i].type); -+ for (j = 0; j < obj->num_attributes; j++) { -+ if (obj->attrs[j].secret) { -+ pTemplate[i].ulValueLen = (CK_ULONG)-1; -+ break; -+ } -+ if (pTemplate[i].type == obj->attrs[j].attribute.type) { -+ if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) { -+ if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen) -+ memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue, -+ obj->attrs[j].attribute.ulValueLen); -+ } -+ pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen; -+ break; -+ } -+ } -+ if (j == obj->num_attributes) { -+ st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type); -+ pTemplate[i].ulValueLen = (CK_ULONG)-1; -+ ret = CKR_ATTRIBUTE_TYPE_INVALID; -+ } -+ -+ } -+ return ret; -+} -+ -+CK_RV -+C_FindObjectsInit(CK_SESSION_HANDLE hSession, -+ CK_ATTRIBUTE_PTR pTemplate, -+ CK_ULONG ulCount) -+{ -+ struct session_state *state; -+ -+ st_logf("FindObjectsInit\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->find.next_object != -1) { -+ application_error("application didn't do C_FindObjectsFinal\n"); -+ find_object_final(state); -+ } -+ if (ulCount) { -+ CK_ULONG i; -+ -+ print_attributes(pTemplate, ulCount); -+ -+ state->find.attributes = -+ calloc(1, ulCount * sizeof(state->find.attributes[0])); -+ if (state->find.attributes == NULL) -+ return CKR_DEVICE_MEMORY; -+ for (i = 0; i < ulCount; i++) { -+ state->find.attributes[i].pValue = -+ malloc(pTemplate[i].ulValueLen); -+ if (state->find.attributes[i].pValue == NULL) { -+ find_object_final(state); -+ return CKR_DEVICE_MEMORY; -+ } -+ memcpy(state->find.attributes[i].pValue, -+ pTemplate[i].pValue, pTemplate[i].ulValueLen); -+ state->find.attributes[i].type = pTemplate[i].type; -+ state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen; -+ } -+ state->find.num_attributes = ulCount; -+ state->find.next_object = 0; -+ } else { -+ st_logf("find all objects\n"); -+ state->find.attributes = NULL; -+ state->find.num_attributes = 0; -+ state->find.next_object = 0; -+ } -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_FindObjects(CK_SESSION_HANDLE hSession, -+ CK_OBJECT_HANDLE_PTR phObject, -+ CK_ULONG ulMaxObjectCount, -+ CK_ULONG_PTR pulObjectCount) -+{ -+ struct session_state *state; -+ int i; -+ -+ st_logf("FindObjects\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->find.next_object == -1) { -+ application_error("application didn't do C_FindObjectsInit\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ if (ulMaxObjectCount == 0) { -+ application_error("application asked for 0 objects\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ *pulObjectCount = 0; -+ for (i = state->find.next_object; i < soft_token.object.num_objs; i++) { -+ st_logf("FindObjects: %d\n", i); -+ state->find.next_object = i + 1; -+ if (attributes_match(soft_token.object.objs[i], -+ state->find.attributes, -+ state->find.num_attributes)) { -+ *phObject++ = soft_token.object.objs[i]->object_handle; -+ ulMaxObjectCount--; -+ (*pulObjectCount)++; -+ if (ulMaxObjectCount == 0) -+ break; -+ } -+ } -+ return CKR_OK; -+} -+ -+CK_RV -+C_FindObjectsFinal(CK_SESSION_HANDLE hSession) -+{ -+ struct session_state *state; -+ -+ st_logf("FindObjectsFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ find_object_final(state); -+ return CKR_OK; -+} -+ -+static CK_RV -+commonInit(CK_ATTRIBUTE *attr_match, int attr_match_len, -+ const CK_MECHANISM_TYPE *mechs, int mechs_len, -+ const CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey, -+ struct st_object **o) -+{ -+ CK_RV ret; -+ int i; -+ -+ *o = NULL; -+ if ((ret = object_handle_to_object(hKey, o)) != CKR_OK) -+ return ret; -+ -+ ret = attributes_match(*o, attr_match, attr_match_len); -+ if (!ret) { -+ application_error("called commonInit on key that doesn't " -+ "support required attr"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ for (i = 0; i < mechs_len; i++) -+ if (mechs[i] == pMechanism->mechanism) -+ break; -+ if (i == mechs_len) { -+ application_error("called mech (%08lx) not supported\n", -+ pMechanism->mechanism); -+ return CKR_ARGUMENTS_BAD; -+ } -+ return CKR_OK; -+} -+ -+ -+static CK_RV -+dup_mechanism(CK_MECHANISM_PTR *dup, const CK_MECHANISM_PTR pMechanism) -+{ -+ CK_MECHANISM_PTR p; -+ -+ p = malloc(sizeof(*p)); -+ if (p == NULL) -+ return CKR_DEVICE_MEMORY; -+ -+ if (*dup) -+ free(*dup); -+ *dup = p; -+ memcpy(p, pMechanism, sizeof(*p)); -+ -+ return CKR_OK; -+} -+ -+ -+CK_RV -+C_EncryptInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_ENCRYPT, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("EncryptInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->encrypt_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->encrypt_object = OBJECT_ID(o); -+ -+ return ret; -+} -+ -+CK_RV -+C_Encrypt(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pEncryptedData, -+ CK_ULONG_PTR pulEncryptedDataLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len, padding_len; -+ -+ st_logf("Encrypt\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->encrypt_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->encrypt_object]; -+ -+ if (o->u.public_key == NULL) { -+ st_logf("public key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->encrypt_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len + padding_len < (long) ulDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulEncryptedDataLen == NULL) { -+ st_logf("pulEncryptedDataLen NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_public_encrypt(ulDataLen, pData, buffer, rsa, padding); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pEncryptedData != NULL_PTR) -+ memcpy(pEncryptedData, buffer, len); -+ *pulEncryptedDataLen = len; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+CK_RV -+C_EncryptUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen, -+ CK_BYTE_PTR pEncryptedPart, -+ CK_ULONG_PTR pulEncryptedPartLen) -+{ -+ st_logf("EncryptUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_EncryptFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pLastEncryptedPart, -+ CK_ULONG_PTR pulLastEncryptedPartLen) -+{ -+ st_logf("EncryptFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+/* C_DecryptInit initializes a decryption operation. */ -+CK_RV -+C_DecryptInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_DECRYPT, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("DecryptInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->decrypt_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->decrypt_object = OBJECT_ID(o); -+ -+ return CKR_OK; -+} -+ -+ -+CK_RV -+C_Decrypt(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pEncryptedData, -+ CK_ULONG ulEncryptedDataLen, -+ CK_BYTE_PTR pData, -+ CK_ULONG_PTR pulDataLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len, padding_len; -+ -+ st_logf("Decrypt\n"); -+ -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->decrypt_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->decrypt_object]; -+ -+ if (o->u.private_key.key == NULL) { -+ st_logf("private key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->decrypt_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len + padding_len < (long) ulEncryptedDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulDataLen == NULL) { -+ st_logf("pulDataLen NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pEncryptedData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_private_decrypt(ulEncryptedDataLen, pEncryptedData, buffer, -+ rsa, padding); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pData != NULL_PTR) -+ memcpy(pData, buffer, len); -+ *pulDataLen = len; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+ -+CK_RV -+C_DecryptUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pEncryptedPart, -+ CK_ULONG ulEncryptedPartLen, -+ CK_BYTE_PTR pPart, -+ CK_ULONG_PTR pulPartLen) -+ -+{ -+ st_logf("DecryptUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_DecryptFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pLastPart, -+ CK_ULONG_PTR pulLastPartLen) -+{ -+ st_logf("DecryptFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_DigestInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism) -+{ -+ st_logf("DigestInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_SignInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_SIGN, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("SignInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->sign_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->sign_object = OBJECT_ID(o); -+ -+ return CKR_OK; -+} -+ -+CK_RV -+C_Sign(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG_PTR pulSignatureLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len; -+ size_t padding_len; -+ -+ st_logf("Sign\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->sign_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->sign_object]; -+ -+ if (o->u.private_key.key == NULL) { -+ st_logf("private key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.private_key.key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ switch(state->sign_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ padding_len = RSA_PKCS1_PADDING_SIZE; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ padding_len = 0; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if ((size_t) buffer_len < ulDataLen + padding_len) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pulSignatureLen == NULL) { -+ st_logf("signature len NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_private_encrypt(ulDataLen, pData, buffer, rsa, padding); -+ st_logf("private encrypt done\n"); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if (pSignature != NULL_PTR) -+ memcpy(pSignature, buffer, len); -+ *pulSignatureLen = len; -+ -+ ret = CKR_OK; -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+CK_RV -+C_SignUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen) -+{ -+ st_logf("SignUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_RV -+C_SignFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG_PTR pulSignatureLen) -+{ -+ st_logf("SignUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_VerifyInit(CK_SESSION_HANDLE hSession, -+ CK_MECHANISM_PTR pMechanism, -+ CK_OBJECT_HANDLE hKey) -+{ -+ struct session_state *state; -+ CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS, CKM_RSA_X_509 }; -+ CK_BBOOL bool_true = CK_TRUE; -+ CK_ATTRIBUTE attr[] = { -+ { CKA_VERIFY, &bool_true, sizeof(bool_true) } -+ }; -+ struct st_object *o; -+ CK_RV ret; -+ -+ st_logf("VerifyInit\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]), -+ mechs, sizeof(mechs)/sizeof(mechs[0]), -+ pMechanism, hKey, &o); -+ if (ret) -+ return ret; -+ -+ ret = dup_mechanism(&state->verify_mechanism, pMechanism); -+ if (ret == CKR_OK) -+ state->verify_object = OBJECT_ID(o); -+ -+ return ret; -+} -+ -+CK_RV -+C_Verify(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pData, -+ CK_ULONG ulDataLen, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG ulSignatureLen) -+{ -+ struct session_state *state; -+ struct st_object *o; -+ void *buffer = NULL; -+ CK_RV ret; -+ RSA *rsa; -+ int padding, len, buffer_len; -+ -+ st_logf("Verify\n"); -+ VERIFY_SESSION_HANDLE(hSession, &state); -+ -+ if (state->verify_object == -1) -+ return CKR_ARGUMENTS_BAD; -+ -+ o = soft_token.object.objs[state->verify_object]; -+ -+ if (o->u.public_key == NULL) { -+ st_logf("public key NULL\n"); -+ return CKR_ARGUMENTS_BAD; -+ } -+ -+ rsa = EVP_PKEY_get0_RSA(o->u.public_key); -+ if (rsa == NULL) -+ return CKR_ARGUMENTS_BAD; -+ -+ RSA_blinding_off(rsa); /* XXX RAND is broken while running in mozilla ? */ -+ -+ buffer_len = RSA_size(rsa); -+ -+ buffer = malloc(buffer_len); -+ if (buffer == NULL) { -+ ret = CKR_DEVICE_MEMORY; -+ goto out; -+ } -+ -+ ret = CKR_OK; -+ switch(state->verify_mechanism->mechanism) { -+ case CKM_RSA_PKCS: -+ padding = RSA_PKCS1_PADDING; -+ break; -+ case CKM_RSA_X_509: -+ padding = RSA_NO_PADDING; -+ break; -+ default: -+ ret = CKR_FUNCTION_NOT_SUPPORTED; -+ goto out; -+ } -+ -+ if (buffer_len < (long) ulDataLen) { -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pSignature == NULL) { -+ st_logf("signature NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ if (pData == NULL_PTR) { -+ st_logf("data NULL\n"); -+ ret = CKR_ARGUMENTS_BAD; -+ goto out; -+ } -+ -+ len = RSA_public_decrypt(ulDataLen, pData, buffer, rsa, padding); -+ st_logf("private encrypt done\n"); -+ if (len <= 0) { -+ ret = CKR_DEVICE_ERROR; -+ goto out; -+ } -+ if (len > buffer_len) -+ abort(); -+ -+ if ((size_t) len != ulSignatureLen) { -+ ret = CKR_GENERAL_ERROR; -+ goto out; -+ } -+ -+ if (memcmp(pSignature, buffer, len) != 0) { -+ ret = CKR_GENERAL_ERROR; -+ goto out; -+ } -+ -+ out: -+ if (buffer) { -+ memset(buffer, 0, buffer_len); -+ free(buffer); -+ } -+ return ret; -+} -+ -+ -+CK_RV -+C_VerifyUpdate(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pPart, -+ CK_ULONG ulPartLen) -+{ -+ st_logf("VerifyUpdate\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_VerifyFinal(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR pSignature, -+ CK_ULONG ulSignatureLen) -+{ -+ st_logf("VerifyFinal\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+CK_RV -+C_GenerateRandom(CK_SESSION_HANDLE hSession, -+ CK_BYTE_PTR RandomData, -+ CK_ULONG ulRandomLen) -+{ -+ st_logf("GenerateRandom\n"); -+ VERIFY_SESSION_HANDLE(hSession, NULL); -+ return CKR_FUNCTION_NOT_SUPPORTED; -+} -+ -+ -+CK_FUNCTION_LIST funcs = { -+ { 2, 11 }, -+ C_Initialize, -+ C_Finalize, -+ C_GetInfo, -+ C_GetFunctionList, -+ C_GetSlotList, -+ C_GetSlotInfo, -+ C_GetTokenInfo, -+ C_GetMechanismList, -+ C_GetMechanismInfo, -+ C_InitToken, -+ (void *)func_not_supported, /* C_InitPIN */ -+ (void *)func_not_supported, /* C_SetPIN */ -+ C_OpenSession, -+ C_CloseSession, -+ C_CloseAllSessions, -+ C_GetSessionInfo, -+ (void *)func_not_supported, /* C_GetOperationState */ -+ (void *)func_not_supported, /* C_SetOperationState */ -+ C_Login, -+ C_Logout, -+ (void *)func_not_supported, /* C_CreateObject */ -+ (void *)func_not_supported, /* C_CopyObject */ -+ (void *)func_not_supported, /* C_DestroyObject */ -+ (void *)func_not_supported, /* C_GetObjectSize */ -+ C_GetAttributeValue, -+ (void *)func_not_supported, /* C_SetAttributeValue */ -+ C_FindObjectsInit, -+ C_FindObjects, -+ C_FindObjectsFinal, -+ C_EncryptInit, -+ C_Encrypt, -+ C_EncryptUpdate, -+ C_EncryptFinal, -+ C_DecryptInit, -+ C_Decrypt, -+ C_DecryptUpdate, -+ C_DecryptFinal, -+ C_DigestInit, -+ (void *)func_not_supported, /* C_Digest */ -+ (void *)func_not_supported, /* C_DigestUpdate */ -+ (void *)func_not_supported, /* C_DigestKey */ -+ (void *)func_not_supported, /* C_DigestFinal */ -+ C_SignInit, -+ C_Sign, -+ C_SignUpdate, -+ C_SignFinal, -+ (void *)func_not_supported, /* C_SignRecoverInit */ -+ (void *)func_not_supported, /* C_SignRecover */ -+ C_VerifyInit, -+ C_Verify, -+ C_VerifyUpdate, -+ C_VerifyFinal, -+ (void *)func_not_supported, /* C_VerifyRecoverInit */ -+ (void *)func_not_supported, /* C_VerifyRecover */ -+ (void *)func_not_supported, /* C_DigestEncryptUpdate */ -+ (void *)func_not_supported, /* C_DecryptDigestUpdate */ -+ (void *)func_not_supported, /* C_SignEncryptUpdate */ -+ (void *)func_not_supported, /* C_DecryptVerifyUpdate */ -+ (void *)func_not_supported, /* C_GenerateKey */ -+ (void *)func_not_supported, /* C_GenerateKeyPair */ -+ (void *)func_not_supported, /* C_WrapKey */ -+ (void *)func_not_supported, /* C_UnwrapKey */ -+ (void *)func_not_supported, /* C_DeriveKey */ -+ (void *)func_not_supported, /* C_SeedRandom */ -+ C_GenerateRandom, -+ (void *)func_not_supported, /* C_GetFunctionStatus */ -+ (void *)func_not_supported, /* C_CancelFunction */ -+ (void *)func_not_supported /* C_WaitForSlotEvent */ -+}; -diff -up openssh/regress/unittests/Makefile.pkcs11-uri openssh/regress/unittests/Makefile ---- openssh/regress/unittests/Makefile.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/regress/unittests/Makefile 2018-10-12 13:52:55.453191425 +0200 -@@ -2,6 +2,6 @@ - - REGRESS_FAIL_EARLY?= yes - SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion --SUBDIR+=authopt -+SUBDIR+=pkcs11 authopt - - .include -diff -up openssh/regress/unittests/pkcs11/Makefile.pkcs11-uri openssh/regress/unittests/pkcs11/Makefile ---- openssh/regress/unittests/pkcs11/Makefile.pkcs11-uri 2018-10-12 13:52:55.453191425 +0200 -+++ openssh/regress/unittests/pkcs11/Makefile 2018-10-12 13:52:55.453191425 +0200 -@@ -0,0 +1,9 @@ -+ -+PROG=test_pkcs11 -+SRCS=tests.c -+REGRESS_TARGETS=run-regress-${PROG} -+ -+run-regress-${PROG}: ${PROG} -+ env ${TEST_ENV} ./${PROG} -+ -+.include -diff -up openssh/regress/unittests/pkcs11/tests.c.pkcs11-uri openssh/regress/unittests/pkcs11/tests.c ---- openssh/regress/unittests/pkcs11/tests.c.pkcs11-uri 2018-10-12 13:52:55.453191425 +0200 -+++ openssh/regress/unittests/pkcs11/tests.c 2018-10-12 13:52:55.453191425 +0200 -@@ -0,0 +1,330 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * Permission to use, copy, modify, and distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include "includes.h" -+ -+#include -+#include -+ -+#include "../test_helper/test_helper.h" -+ -+#include "sshbuf.h" -+#include "ssh-pkcs11-uri.h" -+ -+#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL) -+ -+/* prototypes are not public -- specify them here internally for tests */ -+struct sshbuf *percent_encode(const char *, size_t, char *); -+int percent_decode(char *, char **); -+ -+void -+compare_uri(struct pkcs11_uri *a, struct pkcs11_uri *b) -+{ -+ ASSERT_PTR_NE(a, NULL); -+ ASSERT_PTR_NE(b, NULL); -+ ASSERT_SIZE_T_EQ(a->id_len, b->id_len); -+ ASSERT_MEM_EQ(a->id, b->id, a->id_len); -+ if (b->object != NULL) -+ ASSERT_STRING_EQ(a->object, b->object); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->object, b->object); -+ if (b->module_path != NULL) -+ ASSERT_STRING_EQ(a->module_path, b->module_path); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->module_path, b->module_path); -+ if (b->token != NULL) -+ ASSERT_STRING_EQ(a->token, b->token); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->token, b->token); -+ if (b->manuf != NULL) -+ ASSERT_STRING_EQ(a->manuf, b->manuf); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->manuf, b->manuf); -+ if (b->lib_manuf != NULL) -+ ASSERT_STRING_EQ(a->lib_manuf, b->lib_manuf); -+ else /* both should be null */ -+ ASSERT_PTR_EQ(a->lib_manuf, b->lib_manuf); -+} -+ -+void -+check_parse_rv(char *uri, struct pkcs11_uri *expect, int expect_rv) -+{ -+ char *buf = NULL, *str; -+ struct pkcs11_uri *pkcs11uri = NULL; -+ int rv; -+ -+ if (expect_rv == 0) -+ str = "Valid"; -+ else -+ str = "Invalid"; -+ asprintf(&buf, "%s PKCS#11 URI parsing: %s", str, uri); -+ TEST_START(buf); -+ free(buf); -+ pkcs11uri = pkcs11_uri_init(); -+ rv = pkcs11_uri_parse(uri, pkcs11uri); -+ ASSERT_INT_EQ(rv, expect_rv); -+ if (rv == 0) /* in case of failure result is undefined */ -+ compare_uri(pkcs11uri, expect); -+ pkcs11_uri_cleanup(pkcs11uri); -+ free(expect); -+ TEST_DONE(); -+} -+ -+void -+check_parse(char *uri, struct pkcs11_uri *expect) -+{ -+ check_parse_rv(uri, expect, 0); -+} -+ -+struct pkcs11_uri * -+compose_uri(unsigned char *id, size_t id_len, char *token, char *lib_manuf, -+ char *manuf, char *module_path, char *object) -+{ -+ struct pkcs11_uri *uri = pkcs11_uri_init(); -+ if (id_len > 0) { -+ uri->id_len = id_len; -+ uri->id = id; -+ } -+ uri->module_path = module_path; -+ uri->token = token; -+ uri->lib_manuf = lib_manuf; -+ uri->manuf = manuf; -+ uri->object = object; -+ return uri; -+} -+ -+static void -+test_parse_valid(void) -+{ -+ /* path arguments */ -+ check_parse("pkcs11:id=%01", -+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:id=%00%01", -+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:token=SSH%20Keys", -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ check_parse("pkcs11:library-manufacturer=OpenSC", -+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL)); -+ check_parse("pkcs11:manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); -+ check_parse("pkcs11:object=SIGN%20Key", -+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "SIGN Key")); -+ /* query arguments */ -+ check_parse("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ -+ /* combinations */ -+ /* ID SHOULD be percent encoded */ -+ check_parse("pkcs11:token=SSH%20Key;id=0", -+ compose_uri("0", 1, "SSH Key", NULL, NULL, NULL, NULL)); -+ check_parse( -+ "pkcs11:manufacturer=CAC?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, "CAC", -+ "/usr/lib64/p11-kit-proxy.so", NULL)); -+ check_parse( -+ "pkcs11:object=RSA%20Key?module-path=/usr/lib64/pkcs11/opencryptoki.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, -+ "/usr/lib64/pkcs11/opencryptoki.so", "RSA Key")); -+ -+ /* empty path component matches everything */ -+ check_parse("pkcs11:", EMPTY_URI); -+ -+ /* empty string is a valid to match against (and different from NULL) */ -+ check_parse("pkcs11:token=", -+ compose_uri(NULL, 0, "", NULL, NULL, NULL, NULL)); -+ /* Percent character needs to be percent-encoded */ -+ check_parse("pkcs11:token=%25", -+ compose_uri(NULL, 0, "%", NULL, NULL, NULL, NULL)); -+} -+ -+static void -+test_parse_invalid(void) -+{ -+ /* Invalid percent encoding */ -+ check_parse_rv("pkcs11:id=%0", EMPTY_URI, -1); -+ /* Invalid percent encoding */ -+ check_parse_rv("pkcs11:id=%ZZ", EMPTY_URI, -1); -+ /* Space MUST be percent encoded -- XXX not enforced yet */ -+ check_parse("pkcs11:token=SSH Keys", -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ /* MUST NOT contain duplicate attributes of the same name */ -+ check_parse_rv("pkcs11:id=%01;id=%02", EMPTY_URI, -1); -+ /* Unrecognized attribute in path are ignored with log message */ -+ check_parse("pkcs11:key_name=SSH", EMPTY_URI); -+ /* Unrecognized attribute in query SHOULD be ignored */ -+ check_parse("pkcs11:?key_name=SSH", EMPTY_URI); -+} -+ -+void -+check_gen(char *expect, struct pkcs11_uri *uri) -+{ -+ char *buf = NULL, *uri_str; -+ -+ asprintf(&buf, "Valid PKCS#11 URI generation: %s", expect); -+ TEST_START(buf); -+ free(buf); -+ uri_str = pkcs11_uri_get(uri); -+ ASSERT_PTR_NE(uri_str, NULL); -+ ASSERT_STRING_EQ(uri_str, expect); -+ free(uri_str); -+ TEST_DONE(); -+} -+ -+static void -+test_generate_valid(void) -+{ -+ /* path arguments */ -+ check_gen("pkcs11:id=%01", -+ compose_uri("\x01", 1, NULL, NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:id=%00%01", -+ compose_uri("\x00\x01", 2, NULL, NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:token=SSH%20Keys", /* space must be percent encoded */ -+ compose_uri(NULL, 0, "SSH Keys", NULL, NULL, NULL, NULL)); -+ /* library-manufacturer is not implmented now */ -+ /*check_gen("pkcs11:library-manufacturer=OpenSC", -+ compose_uri(NULL, 0, NULL, "OpenSC", NULL, NULL, NULL));*/ -+ check_gen("pkcs11:manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, NULL)); -+ check_gen("pkcs11:object=RSA%20Key", -+ compose_uri(NULL, 0, NULL, NULL, NULL, NULL, "RSA Key")); -+ /* query arguments */ -+ check_gen("pkcs11:?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri(NULL, 0, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ -+ /* combinations */ -+ check_gen("pkcs11:id=%02;token=SSH%20Keys", -+ compose_uri("\x02", 1, "SSH Keys", NULL, NULL, NULL, NULL)); -+ check_gen("pkcs11:id=%EE%02?module-path=/usr/lib64/p11-kit-proxy.so", -+ compose_uri("\xEE\x02", 2, NULL, NULL, NULL, "/usr/lib64/p11-kit-proxy.so", NULL)); -+ check_gen("pkcs11:object=Encryption%20Key;manufacturer=piv_II", -+ compose_uri(NULL, 0, NULL, NULL, "piv_II", NULL, "Encryption Key")); -+ -+ /* empty path component matches everything */ -+ check_gen("pkcs11:", EMPTY_URI); -+ -+} -+ -+void -+check_encode(char *source, size_t len, char *whitelist, char *expect) -+{ -+ char *buf = NULL; -+ struct sshbuf *b; -+ -+ asprintf(&buf, "percent_encode: expected %s", expect); -+ TEST_START(buf); -+ free(buf); -+ -+ b = percent_encode(source, len, whitelist); -+ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); -+ sshbuf_free(b); -+ TEST_DONE(); -+} -+ -+static void -+test_percent_encode_multibyte(void) -+{ -+ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ -+ -+ /* multi-byte characters are "for free" */ -+ check_encode("$", 1, "", "%24"); -+ check_encode("¢", 2, "", "%C2%A2"); -+ check_encode("€", 3, "", "%E2%82%AC"); -+ check_encode("𐍈", 4, "", "%F0%90%8D%88"); -+ -+ /* CK_UTF8CHAR is unsigned char (1 byte) */ -+ /* labels SHOULD be normalized to NFC [UAX15] */ -+ -+} -+ -+static void -+test_percent_encode(void) -+{ -+ /* Without whitelist encodes everything (for CKA_ID) */ -+ check_encode("A*", 2, "", "%41%2A"); -+ check_encode("\x00", 1, "", "%00"); -+ check_encode("\x7F", 1, "", "%7F"); -+ check_encode("\x80", 1, "", "%80"); -+ check_encode("\xff", 1, "", "%FF"); -+ -+ /* Default whitelist encodes anything but safe letters */ -+ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, -+ "test%000alpha"); -+ check_encode(" ", 1, PKCS11_URI_WHITELIST, -+ "%20"); /* Space MUST be percent encoded */ -+ check_encode("/", 1, PKCS11_URI_WHITELIST, -+ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ -+ check_encode("?", 1, PKCS11_URI_WHITELIST, -+ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ -+ check_encode("#", 1, PKCS11_URI_WHITELIST, -+ "%23"); /* '#' MUST be always percent encoded */ -+ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, -+ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); -+ -+ /* Components in query can have '/' unencoded (useful for paths) */ -+ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", -+ "/path/to.file"); -+} -+ -+void -+check_decode(char *source, char *expect, int expect_len) -+{ -+ char *buf = NULL, *out = NULL; -+ int rv; -+ -+ asprintf(&buf, "percent_decode: %s", source); -+ TEST_START(buf); -+ free(buf); -+ -+ rv = percent_decode(source, &out); -+ ASSERT_INT_EQ(rv, expect_len); -+ if (rv >= 0) -+ ASSERT_MEM_EQ(out, expect, expect_len); -+ free(out); -+ TEST_DONE(); -+} -+ -+static void -+test_percent_decode(void) -+{ -+ /* simple valid cases */ -+ check_decode("%00", "\x00", 1); -+ check_decode("%FF", "\xFF", 1); -+ -+ /* normal strings shold be kept intact */ -+ check_decode("strings are left", "strings are left", 16); -+ check_decode("10%25 of trees", "10% of trees", 12); -+ -+ /* make sure no more than 2 bytes are parsed */ -+ check_decode("%222", "\x22" "2", 2); -+ -+ /* invalid expects failure */ -+ check_decode("%0", "", -1); -+ check_decode("%Z", "", -1); -+ check_decode("%FG", "", -1); -+} -+ -+void -+tests(void) -+{ -+ test_percent_encode(); -+ test_percent_encode_multibyte(); -+ test_percent_decode(); -+ test_parse_valid(); -+ test_parse_invalid(); -+ test_generate_valid(); -+} -diff -up openssh/ssh-add.c.pkcs11-uri openssh/ssh-add.c ---- openssh/ssh-add.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-add.c 2018-10-12 13:52:55.454191434 +0200 -@@ -64,6 +64,7 @@ - #include "misc.h" - #include "ssherr.h" - #include "digest.h" -+#include "ssh-pkcs11-uri.h" - - /* argv0 */ - extern char *__progname; -@@ -188,6 +189,24 @@ delete_all(int agent_fd, int qflag) - return ret; - } - -+#ifdef ENABLE_PKCS11 -+static int update_card(int, int, const char *, int); -+ -+int -+update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag) -+{ -+ struct pkcs11_uri *uri; -+ -+ /* dry-run parse to make sure the URI is valid and to report errors */ -+ uri = pkcs11_uri_init(); -+ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ pkcs11_uri_cleanup(uri); -+ -+ return update_card(agent_fd, adding, pkcs11_uri, qflag); -+} -+#endif -+ - static int - add_file(int agent_fd, const char *filename, int key_only, int qflag) - { -@@ -495,6 +514,13 @@ lock_agent(int agent_fd, int lock) - static int - do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) - { -+#ifdef ENABLE_PKCS11 -+ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(file, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ return update_pkcs11_uri(agent_fd, !deleting, file, qflag); -+ } -+#endif - if (deleting) { - if (delete_file(agent_fd, file, key_only, qflag) == -1) - return -1; -diff -up openssh/ssh-agent.c.pkcs11-uri openssh/ssh-agent.c ---- openssh/ssh-agent.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-agent.c 2018-10-12 13:52:55.454191434 +0200 -@@ -546,10 +546,72 @@ no_identities(SocketEntry *e) - } - - #ifdef ENABLE_PKCS11 -+static char * -+sanitize_pkcs11_provider(const char *provider) -+{ -+ struct pkcs11_uri *uri = NULL; -+ char *sane_uri, *module_path = NULL; /* default path */ -+ char canonical_provider[PATH_MAX]; -+ -+ if (provider == NULL) -+ return NULL; -+ -+ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ /* PKCS#11 URI */ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) { -+ error("Failed to init PCKS#11 URI"); -+ return NULL; -+ } -+ -+ if (pkcs11_uri_parse(provider, uri) != 0) { -+ error("Failed to parse PKCS#11 URI"); -+ return NULL; -+ } -+ /* validate also provider from URI */ -+ if (uri->module_path) -+ module_path = strdup(uri->module_path); -+ } else -+ module_path = strdup(provider); /* simple path */ -+ -+ if (module_path != NULL) { /* do not validate default NULL path in URI */ -+ if (realpath(module_path, canonical_provider) == NULL) { -+ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", -+ module_path, strerror(errno)); -+ free(module_path); -+ pkcs11_uri_cleanup(uri); -+ return NULL; -+ } -+ free(module_path); -+ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { -+ verbose("refusing PKCS#11 provider \"%.100s\": " -+ "not whitelisted", canonical_provider); -+ pkcs11_uri_cleanup(uri); -+ return NULL; -+ } -+ -+ /* copy verified and sanitized provider path back to the uri */ -+ if (uri) { -+ free(uri->module_path); -+ uri->module_path = xstrdup(canonical_provider); -+ } -+ } -+ -+ if (uri) { -+ sane_uri = pkcs11_uri_get(uri); -+ pkcs11_uri_cleanup(uri); -+ return sane_uri; -+ } else { -+ return xstrdup(canonical_provider); /* simple path */ -+ } -+} -+ - static void - process_add_smartcard_key(SocketEntry *e) - { -- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; -+ char *provider = NULL, *pin = NULL, *sane_uri = NULL; - int r, i, count = 0, success = 0, confirm = 0; - u_int seconds; - time_t death = 0; -@@ -585,28 +647,23 @@ process_add_smartcard_key(SocketEntry *e - goto send; - } - } -- if (realpath(provider, canonical_provider) == NULL) { -- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", -- provider, strerror(errno)); -- goto send; -- } -- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { -- verbose("refusing PKCS#11 add of \"%.100s\": " -- "provider not whitelisted", canonical_provider); -+ -+ sane_uri = sanitize_pkcs11_provider(provider); -+ if (sane_uri == NULL) - goto send; -- } -- debug("%s: add %.100s", __func__, canonical_provider); -+ - if (lifetime && !death) - death = monotime() + lifetime; - -- count = pkcs11_add_provider(canonical_provider, pin, &keys); -+ debug("%s: add %.100s", __func__, sane_uri); -+ count = pkcs11_add_provider(sane_uri, pin, &keys); - for (i = 0; i < count; i++) { - k = keys[i]; - if (lookup_identity(k) == NULL) { - id = xcalloc(1, sizeof(Identity)); - id->key = k; -- id->provider = xstrdup(canonical_provider); -- id->comment = xstrdup(canonical_provider); /* XXX */ -+ id->provider = xstrdup(sane_uri); -+ id->comment = xstrdup(sane_uri); - id->death = death; - id->confirm = confirm; - TAILQ_INSERT_TAIL(&idtab->idlist, id, next); -@@ -620,6 +677,7 @@ process_add_smartcard_key(SocketEntry *e - send: - free(pin); - free(provider); -+ free(sane_uri); - free(keys); - send_status(e, success); - } -@@ -627,7 +685,7 @@ send: - static void - process_remove_smartcard_key(SocketEntry *e) - { -- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; -+ char *provider = NULL, *pin = NULL, *sane_uri = NULL; - int r, success = 0; - Identity *id, *nxt; - -@@ -638,30 +696,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("%s: remove %.100s", __func__, canonical_provider); -+ debug("%s: remove %.100s", __func__, sane_uri); - for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { - nxt = TAILQ_NEXT(id, next); - /* Skip file--based keys */ - if (id->provider == NULL) - continue; -- if (!strcmp(canonical_provider, id->provider)) { -+ if (!strcmp(sane_uri, id->provider)) { - TAILQ_REMOVE(&idtab->idlist, id, next); - free_identity(id); - idtab->nentries--; - } - } -- if (pkcs11_del_provider(canonical_provider) == 0) -+ if (pkcs11_del_provider(sane_uri) == 0) - success = 1; - else - error("%s: pkcs11_del_provider failed", __func__); - send: - free(provider); -+ free(sane_uri); - send_status(e, success); - } - #endif /* ENABLE_PKCS11 */ -diff -up openssh/ssh_config.5.pkcs11-uri openssh/ssh_config.5 ---- openssh/ssh_config.5.pkcs11-uri 2018-10-12 13:52:55.437191293 +0200 -+++ openssh/ssh_config.5 2018-10-12 13:52:55.459191475 +0200 -@@ -991,6 +991,19 @@ may also be used in conjunction with - .Cm CertificateFile - in order to provide any certificate also needed for authentication with - the identity. -+.Pp -+The authentication identity can be also specified in a form of PKCS#11 URI -+starting with a string -+.Cm pkcs11: . -+There is supported a subset of the PKCS#11 URI as defined -+in RFC 7512 (implemented path arguments -+.Cm id , -+.Cm manufacturer , -+.Cm object , -+.Cm token -+and query argument -+.Cm module-path -+). The URI can not be in quotes. - .It Cm IgnoreUnknown - Specifies a pattern-list of unknown options to be ignored if they are - encountered in configuration parsing. -diff -up openssh/ssh.c.pkcs11-uri openssh/ssh.c ---- openssh/ssh.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh.c 2018-10-12 13:52:55.458191467 +0200 -@@ -769,6 +769,14 @@ main(int ac, char **av) - options.gss_deleg_creds = 1; - break; - case 'i': -+#ifdef ENABLE_PKCS11 -+ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(optarg, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ add_identity_file(&options, NULL, optarg, 1); -+ break; -+ } -+#endif - p = tilde_expand_filename(optarg, getuid()); - if (stat(p, &st) < 0) - fprintf(stderr, "Warning: Identity file %s " -@@ -1975,6 +1983,45 @@ ssh_session2(struct ssh *ssh, struct pas - options.escape_char : SSH_ESCAPECHAR_NONE, id); - } - -+#ifdef ENABLE_PKCS11 -+static void -+load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], -+ struct sshkey *identity_keys[], int *n_ids) -+{ -+ int nkeys, i; -+ struct sshkey **keys; -+ struct pkcs11_uri *uri; -+ -+ debug("identity file '%s' from pkcs#11", pkcs11_uri); -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); -+ -+ /* we need to merge URI and provider together */ -+ if (options.pkcs11_provider != NULL && uri->module_path == NULL) -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && -+ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { -+ for (i = 0; i < nkeys; i++) { -+ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { -+ sshkey_free(keys[i]); -+ continue; -+ } -+ identity_keys[*n_ids] = keys[i]; -+ identity_files[*n_ids] = pkcs11_uri_get(uri); -+ (*n_ids)++; -+ } -+ free(keys); -+ } -+ -+ pkcs11_uri_cleanup(uri); -+} -+#endif /* ENABLE_PKCS11 */ -+ - /* Loads all IdentityFile and CertificateFile keys */ - static void - load_public_identity_files(struct passwd *pw) -@@ -1989,10 +2036,6 @@ load_public_identity_files(struct passwd - char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; - struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; - int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; --#ifdef ENABLE_PKCS11 -- struct sshkey **keys; -- int nkeys; --#endif /* PKCS11 */ - - n_ids = n_certs = 0; - memset(identity_files, 0, sizeof(identity_files)); -@@ -2005,32 +2048,46 @@ load_public_identity_files(struct passwd - sizeof(certificate_file_userprovided)); - - #ifdef ENABLE_PKCS11 -- if (options.pkcs11_provider != NULL && -- options.num_identity_files < SSH_MAX_IDENTITY_FILES && -- (pkcs11_init(!options.batch_mode) == 0) && -- (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, -- &keys)) > 0) { -- for (i = 0; i < nkeys; i++) { -- if (n_ids >= SSH_MAX_IDENTITY_FILES) { -- sshkey_free(keys[i]); -- continue; -- } -- identity_keys[n_ids] = keys[i]; -- identity_files[n_ids] = -- xstrdup(options.pkcs11_provider); /* XXX */ -- n_ids++; -- } -- free(keys); -+ /* handle fallback from PKCS11Provider option */ -+ pkcs11_init(!options.batch_mode); -+ -+ if (options.pkcs11_provider != NULL) { -+ struct pkcs11_uri *uri; -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ /* Construct simple PKCS#11 URI to simplify access */ -+ uri->module_path = strdup(options.pkcs11_provider); -+ -+ /* Add it as any other IdentityFile */ -+ cp = pkcs11_uri_get(uri); -+ add_identity_file(&options, NULL, cp, 1); -+ free(cp); -+ -+ pkcs11_uri_cleanup(uri); - } - #endif /* ENABLE_PKCS11 */ - for (i = 0; i < options.num_identity_files; i++) { -+ char *name = options.identity_files[i]; - if (n_ids >= SSH_MAX_IDENTITY_FILES || -- strcasecmp(options.identity_files[i], "none") == 0) { -+ strcasecmp(name, "none") == 0) { - free(options.identity_files[i]); - options.identity_files[i] = NULL; - continue; - } -- cp = tilde_expand_filename(options.identity_files[i], getuid()); -+#ifdef ENABLE_PKCS11 -+ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(name, PKCS11_URI_SCHEME, -+ strlen(PKCS11_URI_SCHEME)) == 0) { -+ load_pkcs11_identity(name, identity_files, -+ identity_keys, &n_ids); -+ free(options.identity_files[i]); -+ continue; -+ } -+#endif /* ENABLE_PKCS11 */ -+ cp = tilde_expand_filename(name, getuid()); - filename = percent_expand(cp, "d", pw->pw_dir, - "u", pw->pw_name, "l", thishost, "h", host, - "r", options.user, (char *)NULL); -diff -up openssh/ssh-keygen.c.pkcs11-uri openssh/ssh-keygen.c ---- openssh/ssh-keygen.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-keygen.c 2018-10-12 13:52:55.455191442 +0200 -@@ -825,6 +825,7 @@ do_download(struct passwd *pw) - free(fp); - } else { - (void) sshkey_write(keys[i], stdout); /* XXX check */ -+ (void) pkcs11_uri_write(keys[i], stdout); - fprintf(stdout, "\n"); - } - sshkey_free(keys[i]); -diff -up openssh/ssh-pkcs11-client.c.pkcs11-uri openssh/ssh-pkcs11-client.c ---- openssh/ssh-pkcs11-client.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-pkcs11-client.c 2018-10-12 13:52:55.455191442 +0200 -@@ -126,6 +126,7 @@ pkcs11_rsa_private_encrypt(int flen, con - return (-1); - key.type = KEY_RSA; - key.rsa = rsa; -+ key.ecdsa_nid = 0; - if ((r = sshkey_to_blob(&key, &blob, &blen)) != 0) { - error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); - return -1; -@@ -210,6 +211,8 @@ pkcs11_add_provider(char *name, char *pi - u_int nkeys, i; - struct sshbuf *msg; - -+ debug("%s: called, name = %s", __func__, name); -+ - if (fd < 0 && pkcs11_start_helper() < 0) - return (-1); - -@@ -226,6 +229,7 @@ pkcs11_add_provider(char *name, char *pi - if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); - *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); -+ debug("%s: nkeys = %u", __func__, nkeys); - for (i = 0; i < nkeys; i++) { - /* XXX clean up properly instead of fatal() */ - if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || -diff -up openssh/ssh-pkcs11.c.pkcs11-uri openssh/ssh-pkcs11.c ---- openssh/ssh-pkcs11.c.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-pkcs11.c 2018-10-12 13:54:35.755023297 +0200 -@@ -49,8 +49,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; -@@ -59,6 +59,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; - }; - -@@ -71,10 +78,46 @@ struct pkcs11_key { - RSA_METHOD *rsa_method; - char *keyid; - int keyid_len; -+ char *label; - }; - - int pkcs11_interactive = 0; - -+/* -+ * This can't be in the ssh-pkcs11-uri, becase we can not depend on -+ * PKCS#11 structures in ssh-agent (using client-helper communication) -+ */ -+int -+pkcs11_uri_write(const struct sshkey *key, FILE *f) -+{ -+ char *p = NULL; -+ struct pkcs11_uri uri; -+ struct pkcs11_key *k11; -+ -+ /* sanity - is it a RSA key with associated app_data? */ -+ if (key->type != KEY_RSA || -+ (k11 = RSA_get_app_data(key->rsa)) == NULL) -+ return -1; -+ -+ /* omit type -- we are looking for private-public or private-certificate pairs */ -+ uri.id = k11->keyid; -+ uri.id_len = k11->keyid_len; -+ uri.token = k11->provider->module->slotinfo[k11->slotidx].token.label; -+ uri.object = k11->label; -+ uri.module_path = k11->provider->module->module_path; -+ uri.lib_manuf = k11->provider->module->info.manufacturerID; -+ uri.manuf = k11->provider->module->slotinfo[k11->slotidx].token.manufacturerID; -+ -+ p = pkcs11_uri_get(&uri); -+ /* do not cleanup -- we do not allocate here, only reference */ -+ if (p == NULL) -+ return -1; -+ -+ fprintf(f, " %s", p); -+ free(p); -+ return 0; -+} -+ - int - pkcs11_init(int interactive) - { -@@ -90,26 +133,63 @@ pkcs11_init(int interactive) - * this is called when a provider gets unregistered. - */ - static void --pkcs11_provider_finalize(struct pkcs11_provider *p) -+pkcs11_module_finalize(struct pkcs11_module *m) - { - CK_RV rv; - CK_ULONG i; - -- debug("pkcs11_provider_finalize: %p refcount %d valid %d", -- p, p->refcount, p->valid); -- if (!p->valid) -+ debug("%s: %p refcount %d valid %d", __func__, -+ m, m->refcount, m->valid); -+ if (!m->valid) - return; -- for (i = 0; i < p->nslots; i++) { -- if (p->slotinfo[i].session && -- (rv = p->function_list->C_CloseSession( -- p->slotinfo[i].session)) != CKR_OK) -+ for (i = 0; i < m->nslots; i++) { -+ if (m->slotinfo[i].session && -+ (rv = m->function_list->C_CloseSession( -+ m->slotinfo[i].session)) != CKR_OK) - error("C_CloseSession failed: %lu", rv); - } -- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) -+ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) - error("C_Finalize failed: %lu", rv); -+ m->valid = 0; -+ m->function_list = NULL; -+ dlclose(m->handle); -+} -+ -+/* -+ * remove a reference to the pkcs11 module. -+ * called when a provider is unregistered. -+ */ -+static void -+pkcs11_module_unref(struct pkcs11_module *m) -+{ -+ debug("%s: %p refcount %d", __func__, m, m->refcount); -+ if (--m->refcount <= 0) { -+ pkcs11_module_finalize(m); -+ if (m->valid) -+ error("%s: %p still valid", __func__, m); -+ free(m->slotlist); -+ free(m->slotinfo); -+ free(m->module_path); -+ free(m); -+ } -+} -+ -+/* -+ * finalize a provider shared libarary, it's no longer usable. -+ * however, there might still be keys referencing this provider, -+ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). -+ * this is called when a provider gets unregistered. -+ */ -+static void -+pkcs11_provider_finalize(struct pkcs11_provider *p) -+{ -+ debug("%s: %p refcount %d valid %d", __func__, -+ p, p->refcount, p->valid); -+ if (!p->valid) -+ return; -+ pkcs11_module_unref(p->module); -+ p->module = NULL; - p->valid = 0; -- p->function_list = NULL; -- dlclose(p->handle); - } - - /* -@@ -119,12 +199,11 @@ pkcs11_provider_finalize(struct pkcs11_p - static void - pkcs11_provider_unref(struct pkcs11_provider *p) - { -- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); -+ debug("%s: %p refcount %d", __func__, p, p->refcount); - if (--p->refcount <= 0) { -- if (p->valid) -- error("pkcs11_provider_unref: %p still valid", p); -- free(p->slotlist); -- free(p->slotinfo); -+ if (p->module) -+ pkcs11_module_unref(p->module); -+ free(p->name); - free(p); - } - } -@@ -142,6 +221,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) -@@ -156,19 +249,52 @@ pkcs11_provider_lookup(char *provider_id - return (NULL); - } - -+int pkcs11_del_provider_by_uri(struct pkcs11_uri *); -+ - /* unregister provider by name */ - int - pkcs11_del_provider(char *provider_id) - { -+ int rv; -+ struct pkcs11_uri *uri; -+ -+ debug("%s: called, provider_id = %s", __func__, provider_id); -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); -+ } -+ -+ rv = pkcs11_del_provider_by_uri(uri); -+ pkcs11_uri_cleanup(uri); -+ return rv; -+} -+ -+/* unregister provider by PKCS#11 URI */ -+int -+pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) -+{ - struct pkcs11_provider *p; -+ int rv = -1; -+ char *provider_uri = pkcs11_uri_get(uri); - -- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { -+ debug3("%s(%s): called", __func__, provider_uri); -+ -+ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { - TAILQ_REMOVE(&pkcs11_providers, p, next); - pkcs11_provider_finalize(p); - pkcs11_provider_unref(p); -- return (0); -+ rv = 0; - } -- return (-1); -+ free(provider_uri); -+ return rv; - } - - /* openssl callback for freeing an RSA key */ -@@ -185,6 +311,7 @@ pkcs11_rsa_finish(RSA *rsa) - pkcs11_provider_unref(k11->provider); - RSA_meth_free(k11->rsa_method); - free(k11->keyid); -+ free(k11->label); - free(k11); - } - return (rv); -@@ -201,8 +328,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); -@@ -249,12 +376,13 @@ pkcs11_rsa_private_encrypt(int flen, con - error("RSA_get_app_data failed for rsa %p", rsa); - return (-1); - } -- if (!k11->provider || !k11->provider->valid) { -+ if (!k11->provider || !k11->provider->valid || !k11->provider->module -+ || !k11->provider->module->valid) { - error("no pkcs11 (valid) provider for rsa %p", rsa); - return (-1); - } -- f = k11->provider->function_list; -- si = &k11->provider->slotinfo[k11->slotidx]; -+ f = k11->provider->module->function_list; -+ si = &k11->provider->module->slotinfo[k11->slotidx]; - if ((si->token.flags & CKF_LOGIN_REQUIRED) && !si->logged_in) { - if (!pkcs11_interactive) { - error("need pin entry%s", (si->token.flags & -@@ -313,7 +441,7 @@ pkcs11_rsa_private_decrypt(int flen, con - /* redirect private key operations for rsa key to pkcs11 token */ - static int - pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, -- CK_ATTRIBUTE *keyid_attrib, RSA *rsa) -+ CK_ATTRIBUTE *keyid_attrib, CK_ATTRIBUTE *label_attrib, RSA *rsa) - { - struct pkcs11_key *k11; - const RSA_METHOD *def = RSA_get_default_method(); -@@ -328,6 +456,11 @@ pkcs11_rsa_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; -+ } - k11->rsa_method = RSA_meth_dup(def); - if (k11->rsa_method == NULL) - fatal("%s: RSA_meth_dup failed", __func__); -@@ -371,16 +504,16 @@ pkcs11_open_session(struct pkcs11_provid - CK_SESSION_HANDLE session; - int login_required; - -- f = p->function_list; -- login_required = p->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; -+ f = p->module->function_list; -+ login_required = p->module->slotinfo[slotidx].token.flags & CKF_LOGIN_REQUIRED; - if (pin && login_required && !strlen(pin)) { - error("pin required"); - return (-1); - } -- if ((rv = f->C_OpenSession(p->slotlist[slotidx], CKF_RW_SESSION| -+ if ((rv = f->C_OpenSession(p->module->slotlist[slotidx], CKF_RW_SESSION| - CKF_SERIAL_SESSION, NULL, NULL, &session)) - != CKR_OK) { -- error("C_OpenSession failed: %lu", rv); -+ error("C_OpenSession failed for slot %lu: %lu", slotidx, rv); - return (-1); - } - if (login_required && pin) { -@@ -392,9 +525,9 @@ pkcs11_open_session(struct pkcs11_provid - error("C_CloseSession failed: %lu", rv); - return (-1); - } -- p->slotinfo[slotidx].logged_in = 1; -+ p->module->slotinfo[slotidx].logged_in = 1; - } -- p->slotinfo[slotidx].session = session; -+ p->module->slotinfo[slotidx].session = session; - return (0); - } - -@@ -404,39 +537,72 @@ pkcs11_open_session(struct pkcs11_provid - * keysp points to an (possibly empty) array with *nkeys keys. - */ - static int pkcs11_fetch_keys_filter(struct pkcs11_provider *, CK_ULONG, -- CK_ATTRIBUTE [], CK_ATTRIBUTE [3], struct sshkey ***, int *) -+ CK_ATTRIBUTE [], size_t, CK_ATTRIBUTE [3], struct sshkey ***, int *) - __attribute__((__bounded__(__minbytes__,4, 3 * sizeof(CK_ATTRIBUTE)))); - - static int - pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, -- struct sshkey ***keysp, int *nkeys) -+ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) - { -+ size_t filter_size = 1; - CK_OBJECT_CLASS pubkey_class = CKO_PUBLIC_KEY; - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE pubkey_filter[] = { -- { CKA_CLASS, NULL, sizeof(pubkey_class) } -+ { CKA_CLASS, NULL, sizeof(pubkey_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE cert_filter[] = { -- { CKA_CLASS, NULL, sizeof(cert_class) } -+ { CKA_CLASS, NULL, sizeof(cert_class) }, -+ { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 } - }; - CK_ATTRIBUTE pubkey_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_MODULUS, NULL, 0 }, - { CKA_PUBLIC_EXPONENT, NULL, 0 } - }; - CK_ATTRIBUTE cert_attribs[] = { - { CKA_ID, NULL, 0 }, -+ { CKA_LABEL, NULL, 0 }, - { CKA_SUBJECT, NULL, 0 }, - { CKA_VALUE, NULL, 0 } - }; - pubkey_filter[0].pValue = &pubkey_class; - cert_filter[0].pValue = &cert_class; - -- if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, pubkey_attribs, -- keysp, nkeys) < 0 || -- pkcs11_fetch_keys_filter(p, slotidx, cert_filter, cert_attribs, -- keysp, nkeys) < 0) -+ if (uri->id != NULL) { -+ pubkey_filter[filter_size].pValue = uri->id; -+ pubkey_filter[filter_size].ulValueLen = uri->id_len; -+ cert_filter[filter_size].pValue = uri->id; -+ cert_filter[filter_size].ulValueLen = uri->id_len; -+ filter_size++; -+ } -+ if (uri->object != NULL) { -+ pubkey_filter[filter_size].pValue = uri->object; -+ pubkey_filter[filter_size].ulValueLen = strlen(uri->object); -+ pubkey_filter[filter_size].type = CKA_LABEL; -+ cert_filter[filter_size].pValue = uri->object; -+ cert_filter[filter_size].ulValueLen = strlen(uri->object); -+ cert_filter[filter_size].type = CKA_LABEL; -+ filter_size++; -+ } -+ -+ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, -+ pubkey_attribs, keysp, nkeys) < 0 || -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+ cert_attribs, keysp, nkeys) < 0) - return (-1); -+ if (*nkeys == 0) { -+ /* Try once more without the label filter */ -+ filter_size--; -+ if (pkcs11_fetch_keys_filter(p, slotidx, pubkey_filter, filter_size, -+ pubkey_attribs, keysp, nkeys) < 0 || -+ pkcs11_fetch_keys_filter(p, slotidx, cert_filter, filter_size, -+ cert_attribs, keysp, nkeys) < 0) -+ return (-1); -+ } - return (0); - } - -@@ -462,14 +619,15 @@ have_rsa_key(const RSA *rsa) - - static int - pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx, -- CK_ATTRIBUTE filter[], CK_ATTRIBUTE attribs[3], -+ CK_ATTRIBUTE filter[], size_t filter_size, CK_ATTRIBUTE attribs[4], - struct sshkey ***keysp, int *nkeys) - { - struct sshkey *key; - RSA *rsa; - X509 *x509; -- EVP_PKEY *evp; -+ EVP_PKEY *evp = NULL; - int i; -+ int nattribs = 4; - const u_char *cp; - CK_RV rv; - CK_OBJECT_HANDLE obj; -@@ -477,16 +635,15 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - CK_SESSION_HANDLE session; - CK_FUNCTION_LIST *f; - -- f = p->function_list; -- session = p->slotinfo[slotidx].session; -+ f = p->module->function_list; -+ session = p->module->slotinfo[slotidx].session; - /* setup a filter the looks for public keys */ -- if ((rv = f->C_FindObjectsInit(session, filter, 1)) != CKR_OK) { -+ if ((rv = f->C_FindObjectsInit(session, filter, filter_size)) != CKR_OK) { - error("C_FindObjectsInit failed: %lu", rv); - return (-1); - } - while (1) { -- /* XXX 3 attributes in attribs[] */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - attribs[i].pValue = NULL; - attribs[i].ulValueLen = 0; - } -@@ -494,22 +651,22 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - || nfound == 0) - break; - /* found a key, so figure out size of the attributes */ -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); - continue; - } - /* -- * Allow CKA_ID (always first attribute) to be empty, but -- * ensure that none of the others are zero length. -+ * Allow CKA_ID (always first attribute) and CKA_LABEL (second) -+ * to be empty, but ensure that none of the others are zero length. - * XXX assumes CKA_ID is always first. - */ -- if (attribs[1].ulValueLen == 0 || -- attribs[2].ulValueLen == 0) { -+ if (attribs[2].ulValueLen == 0 || -+ attribs[3].ulValueLen == 0) { - continue; - } - /* allocate buffers for attributes */ -- for (i = 0; i < 3; i++) { -+ for (i = 0; i < nattribs; i++) { - if (attribs[i].ulValueLen > 0) { - attribs[i].pValue = xmalloc( - attribs[i].ulValueLen); -@@ -517,23 +674,23 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } - - /* -- * retrieve ID, modulus and public exponent of RSA key, -- * or ID, subject and value for certificates. -+ * retrieve ID, label, modulus and public exponent of RSA key, -+ * or ID, label, subject and value for certificates. - */ - rsa = NULL; -- if ((rv = f->C_GetAttributeValue(session, obj, attribs, 3)) -+ if ((rv = f->C_GetAttributeValue(session, obj, attribs, nattribs)) - != CKR_OK) { - error("C_GetAttributeValue failed: %lu", rv); -- } else if (attribs[1].type == CKA_MODULUS ) { -+ } else if (attribs[2].type == CKA_MODULUS ) { - if ((rsa = RSA_new()) == NULL) { - error("RSA_new failed"); - } else { - BIGNUM *rsa_n, *rsa_e; - -- rsa_n = BN_bin2bn(attribs[1].pValue, -- attribs[1].ulValueLen, NULL); -- rsa_e = BN_bin2bn(attribs[2].pValue, -+ rsa_n = BN_bin2bn(attribs[2].pValue, - attribs[2].ulValueLen, NULL); -+ rsa_e = BN_bin2bn(attribs[3].pValue, -+ attribs[3].ulValueLen, NULL); - if (rsa_n != NULL && rsa_e != NULL) { - if (!RSA_set0_key(rsa, - rsa_n, rsa_e, NULL)) -@@ -544,10 +701,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - BN_free(rsa_e); - } - } else { -- cp = attribs[2].pValue; -+ cp = attribs[3].pValue; - if ((x509 = X509_new()) == NULL) { - error("X509_new failed"); -- } else if (d2i_X509(&x509, &cp, attribs[2].ulValueLen) -+ } else if (d2i_X509(&x509, &cp, attribs[3].ulValueLen) - == NULL) { - error("d2i_X509 failed"); - } else if ((evp = X509_get_pubkey(x509)) == NULL || -@@ -559,9 +716,10 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - error("RSAPublicKey_dup"); - } - X509_free(x509); -+ EVP_PKEY_free(evp); - } - if (rsa && have_rsa_key(rsa) && -- pkcs11_rsa_wrap(p, slotidx, &attribs[0], rsa) == 0) { -+ pkcs11_rsa_wrap(p, slotidx, &attribs[0], &attribs[1], rsa) == 0) { - if ((key = sshkey_new(KEY_UNSPEC)) == NULL) - fatal("sshkey_new failed"); - key->rsa = rsa; -@@ -580,7 +738,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - } else if (rsa) { - RSA_free(rsa); - } -- for (i = 0; i < 3; i++) -+ for (i = 0; i < nattribs; i++) - free(attribs[i].pValue); - } - if ((rv = f->C_FindObjectsFinal(session)) != CKR_OK) -@@ -592,126 +750,240 @@ pkcs11_fetch_keys_filter(struct pkcs11_p - int - pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) - { -- int nkeys, need_finalize = 0; -- struct pkcs11_provider *p = NULL; -+ int rv; -+ struct pkcs11_uri *uri; -+ -+ debug("%s: called, provider_id = %s", __func__, provider_id); -+ -+ uri = pkcs11_uri_init(); -+ if (uri == NULL) -+ fatal("Failed to init PCKS#11 URI"); -+ -+ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && -+ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { -+ if (pkcs11_uri_parse(provider_id, uri) != 0) -+ fatal("Failed to parse PKCS#11 URI"); -+ } else { -+ uri->module_path = strdup(provider_id); -+ } -+ -+ rv = pkcs11_add_provider_by_uri(uri, pin, keyp); -+ pkcs11_uri_cleanup(uri); -+ return rv; -+} -+ -+struct pkcs11_provider * -+pkcs11_provider_initialize(struct pkcs11_uri *uri) -+{ -+ int need_finalize = 0; - void *handle = NULL; - CK_RV (*getfunctionlist)(CK_FUNCTION_LIST **); - CK_RV rv; - CK_FUNCTION_LIST *f = NULL; - CK_TOKEN_INFO *token; - CK_ULONG i; -+ char *provider_module = NULL; -+ struct pkcs11_provider *p; -+ struct pkcs11_module *m; - -- *keyp = NULL; -- if (pkcs11_provider_lookup(provider_id) != NULL) { -- debug("%s: provider already registered: %s", -- __func__, provider_id); -+ /* if no provider specified, fallback to p11-kit */ -+ if (uri->module_path == NULL) { -+#ifdef PKCS11_DEFAULT_PROVIDER -+ provider_module = strdup(PKCS11_DEFAULT_PROVIDER); -+#else -+ error("%s: No module path provided", __func__); - goto fail; -+#endif -+ } else -+ provider_module = strdup(uri->module_path); -+ -+ p = xcalloc(1, sizeof(*p)); -+ p->name = pkcs11_uri_get(uri); -+ -+ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL -+ && m->valid) { -+ debug("%s: provider module already initialized: %s", -+ __func__, provider_module); -+ free(provider_module); -+ /* Skip the initialization of PKCS#11 module */ -+ m->refcount++; -+ p->module = m; -+ p->valid = 1; -+ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -+ p->refcount++; /* add to provider list */ -+ return p; -+ } else { -+ m = xcalloc(1, sizeof(*m)); -+ p->module = m; -+ m->refcount++; - } -+ - /* open shared pkcs11-libarary */ -- if ((handle = dlopen(provider_id, RTLD_NOW)) == NULL) { -- error("dlopen %s failed: %s", provider_id, dlerror()); -+ if ((handle = dlopen(provider_module, RTLD_NOW)) == NULL) { -+ error("dlopen %s failed: %s", provider_module, dlerror()); - goto fail; - } - if ((getfunctionlist = dlsym(handle, "C_GetFunctionList")) == NULL) { - error("dlsym(C_GetFunctionList) failed: %s", dlerror()); - goto fail; - } -- p = xcalloc(1, sizeof(*p)); -- p->name = xstrdup(provider_id); -- p->handle = handle; -+ m->handle = handle; - /* setup the pkcs11 callbacks */ - if ((rv = (*getfunctionlist)(&f)) != CKR_OK) { - error("C_GetFunctionList for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } -- p->function_list = f; -+ m->function_list = f; - if ((rv = f->C_Initialize(NULL)) != CKR_OK) { - error("C_Initialize for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } - need_finalize = 1; -- if ((rv = f->C_GetInfo(&p->info)) != CKR_OK) { -+ if ((rv = f->C_GetInfo(&m->info)) != CKR_OK) { - error("C_GetInfo for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); -+ goto fail; -+ } -+ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); -+ if (uri->lib_manuf != NULL && -+ strcmp(uri->lib_manuf, m->info.manufacturerID)) { -+ debug("%s: Skipping provider %s not matching library_manufacturer", -+ __func__, m->info.manufacturerID); - goto fail; - } -- rmspace(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) { -+ if (m->nslots == 0) { - debug("%s: provider %s returned no slots", __func__, -- provider_id); -+ provider_module); - goto fail; - } -- p->slotlist = xcalloc(p->nslots, sizeof(CK_SLOT_ID)); -- if ((rv = f->C_GetSlotList(CK_TRUE, p->slotlist, &p->nslots)) -+ m->slotlist = xcalloc(m->nslots, sizeof(CK_SLOT_ID)); -+ if ((rv = f->C_GetSlotList(CK_TRUE, m->slotlist, &m->nslots)) - != CKR_OK) { - error("C_GetSlotList for provider %s failed: %lu", -- provider_id, rv); -+ provider_module, rv); - goto fail; - } -- p->slotinfo = xcalloc(p->nslots, sizeof(struct pkcs11_slotinfo)); -+ m->slotinfo = xcalloc(m->nslots, sizeof(struct pkcs11_slotinfo)); -+ m->valid = 1; - p->valid = 1; -- nkeys = 0; -- for (i = 0; i < p->nslots; i++) { -- token = &p->slotinfo[i].token; -- if ((rv = f->C_GetTokenInfo(p->slotlist[i], token)) -+ -+ for (i = 0; i < m->nslots; i++) { -+ token = &m->slotinfo[i].token; -+ if ((rv = f->C_GetTokenInfo(m->slotlist[i], token)) - != CKR_OK) { - error("C_GetTokenInfo for provider %s slot %lu " -- "failed: %lu", provider_id, (unsigned long)i, rv); -+ "failed: %lu", provider_module, (unsigned long)i, rv); - continue; - } - if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { -- debug2("%s: ignoring uninitialised token in " -- "provider %s slot %lu", __func__, -- provider_id, (unsigned long)i); - continue; - } - rmspace(token->label, sizeof(token->label)); - rmspace(token->manufacturerID, sizeof(token->manufacturerID)); - rmspace(token->model, sizeof(token->model)); - rmspace(token->serialNumber, sizeof(token->serialNumber)); -+ } -+ m->module_path = provider_module; -+ provider_module = NULL; -+ -+ /* insert unconditionally -- remove if there will be no keys later */ -+ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -+ p->refcount++; /* add to provider list */ -+ return p; -+ -+fail: -+ if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) -+ error("C_Finalize for provider %s failed: %lu", -+ provider_module, rv); -+ free(provider_module); -+ free(p); -+ if (handle) -+ dlclose(handle); -+ return NULL; -+} -+ -+int -+pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, struct sshkey ***keyp) -+{ -+ int nkeys; -+ struct pkcs11_provider *p = NULL; -+ CK_TOKEN_INFO *token; -+ CK_ULONG i; -+ char *provider_uri = pkcs11_uri_get(uri); -+ -+ debug("%s: called, provider_uri = %s", __func__, provider_uri); -+ -+ *keyp = NULL; -+ if ((p = pkcs11_provider_initialize(uri)) == NULL) { -+ debug("%s: failed to initialize provider: %s", -+ __func__, provider_uri); -+ goto fail; -+ } -+ -+ nkeys = 0; -+ for (i = 0; i < p->module->nslots; i++) { -+ token = &p->module->slotinfo[i].token; -+ if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { -+ debug2("%s: ignoring uninitialised token in " -+ "provider %s slot %lu", __func__, -+ provider_uri, (unsigned long)i); -+ continue; -+ } -+ if (uri->token != NULL && -+ strcmp(token->label, uri->token) != 0) { -+ debug2("%s: ignoring token not matching label (%s) " -+ "specified by PKCS#11 URI in slot %lu", __func__, -+ token->label, (unsigned long)i); -+ continue; -+ } -+ if (uri->manuf != NULL && -+ strcmp(token->manufacturerID, uri->manuf) != 0) { -+ debug2("%s: ignoring token not matching requrested " -+ "manufacturerID (%s) specified by PKCS#11 URI in " -+ "slot %lu", __func__, -+ token->manufacturerID, (unsigned long)i); -+ continue; -+ } - debug("provider %s slot %lu: label <%s> manufacturerID <%s> " - "model <%s> serial <%s> flags 0x%lx", -- provider_id, (unsigned long)i, -+ provider_uri, (unsigned long)i, - token->label, token->manufacturerID, token->model, - token->serialNumber, token->flags); -- /* open session, login with pin and retrieve public keys */ -- if (pkcs11_open_session(p, i, pin) == 0) -- pkcs11_fetch_keys(p, i, keyp, &nkeys); -+ /* open session if not yet opened, login with pin -+ * and retrieve public keys */ -+ if ((p->module->slotinfo[i].session != 0) || -+ pkcs11_open_session(p, i, pin) == 0) -+ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); - } - if (nkeys > 0) { -- TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); -- p->refcount++; /* add to provider list */ -+ free(provider_uri); - return (nkeys); - } -- debug("%s: provider %s returned no keys", __func__, provider_id); -+ debug("%s: provider %s returned no keys", __func__, provider_uri); - /* don't add the provider, since it does not have any keys */ - fail: -- if (need_finalize && (rv = f->C_Finalize(NULL)) != CKR_OK) -- error("C_Finalize for provider %s failed: %lu", -- provider_id, rv); - if (p) { -- free(p->slotlist); -- free(p->slotinfo); -- free(p); -+ TAILQ_REMOVE(&pkcs11_providers, p, next); -+ pkcs11_provider_unref(p); - } -- if (handle) -- dlclose(handle); -+ free(provider_uri); - return (-1); - } - -diff -up openssh/ssh-pkcs11.h.pkcs11-uri openssh/ssh-pkcs11.h ---- openssh/ssh-pkcs11.h.pkcs11-uri 2018-10-11 02:56:36.000000000 +0200 -+++ openssh/ssh-pkcs11.h 2018-10-12 13:52:55.457191459 +0200 -@@ -14,10 +14,15 @@ - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -+ -+#include "ssh-pkcs11-uri.h" -+ - int pkcs11_init(int); - void pkcs11_terminate(void); - int pkcs11_add_provider(char *, char *, struct sshkey ***); -+int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); - int pkcs11_del_provider(char *); -+int pkcs11_uri_write(const struct sshkey *, FILE *); - - #if !defined(WITH_OPENSSL) && defined(ENABLE_PKCS11) - #undef ENABLE_PKCS11 -diff -up openssh/ssh-pkcs11-uri.c.pkcs11-uri openssh/ssh-pkcs11-uri.c ---- openssh/ssh-pkcs11-uri.c.pkcs11-uri 2018-10-12 13:52:55.456191450 +0200 -+++ openssh/ssh-pkcs11-uri.c 2018-10-12 13:52:55.455191442 +0200 -@@ -0,0 +1,395 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * Permission to use, copy, modify, and distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#include "includes.h" -+ -+#ifdef ENABLE_PKCS11 -+ -+#include -+#include -+ -+#include "sshkey.h" -+#include "sshbuf.h" -+#include "log.h" -+ -+#define CRYPTOKI_COMPAT -+#include "pkcs11.h" -+ -+#include "ssh-pkcs11-uri.h" -+ -+#define PKCS11_URI_PATH_SEPARATOR ";" -+#define PKCS11_URI_QUERY_SEPARATOR "&" -+#define PKCS11_URI_VALUE_SEPARATOR "=" -+#define PKCS11_URI_ID "id" -+#define PKCS11_URI_TOKEN "token" -+#define PKCS11_URI_OBJECT "object" -+#define PKCS11_URI_LIB_MANUF "library-manufacturer" -+#define PKCS11_URI_MANUF "manufacturer" -+#define PKCS11_URI_MODULE_PATH "module-path" -+ -+/* Keyword tokens. */ -+typedef enum { -+ pId, pToken, pObject, pLibraryManufacturer, pManufacturer, pModulePath, -+ pBadOption -+} pkcs11uriOpCodes; -+ -+/* Textual representation of the tokens. */ -+static struct { -+ const char *name; -+ pkcs11uriOpCodes opcode; -+} keywords[] = { -+ { PKCS11_URI_ID, pId }, -+ { PKCS11_URI_TOKEN, pToken }, -+ { PKCS11_URI_OBJECT, pObject }, -+ { PKCS11_URI_LIB_MANUF, pLibraryManufacturer }, -+ { PKCS11_URI_MANUF, pManufacturer }, -+ { PKCS11_URI_MODULE_PATH, pModulePath }, -+ { NULL, pBadOption } -+}; -+ -+static pkcs11uriOpCodes -+parse_token(const char *cp) -+{ -+ u_int i; -+ -+ for (i = 0; keywords[i].name; i++) -+ if (strncasecmp(cp, keywords[i].name, -+ strlen(keywords[i].name)) == 0) -+ return keywords[i].opcode; -+ -+ return pBadOption; -+} -+ -+int -+percent_decode(char *data, char **outp) -+{ -+ char tmp[3]; -+ char *out, *tmp_end; -+ char *p = data; -+ long value; -+ size_t outlen = 0; -+ -+ out = malloc(strlen(data)+1); /* upper bound */ -+ if (out == NULL) -+ return -1; -+ while (*p != '\0') { -+ switch (*p) { -+ case '%': -+ p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[0] = *p++; -+ if (*p == '\0') -+ goto fail; -+ tmp[1] = *p++; -+ tmp[2] = '\0'; -+ tmp_end = NULL; -+ value = strtol(tmp, &tmp_end, 16); -+ if (tmp_end != tmp+2) -+ goto fail; -+ else -+ out[outlen++] = (char) value; -+ break; -+ default: -+ out[outlen++] = *p++; -+ break; -+ } -+ } -+ -+ /* zero terminate */ -+ out[outlen] = '\0'; -+ *outp = out; -+ return outlen; -+fail: -+ free(out); -+ return -1; -+} -+ -+struct sshbuf * -+percent_encode(const char *data, size_t length, const char *whitelist) -+{ -+ struct sshbuf *b = NULL; -+ char tmp[4], *cp; -+ size_t i; -+ -+ if ((b = sshbuf_new()) == NULL) -+ return NULL; -+ for (i = 0; i < length; i++) { -+ cp = strchr(whitelist, data[i]); -+ /* if c is specified as '\0' pointer to terminator is returned !! */ -+ if (cp != NULL && *cp != '\0') { -+ if (sshbuf_put(b, &data[i], 1) != 0) -+ goto err; -+ } else -+ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 -+ || sshbuf_put(b, tmp, 3) != 0) -+ goto err; -+ } -+ if (sshbuf_put(b, "\0", 1) == 0) -+ return b; -+err: -+ sshbuf_free(b); -+ return NULL; -+} -+ -+char * -+pkcs11_uri_append(char *part, const char *separator, const char *key, -+ struct sshbuf *value) -+{ -+ char *new_part; -+ size_t size = 0; -+ -+ if (value == NULL) -+ return NULL; -+ -+ size = asprintf(&new_part, -+ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", -+ (part != NULL ? part : ""), -+ (part != NULL ? separator : ""), -+ key, sshbuf_ptr(value)); -+ sshbuf_free(value); -+ free(part); -+ -+ if (size <= 0) -+ return NULL; -+ return new_part; -+} -+ -+char * -+pkcs11_uri_get(struct pkcs11_uri *uri) -+{ -+ size_t size = 0; -+ char *p = NULL, *path = NULL, *query = NULL; -+ -+ /* compose a percent-encoded ID */ -+ if (uri->id_len > 0) { -+ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_ID, key_id); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write object label */ -+ if (uri->object) { -+ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), -+ PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_OBJECT, label); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write token label */ -+ if (uri->token) { -+ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), -+ PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_TOKEN, label); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write manufacturer */ -+ if (uri->manuf) { -+ struct sshbuf *manuf = percent_encode(uri->manuf, -+ strlen(uri->manuf), PKCS11_URI_WHITELIST); -+ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, -+ PKCS11_URI_MANUF, manuf); -+ if (path == NULL) -+ goto err; -+ } -+ -+ /* Write module_path */ -+ if (uri->module_path) { -+ struct sshbuf *module = percent_encode(uri->module_path, -+ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); -+ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, -+ PKCS11_URI_MODULE_PATH, module); -+ if (query == NULL) -+ goto err; -+ } -+ -+ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", -+ path != NULL ? path : "", -+ query != NULL ? "?" : "", -+ query != NULL ? query : ""); -+err: -+ free(query); -+ free(path); -+ if (size <= 0) -+ return NULL; -+ return p; -+} -+ -+struct pkcs11_uri * -+pkcs11_uri_init() -+{ -+ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); -+ return d; -+} -+ -+void -+pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) -+{ -+ free(pkcs11->id); -+ free(pkcs11->module_path); -+ free(pkcs11->token); -+ free(pkcs11->object); -+ free(pkcs11->lib_manuf); -+ free(pkcs11->manuf); -+ free(pkcs11); -+} -+ -+int -+pkcs11_uri_parse(const char *uri, struct pkcs11_uri *pkcs11) -+{ -+ char *saveptr1, *saveptr2, *str1, *str2, *tok; -+ int rv = 0, len; -+ char *p = NULL; -+ -+ size_t scheme_len = strlen(PKCS11_URI_SCHEME); -+ if (strlen(uri) < scheme_len || /* empty URI matches everything */ -+ strncmp(uri, PKCS11_URI_SCHEME, scheme_len) != 0) { -+ error("%s: The '%s' does not look like PKCS#11 URI", -+ __func__, uri); -+ return -1; -+ } -+ -+ if (pkcs11 == NULL) { -+ error("%s: Bad arguments. The pkcs11 can't be null", __func__); -+ return -1; -+ } -+ -+ /* skip URI schema name */ -+ p = strdup(uri); -+ str1 = p; -+ -+ /* everything before ? */ -+ tok = strtok_r(str1, "?", &saveptr1); -+ if (tok == NULL) { -+ free(p); -+ error("%s: pk11-path expected, got EOF", __func__); -+ return -1; -+ } -+ -+ /* skip URI schema name: -+ * the scheme ensures that there is at least something before "?" -+ * allowing empty pk11-path. Resulting token at worst pointing to -+ * \0 byte */ -+ tok = tok + scheme_len; -+ -+ /* parse pk11-path */ -+ for (str2 = tok; ; str2 = NULL) { -+ char **charptr; -+ pkcs11uriOpCodes opcode; -+ tok = strtok_r(str2, PKCS11_URI_PATH_SEPARATOR, &saveptr2); -+ if (tok == NULL) -+ break; -+ opcode = parse_token(tok); -+ -+ char *arg = tok + strlen(keywords[opcode].name) + 1; /* separator "=" */ -+ switch (opcode) { -+ case pId: -+ /* CKA_ID */ -+ if (pkcs11->id != NULL) { -+ verbose("%s: The id already set in the PKCS#11 URI", -+ __func__); -+ rv = -1; -+ } -+ len = percent_decode(arg, &pkcs11->id); -+ if (len <= 0) { -+ verbose("%s: Failed to percent-decode CKA_ID: %s", -+ __func__, arg); -+ rv = -1; -+ } else -+ pkcs11->id_len = len; -+ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", -+ __func__, arg); -+ break; -+ case pToken: -+ /* CK_TOKEN_INFO -> label */ -+ charptr = &pkcs11->token; -+ parse_string: -+ if (*charptr != NULL) { -+ verbose("%s: The %s already set in the PKCS#11 URI", -+ keywords[opcode].name, __func__); -+ rv = -1; -+ } -+ percent_decode(arg, charptr); -+ debug3("%s: Setting %s = %s from PKCS#11 URI", -+ __func__, keywords[opcode].name, *charptr); -+ break; -+ -+ case pObject: -+ /* CK_TOKEN_INFO -> manufacturerID */ -+ charptr = &pkcs11->object; -+ goto parse_string; -+ -+ case pManufacturer: -+ /* CK_TOKEN_INFO -> manufacturerID */ -+ charptr = &pkcs11->manuf; -+ goto parse_string; -+ -+ case pLibraryManufacturer: -+ /* CK_INFO -> manufacturerID */ -+ charptr = &pkcs11->lib_manuf; -+ goto parse_string; -+ -+ case pBadOption: -+ default: -+ /* Unrecognized attribute in the URI path SHOULD be error */ -+ verbose("%s: Unknown part of path in PKCS#11 URI: %s", -+ __func__, tok); -+ } -+ } -+ -+ tok = strtok_r(NULL, "?", &saveptr1); -+ if (tok == NULL) { -+ free(p); -+ return rv; -+ } -+ /* parse pk11-query (optional) */ -+ for (str2 = tok; ; str2 = NULL) { -+ size_t key_len = strlen(PKCS11_URI_MODULE_PATH) + 1; -+ tok = strtok_r(str2, PKCS11_URI_QUERY_SEPARATOR, &saveptr2); -+ if (tok == NULL) -+ break; -+ if (strncasecmp(tok, PKCS11_URI_MODULE_PATH -+ PKCS11_URI_VALUE_SEPARATOR, key_len) == 0) { -+ /* module-path is PKCS11Provider */ -+ if (pkcs11->module_path != NULL) { -+ verbose("%s: Multiple module-path attributes are" -+ "not supported the PKCS#11 URI", __func__); -+ rv = -1; -+ } -+ percent_decode(tok + key_len, &pkcs11->module_path); -+ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", -+ __func__, pkcs11->module_path); -+ /* } else if ( pin-value ) { */ -+ } else { -+ /* Unrecognized attribute in the URI query SHOULD be ignored */ -+ verbose("%s: Unknown part of query in PKCS#11 URI: %s", -+ __func__, tok); -+ } -+ } -+ free(p); -+ return rv; -+} -+ -+#endif /* ENABLE_PKCS11 */ -diff -up openssh/ssh-pkcs11-uri.h.pkcs11-uri openssh/ssh-pkcs11-uri.h ---- openssh/ssh-pkcs11-uri.h.pkcs11-uri 2018-10-12 13:52:55.456191450 +0200 -+++ openssh/ssh-pkcs11-uri.h 2018-10-12 13:52:55.456191450 +0200 -@@ -0,0 +1,41 @@ -+/* -+ * Copyright (c) 2017 Red Hat -+ * -+ * Authors: Jakub Jelen -+ * -+ * Permission to use, copy, modify, and distribute this software for any -+ * purpose with or without fee is hereby granted, provided that the above -+ * copyright notice and this permission notice appear in all copies. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -+ */ -+ -+#define PKCS11_URI_SCHEME "pkcs11:" -+#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ -+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ -+ "0123456789_-.()" -+ -+struct pkcs11_uri { -+ /* path */ -+ char *id; -+ size_t id_len; -+ char *token; -+ char *object; -+ char *lib_manuf; -+ char *manuf; -+ /* query */ -+ char *module_path; -+}; -+ -+struct pkcs11_uri *pkcs11_uri_init(); -+void pkcs11_uri_cleanup(struct pkcs11_uri *); -+int pkcs11_uri_parse(const char *, struct pkcs11_uri *); -+struct pkcs11_uri *pkcs11_uri_init(); -+char * pkcs11_uri_get(struct pkcs11_uri *uri); -+ diff --git a/openssh-7.7p1-fips.patch b/openssh-7.7p1-fips.patch index bdaf525..14cf5c3 100644 --- a/openssh-7.7p1-fips.patch +++ b/openssh-7.7p1-fips.patch @@ -154,20 +154,12 @@ diff -up openssh-7.9p1/dh.h.fips openssh-7.9p1/dh.h u_int dh_estimate(int); -@@ -51,6 +52,7 @@ u_int dh_estimate(int); - * Miniumum increased in light of DH precomputation attacks. - */ - #define DH_GRP_MIN 2048 -+#define DH_GRP_MIN_FIPS 2048 - #define DH_GRP_MAX 8192 - - /* diff -up openssh-7.9p1/entropy.c.fips openssh-7.9p1/entropy.c --- openssh-7.9p1/entropy.c.fips 2018-10-17 02:01:20.000000000 +0200 +++ openssh-7.9p1/entropy.c 2019-03-11 17:06:37.621878041 +0100 @@ -223,6 +223,11 @@ seed_rng(void) - fatal("OpenSSL version mismatch. Built against %lx, you " - "have %lx", (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); + "have %lx", (u_long)OPENSSL_VERSION_NUMBER, + OpenSSL_version_num()); + /* clean the PRNG status when exiting the program */ +#if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -175,13 +167,13 @@ diff -up openssh-7.9p1/entropy.c.fips openssh-7.9p1/entropy.c +#endif + #ifndef OPENSSL_PRNG_ONLY - if (RAND_status() == 1) { + if (RAND_status() == 1) debug3("RNG is ready, skipping seeding"); diff -up openssh-7.9p1/kex.c.fips openssh-7.9p1/kex.c --- openssh-7.9p1/kex.c.fips 2019-03-11 17:06:37.614877975 +0100 +++ openssh-7.9p1/kex.c 2019-03-11 17:06:37.621878041 +0100 @@ -122,6 +123,26 @@ static const struct kexalg kexalgs[] = { - { NULL, -1, -1, -1}, + { NULL, -1, -1, -1 }, }; +static const struct kexalg kexalgs_fips[] = { @@ -201,22 +193,22 @@ diff -up openssh-7.9p1/kex.c.fips openssh-7.9p1/kex.c + SSH_DIGEST_SHA512 }, +# endif +#endif -+ { NULL, -1, -1, -1}, ++ { NULL, -1, -1, -1 }, +}; + + static char * + kex_alg_list_internal(char sep, const struct kexalg *algs) + { +@@ -129,7 +150,7 @@ kex_alg_list(char sep) char * kex_alg_list(char sep) { -@@ -129,7 +150,7 @@ kex_alg_list(char sep) - size_t nlen, rlen = 0; - const struct kexalg *k; +- return kex_alg_list_internal(sep, kexalgs); ++ return kex_alg_list_internal(sep, (FIPS_mode() ? kexalgs_fips : kexalgs)); + } -- for (k = kexalgs; k->name != NULL; k++) { -+ for (k = (FIPS_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { - if (ret != NULL) - ret[rlen++] = sep; - nlen = strlen(k->name); -@@ -149,7 +170,7 @@ kex_alg_by_name(const char *name) + char * +@@ -149,11 +170,11 @@ kex_alg_by_name(const char *name) { const struct kexalg *k; @@ -224,7 +216,13 @@ diff -up openssh-7.9p1/kex.c.fips openssh-7.9p1/kex.c + for (k = (FIPS_mode() ? kexalgs_fips : kexalgs); k->name != NULL; k++) { if (strcmp(k->name, name) == 0) return k; - #ifdef GSSAPI + } +- for (k = gss_kexalgs; k->name != NULL; k++) { ++ for (k = (FIPS_mode() ? NULL : gss_kexalgs); k->name != NULL; k++) { + if (strncmp(k->name, name, strlen(k->name)) == 0) + return k; + } + return NULL; @@ -175,7 +196,10 @@ kex_names_valid(const char *names) for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { @@ -248,15 +246,6 @@ diff -up openssh-7.9p1/kexgexc.c.fips openssh-7.9p1/kexgexc.c #include #include -@@ -65,7 +66,7 @@ kexgex_client(struct ssh *ssh) - - nbits = dh_estimate(kex->dh_need * 8); - -- kex->min = DH_GRP_MIN; -+ kex->min = FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN; - kex->max = DH_GRP_MAX; - kex->nbits = nbits; - if (datafellows & SSH_BUG_DHGEX_LARGE) @@ -118,6 +119,10 @@ input_kex_dh_gex_group(int type, u_int32 r = SSH_ERR_ALLOC_FAIL; goto out; @@ -268,21 +257,6 @@ diff -up openssh-7.9p1/kexgexc.c.fips openssh-7.9p1/kexgexc.c p = g = NULL; /* belong to kex->dh now */ /* generate and send 'e', client DH public key */ -diff -up openssh-7.9p1/kexgexs.c.fips openssh-7.9p1/kexgexs.c ---- openssh-7.9p1/kexgexs.c.fips 2018-10-17 02:01:20.000000000 +0200 -+++ openssh-7.9p1/kexgexs.c 2019-03-11 17:06:37.621878041 +0100 -@@ -85,9 +85,9 @@ input_kex_dh_gex_request(int type, u_int - kex->nbits = nbits; - kex->min = min; - kex->max = max; -- min = MAXIMUM(DH_GRP_MIN, min); -+ min = MAXIMUM(FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, min); - max = MINIMUM(DH_GRP_MAX, max); -- nbits = MAXIMUM(DH_GRP_MIN, nbits); -+ nbits = MAXIMUM(FIPS_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN, nbits); - nbits = MINIMUM(DH_GRP_MAX, nbits); - - if (kex->max < kex->min || kex->nbits < kex->min || diff -up openssh-7.9p1/mac.c.fips openssh-7.9p1/mac.c --- openssh-7.9p1/mac.c.fips 2019-03-11 17:06:37.614877975 +0100 +++ openssh-7.9p1/mac.c 2019-03-11 17:06:37.621878041 +0100 @@ -294,7 +268,7 @@ diff -up openssh-7.9p1/mac.c.fips openssh-7.9p1/mac.c + #include #include - + #include @@ -54,7 +56,7 @@ struct macalg { int etm; /* Encrypt-then-MAC */ }; @@ -376,7 +350,7 @@ diff -up openssh-7.9p1/Makefile.in.fips openssh-7.9p1/Makefile.in - $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) - ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o + ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o uidswap.o compat.o - $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) + $(LD) -o $@ ssh-keysign.o readconf.o uidswap.o $(LDFLAGS) -lssh -lopenbsd-compat -lfipscheck $(LIBS) @@ -464,15 +438,15 @@ diff -up openssh-7.9p1/readconf.c.fips openssh-7.9p1/readconf.c + all)) != 0) \ fatal("%s: %s: %s", __func__, #what, ssh_err(r)); \ } while (0) -- ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, all_cipher); -- ASSEMBLE(macs, KEX_SERVER_MAC, all_mac); -- ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, all_kex); +- ASSEMBLE(ciphers, KEX_CLIENT_ENCRYPT, all_cipher); +- ASSEMBLE(macs, KEX_CLIENT_MAC, all_mac); +- ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, all_kex); - ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, all_key); - ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, all_sig); -+ ASSEMBLE(ciphers, KEX_SERVER_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); -+ ASSEMBLE(macs, KEX_SERVER_MAC, KEX_FIPS_MAC, all_mac); -+ ASSEMBLE(kex_algorithms, KEX_SERVER_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); ++ ASSEMBLE(ciphers, KEX_CLIENT_ENCRYPT, KEX_FIPS_ENCRYPT, all_cipher); ++ ASSEMBLE(macs, KEX_CLIENT_MAC, KEX_FIPS_MAC, all_mac); ++ ASSEMBLE(kex_algorithms, KEX_CLIENT_KEX, KEX_DEFAULT_KEX_FIPS, all_kex); + ASSEMBLE(hostbased_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + ASSEMBLE(pubkey_key_types, KEX_DEFAULT_PK_ALG, KEX_FIPS_PK_ALG, all_key); + ASSEMBLE(ca_sign_algorithms, SSH_ALLOWED_CA_SIGALGS, KEX_FIPS_PK_ALG, all_sig); @@ -553,14 +527,6 @@ diff -up openssh-7.9p1/ssh.c.fips openssh-7.9p1/ssh.c #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ -@@ -1047,7 +1059,6 @@ main(int ac, char **av) - host_arg = xstrdup(host); - - #ifdef WITH_OPENSSL -- OpenSSL_add_all_algorithms(); - ERR_load_crypto_strings(); - #endif - @@ -1283,6 +1294,10 @@ main(int ac, char **av) seed_rng(); @@ -569,9 +535,9 @@ diff -up openssh-7.9p1/ssh.c.fips openssh-7.9p1/ssh.c + logit("FIPS mode initialized"); + } + - if (options.user == NULL) - options.user = xstrdup(pw->pw_name); - + /* + * Discard other fds that are hanging around. These can cause problem + * with backgrounded ssh processes started by ControlPersist. diff -up openssh-7.9p1/sshconnect2.c.fips openssh-7.9p1/sshconnect2.c --- openssh-7.9p1/sshconnect2.c.fips 2019-03-11 17:06:37.580877655 +0100 +++ openssh-7.9p1/sshconnect2.c 2019-03-11 17:06:37.623878060 +0100 @@ -584,7 +550,7 @@ diff -up openssh-7.9p1/sshconnect2.c.fips openssh-7.9p1/sshconnect2.c #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" -@@ -235,7 +237,8 @@ order_hostkeyalgs(char *host, struct soc +@@ -117,7 +117,8 @@ order_hostkeyalgs(char *host, struct soc for (i = 0; i < options.num_system_hostfiles; i++) load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); @@ -594,71 +560,83 @@ diff -up openssh-7.9p1/sshconnect2.c.fips openssh-7.9p1/sshconnect2.c maxlen = strlen(avail) + 1; first = xmalloc(maxlen); last = xmalloc(maxlen); -@@ -290,23 +293,28 @@ ssh_kex2(char *host, struct sockaddr *ho +@@ -185,14 +185,16 @@ ssh_kex2(char *host, struct sockaddr *ho + if (options.hostkeyalgorithms != NULL) { + all_key = sshkey_alg_list(0, 0, 1, ','); + if (kex_assemble_names(&options.hostkeyalgorithms, +- KEX_DEFAULT_PK_ALG, all_key) != 0) ++ (FIPS_mode() ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG), ++ all_key) != 0) + fatal("%s: kex_assemble_namelist", __func__); + free(all_key); + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal(options.hostkeyalgorithms); + } else { + /* Enforce default */ +- options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); ++ options.hostkeyalgorithms = xstrdup((FIPS_mode() ++ ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG)); + /* Prefer algorithms that we already have keys for */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal( +@@ -201,29 +201,34 @@ ssh_kex2(char *host, struct sockaddr *ho - #ifdef GSSAPI + #if defined(GSSAPI) && defined(WITH_OPENSSL) if (options.gss_keyex) { -- /* Add the GSSAPI mechanisms currently supported on this +- /* Add the GSSAPI mechanisms currently supported on this - * client to the key exchange algorithm proposal */ -- orig = options.kex_algorithms; +- orig = myproposal[PROPOSAL_KEX_ALGS]; - - if (options.gss_server_identity) -- gss_host = options.gss_server_identity; +- gss_host = xstrdup(options.gss_server_identity); - else if (options.gss_trust_dns) -- gss_host = (char *)get_canonical_hostname(active_state, 1); +- gss_host = remote_hostname(ssh); - else -- gss_host = host; +- 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(&options.kex_algorithms, +- 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); + if (FIPS_mode()) { + logit("Disabling GSSAPIKeyExchange. Not usable in FIPS mode"); + options.gss_keyex = 0; + } else { + /* Add the GSSAPI mechanisms currently supported on this + * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; ++ orig = myproposal[PROPOSAL_KEX_ALGS]; + + if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; ++ gss_host = xstrdup(options.gss_server_identity); + else if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(active_state, 1); ++ gss_host = remote_hostname(ssh); + else -+ gss_host = host; ++ 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(&options.kex_algorithms, ++ 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 -@@ -326,14 +334,16 @@ ssh_kex2(char *host, struct sockaddr *ho - if (options.hostkeyalgorithms != NULL) { - all_key = sshkey_alg_list(0, 0, 1, ','); - if (kex_assemble_names(&options.hostkeyalgorithms, -- KEX_DEFAULT_PK_ALG, all_key) != 0) -+ (FIPS_mode() ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG), -+ all_key) != 0) - fatal("%s: kex_assemble_namelist", __func__); - free(all_key); - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = - compat_pkalg_proposal(options.hostkeyalgorithms); - } else { - /* Enforce default */ -- options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); -+ options.hostkeyalgorithms = xstrdup((FIPS_mode() -+ ? KEX_FIPS_PK_ALG : KEX_DEFAULT_PK_ALG)); - /* Prefer algorithms that we already have keys for */ - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = - compat_pkalg_proposal( diff -up openssh-7.9p1/sshd.c.fips openssh-7.9p1/sshd.c --- openssh-7.9p1/sshd.c.fips 2019-03-11 17:06:37.617878003 +0100 +++ openssh-7.9p1/sshd.c 2019-03-11 17:06:37.624878069 +0100 @@ -698,15 +676,6 @@ diff -up openssh-7.9p1/sshd.c.fips openssh-7.9p1/sshd.c /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; -@@ -1722,7 +1737,7 @@ main(int ac, char **av) - else - closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); - --#ifdef WITH_OPENSSL -+#if 0 /* FIPS */ - OpenSSL_add_all_algorithms(); - #endif - @@ -2036,6 +2051,10 @@ main(int ac, char **av) /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); diff --git a/openssh-7.7p1-gssapi-new-unique.patch b/openssh-7.7p1-gssapi-new-unique.patch index 3f46ac1..2eea250 100644 --- a/openssh-7.7p1-gssapi-new-unique.patch +++ b/openssh-7.7p1-gssapi-new-unique.patch @@ -411,7 +411,7 @@ diff -up openssh-7.9p1/gss-serv-krb5.c.ccache_name openssh-7.9p1/gss-serv-krb5.c } krb5_free_principal(krb_context, princ); -@@ -331,29 +332,19 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl +@@ -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); @@ -425,6 +425,7 @@ diff -up openssh-7.9p1/gss-serv-krb5.c.ccache_name openssh-7.9p1/gss-serv-krb5.c - 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++; @@ -438,6 +439,8 @@ diff -up openssh-7.9p1/gss-serv-krb5.c.ccache_name openssh-7.9p1/gss-serv-krb5.c + 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 @@ -477,15 +480,16 @@ index 6cae720e..16e55cbc 100644 } /* This allows GSSAPI methods to do things to the childs environment based -@@ -496,8 +498,7 @@ ssh_gssapi_rekey_creds() { +@@ -498,9 +500,7 @@ ssh_gssapi_rekey_creds() { char *envstr; #endif -- if (gssapi_client.store.envval == NULL && +- 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-7.9p1/servconf.c.ccache_name openssh-7.9p1/servconf.c --- openssh-7.9p1/servconf.c.ccache_name 2019-03-01 15:17:42.704611768 +0100 @@ -590,14 +594,6 @@ diff --git a/ssh-gss.h b/ssh-gss.h index 6593e422..245178af 100644 --- a/ssh-gss.h +++ b/ssh-gss.h -@@ -62,7 +62,6 @@ - #define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" - - typedef struct { -- char *filename; - char *envvar; - char *envval; - struct passwd *owner; @@ -83,7 +82,7 @@ typedef struct ssh_gssapi_mech_struct { int (*dochild) (ssh_gssapi_client *); int (*userok) (ssh_gssapi_client *, char *); diff --git a/openssh-7.8p1-UsePAM-warning.patch b/openssh-7.8p1-UsePAM-warning.patch index da39361..ef2c737 100644 --- a/openssh-7.8p1-UsePAM-warning.patch +++ b/openssh-7.8p1-UsePAM-warning.patch @@ -9,9 +9,9 @@ diff --git a/sshd.c b/sshd.c + if (! options.use_pam) + logit("WARNING: 'UsePAM no' is not supported in Fedora and may cause several problems."); + - seed_rng(); - /* Fill in default values for those options not explicitly set. */ + fill_default_server_options(&options); + diff --git a/sshd_config b/sshd_config --- a/sshd_config +++ b/sshd_config diff --git a/openssh-7.8p1-gsskex.patch b/openssh-7.8p1-gsskex.patch deleted file mode 100644 index ce35d90..0000000 --- a/openssh-7.8p1-gsskex.patch +++ /dev/null @@ -1,2905 +0,0 @@ -diff -up openssh/auth2.c.gsskex openssh/auth2.c ---- openssh/auth2.c.gsskex 2018-08-22 11:47:33.260216045 +0200 -+++ openssh/auth2.c 2018-08-22 11:47:33.307216424 +0200 -@@ -74,6 +74,7 @@ extern Authmethod method_passwd; - extern Authmethod method_kbdint; - extern Authmethod method_hostbased; - #ifdef GSSAPI -+extern Authmethod method_gsskeyex; - extern Authmethod method_gssapi; - #endif - -@@ -81,6 +82,7 @@ Authmethod *authmethods[] = { - &method_none, - &method_pubkey, - #ifdef GSSAPI -+ &method_gsskeyex, - &method_gssapi, - #endif - &method_passwd, -diff -up openssh/auth2-gss.c.gsskex openssh/auth2-gss.c ---- openssh/auth2-gss.c.gsskex 2018-08-22 11:47:33.260216045 +0200 -+++ openssh/auth2-gss.c 2018-08-22 13:00:48.722680124 +0200 -@@ -31,6 +31,7 @@ - #include - - #include -+#include - - #include "xmalloc.h" - #include "sshkey.h" -@@ -54,6 +55,44 @@ static int input_gssapi_mic(int type, u_ - static int input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh); - static int input_gssapi_errtok(int, u_int32_t, struct ssh *); - -+/* -+ * The 'gssapi_keyex' userauth mechanism. -+ */ -+static int -+userauth_gsskeyex(struct ssh *ssh) -+{ -+ Authctxt *authctxt = ssh->authctxt; -+ int authenticated = 0; -+ struct sshbuf *b = NULL; -+ gss_buffer_desc mic, gssbuf; -+ u_int len; -+ -+ mic.value = packet_get_string(&len); -+ mic.length = len; -+ -+ packet_check_eom(); -+ -+ if ((b = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, -+ "gssapi-keyex"); -+ -+ gssbuf.value = sshbuf_mutable_ptr(b); -+ gssbuf.length = sshbuf_len(b); -+ -+ /* gss_kex_context is NULL with privsep, so we can't check it here */ -+ if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, -+ &gssbuf, &mic)))) -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw)); -+ -+ sshbuf_free(b); -+ free(mic.value); -+ -+ return (authenticated); -+} -+ - /* - * We only support those mechanisms that we know about (ie ones that we know - * how to check local user kuserok and the like) -@@ -260,7 +296,8 @@ input_gssapi_exchange_complete(int type, - if ((r = sshpkt_get_end(ssh)) != 0) - fatal("%s: %s", __func__, ssh_err(r)); - -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -+ authctxt->pw)); - - if ((!use_privsep || mm_is_monitor()) && - (displayname = ssh_gssapi_displayname()) != NULL) -@@ -313,7 +350,8 @@ input_gssapi_mic(int type, u_int32_t ple - gssbuf.length = sshbuf_len(b); - - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) -- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); -+ authenticated = -+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); - else - logit("GSSAPI MIC check failed"); - -@@ -335,6 +373,12 @@ input_gssapi_mic(int type, u_int32_t ple - return 0; - } - -+Authmethod method_gsskeyex = { -+ "gssapi-keyex", -+ userauth_gsskeyex, -+ &options.gss_authentication -+}; -+ - Authmethod method_gssapi = { - "gssapi-with-mic", - userauth_gssapi, -diff -up openssh/auth.c.gsskex openssh/auth.c ---- openssh/auth.c.gsskex 2018-08-22 11:47:33.274216158 +0200 -+++ openssh/auth.c 2018-08-22 11:47:33.308216432 +0200 -@@ -395,6 +395,7 @@ auth_root_allowed(struct ssh *ssh, const - case PERMIT_NO_PASSWD: - if (strcmp(method, "publickey") == 0 || - strcmp(method, "hostbased") == 0 || -+ strcmp(method, "gssapi-keyex") == 0 || - strcmp(method, "gssapi-with-mic") == 0) - return 1; - break; -diff -up openssh/clientloop.c.gsskex openssh/clientloop.c ---- openssh/clientloop.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/clientloop.c 2018-08-22 11:47:33.309216441 +0200 -@@ -112,6 +112,10 @@ - #include "ssherr.h" - #include "hostfile.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - /* import options */ - extern Options options; - -@@ -1357,9 +1361,18 @@ client_loop(struct ssh *ssh, int have_pt - break; - - /* Do channel operations unless rekeying in progress. */ -- if (!ssh_packet_is_rekeying(ssh)) -+ if (!ssh_packet_is_rekeying(ssh)) { - channel_after_select(ssh, readset, writeset); - -+#ifdef GSSAPI -+ if (options.gss_renewal_rekey && -+ ssh_gssapi_credentials_updated(NULL)) { -+ debug("credentials updated - forcing rekey"); -+ need_rekeying = 1; -+ } -+#endif -+ } -+ - /* Buffer input from the connection. */ - client_process_net_input(readset); - -diff -up openssh/configure.ac.gsskex openssh/configure.ac ---- openssh/configure.ac.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/configure.ac 2018-08-22 11:47:33.309216441 +0200 -@@ -673,6 +673,30 @@ main() { if (NSVersionOfRunTimeLibrary(" - [Use tunnel device compatibility to OpenBSD]) - AC_DEFINE([SSH_TUN_PREPEND_AF], [1], - [Prepend the address family to IP tunnel traffic]) -+ AC_MSG_CHECKING(if we have the Security Authorization Session API) -+ AC_TRY_COMPILE([#include ], -+ [SessionCreate(0, 0);], -+ [ac_cv_use_security_session_api="yes" -+ AC_DEFINE(USE_SECURITY_SESSION_API, 1, -+ [platform has the Security Authorization Session API]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes)], -+ [ac_cv_use_security_session_api="no" -+ AC_MSG_RESULT(no)]) -+ AC_MSG_CHECKING(if we have an in-memory credentials cache) -+ AC_TRY_COMPILE( -+ [#include ], -+ [cc_context_t c; -+ (void) cc_initialize (&c, 0, NULL, NULL);], -+ [AC_DEFINE(USE_CCAPI, 1, -+ [platform uses an in-memory credentials cache]) -+ LIBS="$LIBS -framework Security" -+ AC_MSG_RESULT(yes) -+ if test "x$ac_cv_use_security_session_api" = "xno"; then -+ AC_MSG_ERROR(*** Need a security framework to use the credentials cache API ***) -+ fi], -+ [AC_MSG_RESULT(no)] -+ ) - m4_pattern_allow([AU_IPv]) - AC_CHECK_DECL([AU_IPv4], [], - AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) -diff -up openssh/gss-genr.c.gsskex openssh/gss-genr.c ---- openssh/gss-genr.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-genr.c 2018-08-22 13:18:47.444383602 +0200 -@@ -35,18 +35,179 @@ - #include - #include - #include -+#include - - #include "xmalloc.h" - #include "ssherr.h" - #include "sshbuf.h" - #include "log.h" - #include "ssh2.h" -+#include "cipher.h" -+#include "sshkey.h" -+#include "kex.h" - - #include "ssh-gss.h" - - extern u_char *session_id2; - extern u_int session_id2_len; - -+typedef struct { -+ char *encoded; -+ gss_OID oid; -+} ssh_gss_kex_mapping; -+ -+/* -+ * XXX - It would be nice to find a more elegant way of handling the -+ * XXX passing of the key exchange context to the userauth routines -+ */ -+ -+Gssctxt *gss_kex_context = NULL; -+ -+static ssh_gss_kex_mapping *gss_enc2oid = NULL; -+ -+int -+ssh_gssapi_oid_table_ok() { -+ return (gss_enc2oid != NULL); -+} -+ -+/* -+ * Return a list of the gss-group1-sha1 mechanisms supported by this program -+ * -+ * We test mechanisms to ensure that we can use them, to avoid starting -+ * a key exchange with a bad mechanism -+ */ -+ -+char * -+ssh_gssapi_client_mechanisms(const char *host, const char *client) { -+ gss_OID_set gss_supported; -+ OM_uint32 min_status; -+ -+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &gss_supported))) -+ return NULL; -+ -+ return(ssh_gssapi_kex_mechs(gss_supported, ssh_gssapi_check_mechanism, -+ host, client)); -+} -+ -+char * -+ssh_gssapi_kex_mechs(gss_OID_set gss_supported, ssh_gssapi_check_fn *check, -+ const char *host, const char *client) { -+ struct sshbuf *buf; -+ size_t i; -+ int oidpos, enclen, r; -+ char *mechs, *encoded; -+ u_char digest[EVP_MAX_MD_SIZE]; -+ char deroid[2]; -+ const EVP_MD *evp_md = EVP_md5(); -+ EVP_MD_CTX *md; -+ -+ if (gss_enc2oid != NULL) { -+ for (i = 0; gss_enc2oid[i].encoded != NULL; i++) -+ free(gss_enc2oid[i].encoded); -+ free(gss_enc2oid); -+ } -+ -+ gss_enc2oid = xmalloc(sizeof(ssh_gss_kex_mapping) * -+ (gss_supported->count + 1)); -+ -+ if ((buf = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ md = EVP_MD_CTX_new(); -+ oidpos = 0; -+ for (i = 0; i < gss_supported->count; i++) { -+ if (gss_supported->elements[i].length < 128 && -+ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { -+ -+ deroid[0] = SSH_GSS_OIDTYPE; -+ deroid[1] = gss_supported->elements[i].length; -+ -+ EVP_DigestInit(md, evp_md); -+ EVP_DigestUpdate(md, deroid, 2); -+ EVP_DigestUpdate(md, -+ gss_supported->elements[i].elements, -+ gss_supported->elements[i].length); -+ EVP_DigestFinal(md, digest, NULL); -+ -+ encoded = xmalloc(EVP_MD_size(evp_md) * 2); -+ enclen = __b64_ntop(digest, EVP_MD_size(evp_md), -+ encoded, EVP_MD_size(evp_md) * 2); -+ -+ if (oidpos != 0) -+ if ((r = sshbuf_put_u8(buf, ',')) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ if ((r = sshbuf_put(buf, KEX_GSS_GEX_SHA1_ID, -+ sizeof(KEX_GSS_GEX_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0 || -+ (r = sshbuf_put_u8(buf, ',')) != 0 || -+ (r = sshbuf_put(buf, KEX_GSS_GRP1_SHA1_ID, -+ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0 || -+ (r = sshbuf_put_u8(buf, ',')) != 0 || -+ (r = sshbuf_put(buf, KEX_GSS_GRP14_SHA1_ID, -+ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1)) != 0 || -+ (r = sshbuf_put(buf, encoded, enclen)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); -+ gss_enc2oid[oidpos].encoded = encoded; -+ oidpos++; -+ } -+ } -+ EVP_MD_CTX_free(md); -+ gss_enc2oid[oidpos].oid = NULL; -+ gss_enc2oid[oidpos].encoded = NULL; -+ -+ if ((r = sshbuf_put_u8(buf, '\0')) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mechs = xmalloc(sshbuf_len(buf)); -+ sshbuf_get(buf, mechs, sshbuf_len(buf)); -+ sshbuf_free(buf); -+ -+ if (strlen(mechs) == 0) { -+ free(mechs); -+ mechs = NULL; -+ } -+ -+ return (mechs); -+} -+ -+gss_OID -+ssh_gssapi_id_kex(Gssctxt *ctx, char *name, int kex_type) { -+ int i = 0; -+ -+ switch (kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP1_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP1_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GRP14_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GRP14_SHA1_ID) - 1; -+ break; -+ case KEX_GSS_GEX_SHA1: -+ if (strlen(name) < sizeof(KEX_GSS_GEX_SHA1_ID)) -+ return GSS_C_NO_OID; -+ name += sizeof(KEX_GSS_GEX_SHA1_ID) - 1; -+ break; -+ default: -+ return GSS_C_NO_OID; -+ } -+ -+ while (gss_enc2oid[i].encoded != NULL && -+ strcmp(name, gss_enc2oid[i].encoded) != 0) -+ i++; -+ -+ if (gss_enc2oid[i].oid != NULL && ctx != NULL) -+ ssh_gssapi_set_oid(ctx, gss_enc2oid[i].oid); -+ -+ return gss_enc2oid[i].oid; -+} -+ - /* sshbuf_get for gss_buffer_desc */ - int - ssh_gssapi_get_buffer_desc(struct sshbuf *b, gss_buffer_desc *g) -@@ -218,7 +373,7 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de - } - - ctx->major = gss_init_sec_context(&ctx->minor, -- GSS_C_NO_CREDENTIAL, &ctx->context, ctx->name, ctx->oid, -+ ctx->client_creds, &ctx->context, ctx->name, ctx->oid, - GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | deleg_flag, - 0, NULL, recv_tok, NULL, send_tok, flags, NULL); - -@@ -248,8 +403,42 @@ ssh_gssapi_import_name(Gssctxt *ctx, con - } - - OM_uint32 -+ssh_gssapi_client_identity(Gssctxt *ctx, const char *name) -+{ -+ gss_buffer_desc gssbuf; -+ gss_name_t gssname; -+ OM_uint32 status; -+ gss_OID_set oidset; -+ -+ gssbuf.value = (void *) name; -+ gssbuf.length = strlen(gssbuf.value); -+ -+ gss_create_empty_oid_set(&status, &oidset); -+ gss_add_oid_set_member(&status, ctx->oid, &oidset); -+ -+ ctx->major = gss_import_name(&ctx->minor, &gssbuf, -+ GSS_C_NT_USER_NAME, &gssname); -+ -+ if (!ctx->major) -+ ctx->major = gss_acquire_cred(&ctx->minor, -+ gssname, 0, oidset, GSS_C_INITIATE, -+ &ctx->client_creds, NULL, NULL); -+ -+ gss_release_name(&status, &gssname); -+ gss_release_oid_set(&status, &oidset); -+ -+ if (ctx->major) -+ ssh_gssapi_error(ctx); -+ -+ return(ctx->major); -+} -+ -+OM_uint32 - ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_t buffer, gss_buffer_t hash) - { -+ if (ctx == NULL) -+ return -1; -+ - if ((ctx->major = gss_get_mic(&ctx->minor, ctx->context, - GSS_C_QOP_DEFAULT, buffer, hash))) - ssh_gssapi_error(ctx); -@@ -257,6 +446,19 @@ ssh_gssapi_sign(Gssctxt *ctx, gss_buffer - return (ctx->major); - } - -+/* Priviledged when used by server */ -+OM_uint32 -+ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+{ -+ if (ctx == NULL) -+ return -1; -+ -+ ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -+ gssbuf, gssmic, NULL); -+ -+ return (ctx->major); -+} -+ - void - ssh_gssapi_buildmic(struct sshbuf *b, const char *user, const char *service, - const char *context) -@@ -273,11 +475,16 @@ ssh_gssapi_buildmic(struct sshbuf *b, co - } - - int --ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host) -+ssh_gssapi_check_mechanism(Gssctxt **ctx, gss_OID oid, const char *host, -+ const char *client) - { - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 major, minor; - gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"}; -+ Gssctxt *intctx = NULL; -+ -+ if (ctx == NULL) -+ ctx = &intctx; - - /* RFC 4462 says we MUST NOT do SPNEGO */ - if (oid->length == spnego_oid.length && -@@ -287,6 +494,10 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - ssh_gssapi_build_ctx(ctx); - ssh_gssapi_set_oid(*ctx, oid); - major = ssh_gssapi_import_name(*ctx, host); -+ -+ if (!GSS_ERROR(major) && client) -+ major = ssh_gssapi_client_identity(*ctx, client); -+ - if (!GSS_ERROR(major)) { - major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, - NULL); -@@ -296,10 +507,66 @@ ssh_gssapi_check_mechanism(Gssctxt **ctx - GSS_C_NO_BUFFER); - } - -- if (GSS_ERROR(major)) -+ if (GSS_ERROR(major) || intctx != NULL) - ssh_gssapi_delete_ctx(ctx); - - return (!GSS_ERROR(major)); - } - -+int -+ssh_gssapi_credentials_updated(Gssctxt *ctxt) { -+ static gss_name_t saved_name = GSS_C_NO_NAME; -+ static OM_uint32 saved_lifetime = 0; -+ static gss_OID saved_mech = GSS_C_NO_OID; -+ static gss_name_t name; -+ static OM_uint32 last_call = 0; -+ OM_uint32 lifetime, now, major, minor; -+ int equal; -+ -+ now = time(NULL); -+ -+ if (ctxt) { -+ debug("Rekey has happened - updating saved versions"); -+ -+ if (saved_name != GSS_C_NO_NAME) -+ gss_release_name(&minor, &saved_name); -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &saved_name, &saved_lifetime, NULL, NULL); -+ -+ if (!GSS_ERROR(major)) { -+ saved_mech = ctxt->oid; -+ saved_lifetime+= now; -+ } else { -+ /* Handle the error */ -+ } -+ return 0; -+ } -+ -+ if (now - last_call < 10) -+ return 0; -+ -+ last_call = now; -+ -+ if (saved_mech == GSS_C_NO_OID) -+ return 0; -+ -+ major = gss_inquire_cred(&minor, GSS_C_NO_CREDENTIAL, -+ &name, &lifetime, NULL, NULL); -+ if (major == GSS_S_CREDENTIALS_EXPIRED) -+ return 0; -+ else if (GSS_ERROR(major)) -+ return 0; -+ -+ major = gss_compare_name(&minor, saved_name, name, &equal); -+ gss_release_name(&minor, &name); -+ if (GSS_ERROR(major)) -+ return 0; -+ -+ if (equal && (saved_lifetime < lifetime + now - 10)) -+ return 1; -+ -+ return 0; -+} -+ - #endif /* GSSAPI */ -diff -up openssh/gss-serv.c.gsskex openssh/gss-serv.c ---- openssh/gss-serv.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-serv.c 2018-08-22 11:47:33.310216448 +0200 -@@ -44,17 +44,19 @@ - #include "session.h" - #include "misc.h" - #include "servconf.h" -+#include "uidswap.h" - - #include "ssh-gss.h" -+#include "monitor_wrap.h" - - extern ServerOptions options; - - static ssh_gssapi_client gssapi_client = -- { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, -- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; -+ { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, GSS_C_NO_CREDENTIAL, -+ GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL}, 0, 0}; - - ssh_gssapi_mech gssapi_null_mech = -- { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL}; -+ { NULL, NULL, {0, NULL}, NULL, NULL, NULL, NULL, NULL}; - - #ifdef KRB5 - extern ssh_gssapi_mech gssapi_kerberos_mech; -@@ -141,6 +143,28 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss - } - - /* Unprivileged */ -+char * -+ssh_gssapi_server_mechanisms() { -+ if (supported_oids == NULL) -+ ssh_gssapi_prepare_supported_oids(); -+ return (ssh_gssapi_kex_mechs(supported_oids, -+ &ssh_gssapi_server_check_mech, NULL, NULL)); -+} -+ -+/* Unprivileged */ -+int -+ssh_gssapi_server_check_mech(Gssctxt **dum, gss_OID oid, const char *data, -+ const char *dummy) { -+ Gssctxt *ctx = NULL; -+ int res; -+ -+ res = !GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctx, oid))); -+ ssh_gssapi_delete_ctx(&ctx); -+ -+ return (res); -+} -+ -+/* Unprivileged */ - void - ssh_gssapi_supported_oids(gss_OID_set *oidset) - { -@@ -150,7 +174,9 @@ ssh_gssapi_supported_oids(gss_OID_set *o - gss_OID_set supported; - - gss_create_empty_oid_set(&min_status, oidset); -- gss_indicate_mechs(&min_status, &supported); -+ -+ if (GSS_ERROR(gss_indicate_mechs(&min_status, &supported))) -+ return; - - while (supported_mechs[i]->name != NULL) { - if (GSS_ERROR(gss_test_oid_set_member(&min_status, -@@ -276,8 +302,48 @@ OM_uint32 - ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) - { - int i = 0; -+ int equal = 0; -+ gss_name_t new_name = GSS_C_NO_NAME; -+ gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; -+ -+ if (options.gss_store_rekey && client->used && ctx->client_creds) { -+ if (client->mech->oid.length != ctx->oid->length || -+ (memcmp(client->mech->oid.elements, -+ ctx->oid->elements, ctx->oid->length) !=0)) { -+ debug("Rekeyed credentials have different mechanism"); -+ return GSS_S_COMPLETE; -+ } -+ -+ if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, -+ ctx->client_creds, ctx->oid, &new_name, -+ NULL, NULL, NULL))) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ -+ ctx->major = gss_compare_name(&ctx->minor, client->name, -+ new_name, &equal); -+ -+ if (GSS_ERROR(ctx->major)) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ -+ if (!equal) { -+ debug("Rekeyed credentials have different name"); -+ return GSS_S_COMPLETE; -+ } -+ -+ debug("Marking rekeyed credentials for export"); - -- gss_buffer_desc ename; -+ gss_release_name(&ctx->minor, &client->name); -+ gss_release_cred(&ctx->minor, &client->creds); -+ client->name = new_name; -+ client->creds = ctx->client_creds; -+ ctx->client_creds = GSS_C_NO_CREDENTIAL; -+ client->updated = 1; -+ return GSS_S_COMPLETE; -+ } - - client->mech = NULL; - -@@ -292,6 +358,13 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - if (client->mech == NULL) - return GSS_S_FAILURE; - -+ if (ctx->client_creds && -+ (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, -+ ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { -+ ssh_gssapi_error(ctx); -+ return (ctx->major); -+ } -+ - if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, - &client->displayname, NULL))) { - ssh_gssapi_error(ctx); -@@ -309,6 +382,8 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - return (ctx->major); - } - -+ gss_release_buffer(&ctx->minor, &ename); -+ - /* We can't copy this structure, so we just move the pointer to it */ - client->creds = ctx->client_creds; - ctx->client_creds = GSS_C_NO_CREDENTIAL; -@@ -319,11 +394,20 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g - void - ssh_gssapi_cleanup_creds(void) - { -- if (gssapi_client.store.filename != NULL) { -- /* Unlink probably isn't sufficient */ -- debug("removing gssapi cred file\"%s\"", -- gssapi_client.store.filename); -- unlink(gssapi_client.store.filename); -+ krb5_ccache ccache = NULL; -+ krb5_error_code problem; -+ -+ if (gssapi_client.store.data != NULL) { -+ if ((problem = krb5_cc_resolve(gssapi_client.store.data, gssapi_client.store.envval, &ccache))) { -+ debug("%s: krb5_cc_resolve(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { -+ debug("%s: krb5_cc_destroy(): %.100s", __func__, -+ krb5_get_err_text(gssapi_client.store.data, problem)); -+ } else { -+ krb5_free_context(gssapi_client.store.data); -+ gssapi_client.store.data = NULL; -+ } - } - } - -@@ -356,7 +440,7 @@ ssh_gssapi_do_child(char ***envp, u_int - - /* Privileged */ - int --ssh_gssapi_userok(char *user) -+ssh_gssapi_userok(char *user, struct passwd *pw) - { - OM_uint32 lmin; - -@@ -366,9 +450,11 @@ ssh_gssapi_userok(char *user) - return 0; - } - if (gssapi_client.mech && gssapi_client.mech->userok) -- if ((*gssapi_client.mech->userok)(&gssapi_client, user)) -+ if ((*gssapi_client.mech->userok)(&gssapi_client, user)) { -+ gssapi_client.used = 1; -+ gssapi_client.store.owner = pw; - return 1; -- else { -+ } else { - /* Destroy delegated credentials if userok fails */ - gss_release_buffer(&lmin, &gssapi_client.displayname); - gss_release_buffer(&lmin, &gssapi_client.exportedname); -@@ -382,14 +468,89 @@ ssh_gssapi_userok(char *user) - return (0); - } - --/* Privileged */ --OM_uint32 --ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) -+/* These bits are only used for rekeying. The unpriviledged child is running -+ * as the user, the monitor is root. -+ * -+ * In the child, we want to : -+ * *) Ask the monitor to store our credentials into the store we specify -+ * *) If it succeeds, maybe do a PAM update -+ */ -+ -+/* Stuff for PAM */ -+ -+#ifdef USE_PAM -+static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, -+ struct pam_response **resp, void *data) - { -- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, -- gssbuf, gssmic, NULL); -+ return (PAM_CONV_ERR); -+} -+#endif - -- return (ctx->major); -+void -+ssh_gssapi_rekey_creds() { -+ int ok; -+ int ret; -+#ifdef USE_PAM -+ pam_handle_t *pamh = NULL; -+ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; -+ char *envstr; -+#endif -+ -+ if (gssapi_client.store.envval == NULL && -+ gssapi_client.store.envvar == NULL) -+ return; -+ -+ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); -+ -+ if (!ok) -+ return; -+ -+ debug("Rekeyed credentials stored successfully"); -+ -+ /* Actually managing to play with the ssh pam stack from here will -+ * be next to impossible. In any case, we may want different options -+ * for rekeying. So, use our own :) -+ */ -+#ifdef USE_PAM -+ if (!use_privsep) { -+ debug("Not even going to try and do PAM with privsep disabled"); -+ return; -+ } -+ -+ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, -+ &pamconv, &pamh); -+ if (ret) -+ return; -+ -+ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, -+ gssapi_client.store.envval); -+ -+ ret = pam_putenv(pamh, envstr); -+ if (!ret) -+ pam_setcred(pamh, PAM_REINITIALIZE_CRED); -+ pam_end(pamh, PAM_SUCCESS); -+#endif -+} -+ -+int -+ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { -+ int ok = 0; -+ -+ /* Check we've got credentials to store */ -+ if (!gssapi_client.updated) -+ return 0; -+ -+ gssapi_client.updated = 0; -+ -+ temporarily_use_uid(gssapi_client.store.owner); -+ if (gssapi_client.mech && gssapi_client.mech->updatecreds) -+ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); -+ else -+ debug("No update function for this mechanism"); -+ -+ restore_uid(); -+ -+ return ok; - } - - /* Privileged */ -diff -up openssh/gss-serv-krb5.c.gsskex openssh/gss-serv-krb5.c ---- openssh/gss-serv-krb5.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/gss-serv-krb5.c 2018-08-22 11:47:33.311216457 +0200 -@@ -120,7 +120,7 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - krb5_error_code problem; - krb5_principal princ; - OM_uint32 maj_status, min_status; -- int len; -+ const char *new_ccname, *new_cctype; - const char *errmsg; - - if (client->creds == NULL) { -@@ -180,11 +180,23 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - return; - } - -- client->store.filename = xstrdup(krb5_cc_get_name(krb_context, ccache)); -+ new_cctype = krb5_cc_get_type(krb_context, ccache); -+ new_ccname = krb5_cc_get_name(krb_context, ccache); -+ - client->store.envvar = "KRB5CCNAME"; -- len = strlen(client->store.filename) + 6; -- client->store.envval = xmalloc(len); -- snprintf(client->store.envval, len, "FILE:%s", client->store.filename); -+#ifdef USE_CCAPI -+ xasprintf(&client->store.envval, "API:%s", new_ccname); -+#else -+ if (new_ccname[0] == ':') -+ new_ccname++; -+ xasprintf(&client->store.envval, "%s:%s", new_cctype, new_ccname); -+ if (strcmp(new_cctype, "DIR") == 0) { -+ char *p; -+ p = strrchr(client->store.envval, '/'); -+ if (p) -+ *p = '\0'; -+ } -+#endif - - #ifdef USE_PAM - if (options.use_pam) -@@ -193,9 +205,76 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl - - krb5_cc_close(krb_context, ccache); - -+ client->store.data = krb_context; -+ - return; - } - -+int -+ssh_gssapi_krb5_updatecreds(ssh_gssapi_ccache *store, -+ ssh_gssapi_client *client) -+{ -+ krb5_ccache ccache = NULL; -+ krb5_principal principal = NULL; -+ char *name = NULL; -+ krb5_error_code problem; -+ OM_uint32 maj_status, min_status; -+ -+ if ((problem = krb5_cc_resolve(krb_context, store->envval, &ccache))) { -+ logit("krb5_cc_resolve(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ return 0; -+ } -+ -+ /* Find out who the principal in this cache is */ -+ if ((problem = krb5_cc_get_principal(krb_context, ccache, -+ &principal))) { -+ logit("krb5_cc_get_principal(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ if ((problem = krb5_unparse_name(krb_context, principal, &name))) { -+ logit("krb5_unparse_name(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ -+ if (strcmp(name,client->exportedname.value)!=0) { -+ debug("Name in local credentials cache differs. Not storing"); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ krb5_free_unparsed_name(krb_context, name); -+ return 0; -+ } -+ krb5_free_unparsed_name(krb_context, name); -+ -+ /* Name matches, so lets get on with it! */ -+ -+ if ((problem = krb5_cc_initialize(krb_context, ccache, principal))) { -+ logit("krb5_cc_initialize(): %.100s", -+ krb5_get_err_text(krb_context, problem)); -+ krb5_free_principal(krb_context, principal); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ krb5_free_principal(krb_context, principal); -+ -+ if ((maj_status = gss_krb5_copy_ccache(&min_status, client->creds, -+ ccache))) { -+ logit("gss_krb5_copy_ccache() failed. Sorry!"); -+ krb5_cc_close(krb_context, ccache); -+ return 0; -+ } -+ -+ return 1; -+} -+ - ssh_gssapi_mech gssapi_kerberos_mech = { - "toWM5Slw5Ew8Mqkay+al2g==", - "Kerberos", -@@ -203,7 +282,8 @@ ssh_gssapi_mech gssapi_kerberos_mech = { - NULL, - &ssh_gssapi_krb5_userok, - NULL, -- &ssh_gssapi_krb5_storecreds -+ &ssh_gssapi_krb5_storecreds, -+ &ssh_gssapi_krb5_updatecreds - }; - - #endif /* KRB5 */ -diff -up openssh/kex.c.gsskex openssh/kex.c ---- openssh/kex.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/kex.c 2018-08-22 11:47:33.311216457 +0200 -@@ -54,6 +54,10 @@ - #include "sshbuf.h" - #include "digest.h" - -+#ifdef GSSAPI -+#include "ssh-gss.h" -+#endif -+ - /* prototype */ - static int kex_choose_conf(struct ssh *); - static int kex_input_newkeys(int, u_int32_t, struct ssh *); -@@ -103,6 +107,11 @@ static const struct kexalg kexalgs[] = { - { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, - #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ -+#ifdef GSSAPI -+ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP1_SHA1_ID, KEX_GSS_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, -+ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, -+#endif - { NULL, -1, -1, -1}, - }; - -@@ -136,6 +145,12 @@ kex_alg_by_name(const char *name) - for (k = kexalgs; k->name != NULL; k++) { - if (strcmp(k->name, name) == 0) - return k; -+#ifdef GSSAPI -+ if (strncmp(name, "gss-", 4) == 0) { -+ if (strncmp(k->name, name, strlen(k->name)) == 0) -+ return k; -+ } -+#endif - } - return NULL; - } -diff -up openssh/kexgssc.c.gsskex openssh/kexgssc.c ---- openssh/kexgssc.c.gsskex 2018-08-22 11:47:33.311216457 +0200 -+++ openssh/kexgssc.c 2018-08-22 11:47:33.311216457 +0200 -@@ -0,0 +1,341 @@ -+/* -+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR -+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "includes.h" -+ -+#ifdef GSSAPI -+ -+#include "includes.h" -+ -+#include -+#include -+ -+#include -+ -+#include "xmalloc.h" -+#include "sshbuf.h" -+#include "ssh2.h" -+#include "sshkey.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "digest.h" -+ -+#include "ssh-gss.h" -+ -+int -+kexgss_client(struct ssh *ssh) { -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr; -+ Gssctxt *ctxt; -+ OM_uint32 maj_status, min_status, ret_flags; -+ u_int klen, kout, slen = 0, strlen; -+ DH *dh; -+ BIGNUM *dh_server_pub = NULL; -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *p = NULL; -+ BIGNUM *g = NULL; -+ const BIGNUM *pub_key, *p1, *g1; -+ u_char *kbuf; -+ u_char *serverhostkey = NULL; -+ u_char *empty = ""; -+ char *msg; -+ char *lang; -+ int type = 0; -+ int first = 1; -+ int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ -+ /* Initialise our GSSAPI world */ -+ ssh_gssapi_build_ctx(&ctxt); -+ if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type) -+ == GSS_C_NO_OID) -+ fatal("Couldn't identify host exchange"); -+ -+ if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host)) -+ fatal("Couldn't import hostname"); -+ -+ if (ssh->kex->gss_client && -+ ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client)) -+ fatal("Couldn't acquire client credentials"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange\n"); -+ nbits = dh_estimate(ssh->kex->we_need * 8); -+ packet_start(SSH2_MSG_KEXGSS_GROUPREQ); -+ packet_put_int(min); -+ packet_put_int(nbits); -+ packet_put_int(max); -+ -+ packet_send(); -+ -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUP); -+ -+ if ((p = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(p); -+ if ((g = BN_new()) == NULL) -+ fatal("BN_new() failed"); -+ packet_get_bignum2(g); -+ packet_check_eom(); -+ -+ if (BN_num_bits(p) < min || BN_num_bits(p) > max) -+ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", -+ min, BN_num_bits(p), max); -+ -+ dh = dh_new_group(g, p); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ /* Step 1 - e is pub_key */ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ DH_get0_key(dh, &pub_key, NULL); -+ -+ /* This is f, we initialise it now to make life easier */ -+ dh_server_pub = BN_new(); -+ if (dh_server_pub == NULL) -+ fatal("dh_server_pub == NULL"); -+ -+ token_ptr = GSS_C_NO_BUFFER; -+ -+ do { -+ debug("Calling gss_init_sec_context"); -+ -+ maj_status = ssh_gssapi_init_ctx(ctxt, -+ ssh->kex->gss_deleg_creds, token_ptr, &send_tok, -+ &ret_flags); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length != 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ fatal("gss_init_context failed"); -+ } -+ -+ /* If we've got an old receive buffer get rid of it */ -+ if (token_ptr != GSS_C_NO_BUFFER) -+ free(recv_tok.value); -+ -+ if (maj_status == GSS_S_COMPLETE) { -+ /* If mutual state flag is not true, kex fails */ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual authentication failed"); -+ -+ /* If integ avail flag is not true kex fails */ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity check failed"); -+ } -+ -+ /* -+ * If we have data to send, then the last message that we -+ * received cannot have been a 'complete'. -+ */ -+ if (send_tok.length != 0) { -+ if (first) { -+ packet_start(SSH2_MSG_KEXGSS_INIT); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ packet_put_bignum2((BIGNUM *)pub_key); -+ first = 0; -+ } else { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, -+ send_tok.length); -+ } -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ -+ /* If we've sent them data, they should reply */ -+ do { -+ type = packet_read(); -+ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { -+ debug("Received KEXGSS_HOSTKEY"); -+ if (serverhostkey) -+ fatal("Server host key received more than once"); -+ serverhostkey = -+ packet_get_string(&slen); -+ } -+ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); -+ -+ switch (type) { -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ debug("Received GSSAPI_CONTINUE"); -+ if (maj_status == GSS_S_COMPLETE) -+ fatal("GSSAPI Continue received from server when complete"); -+ recv_tok.value = packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ break; -+ case SSH2_MSG_KEXGSS_COMPLETE: -+ debug("Received GSSAPI_COMPLETE"); -+ packet_get_bignum2(dh_server_pub); -+ msg_tok.value = packet_get_string(&strlen); -+ msg_tok.length = strlen; -+ -+ /* Is there a token included? */ -+ if (packet_get_char()) { -+ recv_tok.value= -+ packet_get_string(&strlen); -+ recv_tok.length = strlen; -+ /* If we're already complete - protocol error */ -+ if (maj_status == GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: received token when complete"); -+ } else { -+ /* No token included */ -+ if (maj_status != GSS_S_COMPLETE) -+ packet_disconnect("Protocol error: did not receive final token"); -+ } -+ break; -+ case SSH2_MSG_KEXGSS_ERROR: -+ debug("Received Error"); -+ maj_status = packet_get_int(); -+ min_status = packet_get_int(); -+ msg = packet_get_string(NULL); -+ lang = packet_get_string(NULL); -+ fatal("GSSAPI Error: \n%.400s",msg); -+ default: -+ packet_disconnect("Protocol error: didn't expect packet type %d", -+ type); -+ } -+ token_ptr = &recv_tok; -+ } else { -+ /* No data, and not complete */ -+ if (maj_status != GSS_S_COMPLETE) -+ fatal("Not complete, and no token output"); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ /* -+ * We _must_ have received a COMPLETE message in reply from the -+ * server, which will have set dh_server_pub and msg_tok -+ */ -+ -+ if (type != SSH2_MSG_KEXGSS_COMPLETE) -+ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); -+ -+ /* Check f in range [1, p-1] */ -+ if (!dh_pub_is_valid(dh, dh_server_pub)) -+ packet_disconnect("bad server public DH value"); -+ -+ /* compute K=f^x mod p */ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_server_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_client: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexdh_client: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash(ssh->kex->hash_alg, ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ pub_key, /* e */ -+ dh_server_pub, /* f */ -+ shared_secret, /* K */ -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ DH_get0_pqg(dh, &p1, NULL, &g1); -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, -+ ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ (serverhostkey ? serverhostkey : empty), slen, -+ min, nbits, max, -+ p, g, -+ pub_key, -+ dh_server_pub, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ /* Verify that the hash matches the MIC we just got. */ -+ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) -+ packet_disconnect("Hash's MIC didn't verify"); -+ -+ free(msg_tok.value); -+ -+ DH_free(dh); -+ if (serverhostkey) -+ free(serverhostkey); -+ BN_clear_free(dh_server_pub); -+ -+ /* save session id */ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ if (ssh->kex->gss_deleg_creds) -+ ssh_gssapi_credentials_updated(ctxt); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ return kex_send_newkeys(ssh); -+} -+ -+#endif /* GSSAPI */ -diff -up openssh/kexgsss.c.gsskex openssh/kexgsss.c ---- openssh/kexgsss.c.gsskex 2018-08-22 11:47:33.311216457 +0200 -+++ openssh/kexgsss.c 2018-08-22 11:47:33.311216457 +0200 -@@ -0,0 +1,300 @@ -+/* -+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR -+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+#include "includes.h" -+ -+#ifdef GSSAPI -+ -+#include -+ -+#include -+#include -+ -+#include "xmalloc.h" -+#include "sshbuf.h" -+#include "ssh2.h" -+#include "sshkey.h" -+#include "cipher.h" -+#include "kex.h" -+#include "log.h" -+#include "packet.h" -+#include "dh.h" -+#include "ssh-gss.h" -+#include "monitor_wrap.h" -+#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ -+#include "servconf.h" -+#include "ssh-gss.h" -+#include "digest.h" -+ -+extern ServerOptions options; -+ -+int -+kexgss_server(struct ssh *ssh) -+{ -+ OM_uint32 maj_status, min_status; -+ -+ /* -+ * Some GSSAPI implementations use the input value of ret_flags (an -+ * output variable) as a means of triggering mechanism specific -+ * features. Initializing it to zero avoids inadvertently -+ * activating this non-standard behaviour. -+ */ -+ -+ OM_uint32 ret_flags = 0; -+ gss_buffer_desc gssbuf, recv_tok, msg_tok; -+ gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; -+ Gssctxt *ctxt = NULL; -+ u_int slen, klen, kout; -+ u_char *kbuf; -+ DH *dh; -+ int min = -1, max = -1, nbits = -1; -+ int cmin = -1, cmax = -1; /* client proposal */ -+ BIGNUM *shared_secret = NULL; -+ BIGNUM *dh_client_pub = NULL; -+ int type = 0; -+ gss_OID oid; -+ char *mechs; -+ u_char hash[SSH_DIGEST_MAX_LENGTH]; -+ size_t hashlen; -+ const BIGNUM *p, *g, *pub_key; -+ -+ /* Initialise GSSAPI */ -+ -+ /* If we're rekeying, privsep means that some of the private structures -+ * in the GSSAPI code are no longer available. This kludges them back -+ * into life -+ */ -+ if (!ssh_gssapi_oid_table_ok()) -+ if ((mechs = ssh_gssapi_server_mechanisms())) -+ free(mechs); -+ -+ debug2("%s: Identifying %s", __func__, ssh->kex->name); -+ oid = ssh_gssapi_id_kex(NULL, ssh->kex->name, ssh->kex->kex_type); -+ if (oid == GSS_C_NO_OID) -+ fatal("Unknown gssapi mechanism"); -+ -+ debug2("%s: Acquiring credentials", __func__); -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) -+ fatal("Unable to acquire credentials for the server"); -+ -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ dh = dh_new_group1(); -+ break; -+ case KEX_GSS_GRP14_SHA1: -+ dh = dh_new_group14(); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ debug("Doing group exchange"); -+ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); -+ /* store client proposal to provide valid signature */ -+ cmin = packet_get_int(); -+ nbits = packet_get_int(); -+ cmax = packet_get_int(); -+ min = MAX(DH_GRP_MIN, cmin); -+ max = MIN(DH_GRP_MAX, cmax); -+ packet_check_eom(); -+ if (max < min || nbits < min || max < nbits) -+ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", -+ min, nbits, max); -+ dh = PRIVSEP(choose_dh(min, nbits, max)); -+ if (dh == NULL) -+ packet_disconnect("Protocol error: no matching group found"); -+ -+ DH_get0_pqg(dh, &p, NULL, &g); -+ packet_start(SSH2_MSG_KEXGSS_GROUP); -+ packet_put_bignum2((BIGNUM *)p); -+ packet_put_bignum2((BIGNUM *)g); -+ packet_send(); -+ -+ packet_write_wait(); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ dh_gen_key(dh, ssh->kex->we_need * 8); -+ -+ do { -+ debug("Wait SSH2_MSG_GSSAPI_INIT"); -+ type = packet_read(); -+ switch(type) { -+ case SSH2_MSG_KEXGSS_INIT: -+ if (dh_client_pub != NULL) -+ fatal("Received KEXGSS_INIT after initialising"); -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ -+ if ((dh_client_pub = BN_new()) == NULL) -+ fatal("dh_client_pub == NULL"); -+ -+ packet_get_bignum2(dh_client_pub); -+ -+ /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */ -+ break; -+ case SSH2_MSG_KEXGSS_CONTINUE: -+ recv_tok.value = packet_get_string(&slen); -+ recv_tok.length = slen; -+ break; -+ default: -+ packet_disconnect( -+ "Protocol error: didn't expect packet type %d", -+ type); -+ } -+ -+ maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok, -+ &send_tok, &ret_flags)); -+ -+ free(recv_tok.value); -+ -+ if (maj_status != GSS_S_COMPLETE && send_tok.length == 0) -+ fatal("Zero length token output when incomplete"); -+ -+ if (dh_client_pub == NULL) -+ fatal("No client public key"); -+ -+ if (maj_status & GSS_S_CONTINUE_NEEDED) { -+ debug("Sending GSSAPI_CONTINUE"); -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ gss_release_buffer(&min_status, &send_tok); -+ } -+ } while (maj_status & GSS_S_CONTINUE_NEEDED); -+ -+ if (GSS_ERROR(maj_status)) { -+ if (send_tok.length > 0) { -+ packet_start(SSH2_MSG_KEXGSS_CONTINUE); -+ packet_put_string(send_tok.value, send_tok.length); -+ packet_send(); -+ } -+ fatal("accept_ctx died"); -+ } -+ -+ if (!(ret_flags & GSS_C_MUTUAL_FLAG)) -+ fatal("Mutual Authentication flag wasn't set"); -+ -+ if (!(ret_flags & GSS_C_INTEG_FLAG)) -+ fatal("Integrity flag wasn't set"); -+ -+ if (!dh_pub_is_valid(dh, dh_client_pub)) -+ packet_disconnect("bad client public DH value"); -+ -+ klen = DH_size(dh); -+ kbuf = xmalloc(klen); -+ kout = DH_compute_key(kbuf, dh_client_pub, dh); -+ if ((int)kout < 0) -+ fatal("DH_compute_key: failed"); -+ -+ shared_secret = BN_new(); -+ if (shared_secret == NULL) -+ fatal("kexgss_server: BN_new failed"); -+ -+ if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) -+ fatal("kexgss_server: BN_bin2bn failed"); -+ -+ memset(kbuf, 0, klen); -+ free(kbuf); -+ -+ DH_get0_key(dh, &pub_key, NULL); -+ hashlen = sizeof(hash); -+ switch (ssh->kex->kex_type) { -+ case KEX_GSS_GRP1_SHA1: -+ case KEX_GSS_GRP14_SHA1: -+ kex_dh_hash(ssh->kex->hash_alg, -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ NULL, 0, /* Change this if we start sending host keys */ -+ dh_client_pub, pub_key, shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ case KEX_GSS_GEX_SHA1: -+ kexgex_hash( -+ ssh->kex->hash_alg, -+ ssh->kex->client_version_string, ssh->kex->server_version_string, -+ sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer), -+ sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my), -+ NULL, 0, -+ cmin, nbits, cmax, -+ p, g, -+ dh_client_pub, -+ pub_key, -+ shared_secret, -+ hash, &hashlen -+ ); -+ break; -+ default: -+ fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type); -+ } -+ -+ BN_clear_free(dh_client_pub); -+ -+ if (ssh->kex->session_id == NULL) { -+ ssh->kex->session_id_len = hashlen; -+ ssh->kex->session_id = xmalloc(ssh->kex->session_id_len); -+ memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len); -+ } -+ -+ gssbuf.value = hash; -+ gssbuf.length = hashlen; -+ -+ if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok)))) -+ fatal("Couldn't get MIC"); -+ -+ packet_start(SSH2_MSG_KEXGSS_COMPLETE); -+ packet_put_bignum2(pub_key); -+ packet_put_string(msg_tok.value,msg_tok.length); -+ -+ if (send_tok.length != 0) { -+ packet_put_char(1); /* true */ -+ packet_put_string(send_tok.value, send_tok.length); -+ } else { -+ packet_put_char(0); /* false */ -+ } -+ packet_send(); -+ -+ gss_release_buffer(&min_status, &send_tok); -+ gss_release_buffer(&min_status, &msg_tok); -+ -+ if (gss_kex_context == NULL) -+ gss_kex_context = ctxt; -+ else -+ ssh_gssapi_delete_ctx(&ctxt); -+ -+ DH_free(dh); -+ -+ kex_derive_keys_bn(ssh, hash, hashlen, shared_secret); -+ BN_clear_free(shared_secret); -+ kex_send_newkeys(ssh); -+ -+ /* If this was a rekey, then save out any delegated credentials we -+ * just exchanged. */ -+ if (options.gss_store_rekey) -+ ssh_gssapi_rekey_creds(); -+ return 0; -+} -+#endif /* GSSAPI */ -diff -up openssh/kex.h.gsskex openssh/kex.h ---- openssh/kex.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/kex.h 2018-08-22 11:47:33.311216457 +0200 -@@ -100,6 +100,11 @@ enum kex_exchange { - KEX_DH_GEX_SHA256, - KEX_ECDH_SHA2, - KEX_C25519_SHA256, -+#ifdef GSSAPI -+ KEX_GSS_GRP1_SHA1, -+ KEX_GSS_GRP14_SHA1, -+ KEX_GSS_GEX_SHA1, -+#endif - KEX_MAX - }; - -@@ -148,6 +153,12 @@ struct kex { - u_int flags; - int hash_alg; - int ec_nid; -+#ifdef GSSAPI -+ int gss_deleg_creds; -+ int gss_trust_dns; -+ char *gss_host; -+ char *gss_client; -+#endif - char *client_version_string; - char *server_version_string; - char *failed_choice; -@@ -197,6 +208,10 @@ int kexecdh_client(struct ssh *); - int kexecdh_server(struct ssh *); - int kexc25519_client(struct ssh *); - int kexc25519_server(struct ssh *); -+#ifdef GSSAPI -+int kexgss_client(struct ssh *); -+int kexgss_server(struct ssh *); -+#endif - - int kex_dh_hash(int, const char *, const char *, - const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, -diff -up openssh/Makefile.in.gsskex openssh/Makefile.in ---- openssh/Makefile.in.gsskex 2018-08-22 11:47:33.312216465 +0200 -+++ openssh/Makefile.in 2018-08-22 13:19:54.955928277 +0200 -@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ - readpass.o ttymodes.o xmalloc.o addrmatch.o \ - atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \ - monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ -+ kexgssc.o \ - msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ - ssh-pkcs11.o smult_curve25519_ref.o \ - poly1305.o chacha.o cipher-chachapoly.o \ -@@ -121,7 +122,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw - auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ - auth2-none.o auth2-passwd.o auth2-pubkey.o \ - monitor.o monitor_wrap.o auth-krb5.o \ -- auth2-gss.o gss-serv.o gss-serv-krb5.o \ -+ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ - loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ - sftp-server.o sftp-common.o \ - sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ -diff -up openssh/monitor.c.gsskex openssh/monitor.c ---- openssh/monitor.c.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor.c 2018-08-22 13:22:19.589095240 +0200 -@@ -146,6 +146,8 @@ int mm_answer_gss_setup_ctx(int, struct - int mm_answer_gss_accept_ctx(int, struct sshbuf *); - int mm_answer_gss_userok(int, struct sshbuf *); - int mm_answer_gss_checkmic(int, struct sshbuf *); -+int mm_answer_gss_sign(int, struct sshbuf *); -+int mm_answer_gss_updatecreds(int, struct sshbuf *); - #endif - - #ifdef SSH_AUDIT_EVENTS -@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[] - {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, - {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, - {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, -+ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, - #endif - {0, 0, NULL} - }; - - struct mon_table mon_dispatch_postauth20[] = { -+#ifdef GSSAPI -+ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, -+ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, -+ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, -+ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, -+#endif - #ifdef WITH_OPENSSL - {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, - #endif -@@ -293,6 +302,10 @@ monitor_child_preauth(Authctxt *_authctx - /* Permit requests for moduli and signatures */ - monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); -+#ifdef GSSAPI -+ /* and for the GSSAPI key exchange */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); -+#endif - - /* The first few requests do not require asynchronous access */ - while (!authenticated) { -@@ -405,6 +418,10 @@ monitor_child_postauth(struct monitor *p - monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); -+#ifdef GSSAPI -+ /* and for the GSSAPI key exchange */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSETUP, 1); -+#endif - - if (auth_opts->permit_pty_flag) { - monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); -@@ -1695,6 +1712,13 @@ monitor_apply_keystate(struct monitor *p - # endif - #endif /* WITH_OPENSSL */ - kex->kex[KEX_C25519_SHA256] = kexc25519_server; -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ } -+#endif - kex->load_host_public_key=&get_hostkey_public_by_type; - kex->load_host_private_key=&get_hostkey_private_by_type; - kex->host_key_index=&get_hostkey_index; -@@ -1785,7 +1809,7 @@ mm_answer_gss_setup_ctx(int sock, struct - u_char *p; - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = sshbuf_get_string(m, &p, &len)) != 0) -@@ -1818,7 +1842,7 @@ mm_answer_gss_accept_ctx(int sock, struc - OM_uint32 flags = 0; /* GSI needs this */ - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) -@@ -1839,6 +1863,7 @@ mm_answer_gss_accept_ctx(int sock, struc - monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); - monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); - monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSIGN, 1); - } - return (0); - } -@@ -1850,7 +1875,7 @@ mm_answer_gss_checkmic(int sock, struct - OM_uint32 ret; - int r; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - - if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || -@@ -1880,10 +1905,11 @@ mm_answer_gss_userok(int sock, struct ss - int r, authenticated; - const char *displayname; - -- if (!options.gss_authentication) -+ if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - -- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); -+ authenticated = authctxt->valid && -+ ssh_gssapi_userok(authctxt->user, authctxt->pw); - - sshbuf_reset(m); - if ((r = sshbuf_put_u32(m, authenticated)) != 0) -@@ -1900,5 +1926,74 @@ mm_answer_gss_userok(int sock, struct ss - /* Monitor loop will terminate if authenticated */ - return (authenticated); - } -+ -+int -+mm_answer_gss_sign(int socket, struct sshbuf *m) -+{ -+ gss_buffer_desc data; -+ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; -+ OM_uint32 major, minor; -+ int r; -+ -+ if (!options.gss_authentication && !options.gss_keyex) -+ fatal("In GSSAPI monitor when GSSAPI is disabled"); -+ -+ if ((r = sshbuf_get_string(m, (u_char **)&data.value, &data.length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ if (data.length != 20) -+ fatal("%s: data length incorrect: %d", __func__, -+ (int) data.length); -+ -+ /* Save the session ID on the first time around */ -+ if (session_id2_len == 0) { -+ session_id2_len = data.length; -+ session_id2 = xmalloc(session_id2_len); -+ memcpy(session_id2, data.value, session_id2_len); -+ } -+ major = ssh_gssapi_sign(gsscontext, &data, &hash); -+ -+ free(data.value); -+ -+ sshbuf_reset(m); -+ if ((r = sshbuf_put_u32(m, major)) != 0 || -+ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); -+ -+ gss_release_buffer(&minor, &hash); -+ -+ /* Turn on getpwnam permissions */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); -+ -+ /* And credential updating, for when rekeying */ -+ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); -+ -+ return (0); -+} -+ -+int -+mm_answer_gss_updatecreds(int socket, struct sshbuf *m) { -+ ssh_gssapi_ccache store; -+ int ok, r; -+ -+ if ((r = sshbuf_get_cstring(m, &store.envvar, NULL)) != 0 || -+ (r = sshbuf_get_cstring(m, &store.envval, NULL)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ ok = ssh_gssapi_update_creds(&store); -+ -+ free(store.envvar); -+ free(store.envval); -+ -+ sshbuf_reset(m); -+ if ((r = sshbuf_put_u32(m, ok)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); -+ -+ return(0); -+} -+ - #endif /* GSSAPI */ - -diff -up openssh/monitor.h.gsskex openssh/monitor.h ---- openssh/monitor.h.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor.h 2018-08-22 11:47:33.313216473 +0200 -@@ -58,6 +58,8 @@ enum monitor_reqtype { - #ifdef WITH_SELINUX - MONITOR_REQ_AUTHROLE = 80, - #endif -+ MONITOR_REQ_GSSSIGN = 82, MONITOR_ANS_GSSSIGN = 83, -+ MONITOR_REQ_GSSUPCREDS = 84, MONITOR_ANS_GSSUPCREDS = 85, - - MONITOR_REQ_PAM_START = 100, - MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, -diff -up openssh/monitor_wrap.c.gsskex openssh/monitor_wrap.c ---- openssh/monitor_wrap.c.gsskex 2018-08-22 11:47:33.313216473 +0200 -+++ openssh/monitor_wrap.c 2018-08-22 13:27:38.665669643 +0200 -@@ -1004,7 +1004,7 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss - } - - int --mm_ssh_gssapi_userok(char *user) -+mm_ssh_gssapi_userok(char *user, struct passwd *pw) - { - struct sshbuf *m; - int r, authenticated = 0; -@@ -1023,4 +1023,52 @@ mm_ssh_gssapi_userok(char *user) - debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); - return (authenticated); - } -+ -+OM_uint32 -+mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) -+{ -+ struct sshbuf *m; -+ OM_uint32 major; -+ int r; -+ -+ if ((m = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); -+ -+ if ((r = sshbuf_get_u32(m, &major)) != 0 || -+ (r = sshbuf_get_string(m, (u_char **)&hash->value, &hash->length)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ sshbuf_free(m); -+ -+ return (major); -+} -+ -+int -+mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) -+{ -+ struct sshbuf *m; -+ int ok, r; -+ -+ if ((m = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ if ((r = sshbuf_put_cstring(m, store->envvar ? store->envvar : "")) != 0 || -+ (r = sshbuf_put_cstring(m, store->envval ? store->envval : "")) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); -+ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); -+ -+ if ((r = sshbuf_get_u32(m, &ok)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ -+ sshbuf_free(m); -+ -+ return (ok); -+} - #endif /* GSSAPI */ -diff -up openssh/monitor_wrap.h.gsskex openssh/monitor_wrap.h ---- openssh/monitor_wrap.h.gsskex 2018-08-22 11:47:33.263216069 +0200 -+++ openssh/monitor_wrap.h 2018-08-22 11:47:33.313216473 +0200 -@@ -63,8 +63,10 @@ int mm_sshkey_verify(const struct sshkey - OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); - OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, - gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); --int mm_ssh_gssapi_userok(char *user); -+int mm_ssh_gssapi_userok(char *user, struct passwd *); - OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); -+OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); -+int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); - #endif - - #ifdef USE_PAM -diff -up openssh/readconf.c.gsskex openssh/readconf.c ---- openssh/readconf.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/readconf.c 2018-08-22 13:28:17.487982869 +0200 -@@ -161,6 +161,8 @@ typedef enum { - oClearAllForwardings, oNoHostAuthenticationForLocalhost, - oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, - oAddressFamily, oGssAuthentication, oGssDelegateCreds, -+ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, -+ oGssServerIdentity, - oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, - oSendEnv, oSetEnv, oControlPath, oControlMaster, oControlPersist, - oHashKnownHosts, -@@ -201,10 +203,19 @@ static struct { - /* Sometimes-unsupported options */ - #if defined(GSSAPI) - { "gssapiauthentication", oGssAuthentication }, -+ { "gssapikeyexchange", oGssKeyEx }, - { "gssapidelegatecredentials", oGssDelegateCreds }, -+ { "gssapitrustdns", oGssTrustDns }, -+ { "gssapiclientidentity", oGssClientIdentity }, -+ { "gssapiserveridentity", oGssServerIdentity }, -+ { "gssapirenewalforcesrekey", oGssRenewalRekey }, - # else - { "gssapiauthentication", oUnsupported }, -+ { "gssapikeyexchange", oUnsupported }, - { "gssapidelegatecredentials", oUnsupported }, -+ { "gssapitrustdns", oUnsupported }, -+ { "gssapiclientidentity", oUnsupported }, -+ { "gssapirenewalforcesrekey", oUnsupported }, - #endif - #ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, -@@ -973,10 +984,30 @@ parse_time: - intptr = &options->gss_authentication; - goto parse_flag; - -+ case oGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case oGssDelegateCreds: - intptr = &options->gss_deleg_creds; - goto parse_flag; - -+ case oGssTrustDns: -+ intptr = &options->gss_trust_dns; -+ goto parse_flag; -+ -+ case oGssClientIdentity: -+ charptr = &options->gss_client_identity; -+ goto parse_string; -+ -+ case oGssServerIdentity: -+ charptr = &options->gss_server_identity; -+ goto parse_string; -+ -+ case oGssRenewalRekey: -+ intptr = &options->gss_renewal_rekey; -+ goto parse_flag; -+ - case oBatchMode: - intptr = &options->batch_mode; - goto parse_flag; -@@ -1817,7 +1848,12 @@ initialize_options(Options * options) - options->pubkey_authentication = -1; - options->challenge_response_authentication = -1; - options->gss_authentication = -1; -+ options->gss_keyex = -1; - options->gss_deleg_creds = -1; -+ options->gss_trust_dns = -1; -+ options->gss_renewal_rekey = -1; -+ options->gss_client_identity = NULL; -+ options->gss_server_identity = NULL; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->kbd_interactive_devices = NULL; -@@ -1962,8 +1998,14 @@ fill_default_options(Options * options) - options->challenge_response_authentication = 1; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 0; - if (options->gss_deleg_creds == -1) - options->gss_deleg_creds = 0; -+ if (options->gss_trust_dns == -1) -+ options->gss_trust_dns = 0; -+ if (options->gss_renewal_rekey == -1) -+ options->gss_renewal_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -@@ -2603,7 +2645,12 @@ dump_client_config(Options *o, const cha - dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); - #ifdef GSSAPI - dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); -+ dump_cfg_fmtint(oGssKeyEx, o->gss_keyex); - dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); -+ dump_cfg_fmtint(oGssTrustDns, o->gss_trust_dns); -+ dump_cfg_string(oGssClientIdentity, o->gss_client_identity); -+ dump_cfg_string(oGssServerIdentity, o->gss_client_identity); -+ dump_cfg_fmtint(oGssRenewalRekey, o->gss_renewal_rekey); - #endif /* GSSAPI */ - dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); - dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); -diff -up openssh-7.9p1/readconf.h.gsskex openssh-7.9p1/readconf.h ---- openssh-7.9p1/readconf.h.gsskex 2018-10-17 02:01:20.000000000 +0200 -+++ openssh-7.9p1/readconf.h 2018-11-14 09:14:06.277379857 +0100 -@@ -40,7 +40,12 @@ typedef struct { - int challenge_response_authentication; - /* Try S/Key or TIS, authentication. */ - int gss_authentication; /* Try GSS authentication */ -+ int gss_keyex; /* Try GSS key exchange */ - int gss_deleg_creds; /* Delegate GSS credentials */ -+ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ -+ int gss_renewal_rekey; /* Credential renewal forces rekey */ -+ char *gss_client_identity; /* Principal to initiate GSSAPI with */ -+ char *gss_server_identity; /* GSSAPI target principal */ - int password_authentication; /* Try password - * authentication. */ - int kbd_interactive_authentication; /* Try keyboard-interactive auth. */ -diff -up openssh/regress/cert-hostkey.sh.gsskex openssh/regress/cert-hostkey.sh ---- openssh/regress/cert-hostkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/cert-hostkey.sh 2018-08-22 11:47:33.314216481 +0200 -@@ -66,7 +66,7 @@ touch $OBJ/host_revoked_plain - touch $OBJ/host_revoked_cert - cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'` - - if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then - PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" -diff -up openssh/regress/cert-userkey.sh.gsskex openssh/regress/cert-userkey.sh ---- openssh/regress/cert-userkey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/cert-userkey.sh 2018-08-22 11:47:33.314216481 +0200 -@@ -7,7 +7,7 @@ rm -f $OBJ/authorized_keys_$USER $OBJ/us - cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak - cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak - --PLAIN_TYPES=`$SSH -Q key-plain | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` -+PLAIN_TYPES=`$SSH -Q key-plain | grep -v null | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` - EXTRA_TYPES="" - - if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then -diff -up openssh/regress/kextype.sh.gsskex openssh/regress/kextype.sh ---- openssh/regress/kextype.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/kextype.sh 2018-08-22 11:47:33.315216489 +0200 -@@ -14,6 +14,9 @@ echo "KexAlgorithms=$KEXOPT" >> $OBJ/ssh - - tries="1 2 3 4" - for k in `${SSH} -Q kex`; do -+ if [ $k = "gss-gex-sha1-" -o $k = "gss-group1-sha1-" -o $k = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "kex $k" - for i in $tries; do - ${SSH} -F $OBJ/ssh_proxy -o KexAlgorithms=$k x true -diff -up openssh/regress/rekey.sh.gsskex openssh/regress/rekey.sh ---- openssh/regress/rekey.sh.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/regress/rekey.sh 2018-08-22 11:47:33.315216489 +0200 -@@ -38,6 +38,9 @@ increase_datafile_size 300 - - opts="" - for i in `${SSH} -Q kex`; do -+ if [ $i = "gss-gex-sha1-" -o $i = "gss-group1-sha1-" -o $i = "gss-group14-sha1-" ]; then -+ continue -+ fi - opts="$opts KexAlgorithms=$i" - done - for i in `${SSH} -Q cipher`; do -@@ -56,6 +59,9 @@ done - if ${SSH} -Q cipher-auth | grep '^.*$' >/dev/null 2>&1 ; then - for c in `${SSH} -Q cipher-auth`; do - for kex in `${SSH} -Q kex`; do -+ if [ $kex = "gss-gex-sha1-" -o $kex = "gss-group1-sha1-" -o $kex = "gss-group14-sha1-" ]; then -+ continue -+ fi - verbose "client rekey $c $kex" - ssh_data_rekeying "KexAlgorithms=$kex" -oRekeyLimit=256k -oCiphers=$c - done -diff -up openssh/servconf.c.gsskex openssh/servconf.c ---- openssh/servconf.c.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/servconf.c 2018-08-22 13:28:41.905179879 +0200 -@@ -124,8 +124,10 @@ initialize_server_options(ServerOptions - options->kerberos_ticket_cleanup = -1; - options->kerberos_get_afs_token = -1; - options->gss_authentication=-1; -+ options->gss_keyex = -1; - options->gss_cleanup_creds = -1; - options->gss_strict_acceptor = -1; -+ options->gss_store_rekey = -1; - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->challenge_response_authentication = -1; -@@ -334,10 +336,14 @@ fill_default_server_options(ServerOption - options->kerberos_get_afs_token = 0; - if (options->gss_authentication == -1) - options->gss_authentication = 0; -+ if (options->gss_keyex == -1) -+ options->gss_keyex = 0; - if (options->gss_cleanup_creds == -1) - options->gss_cleanup_creds = 1; - if (options->gss_strict_acceptor == -1) - options->gss_strict_acceptor = 1; -+ if (options->gss_store_rekey == -1) -+ options->gss_store_rekey = 0; - if (options->password_authentication == -1) - options->password_authentication = 1; - if (options->kbd_interactive_authentication == -1) -@@ -484,7 +490,7 @@ typedef enum { - sHostKeyAlgorithms, - sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, - sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, -- sAcceptEnv, sSetEnv, sPermitTunnel, -+ sGssKeyEx, sGssStoreRekey, sAcceptEnv, sSetEnv, sPermitTunnel, - sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, - sUsePrivilegeSeparation, sAllowAgentForwarding, - sHostCertificate, -@@ -559,11 +565,17 @@ static struct { - { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, - { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sGssStoreRekey, SSHCFG_GLOBAL }, - #else - { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, - { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, - { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapikeyexchange", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapistorecredentialsonrekey", sUnsupported, SSHCFG_GLOBAL }, - #endif -+ { "gssusesessionccache", sUnsupported, SSHCFG_GLOBAL }, -+ { "gssapiusesessioncredcache", sUnsupported, SSHCFG_GLOBAL }, - { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, - { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, - { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, -@@ -1463,6 +1475,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_authentication; - goto parse_flag; - -+ case sGssKeyEx: -+ intptr = &options->gss_keyex; -+ goto parse_flag; -+ - case sGssCleanupCreds: - intptr = &options->gss_cleanup_creds; - goto parse_flag; -@@ -1471,6 +1487,10 @@ process_server_config_line(ServerOptions - intptr = &options->gss_strict_acceptor; - goto parse_flag; - -+ case sGssStoreRekey: -+ intptr = &options->gss_store_rekey; -+ goto parse_flag; -+ - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; -@@ -2560,6 +2580,9 @@ dump_config(ServerOptions *o) - #ifdef GSSAPI - dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); - dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); -+ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); -+ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); -+ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); - #endif - dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); - dump_cfg_fmtint(sKbdInteractiveAuthentication, -diff -up openssh/servconf.h.gsskex openssh/servconf.h ---- openssh/servconf.h.gsskex 2018-08-22 11:47:33.296216335 +0200 -+++ openssh/servconf.h 2018-08-22 11:47:33.316216497 +0200 -@@ -124,8 +124,10 @@ typedef struct { - int kerberos_get_afs_token; /* If true, try to get AFS token if - * authenticated with Kerberos. */ - int gss_authentication; /* If true, permit GSSAPI authentication */ -+ int gss_keyex; /* If true, permit GSSAPI key exchange */ - int gss_cleanup_creds; /* If true, destroy cred cache on logout */ - int gss_strict_acceptor; /* If true, restrict the GSSAPI acceptor name */ -+ int gss_store_rekey; - int password_authentication; /* If true, permit password - * authentication. */ - int kbd_interactive_authentication; /* If true, permit */ -diff -up openssh/ssh_config.5.gsskex openssh/ssh_config.5 ---- openssh/ssh_config.5.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/ssh_config.5 2018-08-22 11:47:33.316216497 +0200 -@@ -718,10 +718,40 @@ The default is - Specifies whether user authentication based on GSSAPI is allowed. - The default is - .Cm no . -+.It Cm GSSAPIClientIdentity -+If set, specifies the GSSAPI client identity that ssh should use when -+connecting to the server. The default is unset, which means that the default -+identity will be used. - .It Cm GSSAPIDelegateCredentials - Forward (delegate) credentials to the server. - The default is - .Cm no . -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI may be used. When using -+GSSAPI key exchange the server need not have a host key. -+The default is -+.Dq no . -+.It Cm GSSAPIRenewalForcesRekey -+If set to -+.Dq yes -+then renewal of the client's GSSAPI credentials will force the rekeying of the -+ssh connection. With a compatible server, this can delegate the renewed -+credentials to a session on the server. -+The default is -+.Dq no . -+.It Cm GSSAPIServerIdentity -+If set, specifies the GSSAPI server identity that ssh should expect when -+connecting to the server. The default is unset, which means that the -+expected GSSAPI server identity will be determined from the target -+hostname. -+.It Cm GSSAPITrustDns -+Set to -+.Dq yes to indicate that the DNS is trusted to securely canonicalize -+the name of the host being connected to. If -+.Dq no, the hostname entered on the -+command line will be passed untouched to the GSSAPI library. -+The default is -+.Dq no . - .It Cm HashKnownHosts - Indicates that - .Xr ssh 1 -diff -up openssh/ssh_config.gsskex openssh/ssh_config ---- openssh/ssh_config.gsskex 2018-08-22 11:47:33.289216279 +0200 -+++ openssh/ssh_config 2018-08-22 11:47:33.316216497 +0200 -@@ -24,6 +24,8 @@ - # HostbasedAuthentication no - # GSSAPIAuthentication no - # GSSAPIDelegateCredentials no -+# GSSAPIKeyExchange no -+# GSSAPITrustDNS no - # BatchMode no - # CheckHostIP yes - # AddressFamily any -diff -up openssh/sshconnect2.c.gsskex openssh/sshconnect2.c ---- openssh/sshconnect2.c.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/sshconnect2.c 2018-08-22 13:33:01.674275795 +0200 -@@ -82,6 +82,124 @@ extern char *client_version_string; - extern char *server_version_string; - extern Options options; - -+/* XXX from auth.h -- refactoring move these useful functions away of client context*/ -+ -+/* -+ * Returns the remote DNS hostname as a string. The returned string must not -+ * be freed. NB. this will usually trigger a DNS query the first time it is -+ * called. -+ * This function does additional checks on the hostname to mitigate some -+ * attacks on legacy rhosts-style authentication. -+ * XXX is RhostsRSAAuthentication vulnerable to these? -+ * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) -+ */ -+ -+static char * -+remote_hostname(struct ssh *ssh) -+{ -+ struct sockaddr_storage from; -+ socklen_t fromlen; -+ struct addrinfo hints, *ai, *aitop; -+ char name[NI_MAXHOST], ntop2[NI_MAXHOST]; -+ const char *ntop = ssh_remote_ipaddr(ssh); -+ -+ /* Get IP address of client. */ -+ fromlen = sizeof(from); -+ memset(&from, 0, sizeof(from)); -+ if (getpeername(ssh_packet_get_connection_in(ssh), -+ (struct sockaddr *)&from, &fromlen) < 0) { -+ debug("getpeername failed: %.100s", strerror(errno)); -+ return strdup(ntop); -+ } -+ -+ ipv64_normalise_mapped(&from, &fromlen); -+ if (from.ss_family == AF_INET6) -+ fromlen = sizeof(struct sockaddr_in6); -+ -+ debug3("Trying to reverse map address %.100s.", ntop); -+ /* Map the IP address to a host name. */ -+ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), -+ NULL, 0, NI_NAMEREQD) != 0) { -+ /* Host name not found. Use ip address. */ -+ return strdup(ntop); -+ } -+ -+ /* -+ * if reverse lookup result looks like a numeric hostname, -+ * someone is trying to trick us by PTR record like following: -+ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 -+ */ -+ memset(&hints, 0, sizeof(hints)); -+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ -+ hints.ai_flags = AI_NUMERICHOST; -+ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { -+ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", -+ name, ntop); -+ freeaddrinfo(ai); -+ return strdup(ntop); -+ } -+ -+ /* Names are stored in lowercase. */ -+ lowercase(name); -+ -+ /* -+ * Map it back to an IP address and check that the given -+ * address actually is an address of this host. This is -+ * necessary because anyone with access to a name server can -+ * define arbitrary names for an IP address. Mapping from -+ * name to IP address can be trusted better (but can still be -+ * fooled if the intruder has access to the name server of -+ * the domain). -+ */ -+ memset(&hints, 0, sizeof(hints)); -+ hints.ai_family = from.ss_family; -+ hints.ai_socktype = SOCK_STREAM; -+ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { -+ logit("reverse mapping checking getaddrinfo for %.700s " -+ "[%s] failed.", name, ntop); -+ return strdup(ntop); -+ } -+ /* Look for the address from the list of addresses. */ -+ for (ai = aitop; ai; ai = ai->ai_next) { -+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, -+ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && -+ (strcmp(ntop, ntop2) == 0)) -+ break; -+ } -+ freeaddrinfo(aitop); -+ /* If we reached the end of the list, the address was not there. */ -+ if (ai == NULL) { -+ /* Address not found for the host name. */ -+ logit("Address %.100s maps to %.600s, but this does not " -+ "map back to the address.", ntop, name); -+ return strdup(ntop); -+ } -+ return strdup(name); -+} -+ -+/* -+ * Return the canonical name of the host in the other side of the current -+ * connection. The host name is cached, so it is efficient to call this -+ * several times. -+ */ -+ -+const char * -+get_canonical_hostname(struct ssh *ssh, int use_dns) -+{ -+ static char *dnsname; -+ -+ if (!use_dns) -+ return ssh_remote_ipaddr(ssh); -+ else if (dnsname != NULL) -+ return dnsname; -+ else { -+ dnsname = remote_hostname(ssh); -+ return dnsname; -+ } -+} -+ -+ -+ - /* - * SSH2 key exchange - */ -@@ -162,9 +280,36 @@ ssh_kex2(char *host, struct sockaddr *ho - struct kex *kex; - int r; - -+#ifdef GSSAPI -+ char *orig = NULL, *gss = NULL; -+ char *gss_host = NULL; -+#endif -+ - xxx_host = host; - xxx_hostaddr = hostaddr; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ /* Add the GSSAPI mechanisms currently supported on this -+ * client to the key exchange algorithm proposal */ -+ orig = options.kex_algorithms; -+ -+ if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; -+ else if (options.gss_trust_dns) -+ gss_host = (char *)get_canonical_hostname(active_state, 1); -+ else -+ gss_host = host; -+ -+ gss = ssh_gssapi_client_mechanisms(gss_host, options.gss_client_identity); -+ if (gss) { -+ debug("Offering GSSAPI proposal: %s", gss); -+ xasprintf(&options.kex_algorithms, -+ "%s,%s", gss, orig); -+ } -+ } -+#endif -+ - if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) - fatal("%s: kex_names_cat", __func__); - myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); -@@ -194,6 +339,16 @@ ssh_kex2(char *host, struct sockaddr *ho - order_hostkeyalgs(host, hostaddr, port)); - } - -+#ifdef GSSAPI -+ /* If we've got GSSAPI algorithms, then we also support the -+ * 'null' hostkey, as a last resort */ -+ if (options.gss_keyex && gss) { -+ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], -+ "%s,null", orig); -+ } -+#endif -+ - if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits(options.rekey_limit, - options.rekey_interval); -@@ -214,16 +369,46 @@ ssh_kex2(char *host, struct sockaddr *ho - kex->kex[KEX_ECDH_SHA2] = kexecdh_client; - # endif - #endif -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; -+ } -+#endif - kex->kex[KEX_C25519_SHA256] = kexc25519_client; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; - kex->verify_host_key=&verify_host_key_callback; - -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->gss_deleg_creds = options.gss_deleg_creds; -+ kex->gss_trust_dns = options.gss_trust_dns; -+ kex->gss_client = options.gss_client_identity; -+ if (options.gss_server_identity) { -+ kex->gss_host = options.gss_server_identity; -+ } else { -+ kex->gss_host = gss_host; -+ } -+ } -+#endif -+ - ssh_dispatch_run_fatal(active_state, DISPATCH_BLOCK, &kex->done); - - /* remove ext-info from the KEX proposals for rekeying */ - myproposal[PROPOSAL_KEX_ALGS] = - compat_kex_proposal(options.kex_algorithms); -+#ifdef GSSAPI -+ /* repair myproposal after it was crumpled by the */ -+ /* ext-info removal above */ -+ if (gss) { -+ orig = myproposal[PROPOSAL_KEX_ALGS]; -+ xasprintf(&myproposal[PROPOSAL_KEX_ALGS], -+ "%s,%s", gss, orig); -+ free(gss); -+ } -+#endif - if ((r = kex_prop2buf(kex->my, myproposal)) != 0) - fatal("kex_prop2buf: %s", ssh_err(r)); - -@@ -314,6 +499,7 @@ int input_gssapi_token(int type, u_int32 - int input_gssapi_hash(int type, u_int32_t, struct ssh *); - int input_gssapi_error(int, u_int32_t, struct ssh *); - int input_gssapi_errtok(int, u_int32_t, struct ssh *); -+int userauth_gsskeyex(Authctxt *authctxt); - #endif - - void userauth(Authctxt *, char *); -@@ -330,6 +505,11 @@ static char *authmethods_get(void); - - Authmethod authmethods[] = { - #ifdef GSSAPI -+ {"gssapi-keyex", -+ userauth_gsskeyex, -+ NULL, -+ &options.gss_authentication, -+ NULL}, - {"gssapi-with-mic", - userauth_gssapi, - NULL, -@@ -657,19 +837,31 @@ userauth_gssapi(Authctxt *authctxt) - static u_int mech = 0; - OM_uint32 min; - int r, ok = 0; -+ const char *gss_host; -+ -+ if (options.gss_server_identity) -+ gss_host = options.gss_server_identity; -+ else if (options.gss_trust_dns) -+ gss_host = get_canonical_hostname(active_state, 1); -+ else -+ gss_host = authctxt->host; - - /* Try one GSSAPI method at a time, rather than sending them all at - * once. */ - - if (gss_supported == NULL) -- gss_indicate_mechs(&min, &gss_supported); -+ if (GSS_ERROR(gss_indicate_mechs(&min, &gss_supported))) { -+ gss_supported = NULL; -+ return 0; -+ } - - /* Check to see if the mechanism is usable before we offer it */ - while (mech < gss_supported->count && !ok) { - /* My DER encoding requires length<128 */ - if (gss_supported->elements[mech].length < 128 && - ssh_gssapi_check_mechanism(&gssctxt, -- &gss_supported->elements[mech], authctxt->host)) { -+ &gss_supported->elements[mech], gss_host, -+ options.gss_client_identity)) { - ok = 1; /* Mechanism works */ - } else { - mech++; -@@ -906,6 +1098,51 @@ input_gssapi_error(int type, u_int32_t p - free(lang); - return r; - } -+ -+int -+userauth_gsskeyex(Authctxt *authctxt) -+{ -+ struct sshbuf *b = NULL; -+ gss_buffer_desc gssbuf; -+ gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; -+ OM_uint32 ms; -+ -+ static int attempt = 0; -+ if (attempt++ >= 1) -+ return (0); -+ -+ if (gss_kex_context == NULL) { -+ debug("No valid Key exchange context"); -+ return (0); -+ } -+ -+ if ((b = sshbuf_new()) == NULL) -+ fatal("%s: sshbuf_new failed", __func__); -+ -+ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, -+ "gssapi-keyex"); -+ -+ gssbuf.value = sshbuf_mutable_ptr(b); -+ gssbuf.length = sshbuf_len(b); -+ -+ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { -+ sshbuf_free(b); -+ return (0); -+ } -+ -+ packet_start(SSH2_MSG_USERAUTH_REQUEST); -+ packet_put_cstring(authctxt->server_user); -+ packet_put_cstring(authctxt->service); -+ packet_put_cstring(authctxt->method->name); -+ packet_put_string(mic.value, mic.length); -+ packet_send(); -+ -+ sshbuf_free(b); -+ gss_release_buffer(&ms, &mic); -+ -+ return (1); -+} -+ - #endif /* GSSAPI */ - - int -diff -up openssh/sshd.c.gsskex openssh/sshd.c ---- openssh/sshd.c.gsskex 2018-08-22 11:47:33.299216360 +0200 -+++ openssh/sshd.c 2018-08-22 13:34:28.455975954 +0200 -@@ -887,8 +887,9 @@ notify_hostkeys(struct ssh *ssh) - } - debug3("%s: sent %u hostkeys", __func__, nkeys); - if (nkeys == 0) -- fatal("%s: no hostkeys", __func__); -- packet_send(); -+ debug3("%s: no hostkeys", __func__); -+ else -+ packet_send(); - sshbuf_free(buf); - } - -@@ -1841,7 +1842,8 @@ main(int ac, char **av) - free(fp); - } - accumulate_host_timing_secret(cfg, NULL); -- if (!sensitive_data.have_ssh2_key) { -+ /* The GSSAPI key exchange can run without a host key */ -+ if (!sensitive_data.have_ssh2_key && !options.gss_keyex) { - logit("sshd: no hostkeys available -- exiting."); - exit(1); - } -@@ -2321,6 +2323,48 @@ do_ssh2_kex(void) - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( - list_hostkey_types()); - -+#ifdef GSSAPI -+ { -+ char *orig; -+ char *gss = NULL; -+ char *newstr = NULL; -+ orig = myproposal[PROPOSAL_KEX_ALGS]; -+ -+ /* -+ * If we don't have a host key, then there's no point advertising -+ * the other key exchange algorithms -+ */ -+ -+ if (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]) == 0) -+ orig = NULL; -+ -+ if (options.gss_keyex) -+ gss = ssh_gssapi_server_mechanisms(); -+ else -+ gss = NULL; -+ -+ if (gss && orig) -+ xasprintf(&newstr, "%s,%s", gss, orig); -+ else if (gss) -+ newstr = gss; -+ else if (orig) -+ newstr = orig; -+ -+ /* -+ * If we've got GSSAPI mechanisms, then we've got the 'null' host -+ * key alg, but we can't tell people about it unless its the only -+ * host key algorithm we support -+ */ -+ if (gss && (strlen(myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS])) == 0) -+ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = "null"; -+ -+ if (newstr) -+ myproposal[PROPOSAL_KEX_ALGS] = newstr; -+ else -+ fatal("No supported key exchange algorithms"); -+ } -+#endif -+ - /* start key exchange */ - if ((r = kex_setup(active_state, myproposal)) != 0) - fatal("kex_setup: %s", ssh_err(r)); -@@ -2338,6 +2382,13 @@ do_ssh2_kex(void) - # endif - #endif - kex->kex[KEX_C25519_SHA256] = kexc25519_server; -+#ifdef GSSAPI -+ if (options.gss_keyex) { -+ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_server; -+ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_server; -+ } -+#endif - kex->server = 1; - kex->client_version_string=client_version_string; - kex->server_version_string=server_version_string; -diff -up openssh/sshd_config.5.gsskex openssh/sshd_config.5 ---- openssh/sshd_config.5.gsskex 2018-08-22 11:47:33.297216344 +0200 -+++ openssh/sshd_config.5 2018-08-22 13:35:05.531275099 +0200 -@@ -642,6 +642,11 @@ Specifies whether to automatically destr - on logout. - The default is - .Cm yes . -+.It Cm GSSAPIKeyExchange -+Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange -+doesn't rely on ssh keys to verify host identity. -+The default is -+.Dq no . - .It Cm GSSAPIStrictAcceptorCheck - Determines whether to be strict about the identity of the GSSAPI acceptor - a client authenticates against. -@@ -656,6 +661,11 @@ machine's default store. - This facility is provided to assist with operation on multi homed machines. - The default is - .Cm yes . -+.It Cm GSSAPIStoreCredentialsOnRekey -+Controls whether the user's GSSAPI credentials should be updated following a -+successful connection rekeying. This option can be used to accepted renewed -+or updated credentials from a compatible client. The default is -+.Dq no . - .It Cm HostbasedAcceptedKeyTypes - Specifies the key types that will be accepted for hostbased authentication - as a list of comma-separated patterns. -diff -up openssh/sshd_config.gsskex openssh/sshd_config ---- openssh/sshd_config.gsskex 2018-08-22 11:47:33.299216360 +0200 -+++ openssh/sshd_config 2018-08-22 11:47:33.318216513 +0200 -@@ -85,6 +85,8 @@ ChallengeResponseAuthentication no - # GSSAPI options - GSSAPIAuthentication yes - GSSAPICleanupCredentials no -+#GSSAPIStrictAcceptorCheck yes -+#GSSAPIKeyExchange no - - # Set this to 'yes' to enable PAM authentication, account processing, - # and session processing. If this is enabled, PAM authentication will -diff -up openssh/ssh-gss.h.gsskex openssh/ssh-gss.h ---- openssh/ssh-gss.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/ssh-gss.h 2018-08-22 13:36:44.773075793 +0200 -@@ -1,6 +1,6 @@ - /* $OpenBSD: ssh-gss.h,v 1.14 2018/07/10 09:13:30 djm Exp $ */ - /* -- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. -+ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions -@@ -61,10 +61,22 @@ - - #define SSH_GSS_OIDTYPE 0x06 - -+#define SSH2_MSG_KEXGSS_INIT 30 -+#define SSH2_MSG_KEXGSS_CONTINUE 31 -+#define SSH2_MSG_KEXGSS_COMPLETE 32 -+#define SSH2_MSG_KEXGSS_HOSTKEY 33 -+#define SSH2_MSG_KEXGSS_ERROR 34 -+#define SSH2_MSG_KEXGSS_GROUPREQ 40 -+#define SSH2_MSG_KEXGSS_GROUP 41 -+#define KEX_GSS_GRP1_SHA1_ID "gss-group1-sha1-" -+#define KEX_GSS_GRP14_SHA1_ID "gss-group14-sha1-" -+#define KEX_GSS_GEX_SHA1_ID "gss-gex-sha1-" -+ - typedef struct { - char *filename; - char *envvar; - char *envval; -+ struct passwd *owner; - void *data; - } ssh_gssapi_ccache; - -@@ -72,8 +84,11 @@ typedef struct { - gss_buffer_desc displayname; - gss_buffer_desc exportedname; - gss_cred_id_t creds; -+ gss_name_t name; - struct ssh_gssapi_mech_struct *mech; - ssh_gssapi_ccache store; -+ int used; -+ int updated; - } ssh_gssapi_client; - - typedef struct ssh_gssapi_mech_struct { -@@ -84,6 +99,7 @@ typedef struct ssh_gssapi_mech_struct { - int (*userok) (ssh_gssapi_client *, char *); - int (*localname) (ssh_gssapi_client *, char **); - void (*storecreds) (ssh_gssapi_client *); -+ int (*updatecreds) (ssh_gssapi_ccache *, ssh_gssapi_client *); - } ssh_gssapi_mech; - - typedef struct { -@@ -94,10 +110,11 @@ typedef struct { - gss_OID oid; /* client */ - gss_cred_id_t creds; /* server */ - gss_name_t client; /* server */ -- gss_cred_id_t client_creds; /* server */ -+ gss_cred_id_t client_creds; /* both */ - } Gssctxt; - - extern ssh_gssapi_mech *supported_mechs[]; -+extern Gssctxt *gss_kex_context; - - int ssh_gssapi_check_oid(Gssctxt *, void *, size_t); - void ssh_gssapi_set_oid_data(Gssctxt *, void *, size_t); -@@ -123,17 +140,33 @@ void ssh_gssapi_delete_ctx(Gssctxt **); - OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_buildmic(struct sshbuf *, const char *, - const char *, const char *); --int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *); -+int ssh_gssapi_check_mechanism(Gssctxt **, gss_OID, const char *, const char *); -+OM_uint32 ssh_gssapi_client_identity(Gssctxt *, const char *); -+int ssh_gssapi_credentials_updated(Gssctxt *); - - /* In the server */ -+typedef int ssh_gssapi_check_fn(Gssctxt **, gss_OID, const char *, -+ const char *); -+char *ssh_gssapi_client_mechanisms(const char *, const char *); -+char *ssh_gssapi_kex_mechs(gss_OID_set, ssh_gssapi_check_fn *, const char *, -+ const char *); -+gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); -+int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, -+ const char *); - OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); --int ssh_gssapi_userok(char *name); -+int ssh_gssapi_userok(char *name, struct passwd *); - OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_do_child(char ***, u_int *); - void ssh_gssapi_cleanup_creds(void); - void ssh_gssapi_storecreds(void); - const char *ssh_gssapi_displayname(void); - -+char *ssh_gssapi_server_mechanisms(void); -+int ssh_gssapi_oid_table_ok(); -+ -+int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); -+ -+void ssh_gssapi_rekey_creds(void); - #endif /* GSSAPI */ - - #endif /* _SSH_GSS_H */ -diff -up openssh/sshkey.c.gsskex openssh/sshkey.c ---- openssh/sshkey.c.gsskex 2018-08-22 11:47:33.319216521 +0200 -+++ openssh/sshkey.c 2018-08-22 13:37:18.979351804 +0200 -@@ -140,6 +140,7 @@ static const struct keytype keytypes[] = - # endif /* OPENSSL_HAS_NISTP521 */ - # endif /* OPENSSL_HAS_ECC */ - #endif /* WITH_OPENSSL */ -+ { "null", "null", NULL, KEY_NULL, 0, 0, 1 }, - { NULL, NULL, NULL, -1, -1, 0, 0 } - }; - -diff -up openssh/sshkey.h.gsskex openssh/sshkey.h ---- openssh/sshkey.h.gsskex 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/sshkey.h 2018-08-22 11:47:33.320216529 +0200 -@@ -63,6 +63,7 @@ enum sshkey_types { - KEY_ED25519_CERT, - KEY_XMSS, - KEY_XMSS_CERT, -+ KEY_NULL, - KEY_UNSPEC - }; - diff --git a/openssh-7.8p1-ip-port-config-parser.patch b/openssh-7.8p1-ip-port-config-parser.patch deleted file mode 100644 index 6ca0cf5..0000000 --- a/openssh-7.8p1-ip-port-config-parser.patch +++ /dev/null @@ -1,72 +0,0 @@ -diff -up openssh/misc.c.config openssh/misc.c ---- openssh/misc.c.config 2018-08-22 13:58:54.922807799 +0200 -+++ openssh/misc.c 2018-08-22 13:58:55.000808428 +0200 -@@ -485,7 +485,7 @@ put_host_port(const char *host, u_short - * The delimiter char, if present, is stored in delim. - * If this is the last field, *cp is set to NULL. - */ --static char * -+char * - hpdelim2(char **cp, char *delim) - { - char *s, *old; -diff -up openssh/misc.h.config openssh/misc.h ---- openssh/misc.h.config 2018-08-20 07:57:29.000000000 +0200 -+++ openssh/misc.h 2018-08-22 13:58:55.001808436 +0200 -@@ -54,6 +54,7 @@ int set_rdomain(int, const char *); - int a2port(const char *); - int a2tun(const char *, int *); - char *put_host_port(const char *, u_short); -+char *hpdelim2(char **, char *); - char *hpdelim(char **); - char *cleanhostname(char *); - char *colon(char *); -diff -up openssh/servconf.c.config openssh/servconf.c ---- openssh/servconf.c.config 2018-08-22 13:58:54.989808340 +0200 -+++ openssh/servconf.c 2018-08-22 14:18:49.235443937 +0200 -@@ -886,7 +886,7 @@ process_permitopen_list(struct ssh *ssh, - { - u_int i; - int port; -- char *host, *arg, *oarg; -+ char *host, *arg, *oarg, ch; - int where = opcode == sPermitOpen ? FORWARD_LOCAL : FORWARD_REMOTE; - const char *what = lookup_opcode_name(opcode); - -@@ -904,8 +904,8 @@ process_permitopen_list(struct ssh *ssh, - /* Otherwise treat it as a list of permitted host:port */ - for (i = 0; i < num_opens; i++) { - oarg = arg = xstrdup(opens[i]); -- host = hpdelim(&arg); -- if (host == NULL) -+ host = hpdelim2(&arg, &ch); -+ if (host == NULL || ch == '/') - fatal("%s: missing host in %s", __func__, what); - host = cleanhostname(host); - if (arg == NULL || ((port = permitopen_port(arg)) < 0)) -@@ -1323,8 +1323,10 @@ process_server_config_line(ServerOptions - port = 0; - p = arg; - } else { -- p = hpdelim(&arg); -- if (p == NULL) -+ char ch; -+ arg2 = NULL; -+ p = hpdelim2(&arg, &ch); -+ if (p == NULL || ch == '/') - fatal("%s line %d: bad address:port usage", - filename, linenum); - p = cleanhostname(p); -@@ -1965,9 +1967,10 @@ process_server_config_line(ServerOptions - */ - xasprintf(&arg2, "*:%s", arg); - } else { -+ char ch; - arg2 = xstrdup(arg); -- p = hpdelim(&arg); -- if (p == NULL) { -+ p = hpdelim2(&arg, &ch); -+ if (p == NULL || ch == '/') { - fatal("%s line %d: missing host in %s", - filename, linenum, - lookup_opcode_name(opcode)); diff --git a/openssh-7.8p1-role-mls.patch b/openssh-7.8p1-role-mls.patch index 4c58d71..84ba9b3 100644 --- a/openssh-7.8p1-role-mls.patch +++ b/openssh-7.8p1-role-mls.patch @@ -4,11 +4,11 @@ diff -up openssh/auth2.c.role-mls openssh/auth2.c @@ -256,6 +256,9 @@ input_userauth_request(int type, u_int32 Authctxt *authctxt = ssh->authctxt; Authmethod *m = NULL; - char *user, *service, *method, *style = NULL; + char *user = NULL, *service = NULL, *method = NULL, *style = NULL; +#ifdef WITH_SELINUX + char *role = NULL; +#endif - int authenticated = 0; + int r, authenticated = 0; double tstart = monotime_double(); @@ -268,6 +271,11 @@ input_userauth_request(int type, u_int32 @@ -37,9 +37,9 @@ diff -up openssh/auth2.c.role-mls openssh/auth2.c + mm_inform_authrole(role); +#endif + } - userauth_banner(); + userauth_banner(ssh); if (auth2_setup_methods_lists(authctxt) != 0) - packet_disconnect("no authentication methods enabled"); + 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 @@ -57,7 +57,7 @@ diff -up openssh/auth2-gss.c.role-mls openssh/auth2-gss.c mic.length = len; - ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, +#ifdef WITH_SELINUX -+ if (authctxt->role && (strlen(authctxt->role) > 0)) ++ if (authctxt->role && authctxt->role[0] != 0) + xasprintf(&micuser, "%s/%s", authctxt->user, authctxt->role); + else +#endif @@ -197,15 +197,15 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c --- openssh/monitor.c.role-mls 2018-08-20 07:57:29.000000000 +0200 +++ openssh/monitor.c 2018-08-22 11:19:56.006844867 +0200 @@ -115,6 +115,9 @@ int mm_answer_sign(int, struct sshbuf *) - int mm_answer_pwnamallow(int, struct sshbuf *); - int mm_answer_auth2_read_banner(int, struct sshbuf *); - int mm_answer_authserv(int, struct sshbuf *); + 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(int, struct sshbuf *); ++int mm_answer_authrole(struct ssh *, int, struct sshbuf *); +#endif - int mm_answer_authpassword(int, struct sshbuf *); - int mm_answer_bsdauthquery(int, struct sshbuf *); - int mm_answer_bsdauthrespond(int, struct sshbuf *); + 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 *); @@ -189,6 +192,9 @@ struct mon_table mon_dispatch_proto20[] {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, @@ -227,12 +227,12 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c #ifdef USE_PAM @@ -842,6 +851,26 @@ mm_answer_authserv(int sock, struct sshb - return (0); + return found; } +#ifdef WITH_SELINUX +int -+mm_answer_authrole(int sock, struct sshbuf *m) ++mm_answer_authrole(struct ssh *ssh, int sock, struct sshbuf *m) +{ + int r; + monitor_permit_authentications(1); @@ -251,7 +251,7 @@ diff -up openssh/monitor.c.role-mls openssh/monitor.c +#endif + int - mm_answer_authpassword(int sock, struct sshbuf *m) + mm_answer_authpassword(struct ssh *ssh, int sock, struct sshbuf *m) { @@ -1218,7 +1247,7 @@ monitor_valid_userblob(u_char *data, u_i { @@ -338,13 +338,13 @@ diff -up openssh/monitor_wrap.h.role-mls openssh/monitor_wrap.h --- openssh/monitor_wrap.h.role-mls 2018-08-22 11:14:56.818430941 +0200 +++ openssh/monitor_wrap.h 2018-08-22 11:22:10.439929513 +0200 @@ -44,6 +44,9 @@ DH *mm_choose_dh(int, int, int); - int mm_sshkey_sign(struct sshkey *, u_char **, size_t *, const u_char *, size_t, - const char *, u_int compat); + int mm_sshkey_sign(struct ssh *, struct sshkey *, u_char **, size_t *, + const u_char *, size_t, const char *, u_int compat); void mm_inform_authserv(char *, char *); +#ifdef WITH_SELINUX +void mm_inform_authrole(char *); +#endif - struct passwd *mm_getpwnamallow(const char *); + 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 diff --git a/openssh-7.9p1-CVE-2018-20685.patch b/openssh-7.9p1-CVE-2018-20685.patch deleted file mode 100644 index db7a4f8..0000000 --- a/openssh-7.9p1-CVE-2018-20685.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 6010c0303a422a9c5fa8860c061bf7105eb7f8b2 Mon Sep 17 00:00:00 2001 -From: "djm@openbsd.org" -Date: Fri, 16 Nov 2018 03:03:10 +0000 -Subject: [PATCH] upstream: disallow empty incoming filename or ones that refer - to the - -current directory; based on report/patch from Harry Sintonen - -OpenBSD-Commit-ID: f27651b30eaee2df49540ab68d030865c04f6de9 ---- - scp.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/scp.c b/scp.c -index 60682c687..4f3fdcd3d 100644 ---- a/scp.c -+++ b/scp.c -@@ -1106,7 +1106,8 @@ sink(int argc, char **argv) - SCREWUP("size out of range"); - size = (off_t)ull; - -- if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { -+ if (*cp == '\0' || strchr(cp, '/') != NULL || -+ strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { - run_err("error: unexpected filename: %s", cp); - exit(1); - } - diff --git a/openssh-7.9p1-backports.patch b/openssh-7.9p1-backports.patch deleted file mode 100644 index d89138b..0000000 --- a/openssh-7.9p1-backports.patch +++ /dev/null @@ -1,186 +0,0 @@ -From 631165f6c43d230df9174423aeb19fcf09a67ef4 Mon Sep 17 00:00:00 2001 -From: Damien Miller -Date: Mon, 22 Oct 2018 11:22:50 +1100 -Subject: [PATCH 1/7] fix compile for openssl 1.0.x w/ --with-ssl-engine - -bz#2921, patch from cotequeiroz ---- - openbsd-compat/openssl-compat.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/openbsd-compat/openssl-compat.c b/openbsd-compat/openssl-compat.c -index 8b4a3627..590b66d1 100644 ---- a/openbsd-compat/openssl-compat.c -+++ b/openbsd-compat/openssl-compat.c -@@ -76,7 +76,7 @@ ssh_OpenSSL_add_all_algorithms(void) - ENGINE_load_builtin_engines(); - ENGINE_register_all_complete(); - --#if OPENSSL_VERSION_NUMBER < 0x10001000L -+#if OPENSSL_VERSION_NUMBER < 0x10100000L - OPENSSL_config(NULL); - #else - OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | --- -2.20.1 - - -From 55d7cdda4dc4e8284ccd276f73440be400250a1e Mon Sep 17 00:00:00 2001 -From: Darren Tucker -Date: Mon, 22 Oct 2018 20:05:18 +1100 -Subject: [PATCH 2/7] Include openssl compatibility. - -Patch from rosenp at gmail.com via openssh-unix-dev. ---- - ssh-keysign.c | 1 + - ssh_api.c | 2 ++ - 2 files changed, 3 insertions(+) - -diff --git a/ssh-keysign.c b/ssh-keysign.c -index 744ecb4f..bcd1508c 100644 ---- a/ssh-keysign.c -+++ b/ssh-keysign.c -@@ -40,6 +40,7 @@ - #include - #include - #include -+#include "openbsd-compat/openssl-compat.h" - #endif - - #include "xmalloc.h" -diff --git a/ssh_api.c b/ssh_api.c -index c84b4e71..e727c0d6 100644 ---- a/ssh_api.c -+++ b/ssh_api.c -@@ -29,6 +29,8 @@ - #include "ssherr.h" - #include "sshbuf.h" - -+#include "openbsd-compat/openssl-compat.h" -+ - #include - - int _ssh_exchange_banner(struct ssh *); --- -2.20.1 - - -From 22092e375125dc602227afb8b2d3b285203e77c1 Mon Sep 17 00:00:00 2001 -From: Darren Tucker -Date: Mon, 5 Nov 2018 17:31:24 +1100 -Subject: [PATCH 3/7] Fix pasto for HAVE_EVP_CIPHER_CTX_SET_IV. - -Prevents unnecessary redefinition. Patch from mforney at mforney.org. ---- - configure.ac | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/configure.ac b/configure.ac -index 7379ab35..1041bf25 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -2850,7 +2850,7 @@ if test "x$openssl" = "xyes" ; then - [AC_DEFINE([HAVE_EVP_CIPHER_CTX_GET_IV], [1], - [Define if libcrypto has EVP_CIPHER_CTX_get_iv])]) - AC_SEARCH_LIBS([EVP_CIPHER_CTX_set_iv], [crypto], -- [AC_DEFINE([HAVE_EVP_CIPHER_CTX_GET_IV], [1], -+ [AC_DEFINE([HAVE_EVP_CIPHER_CTX_SET_IV], [1], - [Define if libcrypto has EVP_CIPHER_CTX_set_iv])]) - - AC_SEARCH_LIBS([RSA_get0_crt_params], [crypto], --- -2.20.1 - - -From 904d478f07deabb401f741f88c67ab2e07f742bd Mon Sep 17 00:00:00 2001 -From: Darren Tucker -Date: Sun, 11 Nov 2018 15:54:54 +1100 -Subject: [PATCH 4/7] Remove hardcoded service name in cygwin setup. - -bz#2922, patch from Christian.Lupien at USherbrooke.ca, sanity check -by vinschen at redhat.com. ---- - contrib/cygwin/ssh-host-config | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/contrib/cygwin/ssh-host-config b/contrib/cygwin/ssh-host-config -index 261020af..e9f038db 100644 ---- a/contrib/cygwin/ssh-host-config -+++ b/contrib/cygwin/ssh-host-config -@@ -307,7 +307,7 @@ check_service_files_ownership() { - - if [ -z "${run_service_as}" ] - then -- accnt_name=$(/usr/bin/cygrunsrv -VQ sshd | -+ accnt_name=$(/usr/bin/cygrunsrv -VQ "${service_name}" | - /usr/bin/sed -ne 's/^Account *: *//gp') - if [ "${accnt_name}" = "LocalSystem" ] - then --- -2.20.1 - - -From 4608a60cb4893a25490fd88ffdaaae6256381ae3 Mon Sep 17 00:00:00 2001 -From: Damien Miller -Date: Fri, 23 Nov 2018 10:45:20 +1100 -Subject: [PATCH 6/7] fix configure test for OpenSSL version - -square brackets in case statements may be eaten by autoconf. - -Report and fix from Filipp Gunbin; tweaked by naddy@ ---- - configure.ac | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/configure.ac b/configure.ac -index 1041bf25..9920ede5 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -2616,7 +2616,7 @@ if test "x$openssl" = "xyes" ; then - AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) - ;; - 100*) ;; # 1.0.x -- 101000[0123456]*) -+ 101000[[0123456]]*) - # https://github.com/openssl/openssl/pull/4613 - AC_MSG_ERROR([OpenSSL 1.1.x versions prior to 1.1.0g have a bug that breaks their use with OpenSSH (have "$ssl_library_ver")]) - ;; --- -2.20.1 - - -From f429c1b2ef631f2855e51a790cf71761d752bbca Mon Sep 17 00:00:00 2001 -From: "djm@openbsd.org" -Date: Thu, 27 Dec 2018 23:02:11 +0000 -Subject: [PATCH 7/7] upstream: Request RSA-SHA2 signatures for - -rsa-sha2-{256|512}-cert-v01@openssh.com cert algorithms; ok markus@ - -OpenBSD-Commit-ID: afc6f7ca216ccd821656d1c911d2a3deed685033 ---- - authfd.c | 8 +++++--- - 1 file changed, 5 insertions(+), 3 deletions(-) - -diff --git a/authfd.c b/authfd.c -index ecdd869a..62cbf8c1 100644 ---- a/authfd.c -+++ b/authfd.c -@@ -327,10 +327,12 @@ ssh_free_identitylist(struct ssh_identitylist *idl) - static u_int - agent_encode_alg(const struct sshkey *key, const char *alg) - { -- if (alg != NULL && key->type == KEY_RSA) { -- if (strcmp(alg, "rsa-sha2-256") == 0) -+ if (alg != NULL && sshkey_type_plain(key->type) == KEY_RSA) { -+ if (strcmp(alg, "rsa-sha2-256") == 0 || -+ strcmp(alg, "rsa-sha2-256-cert-v01@openssh.com") == 0) - return SSH_AGENT_RSA_SHA2_256; -- else if (strcmp(alg, "rsa-sha2-512") == 0) -+ if (strcmp(alg, "rsa-sha2-512") == 0 || -+ strcmp(alg, "rsa-sha2-512-cert-v01@openssh.com") == 0) - return SSH_AGENT_RSA_SHA2_512; - } - return 0; --- -2.20.1 - diff --git a/openssh-7.9p1-disable-sha1.patch b/openssh-7.9p1-disable-sha1.patch deleted file mode 100644 index c157b3c..0000000 --- a/openssh-7.9p1-disable-sha1.patch +++ /dev/null @@ -1,62 +0,0 @@ -diff --git a/monitor.c b/monitor.c -index 12b33e7..a1c3c97 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -875,6 +875,34 @@ mm_answer_bsdauthrespond(int sock, struct sshbuf *m) - } - #endif - -+/* -+ * Check that the key type appears in the supplied pattern list, ignoring -+ * mismastches in the signature algorithm. (Signature algorithm checks are -+ * performed in the unprivileged authentication code). -+ * Returns 1 on success, 0 otherwise. -+ */ -+static int -+key_base_type_match(const struct sshkey *key, const char *list) -+{ -+ char *s, *l, *ol = xstrdup(list); -+ int found = 0; -+ -+ l = ol; -+ for ((s = strsep(&l, ",")); s && *s != '\0'; (s = strsep(&l, ","))) { -+ if (sshkey_type_from_name(s) == key->type) { -+ found = 1; -+ break; -+ } -+ } -+ if (!found) { -+ debug("key type %s does not appear in list %s", -+ sshkey_ssh_name(key), list); -+ } -+ -+ free(ol); -+ return found; -+} -+ - int - mm_answer_keyallowed(int sock, struct sshbuf *m) - { -@@ -909,8 +937,8 @@ mm_answer_keyallowed(int sock, struct sshbuf *m) - break; - if (auth2_key_already_used(authctxt, key)) - break; -- if (match_pattern_list(sshkey_ssh_name(key), -- options.pubkey_key_types, 0) != 1) -+ if (!key_base_type_match(key, -+ options.pubkey_key_types)) - break; - allowed = user_key_allowed(ssh, authctxt->pw, key, - pubkey_auth_attempt, &opts); -@@ -921,8 +949,8 @@ mm_answer_keyallowed(int sock, struct sshbuf *m) - break; - if (auth2_key_already_used(authctxt, key)) - break; -- if (match_pattern_list(sshkey_ssh_name(key), -- options.hostbased_key_types, 0) != 1) -+ if (!key_base_type_match(key, -+ options.hostbased_key_types)) - break; - allowed = hostbased_key_allowed(authctxt->pw, - cuser, chost, key); - diff --git a/openssh-7.9p1-gsskex-method.patch b/openssh-7.9p1-gsskex-method.patch deleted file mode 100644 index b06620d..0000000 --- a/openssh-7.9p1-gsskex-method.patch +++ /dev/null @@ -1,150 +0,0 @@ -From bc74944ce7a2eabd228d47051f277ce108914c96 Mon Sep 17 00:00:00 2001 -From: Jakub Jelen -Date: Tue, 16 Oct 2018 16:44:40 +0200 -Subject: [PATCH] Unbreak authentication using gssapi-keyex (#1625366) - ---- - auth2-gss.c | 6 +++--- - gss-serv.c | 4 +++- - monitor.c | 13 ++++++++++--- - monitor_wrap.c | 4 +++- - monitor_wrap.h | 2 +- - ssh-gss.h | 2 +- - 6 files changed, 21 insertions(+), 10 deletions(-) - -diff --git a/auth2-gss.c b/auth2-gss.c -index 3f2ad21d..a61ac089 100644 ---- a/auth2-gss.c -+++ b/auth2-gss.c -@@ -84,7 +84,7 @@ userauth_gsskeyex(Authctxt *authctxt) - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gss_kex_context, - &gssbuf, &mic)))) - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -- authctxt->pw)); -+ authctxt->pw, 1)); - - sshbuf_free(b); - free(mic.value); -@@ -299,7 +299,7 @@ input_gssapi_exchange_complete(int type, u_int32_t plen, struct ssh *ssh) - fatal("%s: %s", __func__, ssh_err(r)); - - authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, -- authctxt->pw)); -+ authctxt->pw, 1)); - - if ((!use_privsep || mm_is_monitor()) && - (displayname = ssh_gssapi_displayname()) != NULL) -@@ -347,7 +347,7 @@ input_gssapi_mic(int type, u_int32_t plen, struct ssh *ssh) - - if (!GSS_ERROR(PRIVSEP(ssh_gssapi_checkmic(gssctxt, &gssbuf, &mic)))) - authenticated = -- PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw)); -+ PRIVSEP(ssh_gssapi_userok(authctxt->user, authctxt->pw, 0)); - else - logit("GSSAPI MIC check failed"); - -diff --git a/gss-serv.c b/gss-serv.c -index 786ac95c..87de2baa 100644 ---- a/gss-serv.c -+++ b/gss-serv.c -@@ -493,10 +493,12 @@ verify_authentication_indicators(Gssctxt *gssctxt) - - /* Privileged */ - int --ssh_gssapi_userok(char *user, struct passwd *pw) -+ssh_gssapi_userok(char *user, struct passwd *pw, int kex) - { - OM_uint32 lmin; - -+ (void) kex; /* used in privilege separation */ -+ - if (gssapi_client.exportedname.length == 0 || - gssapi_client.exportedname.value == NULL) { - debug("No suitable client data"); -diff --git a/monitor.c b/monitor.c -index 9bbe8cc4..7b1903af 100644 ---- a/monitor.c -+++ b/monitor.c -@@ -1877,14 +1877,17 @@ mm_answer_gss_checkmic(int sock, struct sshbuf *m) - int - mm_answer_gss_userok(int sock, struct sshbuf *m) - { -- int r, authenticated; -+ int r, authenticated, kex; - const char *displayname; - - if (!options.gss_authentication && !options.gss_keyex) - fatal("%s: GSSAPI authentication not enabled", __func__); - -+ if ((r = sshbuf_get_u32(m, &kex)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); -+ - authenticated = authctxt->valid && -- ssh_gssapi_userok(authctxt->user, authctxt->pw); -+ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex); - - sshbuf_reset(m); - if ((r = sshbuf_put_u32(m, authenticated)) != 0) -@@ -1893,7 +1896,11 @@ mm_answer_gss_userok(int sock, struct sshbuf *m) - debug3("%s: sending result %d", __func__, authenticated); - mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); - -- auth_method = "gssapi-with-mic"; -+ if (kex) { -+ auth_method = "gssapi-keyex"; -+ } else { -+ auth_method = "gssapi-with-mic"; -+ } - - if ((displayname = ssh_gssapi_displayname()) != NULL) - auth2_record_info(authctxt, "%s", displayname); -diff --git a/monitor_wrap.c b/monitor_wrap.c -index fb52a530..508d926d 100644 ---- a/monitor_wrap.c -+++ b/monitor_wrap.c -@@ -984,13 +984,15 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) - } - - int --mm_ssh_gssapi_userok(char *user, struct passwd *pw) -+mm_ssh_gssapi_userok(char *user, struct passwd *pw, int kex) - { - struct sshbuf *m; - int r, authenticated = 0; - - if ((m = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); -+ if ((r = sshbuf_put_u32(m, kex)) != 0) -+ fatal("%s: buffer error: %s", __func__, ssh_err(r)); - - mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); - mm_request_receive_expect(pmonitor->m_recvfd, -diff --git a/monitor_wrap.h b/monitor_wrap.h -index 494760dd..5eba5ecc 100644 ---- a/monitor_wrap.h -+++ b/monitor_wrap.h -@@ -60,7 +60,7 @@ int mm_sshkey_verify(const struct sshkey *, const u_char *, size_t, - OM_uint32 mm_ssh_gssapi_server_ctx(Gssctxt **, gss_OID); - OM_uint32 mm_ssh_gssapi_accept_ctx(Gssctxt *, - gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); --int mm_ssh_gssapi_userok(char *user, struct passwd *); -+int mm_ssh_gssapi_userok(char *user, struct passwd *, int kex); - OM_uint32 mm_ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - OM_uint32 mm_ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); - int mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *); -diff --git a/ssh-gss.h b/ssh-gss.h -index 39b6ce69..98262837 100644 ---- a/ssh-gss.h -+++ b/ssh-gss.h -@@ -162,7 +162,7 @@ gss_OID ssh_gssapi_id_kex(Gssctxt *, char *, int); - int ssh_gssapi_server_check_mech(Gssctxt **,gss_OID, const char *, - const char *); - OM_uint32 ssh_gssapi_server_ctx(Gssctxt **, gss_OID); --int ssh_gssapi_userok(char *name, struct passwd *); -+int ssh_gssapi_userok(char *name, struct passwd *, int kex); - OM_uint32 ssh_gssapi_checkmic(Gssctxt *, gss_buffer_t, gss_buffer_t); - void ssh_gssapi_do_child(char ***, u_int *); - void ssh_gssapi_cleanup_creds(void); --- -2.17.2 - diff --git a/openssh-7.9p1-log-sftp-only-connections.patch b/openssh-7.9p1-log-sftp-only-connections.patch deleted file mode 100644 index 3b3613d..0000000 --- a/openssh-7.9p1-log-sftp-only-connections.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/session.c b/session.c ---- a/session.c -+++ b/session.c -@@ -1859,6 +1859,7 @@ do_child(Session *s, const char *command) - - if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { - printf("This service allows sftp connections only.\n"); -+ logit("The session allows sftp connections only"); - fflush(NULL); - exit(1); - } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { diff --git a/openssh-7.9p1-match-final.patch b/openssh-7.9p1-match-final.patch deleted file mode 100644 index 4c28fcf..0000000 --- a/openssh-7.9p1-match-final.patch +++ /dev/null @@ -1,306 +0,0 @@ -commit 9e34e0c59ab04514f9de9934a772283f7f372afe -Author: djm@openbsd.org -Date: Fri Nov 23 05:08:07 2018 +0000 - - upstream: add a ssh_config "Match final" predicate - - Matches in same pass as "Match canonical" but doesn't require - hostname canonicalisation be enabled. bz#2906 ok markus - - OpenBSD-Commit-ID: fba1dfe9f6e0cabcd0e2b3be13f7a434199beffa - -diff --git a/readconf.c b/readconf.c -index 7850f2f5..7331ef5a 100644 ---- a/readconf.c -+++ b/readconf.c -@@ -133,10 +133,11 @@ - - static int read_config_file_depth(const char *filename, struct passwd *pw, - const char *host, const char *original_host, Options *options, -- int flags, int *activep, int depth); -+ int flags, int *activep, int *want_final_pass, int depth); - static int process_config_line_depth(Options *options, struct passwd *pw, - const char *host, const char *original_host, char *line, -- const char *filename, int linenum, int *activep, int flags, int depth); -+ const char *filename, int linenum, int *activep, int flags, -+ int *want_final_pass, int depth); - - /* Keyword tokens. */ - -@@ -539,8 +540,8 @@ execute_in_shell(const char *cmd) - */ - static int - match_cfg_line(Options *options, char **condition, struct passwd *pw, -- const char *host_arg, const char *original_host, int post_canon, -- const char *filename, int linenum) -+ const char *host_arg, const char *original_host, int final_pass, -+ int *want_final_pass, const char *filename, int linenum) - { - char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; - const char *ruser; -@@ -554,7 +555,7 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, - */ - port = options->port <= 0 ? default_ssh_port() : options->port; - ruser = options->user == NULL ? pw->pw_name : options->user; -- if (post_canon) { -+ if (final_pass) { - host = xstrdup(options->hostname); - } else if (options->hostname != NULL) { - /* NB. Please keep in sync with ssh.c:main() */ -@@ -586,8 +587,16 @@ match_cfg_line(Options *options, char **condition, struct passwd *pw, - goto out; - } - attributes++; -- if (strcasecmp(attrib, "canonical") == 0) { -- r = !!post_canon; /* force bitmask member to boolean */ -+ if (strcasecmp(attrib, "canonical") == 0 || -+ strcasecmp(attrib, "final") == 0) { -+ /* -+ * If the config requests "Match final" then remember -+ * this so we can perform a second pass later. -+ */ -+ if (strcasecmp(attrib, "final") == 0 && -+ want_final_pass != NULL) -+ *want_final_pass = 1; -+ r = !!final_pass; /* force bitmask member to boolean */ - if (r == (negate ? 1 : 0)) - this_result = result = 0; - debug3("%.200s line %d: %smatched '%s'", -@@ -824,14 +833,14 @@ process_config_line(Options *options, struct passwd *pw, const char *host, - int linenum, int *activep, int flags) - { - return process_config_line_depth(options, pw, host, original_host, -- line, filename, linenum, activep, flags, 0); -+ line, filename, linenum, activep, flags, NULL, 0); - } - - #define WHITESPACE " \t\r\n" - static int - process_config_line_depth(Options *options, struct passwd *pw, const char *host, - const char *original_host, char *line, const char *filename, -- int linenum, int *activep, int flags, int depth) -+ int linenum, int *activep, int flags, int *want_final_pass, int depth) - { - char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; - char **cpptr, fwdarg[256]; -@@ -1339,7 +1348,8 @@ parse_keytypes: - fatal("Host directive not supported as a command-line " - "option"); - value = match_cfg_line(options, &s, pw, host, original_host, -- flags & SSHCONF_POSTCANON, filename, linenum); -+ flags & SSHCONF_FINAL, want_final_pass, -+ filename, linenum); - if (value < 0) - fatal("%.200s line %d: Bad Match condition", filename, - linenum); -@@ -1548,7 +1558,7 @@ parse_keytypes: - pw, host, original_host, options, - flags | SSHCONF_CHECKPERM | - (oactive ? 0 : SSHCONF_NEVERMATCH), -- activep, depth + 1); -+ activep, want_final_pass, depth + 1); - if (r != 1 && errno != ENOENT) { - fatal("Can't open user config file " - "%.100s: %.100s", gl.gl_pathv[i], -@@ -1751,19 +1761,20 @@ parse_keytypes: - */ - int - read_config_file(const char *filename, struct passwd *pw, const char *host, -- const char *original_host, Options *options, int flags) -+ const char *original_host, Options *options, int flags, -+ int *want_final_pass) - { - int active = 1; - - return read_config_file_depth(filename, pw, host, original_host, -- options, flags, &active, 0); -+ options, flags, &active, want_final_pass, 0); - } - - #define READCONF_MAX_DEPTH 16 - static int - read_config_file_depth(const char *filename, struct passwd *pw, - const char *host, const char *original_host, Options *options, -- int flags, int *activep, int depth) -+ int flags, int *activep, int *want_final_pass, int depth) - { - FILE *f; - char *line = NULL; -@@ -1798,7 +1809,8 @@ read_config_file_depth(const char *filename, struct passwd *pw, - /* Update line number counter. */ - linenum++; - if (process_config_line_depth(options, pw, host, original_host, -- line, filename, linenum, activep, flags, depth) != 0) -+ line, filename, linenum, activep, flags, want_final_pass, -+ depth) != 0) - bad_options++; - } - free(line); -diff --git a/readconf.h b/readconf.h -index fc7e3825..8e36bf32 100644 ---- a/readconf.h -+++ b/readconf.h -@@ -185,7 +185,7 @@ typedef struct { - - #define SSHCONF_CHECKPERM 1 /* check permissions on config file */ - #define SSHCONF_USERCONF 2 /* user provided config file not system */ --#define SSHCONF_POSTCANON 4 /* After hostname canonicalisation */ -+#define SSHCONF_FINAL 4 /* Final pass over config, after canon. */ - #define SSHCONF_NEVERMATCH 8 /* Match/Host never matches; internal only */ - - #define SSH_UPDATE_HOSTKEYS_NO 0 -@@ -203,7 +203,7 @@ void fill_default_options_for_canonicalization(Options *); - int process_config_line(Options *, struct passwd *, const char *, - const char *, char *, const char *, int, int *, int); - int read_config_file(const char *, struct passwd *, const char *, -- const char *, Options *, int); -+ const char *, Options *, int, int *); - int parse_forward(struct Forward *, const char *, int, int); - int parse_jump(const char *, Options *, int); - int parse_ssh_uri(const char *, char **, char **, int *); -diff --git a/ssh-keysign.c b/ssh-keysign.c -index 8f487b8c..7ea5ad0e 100644 ---- a/ssh-keysign.c -+++ b/ssh-keysign.c -@@ -208,7 +208,8 @@ main(int argc, char **argv) - - /* verify that ssh-keysign is enabled by the admin */ - initialize_options(&options); -- (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", &options, 0); -+ (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "", -+ &options, 0, NULL); - fill_default_options(&options); - if (options.enable_ssh_keysign != 1) - fatal("ssh-keysign not enabled in %s", -diff --git a/ssh.c b/ssh.c -index 1ac903d1..c6cb7847 100644 ---- a/ssh.c -+++ b/ssh.c -@@ -527,7 +527,8 @@ check_load(int r, const char *path, const char *message) - * file if the user specifies a config file on the command line. - */ - static void --process_config_files(const char *host_name, struct passwd *pw, int post_canon) -+process_config_files(const char *host_name, struct passwd *pw, int final_pass, -+ int *want_final_pass) - { - char buf[PATH_MAX]; - int r; -@@ -535,7 +536,8 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon) - if (config != NULL) { - if (strcasecmp(config, "none") != 0 && - !read_config_file(config, pw, host, host_name, &options, -- SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0))) -+ SSHCONF_USERCONF | (final_pass ? SSHCONF_FINAL : 0), -+ want_final_pass)) - fatal("Can't open user config file %.100s: " - "%.100s", config, strerror(errno)); - } else { -@@ -544,12 +546,12 @@ process_config_files(const char *host_name, struct passwd *pw, int post_canon) - if (r > 0 && (size_t)r < sizeof(buf)) - (void)read_config_file(buf, pw, host, host_name, - &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | -- (post_canon ? SSHCONF_POSTCANON : 0)); -+ (final_pass ? SSHCONF_FINAL : 0), want_final_pass); - - /* Read systemwide configuration file after user config. */ - (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, - host, host_name, &options, -- post_canon ? SSHCONF_POSTCANON : 0); -+ final_pass ? SSHCONF_FINAL : 0, want_final_pass); - } - } - -@@ -581,7 +583,7 @@ main(int ac, char **av) - { - struct ssh *ssh = NULL; - int i, r, opt, exit_status, use_syslog, direct, timeout_ms; -- int was_addr, config_test = 0, opt_terminated = 0; -+ int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0; - char *p, *cp, *line, *argv0, buf[PATH_MAX], *logfile; - char cname[NI_MAXHOST]; - struct stat st; -@@ -1089,7 +1091,9 @@ main(int ac, char **av) - ); - - /* Parse the configuration files */ -- process_config_files(host_arg, pw, 0); -+ process_config_files(host_arg, pw, 0, &want_final_pass); -+ if (want_final_pass) -+ debug("configuration requests final Match pass"); - - /* Hostname canonicalisation needs a few options filled. */ - fill_default_options_for_canonicalization(&options); -@@ -1146,12 +1150,17 @@ main(int ac, char **av) - * If canonicalisation is enabled then re-parse the configuration - * files as new stanzas may match. - */ -- if (options.canonicalize_hostname != 0) { -- debug("Re-reading configuration after hostname " -- "canonicalisation"); -+ if (options.canonicalize_hostname != 0 && !want_final_pass) { -+ debug("hostname canonicalisation enabled, " -+ "will re-parse configuration"); -+ want_final_pass = 1; -+ } -+ -+ if (want_final_pass) { -+ debug("re-parsing configuration"); - free(options.hostname); - options.hostname = xstrdup(host); -- process_config_files(host_arg, pw, 1); -+ process_config_files(host_arg, pw, 1, NULL); - /* - * Address resolution happens early with canonicalisation - * enabled and the port number may have changed since, so -diff --git a/ssh_config.5 b/ssh_config.5 -index 4d5b01d3..58a5fa1c 100644 ---- a/ssh_config.5 -+++ b/ssh_config.5 -@@ -139,6 +139,7 @@ or the single token - which always matches. - The available criteria keywords are: - .Cm canonical , -+.Cm final , - .Cm exec , - .Cm host , - .Cm originalhost , -@@ -148,12 +149,15 @@ and - The - .Cm all - criteria must appear alone or immediately after --.Cm canonical . -+.Cm canonical -+or -+.Cm final . - Other criteria may be combined arbitrarily. - All criteria but - .Cm all --and - .Cm canonical -+and -+.Cm final - require an argument. - Criteria may be negated by prepending an exclamation mark - .Pq Sq !\& . -@@ -166,6 +170,20 @@ after hostname canonicalization (see the - option.) - This may be useful to specify conditions that work with canonical host - names only. -+.Pp -+The -+.Cm final -+keyword requests that the configuration be re-parsed (regardless of whether -+.Cm CanonicalizeHostname -+is enabled), and matches only during this final pass. -+If -+.Cm CanonicalizeHostname -+is enabled, then -+.Cm canonical -+and -+.Cm final -+match during the same pass. -+.Pp - The - .Cm exec - keyword executes the specified command under the user's shell. diff --git a/openssh-7.9p1-updated-cached-pw.patch b/openssh-7.9p1-updated-cached-pw.patch index 7f24ce4..398f6bd 100644 --- a/openssh-7.9p1-updated-cached-pw.patch +++ b/openssh-7.9p1-updated-cached-pw.patch @@ -2,8 +2,8 @@ diff -up openssh-7.4p1/session.c.update-pw openssh-7.4p1/session.c --- openssh-7.4p1/session.c.update-pw 2019-03-04 14:10:57.287054645 +0100 +++ openssh-7.4p1/session.c 2019-03-04 14:12:39.259997218 +0100 @@ -1522,9 +1522,18 @@ do_child(Session *s, const char *command - char **env; - char *argv[ARGV_MAX]; + extern char **environ; + char **env, *argv[ARGV_MAX], remote_id[512]; const char *shell, *shell0; - struct passwd *pw = s->pw; + struct passwd *pw = NULL; @@ -18,6 +18,6 @@ diff -up openssh-7.4p1/session.c.update-pw openssh-7.4p1/session.c + pw = s->pw; + } + + sshpkt_fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + /* remove hostkey from the child's memory */ - destroy_sensitive_data(); - packet_clear_keys(); diff --git a/openssh-8.0p1-gssapi-keyex.patch b/openssh-8.0p1-gssapi-keyex.patch new file mode 100644 index 0000000..fe3e7a6 --- /dev/null +++ b/openssh-8.0p1-gssapi-keyex.patch @@ -0,0 +1,3924 @@ +diff --git a/Makefile.in b/Makefile.in +index 6f001bb3..c31821ac 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -100,6 +100,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexgexc.o kexgexs.o \ + sntrup4591761.o kexsntrup4591761x25519.o kexgen.o \ ++ kexgssc.o \ + platform-pledge.o platform-tracing.o platform-misc.o + + +@@ -114,7 +115,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 \ + monitor.o monitor_wrap.o auth-krb5.o \ +- auth2-gss.o gss-serv.o gss-serv-krb5.o \ ++ auth2-gss.o gss-serv.o gss-serv-krb5.o kexgsss.o \ + loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ + sftp-server.o sftp-common.o \ + sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ +diff --git a/auth.c b/auth.c +index 332b6220..7664aaac 100644 +--- a/auth.c ++++ b/auth.c +@@ -399,7 +399,8 @@ auth_root_allowed(struct ssh *ssh, const char *method) + 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: +@@ -723,99 +724,6 @@ fakepw(void) + return (&fake); + } + +-/* +- * Returns the remote DNS hostname as a string. The returned string must not +- * be freed. NB. this will usually trigger a DNS query the first time it is +- * called. +- * This function does additional checks on the hostname to mitigate some +- * attacks on legacy rhosts-style authentication. +- * XXX is RhostsRSAAuthentication vulnerable to these? +- * XXX Can we remove these checks? (or if not, remove RhostsRSAAuthentication?) +- */ +- +-static char * +-remote_hostname(struct ssh *ssh) +-{ +- struct sockaddr_storage from; +- socklen_t fromlen; +- struct addrinfo hints, *ai, *aitop; +- char name[NI_MAXHOST], ntop2[NI_MAXHOST]; +- const char *ntop = ssh_remote_ipaddr(ssh); +- +- /* Get IP address of client. */ +- fromlen = sizeof(from); +- memset(&from, 0, sizeof(from)); +- if (getpeername(ssh_packet_get_connection_in(ssh), +- (struct sockaddr *)&from, &fromlen) < 0) { +- debug("getpeername failed: %.100s", strerror(errno)); +- return strdup(ntop); +- } +- +- ipv64_normalise_mapped(&from, &fromlen); +- if (from.ss_family == AF_INET6) +- fromlen = sizeof(struct sockaddr_in6); +- +- debug3("Trying to reverse map address %.100s.", ntop); +- /* Map the IP address to a host name. */ +- if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), +- NULL, 0, NI_NAMEREQD) != 0) { +- /* Host name not found. Use ip address. */ +- return strdup(ntop); +- } +- +- /* +- * if reverse lookup result looks like a numeric hostname, +- * someone is trying to trick us by PTR record like following: +- * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_socktype = SOCK_DGRAM; /*dummy*/ +- hints.ai_flags = AI_NUMERICHOST; +- if (getaddrinfo(name, NULL, &hints, &ai) == 0) { +- logit("Nasty PTR record \"%s\" is set up for %s, ignoring", +- name, ntop); +- freeaddrinfo(ai); +- return strdup(ntop); +- } +- +- /* Names are stored in lowercase. */ +- lowercase(name); +- +- /* +- * Map it back to an IP address and check that the given +- * address actually is an address of this host. This is +- * necessary because anyone with access to a name server can +- * define arbitrary names for an IP address. Mapping from +- * name to IP address can be trusted better (but can still be +- * fooled if the intruder has access to the name server of +- * the domain). +- */ +- memset(&hints, 0, sizeof(hints)); +- hints.ai_family = from.ss_family; +- hints.ai_socktype = SOCK_STREAM; +- if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { +- logit("reverse mapping checking getaddrinfo for %.700s " +- "[%s] failed.", name, ntop); +- return strdup(ntop); +- } +- /* Look for the address from the list of addresses. */ +- for (ai = aitop; ai; ai = ai->ai_next) { +- if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, +- sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && +- (strcmp(ntop, ntop2) == 0)) +- break; +- } +- freeaddrinfo(aitop); +- /* If we reached the end of the list, the address was not there. */ +- if (ai == NULL) { +- /* Address not found for the host name. */ +- logit("Address %.100s maps to %.600s, but this does not " +- "map back to the address.", ntop, name); +- return strdup(ntop); +- } +- return strdup(name); +-} +- + /* + * Return the canonical name of the host in the other side of the current + * connection. The host name is cached, so it is efficient to call this +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.29 2018/07/31 03:10:27 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("%s: %s", __func__, ssh_err(r)); ++ ++ if ((b = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ mic.value = p; ++ mic.length = len; ++ ++ ssh_gssapi_buildmic(b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ 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("%s: %s", __func__, ssh_err(r)); + +- 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", + userauth_gssapi, +diff --git a/auth2.c b/auth2.c +index 16ae1a36..7417eafa 100644 +--- a/auth2.c ++++ b/auth2.c +@@ -75,6 +75,7 @@ extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + +@@ -82,6 +83,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 f71a0856..404731d2 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) < 0) { ++ debug("getpeername failed: %.100s", strerror(errno)); ++ return strdup(ntop); ++ } ++ ++ ipv64_normalise_mapped(&from, &fromlen); ++ if (from.ss_family == AF_INET6) ++ fromlen = sizeof(struct sockaddr_in6); ++ ++ debug3("Trying to reverse map address %.100s.", ntop); ++ /* Map the IP address to a host name. */ ++ if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name), ++ NULL, 0, NI_NAMEREQD) != 0) { ++ /* Host name not found. Use ip address. */ ++ return strdup(ntop); ++ } ++ ++ /* ++ * if reverse lookup result looks like a numeric hostname, ++ * someone is trying to trick us by PTR record like following: ++ * 1.1.1.10.in-addr.arpa. IN PTR 2.3.4.5 ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; ++ if (getaddrinfo(name, NULL, &hints, &ai) == 0) { ++ logit("Nasty PTR record \"%s\" is set up for %s, ignoring", ++ name, ntop); ++ freeaddrinfo(ai); ++ return strdup(ntop); ++ } ++ ++ /* Names are stored in lowercase. */ ++ lowercase(name); ++ ++ /* ++ * Map it back to an IP address and check that the given ++ * address actually is an address of this host. This is ++ * necessary because anyone with access to a name server can ++ * define arbitrary names for an IP address. Mapping from ++ * name to IP address can be trusted better (but can still be ++ * fooled if the intruder has access to the name server of ++ * the domain). ++ */ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = from.ss_family; ++ hints.ai_socktype = SOCK_STREAM; ++ if (getaddrinfo(name, NULL, &hints, &aitop) != 0) { ++ logit("reverse mapping checking getaddrinfo for %.700s " ++ "[%s] failed.", name, ntop); ++ return strdup(ntop); ++ } ++ /* Look for the address from the list of addresses. */ ++ for (ai = aitop; ai; ai = ai->ai_next) { ++ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2, ++ sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 && ++ (strcmp(ntop, ntop2) == 0)) ++ break; ++ } ++ freeaddrinfo(aitop); ++ /* If we reached the end of the list, the address was not there. */ ++ if (ai == NULL) { ++ /* Address not found for the host name. */ ++ logit("Address %.100s maps to %.600s, but this does not " ++ "map back to the address.", ntop, name); ++ return strdup(ntop); ++ } ++ return strdup(name); ++} ++ + 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 521467bd..a0578e9d 100644 +--- a/clientloop.c ++++ b/clientloop.c +@@ -112,6 +112,10 @@ + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + +@@ -1374,9 +1378,18 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(ssh)) ++ if (!ssh_packet_is_rekeying(ssh)) { + channel_after_select(ssh, readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated(NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(ssh, readset); + +diff --git a/configure.ac b/configure.ac +index 30be6c18..2869f704 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -665,6 +665,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 ], ++ [SessionCreate(0, 0);], ++ [ac_cv_use_security_session_api="yes" ++ AC_DEFINE([USE_SECURITY_SESSION_API], [1], ++ [platform has the Security Authorization Session API]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes])], ++ [ac_cv_use_security_session_api="no" ++ AC_MSG_RESULT([no])]) ++ AC_MSG_CHECKING([if we have an in-memory credentials cache]) ++ AC_TRY_COMPILE( ++ [#include ], ++ [cc_context_t c; ++ (void) cc_initialize (&c, 0, NULL, NULL);], ++ [AC_DEFINE([USE_CCAPI], [1], ++ [platform uses an in-memory credentials cache]) ++ LIBS="$LIBS -framework Security" ++ AC_MSG_RESULT([yes]) ++ if test "x$ac_cv_use_security_session_api" = "xno"; then ++ AC_MSG_ERROR([*** Need a security framework to use the credentials cache API ***]) ++ fi], ++ [AC_MSG_RESULT([no])] ++ ) + m4_pattern_allow([AU_IPv]) + AC_CHECK_DECL([AU_IPv4], [], + AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) +diff --git a/gss-genr.c b/gss-genr.c +index d56257b4..3eaa5fa5 100644 +--- a/gss-genr.c ++++ b/gss-genr.c +@@ -1,7 +1,7 @@ + /* $OpenBSD: gss-genr.c,v 1.26 2018/07/10 09:13:30 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,12 +41,36 @@ + #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" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok(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,161 @@ 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, 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("%s: sshbuf_new failed", __func__); ++ ++ oidpos = 0; ++ s = cp = xstrdup(kex); ++ for (i = 0; i < gss_supported->count; i++) { ++ if (gss_supported->elements[i].length < 128 && ++ (*check)(NULL, &(gss_supported->elements[i]), host, client)) { ++ ++ 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("%s: digest failed: %s", __func__, ++ ssh_err(r)); ++ 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("%s: sshbuf_put_u8 error: %s", ++ __func__, ssh_err(r)); ++ if ((r = sshbuf_put(buf, p, strlen(p))) != 0 || ++ (r = sshbuf_put(buf, encoded, enclen)) != 0) ++ fatal("%s: sshbuf_put error: %s", ++ __func__, ssh_err(r)); ++ } ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ free(s); ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ if ((mechs = sshbuf_dup_string(buf)) == NULL) ++ fatal("%s: sshbuf_dup_string failed", __func__); ++ ++ 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 +397,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 +426,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 +470,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) +@@ -273,11 +499,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 +518,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 +531,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.31 2018/07/09 21:37:55 markus 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("%s: krb5_cc_resolve(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else if ((problem = krb5_cc_destroy(gssapi_client.store.data, ccache))) { ++ debug("%s: krb5_cc_destroy(): %.100s", __func__, ++ krb5_get_err_text(gssapi_client.store.data, problem)); ++ } else { ++ krb5_free_context(gssapi_client.store.data); ++ gssapi_client.store.data = NULL; ++ } + } + } + +@@ -356,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/hmac.c b/hmac.c +index 1c879640..a29f32c5 100644 +--- a/hmac.c ++++ b/hmac.c +@@ -19,6 +19,7 @@ + + #include + #include ++#include + + #include "sshbuf.h" + #include "digest.h" +diff --git a/kex.c b/kex.c +index 34808b5c..a2a4794e 100644 +--- a/kex.c ++++ b/kex.c +@@ -55,11 +55,16 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" ++#include "xmalloc.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* prototype */ + static int kex_choose_conf(struct ssh *); + static int kex_input_newkeys(int, u_int32_t, struct ssh *); +@@ -113,15 +118,28 @@ static const struct kexalg kexalgs[] = { + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, -1, -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, -1, -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); +@@ -136,6 +154,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) + { +@@ -145,6 +175,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; + } + +@@ -301,6 +335,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; ++} ++ + /* put algorithm proposal into buffer */ + int + kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) +@@ -657,6 +714,9 @@ kex_free(struct kex *kex) + sshbuf_free(kex->server_version); + sshbuf_free(kex->client_pub); + free(kex->session_id); ++#ifdef GSSAPI ++ free(kex->gss_host); ++#endif /* GSSAPI */ + free(kex->failed_choice); + free(kex->hostkey_alg); + free(kex->name); +diff --git a/kex.h b/kex.h +index 6d446d1c..f95dc02c 100644 +--- a/kex.h ++++ b/kex.h +@@ -103,6 +103,15 @@ enum kex_exchange { + KEX_ECDH_SHA2, + KEX_C25519_SHA256, + KEX_KEM_SNTRUP4591761X25519_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 + }; + +@@ -154,6 +163,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 *); +@@ -175,8 +190,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 *); + + int kex_exchange_identification(struct ssh *, int, const char *); + +@@ -203,6 +220,12 @@ int kexgex_client(struct ssh *); + int kexgex_server(struct ssh *); + int kex_gen_client(struct ssh *); + int kex_gen_server(struct ssh *); ++#ifdef GSSAPI ++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 **, +@@ -235,6 +258,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 2abbb9ef..569dc83f 100644 +--- a/kexgen.c ++++ b/kexgen.c +@@ -43,7 +43,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..0b2f6a56 +--- /dev/null ++++ b/kexgssc.c +@@ -0,0 +1,595 @@ ++/* ++ * 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 ++#include ++ ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "digest.h" ++#include "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 = sshbuf_new(); ++ 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("%s: Unexpected KEX type %d", __func__, 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; ++ ++ 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("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ ++ gssbuf.value = hash; ++ gssbuf.length = hashlen; ++ ++ /* Verify that the hash matches the MIC we just got. */ ++ if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok))) ++ 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 = sshbuf_new(); ++ 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); ++ ++ 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; ++ ++ 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); ++ ++ /* save session id */ ++ if (kex->session_id == NULL) { ++ kex->session_id_len = hashlen; ++ kex->session_id = xmalloc(kex->session_id_len); ++ memcpy(kex->session_id, hash, kex->session_id_len); ++ } ++ ++ if (kex->gss_deleg_creds) ++ ssh_gssapi_credentials_updated(ctxt); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ /* Finally derive the keys and send them */ ++ if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++out: ++ 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 ++ ++#include ++#include ++ ++#include "xmalloc.h" ++#include "sshbuf.h" ++#include "ssh2.h" ++#include "sshkey.h" ++#include "cipher.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "misc.h" /* servconf.h needs misc.h for struct ForwardOptions */ ++#include "servconf.h" ++#include "ssh-gss.h" ++#include "digest.h" ++#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("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ 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("%s: Unexpected KEX type %d", __func__, 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("%s: Identifying %s", __func__, kex->name); ++ oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type); ++ if (oid == GSS_C_NO_OID) ++ fatal("Unknown gssapi mechanism"); ++ ++ debug2("%s: Acquiring credentials", __func__); ++ ++ if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid)))) ++ fatal("Unable to acquire credentials for the server"); ++ ++ /* 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/mac.c b/mac.c +index 51dc11d7..3d11eba6 100644 +--- a/mac.c ++++ b/mac.c +@@ -29,6 +29,7 @@ + + #include + #include ++#include + + #include "digest.h" + #include "hmac.h" +diff --git a/monitor.c b/monitor.c +index 60e52944..669cdb4a 100644 +--- a/monitor.c ++++ b/monitor.c +@@ -147,6 +147,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 +@@ -219,11 +221,18 @@ struct mon_table mon_dispatch_proto20[] = { + {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, ++ {MONITOR_REQ_GSSSIGN, MON_ONCE, mm_answer_gss_sign}, + #endif + {0, 0, NULL} + }; + + struct mon_table mon_dispatch_postauth20[] = { ++#ifdef GSSAPI ++ {MONITOR_REQ_GSSSETUP, 0, mm_answer_gss_setup_ctx}, ++ {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, ++ {MONITOR_REQ_GSSSIGN, 0, mm_answer_gss_sign}, ++ {MONITOR_REQ_GSSUPCREDS, 0, mm_answer_gss_updatecreds}, ++#endif + #ifdef WITH_OPENSSL + {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, + #endif +@@ -292,6 +301,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) { +@@ -405,6 +418,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); +@@ -1687,6 +1704,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_SNTRUP4591761X25519_SHA512] = kex_gen_server; +@@ -1780,8 +1808,8 @@ mm_answer_gss_setup_ctx(struct ssh *ssh, int sock, struct sshbuf *m) + u_char *p; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = sshbuf_get_string(m, &p, &len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1813,8 +1841,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("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &in)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); +@@ -1834,6 +1862,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); + } +@@ -1845,8 +1874,8 @@ mm_answer_gss_checkmic(struct ssh *ssh, int sock, struct sshbuf *m) + OM_uint32 ret; + int r; + +- if (!options.gss_authentication) +- fatal("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + + if ((r = ssh_gssapi_get_buffer_desc(m, &gssbuf)) != 0 || + (r = ssh_gssapi_get_buffer_desc(m, &mic)) != 0) +@@ -1872,13 +1901,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("%s: GSSAPI authentication not enabled", __func__); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ if ((r = sshbuf_get_u32(m, &kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw, kex); + + sshbuf_reset(m); + if ((r = sshbuf_put_u32(m, authenticated)) != 0) +@@ -1887,7 +1920,11 @@ mm_answer_gss_userok(struct ssh *ssh, int sock, struct sshbuf *m) + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + +- auth_method = "gssapi-with-mic"; ++ if (kex) { ++ auth_method = "gssapi-keyex"; ++ } else { ++ auth_method = "gssapi-with-mic"; ++ } + + if ((displayname = ssh_gssapi_displayname()) != NULL) + auth2_record_info(authctxt, "%s", displayname); +@@ -1895,5 +1932,85 @@ 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("%s: GSSAPI not enabled", __func__); ++ ++ if ((r = sshbuf_get_string(m, &p, &len)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ 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("%s: data length incorrect: %d", __func__, ++ (int) data.length); ++ ++ /* Save the session ID on the first time around */ ++ if (session_id2_len == 0) { ++ session_id2_len = data.length; ++ session_id2 = xmalloc(session_id2_len); ++ memcpy(session_id2, data.value, session_id2_len); ++ } ++ major = ssh_gssapi_sign(gsscontext, &data, &hash); ++ ++ free(data.value); ++ ++ sshbuf_reset(m); ++ ++ if ((r = sshbuf_put_u32(m, major)) != 0 || ++ (r = sshbuf_put_string(m, hash.value, hash.length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSSIGN, m); ++ ++ gss_release_buffer(&minor, &hash); ++ ++ /* Turn on getpwnam permissions */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); ++ ++ /* And credential updating, for when rekeying */ ++ monitor_permit(mon_dispatch, MONITOR_REQ_GSSUPCREDS, 1); ++ ++ return (0); ++} ++ ++int ++mm_answer_gss_updatecreds(struct ssh *ssh, int socket, struct sshbuf *m) { ++ ssh_gssapi_ccache store; ++ int r, ok; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("%s: GSSAPI not enabled", __func__); ++ ++ 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("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ 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("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ 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 186e8f02..8e4c1c1f 100644 +--- a/monitor_wrap.c ++++ b/monitor_wrap.c +@@ -978,13 +978,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("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_u32(m, kex)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, m); + mm_request_receive_expect(pmonitor->m_recvfd, +@@ -997,4 +999,57 @@ mm_ssh_gssapi_userok(char *user) + debug3("%s: user %sauthenticated",__func__, authenticated ? "" : "not "); + return (authenticated); + } ++ ++OM_uint32 ++mm_ssh_gssapi_sign(Gssctxt *ctx, gss_buffer_desc *data, gss_buffer_desc *hash) ++{ ++ struct sshbuf *m; ++ OM_uint32 major; ++ int r; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ if ((r = sshbuf_put_string(m, data->value, data->length)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, m); ++ ++ if ((r = sshbuf_get_u32(m, &major)) != 0 || ++ (r = ssh_gssapi_get_buffer_desc(m, hash)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ struct sshbuf *m; ++ int r, ok; ++ ++ if ((m = sshbuf_new()) == NULL) ++ fatal("%s: sshbuf_new failed", __func__); ++ ++ 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("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, m); ++ ++ if ((r = sshbuf_get_u32(m, &ok)) != 0) ++ fatal("%s: buffer error: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ +diff --git a/monitor_wrap.h b/monitor_wrap.h +index fdebb3aa..69164a8c 100644 +--- a/monitor_wrap.h ++++ b/monitor_wrap.h +@@ -61,8 +61,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 --git a/readconf.c b/readconf.c +index ec497e79..4d699e5f 100644 +--- a/readconf.c ++++ b/readconf.c +@@ -67,6 +67,7 @@ + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" ++#include "ssh-gss.h" + + /* Format of the configuration file: + +@@ -162,6 +163,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, +@@ -202,10 +205,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 }, +@@ -983,10 +998,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 = strdelim(&s); ++ 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 : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case oBatchMode: + intptr = &options->batch_mode; + goto parse_flag; +@@ -1854,7 +1901,13 @@ initialize_options(Options * options) + options->pubkey_authentication = -1; + options->challenge_response_authentication = -1; + options->gss_authentication = -1; ++ options->gss_keyex = -1; + options->gss_deleg_creds = -1; ++ options->gss_trust_dns = -1; ++ options->gss_renewal_rekey = -1; ++ options->gss_client_identity = NULL; ++ options->gss_server_identity = NULL; ++ options->gss_kex_algorithms = NULL; + options->password_authentication = -1; + options->kbd_interactive_authentication = -1; + options->kbd_interactive_devices = NULL; +@@ -2000,8 +2053,18 @@ fill_default_options(Options * options) + options->challenge_response_authentication = 1; + if (options->gss_authentication == -1) + options->gss_authentication = 0; ++ if (options->gss_keyex == -1) ++ options->gss_keyex = 0; + if (options->gss_deleg_creds == -1) + options->gss_deleg_creds = 0; ++ if (options->gss_trust_dns == -1) ++ options->gss_trust_dns = 0; ++ if (options->gss_renewal_rekey == -1) ++ options->gss_renewal_rekey = 0; ++#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) +@@ -2616,7 +2679,14 @@ dump_client_config(Options *o, const char *host) + 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 --git a/readconf.h b/readconf.h +index 8e36bf32..0bff6d80 100644 +--- a/readconf.h ++++ b/readconf.h +@@ -40,7 +40,13 @@ typedef struct { + int challenge_response_authentication; + /* Try S/Key or TIS, authentication. */ + int gss_authentication; /* Try GSS authentication */ ++ int gss_keyex; /* Try GSS key exchange */ + int gss_deleg_creds; /* Delegate GSS credentials */ ++ int gss_trust_dns; /* Trust DNS for GSS canonicalization */ ++ int gss_renewal_rekey; /* Credential renewal forces rekey */ ++ char *gss_client_identity; /* Principal to initiate GSSAPI with */ ++ char *gss_server_identity; /* GSSAPI target principal */ ++ 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 --git a/servconf.c b/servconf.c +index ffac5d2c..ffdad31e 100644 +--- a/servconf.c ++++ b/servconf.c +@@ -64,6 +64,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); +@@ -124,8 +125,11 @@ initialize_server_options(ServerOptions *options) + 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->challenge_response_authentication = -1; +@@ -351,10 +355,18 @@ fill_default_server_options(ServerOptions *options) + 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) +@@ -498,6 +510,7 @@ typedef enum { + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssKexAlgorithms, sGssStoreRekey, + sAcceptEnv, sSetEnv, sPermitTunnel, + sMatch, sPermitOpen, sPermitListen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, +@@ -572,12 +585,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", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, +@@ -1485,6 +1508,10 @@ process_server_config_line(ServerOptions *options, char *line, + 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; +@@ -1493,6 +1520,22 @@ process_server_config_line(ServerOptions *options, char *line, + intptr = &options->gss_strict_acceptor; + goto parse_flag; + ++ case sGssStoreRekey: ++ intptr = &options->gss_store_rekey; ++ goto parse_flag; ++ ++ case sGssKexAlgorithms: ++ arg = strdelim(&cp); ++ if (!arg || *arg == '\0') ++ fatal("%.200s line %d: Missing argument.", ++ filename, linenum); ++ if (!kex_gss_names_valid(arg)) ++ fatal("%.200s line %d: Bad GSSAPI KexAlgorithms '%s'.", ++ filename, linenum, arg ? arg : ""); ++ if (*activep && options->gss_kex_algorithms == NULL) ++ options->gss_kex_algorithms = xstrdup(arg); ++ break; ++ + case sPasswordAuthentication: + intptr = &options->password_authentication; + goto parse_flag; +@@ -2579,6 +2622,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 54e0a8d8..a476d522 100644 +--- a/servconf.h ++++ b/servconf.h +@@ -126,8 +126,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 48cfaafb..78cc8358 100644 +--- a/session.c ++++ b/session.c +@@ -2674,13 +2674,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.14 2018/07/10 09:13:30 djm Exp $ */ + /* +- * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. ++ * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions +@@ -61,10 +61,30 @@ + + #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_GEX_SHA1_ID "," \ ++ KEX_GSS_GRP14_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 *); +-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 9480eba8..a1c7d230 100644 +--- a/ssh.1 ++++ b/ssh.1 +@@ -497,7 +497,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 HostbasedAuthentication +@@ -573,6 +579,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 91e7c351..42be7d88 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -736,6 +736,8 @@ main(int ac, char **av) + cp = mac_alg_list('\n'); + else if (strcmp(optarg, "kex") == 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) +@@ -748,7 +750,7 @@ main(int ac, char **av) + cp = xstrdup("2"); + else if (strcmp(optarg, "help") == 0) { + cp = xstrdup( +- "cipher\ncipher-auth\nkex\nkey\n" ++ "cipher\ncipher-auth\nkex\nkex-gss\nkey\n" + "key-cert\nkey-plain\nmac\n" + "protocol-version\nsig"); + } +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 41262963..c3c8b274 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -754,10 +754,67 @@ 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-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 +diff --git a/sshconnect2.c b/sshconnect2.c +index dffee90b..0d0a6cb8 100644 +--- a/sshconnect2.c ++++ b/sshconnect2.c +@@ -78,8 +78,6 @@ + #endif + + /* import */ +-extern char *client_version_string; +-extern char *server_version_string; + extern Options options; + + /* +@@ -161,6 +159,11 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + char *s, *all_key; + int r; + ++#if defined(GSSAPI) && defined(WITH_OPENSSL) ++ char *orig = NULL, *gss = NULL; ++ char *gss_host = NULL; ++#endif ++ + xxx_host = host; + xxx_hostaddr = hostaddr; + +@@ -193,6 +196,35 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) + order_hostkeyalgs(host, hostaddr, port)); + } + ++#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); ++ 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 ++ + if (options.rekey_limit || options.rekey_interval) + ssh_packet_set_rekey_limits(ssh, options.rekey_limit, + options.rekey_interval); +@@ -211,16 +243,46 @@ 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_SNTRUP4591761X25519_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 */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(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("kex_prop2buf: %s", ssh_err(r)); + +@@ -317,6 +379,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 *); +@@ -333,6 +396,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, +@@ -698,12 +766,23 @@ userauth_gssapi(struct ssh *ssh) + OM_uint32 min; + int r, ok = 0; + gss_OID mech = NULL; ++ char *gss_host; ++ ++ if (options.gss_server_identity) ++ gss_host = xstrdup(options.gss_server_identity); ++ else if (options.gss_trust_dns) ++ gss_host = remote_hostname(ssh); ++ 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))) { ++ 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 && +@@ -712,13 +791,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; + +@@ -958,6 +1039,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("%s: sshbuf_new failed", __func__); ++ ++ ssh_gssapi_buildmic(b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ if ((gssbuf.value = sshbuf_mutable_ptr(b)) == NULL) ++ fatal("%s: sshbuf_mutable_ptr failed", __func__); ++ 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("%s: %s", __func__, ssh_err(r)); ++ ++ sshbuf_free(b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + static int +diff --git a/sshd.c b/sshd.c +index cbd3bce9..8c223f6a 100644 +--- a/sshd.c ++++ b/sshd.c +@@ -796,8 +796,8 @@ notify_hostkeys(struct ssh *ssh) + } + debug3("%s: sent %u hostkeys", __func__, nkeys); + if (nkeys == 0) +- fatal("%s: no hostkeys", __func__); +- if ((r = sshpkt_send(ssh)) != 0) ++ debug3("%s: no hostkeys", __func__); ++ else if ((r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send", __func__); + sshbuf_free(buf); + } +@@ -1769,7 +1769,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); + } +@@ -2260,6 +2261,48 @@ do_ssh2_kex(struct ssh *ssh) + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( + list_hostkey_types()); + ++#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("kex_setup: %s", ssh_err(r)); +@@ -2275,7 +2318,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_SNTRUP4591761X25519_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 yes + GSSAPICleanupCredentials no ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will +diff --git a/sshd_config.5 b/sshd_config.5 +index b224f292..2baa6622 100644 +--- a/sshd_config.5 ++++ b/sshd_config.5 +@@ -653,6 +653,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. +@@ -667,6 +672,31 @@ 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-gex-sha1-,gss-group14-sha1- . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased authentication + as a list of comma-separated patterns. +diff --git a/sshkey.c b/sshkey.c +index ad195776..789cd61e 100644 +--- a/sshkey.c ++++ b/sshkey.c +@@ -135,6 +135,7 @@ static const struct keytype keytypes[] = { + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", NULL, KEY_NULL, 0, 0, 0 }, + { NULL, NULL, NULL, -1, -1, 0, 0 } + }; + +@@ -223,7 +224,7 @@ sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) + const struct keytype *kt; + + for (kt = keytypes; kt->type != -1; kt++) { +- if (kt->name == NULL) ++ if (kt->name == NULL || kt->type == KEY_NULL) + continue; + if (!include_sigonly && kt->sigonly) + continue; +diff --git a/sshkey.h b/sshkey.h +index a91e6043..c11106c9 100644 +--- a/sshkey.h ++++ b/sshkey.h +@@ -65,6 +65,7 @@ enum sshkey_types { + KEY_ED25519_CERT, + KEY_XMSS, + KEY_XMSS_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + diff --git a/openssh-8.0p1-pkcs11-uri.patch b/openssh-8.0p1-pkcs11-uri.patch new file mode 100644 index 0000000..9618863 --- /dev/null +++ b/openssh-8.0p1-pkcs11-uri.patch @@ -0,0 +1,3068 @@ +diff --git a/Makefile.in b/Makefile.in +index 6f001bb3..c9424f1e 100644 +--- a/Makefile.in ++++ b/Makefile.in +@@ -93,7 +93,7 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + atomicio.o dispatch.o mac.o uuencode.o misc.o utf8.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ +- ssh-pkcs11.o smult_curve25519_ref.o \ ++ ssh-pkcs11.o ssh-pkcs11-uri.o smult_curve25519_ref.o \ + poly1305.o chacha.o cipher-chachapoly.o \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o \ +@@ -250,6 +250,8 @@ clean: regressclean + rm -f regress/unittests/match/test_match$(EXEEXT) + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8$(EXEEXT) ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11$(EXEEXT) + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) + (cd openbsd-compat && $(MAKE) clean) +@@ -280,6 +282,8 @@ distclean: regressclean + rm -f regress/unittests/match/test_match + rm -f regress/unittests/utf8/*.o + rm -f regress/unittests/utf8/test_utf8 ++ rm -f regress/unittests/pkcs11/*.o ++ rm -f regress/unittests/pkcs11/test_pkcs11 + rm -f regress/misc/kexfuzz/*.o + rm -f regress/misc/kexfuzz/kexfuzz$(EXEEXT) + (cd openbsd-compat && $(MAKE) distclean) +@@ -442,6 +446,7 @@ regress-prep: + $(MKDIR_P) `pwd`/regress/unittests/kex + $(MKDIR_P) `pwd`/regress/unittests/match + $(MKDIR_P) `pwd`/regress/unittests/utf8 ++ $(MKDIR_P) `pwd`/regress/unittests/pkcs11 + $(MKDIR_P) `pwd`/regress/misc/kexfuzz + [ -f `pwd`/regress/Makefile ] || \ + ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile +@@ -565,6 +570,16 @@ regress/unittests/utf8/test_utf8$(EXEEXT): \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + ++UNITTESTS_TEST_PKCS11_OBJS=\ ++ regress/unittests/pkcs11/tests.o ++ ++regress/unittests/pkcs11/test_pkcs11$(EXEEXT): \ ++ ${UNITTESTS_TEST_PKCS11_OBJS} \ ++ regress/unittests/test_helper/libtest_helper.a libssh.a ++ $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_PKCS11_OBJS) \ ++ regress/unittests/test_helper/libtest_helper.a \ ++ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ++ + MISC_KEX_FUZZ_OBJS=\ + regress/misc/kexfuzz/kexfuzz.o + +@@ -585,6 +600,7 @@ regress-binaries: regress/modpipe$(EXEEXT) \ + regress/unittests/kex/test_kex$(EXEEXT) \ + regress/unittests/match/test_match$(EXEEXT) \ + regress/unittests/utf8/test_utf8$(EXEEXT) \ ++ regress/unittests/pkcs11/test_pkcs11$(EXEEXT) \ + regress/misc/kexfuzz/kexfuzz$(EXEEXT) + + tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) +diff --git a/authfd.c b/authfd.c +index 95348abf..5383df92 100644 +--- a/authfd.c ++++ b/authfd.c +@@ -312,6 +312,8 @@ ssh_free_identitylist(struct ssh_identitylist *idl) + if (idl->comments != NULL) + free(idl->comments[i]); + } ++ free(idl->keys); ++ free(idl->comments); + free(idl); + } + +diff --git a/configure.ac b/configure.ac +index 30be6c18..82459746 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1854,12 +1854,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 + ] + ) +@@ -1875,6 +1877,40 @@ if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then + ) + fi + ++# Check whether we have a p11-kit, we got default provider on command line ++DEFAULT_PKCS11_PROVIDER_MSG="no" ++AC_ARG_WITH([default-pkcs11-provider], ++ [ --with-default-pkcs11-provider[[=PATH]] Use default pkcs11 provider (p11-kit detected by default)], ++ [ if test "x$withval" != "xno" && test "x$disable_pkcs11" = "x"; then ++ if test "x$withval" = "xyes" ; then ++ AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) ++ if test "x$PKGCONFIG" != "xno"; then ++ AC_MSG_CHECKING([if $PKGCONFIG knows about p11-kit]) ++ if "$PKGCONFIG" "p11-kit-1"; then ++ AC_MSG_RESULT([yes]) ++ use_pkgconfig_for_p11kit=yes ++ else ++ AC_MSG_RESULT([no]) ++ fi ++ fi ++ else ++ PKCS11_PATH="${withval}" ++ fi ++ if test "x$use_pkgconfig_for_p11kit" = "xyes"; then ++ PKCS11_PATH=`$PKGCONFIG --variable=proxy_module p11-kit-1` ++ fi ++ AC_CHECK_FILE("$PKCS11_PATH", ++ [ AC_DEFINE_UNQUOTED([PKCS11_DEFAULT_PROVIDER], ["$PKCS11_PATH"], [Path to default PKCS#11 provider (p11-kit proxy)]) ++ DEFAULT_PKCS11_PROVIDER_MSG="$PKCS11_PATH" ++ ], ++ [ AC_MSG_ERROR([Requested PKCS11 provided not found]) ] ++ ) ++ else ++ AC_MSG_WARN([Needs PKCS11 support to enable default pkcs11 provider]) ++ fi ] ++) ++ ++ + # IRIX has a const char return value for gai_strerror() + AC_CHECK_FUNCS([gai_strerror], [ + AC_DEFINE([HAVE_GAI_STRERROR]) +@@ -5256,6 +5292,7 @@ echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" + echo " BSD Auth support: $BSD_AUTH_MSG" + echo " Random number source: $RAND_MSG" + echo " Privsep sandbox style: $SANDBOX_STYLE" ++echo " Default PKCS#11 provider: $DEFAULT_PKCS11_PROVIDER_MSG" + + echo "" + +diff --git a/regress/Makefile b/regress/Makefile +index 925edf71..94bb25e9 100644 +--- a/regress/Makefile ++++ b/regress/Makefile +@@ -109,9 +109,11 @@ CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ + known_hosts known_hosts-cert known_hosts.* krl-* ls.copy \ + modpipe netcat no_identity_config \ + pidfile putty.rsa2 ready regress.log \ +- remote_pid revoked-* rsa rsa-agent rsa-agent.pub rsa.pub \ ++ remote_pid revoked-* rsa rsa-agent rsa-agent.pub \ ++ rsa-agent-cert.pub rsa.pub \ + rsa1 rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ +- rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ ++ pkcs11*.crt pkcs11*.key \ ++ pkcs11.info rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ + scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ + sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ + ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ +@@ -231,6 +233,7 @@ unit: + V="" ; \ + test "x${USE_VALGRIND}" = "x" || \ + V=${.CURDIR}/valgrind-unit.sh ; \ ++ $$V ${.OBJDIR}/unittests/pkcs11/test_pkcs11 ; \ + $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ + $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ + -d ${.CURDIR}/unittests/sshkey/testdata ; \ +diff --git a/regress/agent-pkcs11.sh b/regress/agent-pkcs11.sh +index 5205d906..5ca49be5 100644 +--- a/regress/agent-pkcs11.sh ++++ b/regress/agent-pkcs11.sh +@@ -29,6 +29,13 @@ fi + + test -f "$TEST_SSH_PKCS11" || fatal "$TEST_SSH_PKCS11 does not exist" + ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ + # setup environment for softhsm2 token + DIR=$OBJ/SOFTHSM + rm -rf $DIR +@@ -113,7 +120,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 --git a/regress/pkcs11.sh b/regress/pkcs11.sh +new file mode 100644 +index 00000000..19fc8169 +--- /dev/null ++++ b/regress/pkcs11.sh +@@ -0,0 +1,352 @@ ++# ++# Copyright (c) 2017 Red Hat ++# ++# Authors: Jakub Jelen ++# ++# Permission to use, copy, modify, and distribute this software for any ++# purpose with or without fee is hereby granted, provided that the above ++# copyright notice and this permission notice appear in all copies. ++# ++# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ ++tid="pkcs11 tests with soft token" ++ ++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" ++ ++# requires ssh-agent built with correct path to ssh-pkcs11-helper ++# otherwise it fails to start the helper ++strings ${TEST_SSH_SSHAGENT} | grep "$TEST_SSH_SSHPKCS11HELPER" ++if [ $? -ne 0 ]; then ++ fatal "Needs to reconfigure with --libexecdir=\`pwd\` or so" ++fi ++ ++# setup environment for 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 "keygen fails to enumerate keys on PKCS#11 token" ++fi ++grep "pkcs11:" $OBJ/token_keys > /dev/null ++if [ $? -ne 0 ]; then ++ fail "The keys from ssh-keygen do not contain PKCS#11 URI as a comment" ++fi ++tail -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++ ++trace "Simple connect with ssh (without PKCS#11 URI)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -I ${TEST_SSH_PKCS11} \ ++ -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with pkcs11 failed (exit code $r)" ++fi ++ ++trace "Connect with PKCS#11 URI" ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace "Connect with PKCS#11 URI including PIN should not prompt" ++trace " (second 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 "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (first 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 "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace "Connect with various filtering options in PKCS#11 URI" ++trace " (by object label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key%202?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by object label, first key should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:object=SSH%20RSA%20Key?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++trace " (by token label, second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID2};token=SoftToken%20(token)?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI failed (exit code $r)" ++fi ++ ++trace " (by wrong token label, should fail)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:token=SoftToken?module-path=${TEST_SSH_PKCS11}" somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI succeeded (should fail)" ++fi ++ ++ ++ ++ ++trace "Test PKCS#11 URI specification in configuration files" ++echo "IdentityFile \"pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}\"" \ ++ >> $OBJ/ssh_proxy ++trace " (second key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config failed (exit code $r)" ++fi ++ ++trace " (first key should fail)" ++head -n 1 $OBJ/token_keys > $OBJ/authorized_keys_$USER ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -eq 5 ]; then ++ fail "ssh connect with PKCS#11 URI in config succeeded (should fail)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++trace "Test PKCS#11 URI specification in configuration files with bogus spaces" ++echo "IdentityFile \" pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11} \"" \ ++ >> $OBJ/ssh_proxy ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI with bogus spaces in config failed" \ ++ "(exit code $r)" ++fi ++sed -i -e "/IdentityFile/d" $OBJ/ssh_proxy ++ ++ ++trace "Combination of PKCS11Provider and PKCS11URI on commandline" ++trace " (first key should succeed)" ++echo ${TEST_SSH_PIN} | notty ${SSH} -F $OBJ/ssh_proxy \ ++ -i "pkcs11:id=${ID1}" -I ${TEST_SSH_PKCS11} somehost exit 5 ++r=$? ++if [ $r -ne 5 ]; then ++ fail "ssh connect with PKCS#11 URI and provider combination" \ ++ "failed (exit code $r)" ++fi ++ ++trace "Regress: Missing provider in PKCS11URI option" ++${SSH} -F $OBJ/ssh_proxy \ ++ -o IdentityFile=\"pkcs11:token=segfault\" somehost exit 5 ++r=$? ++if [ $r -eq 139 ]; then ++ fail "ssh connect with missing provider_id from configuration option" \ ++ "crashed (exit code $r)" ++fi ++ ++ ++trace "SSH Agent can work with PKCS#11 URI" ++trace "start the agent" ++eval `${SSHAGENT} -s -P "${OBJ}/*"` > /dev/null ++ ++r=$? ++if [ $r -ne 0 ]; then ++ fail "could not start ssh-agent: exit code $r" ++else ++ trace "add whole provider to agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 list via agent (all keys)" ++ ${SSHADD} -l > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -l failed with whole provider: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (all keys)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with whole provider (exit code $r)" ++ fi ++ ++ trace " remove pkcs11 keys (all keys)" ++ ${SSHADD} -d "pkcs11:?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with whole provider: exit code $r" ++ fi ++ ++ trace "add only first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (first key)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (exit code $r)" ++ fi ++ ++ trace " remove first pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with first key: exit code $r" ++ fi ++ ++ trace "add only second key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with second key: exit code $r" ++ fi ++ ++ trace " pkcs11 connect via agent (second key should fail)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -eq 5 ]; then ++ fail "ssh connect passed without key (should fail)" ++ fi ++ ++ trace "add also the first key to the agent" ++ echo ${TEST_SSH_PIN} | notty ${SSHADD} \ ++ "pkcs11:id=${ID1}?module-path=${TEST_SSH_PKCS11}" > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add failed with first key: exit code $r" ++ fi ++ ++ trace " remove second pkcs11 key" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -ne 0 ]; then ++ fail "ssh-add -d failed with second key: exit code $r" ++ fi ++ ++ trace " remove already-removed pkcs11 key should fail" ++ ${SSHADD} -d "pkcs11:id=${ID2}?module-path=${TEST_SSH_PKCS11}" \ ++ > /dev/null 2>&1 ++ r=$? ++ if [ $r -eq 0 ]; then ++ fail "ssh-add -d passed with non-existing key (should fail)" ++ fi ++ ++ trace " pkcs11 connect via agent (the first key should be still usable)" ++ ${SSH} -F $OBJ/ssh_proxy somehost exit 5 ++ r=$? ++ if [ $r -ne 5 ]; then ++ fail "ssh connect failed with first key (after removing second): exit code $r" ++ fi ++ ++ trace "kill agent" ++ ${SSHAGENT} -k > /dev/null ++fi +diff --git a/regress/unittests/Makefile b/regress/unittests/Makefile +index e464b085..a0e5a37c 100644 +--- a/regress/unittests/Makefile ++++ b/regress/unittests/Makefile +@@ -2,6 +2,6 @@ + + REGRESS_FAIL_EARLY?= yes + SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion +-SUBDIR+=authopt ++SUBDIR+=pkcs11 authopt + + .include +diff --git a/regress/unittests/pkcs11/tests.c b/regress/unittests/pkcs11/tests.c +new file mode 100644 +index 00000000..b637cb13 +--- /dev/null ++++ b/regress/unittests/pkcs11/tests.c +@@ -0,0 +1,337 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include "includes.h" ++ ++#include ++#include ++ ++#include "../test_helper/test_helper.h" ++ ++#include "sshbuf.h" ++#include "ssh-pkcs11-uri.h" ++ ++#define EMPTY_URI compose_uri(NULL, 0, NULL, NULL, NULL, NULL, NULL, 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 *whitelist, char *expect) ++{ ++ char *buf = NULL; ++ struct sshbuf *b; ++ ++ asprintf(&buf, "percent_encode: expected %s", expect); ++ TEST_START(buf); ++ free(buf); ++ ++ b = percent_encode(source, len, whitelist); ++ ASSERT_STRING_EQ(sshbuf_ptr(b), expect); ++ sshbuf_free(b); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_encode_multibyte(void) ++{ ++ /* SHOULD be encoded as octets according to the UTF-8 character encoding */ ++ ++ /* multi-byte characters are "for free" */ ++ check_encode("$", 1, "", "%24"); ++ check_encode("¢", 2, "", "%C2%A2"); ++ check_encode("€", 3, "", "%E2%82%AC"); ++ check_encode("𐍈", 4, "", "%F0%90%8D%88"); ++ ++ /* CK_UTF8CHAR is unsigned char (1 byte) */ ++ /* labels SHOULD be normalized to NFC [UAX15] */ ++ ++} ++ ++static void ++test_percent_encode(void) ++{ ++ /* Without whitelist encodes everything (for CKA_ID) */ ++ check_encode("A*", 2, "", "%41%2A"); ++ check_encode("\x00", 1, "", "%00"); ++ check_encode("\x7F", 1, "", "%7F"); ++ check_encode("\x80", 1, "", "%80"); ++ check_encode("\xff", 1, "", "%FF"); ++ ++ /* Default whitelist encodes anything but safe letters */ ++ check_encode("test" "\x00" "0alpha", 11, PKCS11_URI_WHITELIST, ++ "test%000alpha"); ++ check_encode(" ", 1, PKCS11_URI_WHITELIST, ++ "%20"); /* Space MUST be percent encoded */ ++ check_encode("/", 1, PKCS11_URI_WHITELIST, ++ "%2F"); /* '/' delimiter MUST be percent encoded (in the path) */ ++ check_encode("?", 1, PKCS11_URI_WHITELIST, ++ "%3F"); /* delimiter '?' MUST be percent encoded (in the path) */ ++ check_encode("#", 1, PKCS11_URI_WHITELIST, ++ "%23"); /* '#' MUST be always percent encoded */ ++ check_encode("key=value;separator?query&#anch", 35, PKCS11_URI_WHITELIST, ++ "key%3Dvalue%3Bseparator%3Fquery%26amp%3B%23anch"); ++ ++ /* Components in query can have '/' unencoded (useful for paths) */ ++ check_encode("/path/to.file", 13, PKCS11_URI_WHITELIST "/", ++ "/path/to.file"); ++} ++ ++void ++check_decode(char *source, char *expect, int expect_len) ++{ ++ char *buf = NULL, *out = NULL; ++ int rv; ++ ++ asprintf(&buf, "percent_decode: %s", source); ++ TEST_START(buf); ++ free(buf); ++ ++ rv = percent_decode(source, &out); ++ ASSERT_INT_EQ(rv, expect_len); ++ if (rv >= 0) ++ ASSERT_MEM_EQ(out, expect, expect_len); ++ free(out); ++ TEST_DONE(); ++} ++ ++static void ++test_percent_decode(void) ++{ ++ /* simple valid cases */ ++ check_decode("%00", "\x00", 1); ++ check_decode("%FF", "\xFF", 1); ++ ++ /* normal strings shold be kept intact */ ++ check_decode("strings are left", "strings are left", 16); ++ check_decode("10%25 of trees", "10% of trees", 12); ++ ++ /* make sure no more than 2 bytes are parsed */ ++ check_decode("%222", "\x22" "2", 2); ++ ++ /* invalid expects failure */ ++ check_decode("%0", "", -1); ++ check_decode("%Z", "", -1); ++ check_decode("%FG", "", -1); ++} ++ ++void ++tests(void) ++{ ++ test_percent_encode(); ++ test_percent_encode_multibyte(); ++ test_percent_decode(); ++ test_parse_valid(); ++ test_parse_invalid(); ++ test_generate_valid(); ++} +diff --git a/ssh-add.c b/ssh-add.c +index ac9c808d..f039e00e 100644 +--- a/ssh-add.c ++++ b/ssh-add.c +@@ -64,6 +64,7 @@ + #include "misc.h" + #include "ssherr.h" + #include "digest.h" ++#include "ssh-pkcs11-uri.h" + + /* argv0 */ + extern char *__progname; +@@ -188,6 +189,24 @@ delete_all(int agent_fd, int qflag) + return ret; + } + ++#ifdef ENABLE_PKCS11 ++static int update_card(int, int, const char *, int); ++ ++int ++update_pkcs11_uri(int agent_fd, int adding, const char *pkcs11_uri, int qflag) ++{ ++ struct pkcs11_uri *uri; ++ ++ /* dry-run parse to make sure the URI is valid and to report errors */ ++ uri = pkcs11_uri_init(); ++ if (pkcs11_uri_parse((char *) pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ pkcs11_uri_cleanup(uri); ++ ++ return update_card(agent_fd, adding, pkcs11_uri, qflag); ++} ++#endif ++ + static int + add_file(int agent_fd, const char *filename, int key_only, int qflag) + { +@@ -529,6 +548,13 @@ lock_agent(int agent_fd, int lock) + static int + do_file(int agent_fd, int deleting, int key_only, char *file, int qflag) + { ++#ifdef ENABLE_PKCS11 ++ if (strlen(file) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(file, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ return update_pkcs11_uri(agent_fd, !deleting, file, qflag); ++ } ++#endif + if (deleting) { + if (delete_file(agent_fd, file, key_only, qflag) == -1) + return -1; +diff --git a/ssh-agent.c b/ssh-agent.c +index d06ecfd9..9c1b328f 100644 +--- a/ssh-agent.c ++++ b/ssh-agent.c +@@ -548,10 +548,72 @@ no_identities(SocketEntry *e) + } + + #ifdef ENABLE_PKCS11 ++static char * ++sanitize_pkcs11_provider(const char *provider) ++{ ++ struct pkcs11_uri *uri = NULL; ++ char *sane_uri, *module_path = NULL; /* default path */ ++ char canonical_provider[PATH_MAX]; ++ ++ if (provider == NULL) ++ return NULL; ++ ++ if (strlen(provider) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ /* PKCS#11 URI */ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) { ++ error("Failed to init PCKS#11 URI"); ++ return NULL; ++ } ++ ++ if (pkcs11_uri_parse(provider, uri) != 0) { ++ error("Failed to parse PKCS#11 URI"); ++ return NULL; ++ } ++ /* validate also provider from URI */ ++ if (uri->module_path) ++ module_path = strdup(uri->module_path); ++ } else ++ module_path = strdup(provider); /* simple path */ ++ ++ if (module_path != NULL) { /* do not validate default NULL path in URI */ ++ if (realpath(module_path, canonical_provider) == NULL) { ++ verbose("failed PKCS#11 provider \"%.100s\": realpath: %s", ++ module_path, strerror(errno)); ++ free(module_path); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ free(module_path); ++ if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { ++ verbose("refusing PKCS#11 provider \"%.100s\": " ++ "not whitelisted", canonical_provider); ++ pkcs11_uri_cleanup(uri); ++ return NULL; ++ } ++ ++ /* copy verified and sanitized provider path back to the uri */ ++ if (uri) { ++ free(uri->module_path); ++ uri->module_path = xstrdup(canonical_provider); ++ } ++ } ++ ++ if (uri) { ++ sane_uri = pkcs11_uri_get(uri); ++ pkcs11_uri_cleanup(uri); ++ return sane_uri; ++ } else { ++ return xstrdup(canonical_provider); /* simple path */ ++ } ++} ++ + static void + process_add_smartcard_key(SocketEntry *e) + { +- char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; ++ char *provider = NULL, *pin = NULL, *sane_uri = NULL; + int r, i, count = 0, success = 0, confirm = 0; + u_int seconds; + time_t death = 0; +@@ -587,28 +649,23 @@ process_add_smartcard_key(SocketEntry *e) + goto send; + } + } +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); +- goto send; +- } +- if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { +- verbose("refusing PKCS#11 add of \"%.100s\": " +- "provider not whitelisted", canonical_provider); ++ ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } +- debug("%s: add %.100s", __func__, canonical_provider); ++ + if (lifetime && !death) + death = monotime() + lifetime; + +- count = pkcs11_add_provider(canonical_provider, pin, &keys); ++ debug("%s: add %.100s", __func__, sane_uri); ++ count = pkcs11_add_provider(sane_uri, pin, &keys); + for (i = 0; i < count; i++) { + k = keys[i]; + if (lookup_identity(k) == NULL) { + id = xcalloc(1, sizeof(Identity)); + id->key = k; +- id->provider = xstrdup(canonical_provider); +- id->comment = xstrdup(canonical_provider); /* XXX */ ++ id->provider = xstrdup(sane_uri); ++ id->comment = xstrdup(sane_uri); + id->death = death; + id->confirm = confirm; + TAILQ_INSERT_TAIL(&idtab->idlist, id, next); +@@ -622,6 +679,7 @@ process_add_smartcard_key(SocketEntry *e) + send: + free(pin); + free(provider); ++ free(sane_uri); + free(keys); + send_status(e, success); + } +@@ -629,7 +687,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; + +@@ -640,30 +698,29 @@ process_remove_smartcard_key(SocketEntry *e) + } + free(pin); + +- if (realpath(provider, canonical_provider) == NULL) { +- verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", +- provider, strerror(errno)); ++ sane_uri = sanitize_pkcs11_provider(provider); ++ if (sane_uri == NULL) + goto send; +- } + +- debug("%s: remove %.100s", __func__, canonical_provider); ++ debug("%s: remove %.100s", __func__, sane_uri); + for (id = TAILQ_FIRST(&idtab->idlist); id; id = nxt) { + nxt = TAILQ_NEXT(id, next); + /* Skip file--based keys */ + if (id->provider == NULL) + continue; +- if (!strcmp(canonical_provider, id->provider)) { ++ if (!strcmp(sane_uri, id->provider)) { + TAILQ_REMOVE(&idtab->idlist, id, next); + free_identity(id); + idtab->nentries--; + } + } +- if (pkcs11_del_provider(canonical_provider) == 0) ++ if (pkcs11_del_provider(sane_uri) == 0) + success = 1; + else + error("%s: pkcs11_del_provider failed", __func__); + send: + free(provider); ++ free(sane_uri); + send_status(e, success); + } + #endif /* ENABLE_PKCS11 */ +diff --git a/ssh-keygen.c b/ssh-keygen.c +index 3898b281..91c43b14 100644 +--- a/ssh-keygen.c ++++ b/ssh-keygen.c +@@ -798,6 +798,7 @@ do_download(struct passwd *pw) + free(fp); + } else { + (void) sshkey_write(keys[i], stdout); /* XXX check */ ++ (void) pkcs11_uri_write(keys[i], stdout); + fprintf(stdout, "\n"); + } + sshkey_free(keys[i]); +diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c +index e7860de8..7b2a9115 100644 +--- a/ssh-pkcs11-client.c ++++ b/ssh-pkcs11-client.c +@@ -321,6 +321,8 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) + u_int nkeys, i; + struct sshbuf *msg; + ++ debug("%s: called, name = %s", __func__, name); ++ + if (fd < 0 && pkcs11_start_helper() < 0) + return (-1); + +@@ -338,6 +340,7 @@ pkcs11_add_provider(char *name, char *pin, struct sshkey ***keysp) + if ((r = sshbuf_get_u32(msg, &nkeys)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + *keysp = xcalloc(nkeys, sizeof(struct sshkey *)); ++ debug("%s: nkeys = %u", __func__, nkeys); + for (i = 0; i < nkeys; i++) { + /* XXX clean up properly instead of fatal() */ + if ((r = sshbuf_get_string(msg, &blob, &blen)) != 0 || +diff --git a/ssh-pkcs11-uri.c b/ssh-pkcs11-uri.c +new file mode 100644 +index 00000000..e1a7b4e0 +--- /dev/null ++++ b/ssh-pkcs11-uri.c +@@ -0,0 +1,421 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#include "includes.h" ++ ++#ifdef ENABLE_PKCS11 ++ ++#include ++#include ++ ++#include "sshkey.h" ++#include "sshbuf.h" ++#include "log.h" ++ ++#define CRYPTOKI_COMPAT ++#include "pkcs11.h" ++ ++#include "ssh-pkcs11-uri.h" ++ ++#define PKCS11_URI_PATH_SEPARATOR ";" ++#define PKCS11_URI_QUERY_SEPARATOR "&" ++#define PKCS11_URI_VALUE_SEPARATOR "=" ++#define PKCS11_URI_ID "id" ++#define PKCS11_URI_TOKEN "token" ++#define PKCS11_URI_OBJECT "object" ++#define PKCS11_URI_LIB_MANUF "library-manufacturer" ++#define PKCS11_URI_MANUF "manufacturer" ++#define PKCS11_URI_MODULE_PATH "module-path" ++#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 *whitelist) ++{ ++ struct sshbuf *b = NULL; ++ char tmp[4], *cp; ++ size_t i; ++ ++ if ((b = sshbuf_new()) == NULL) ++ return NULL; ++ for (i = 0; i < length; i++) { ++ cp = strchr(whitelist, data[i]); ++ /* if c is specified as '\0' pointer to terminator is returned !! */ ++ if (cp != NULL && *cp != '\0') { ++ if (sshbuf_put(b, &data[i], 1) != 0) ++ goto err; ++ } else ++ if (snprintf(tmp, 4, "%%%02X", (unsigned char) data[i]) < 3 ++ || sshbuf_put(b, tmp, 3) != 0) ++ goto err; ++ } ++ if (sshbuf_put(b, "\0", 1) == 0) ++ return b; ++err: ++ sshbuf_free(b); ++ return NULL; ++} ++ ++char * ++pkcs11_uri_append(char *part, const char *separator, const char *key, ++ struct sshbuf *value) ++{ ++ char *new_part; ++ size_t size = 0; ++ ++ if (value == NULL) ++ return NULL; ++ ++ size = asprintf(&new_part, ++ "%s%s%s" PKCS11_URI_VALUE_SEPARATOR "%s", ++ (part != NULL ? part : ""), ++ (part != NULL ? separator : ""), ++ key, sshbuf_ptr(value)); ++ sshbuf_free(value); ++ free(part); ++ ++ if (size <= 0) ++ return NULL; ++ return new_part; ++} ++ ++char * ++pkcs11_uri_get(struct pkcs11_uri *uri) ++{ ++ size_t size = 0; ++ char *p = NULL, *path = NULL, *query = NULL; ++ ++ /* compose a percent-encoded ID */ ++ if (uri->id_len > 0) { ++ struct sshbuf *key_id = percent_encode(uri->id, uri->id_len, ""); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_ID, key_id); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write object label */ ++ if (uri->object) { ++ struct sshbuf *label = percent_encode(uri->object, strlen(uri->object), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_OBJECT, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write token label */ ++ if (uri->token) { ++ struct sshbuf *label = percent_encode(uri->token, strlen(uri->token), ++ PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_TOKEN, label); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write manufacturer */ ++ if (uri->manuf) { ++ struct sshbuf *manuf = percent_encode(uri->manuf, ++ strlen(uri->manuf), PKCS11_URI_WHITELIST); ++ path = pkcs11_uri_append(path, PKCS11_URI_PATH_SEPARATOR, ++ PKCS11_URI_MANUF, manuf); ++ if (path == NULL) ++ goto err; ++ } ++ ++ /* Write module_path */ ++ if (uri->module_path) { ++ struct sshbuf *module = percent_encode(uri->module_path, ++ strlen(uri->module_path), PKCS11_URI_WHITELIST "/"); ++ query = pkcs11_uri_append(query, PKCS11_URI_QUERY_SEPARATOR, ++ PKCS11_URI_MODULE_PATH, module); ++ if (query == NULL) ++ goto err; ++ } ++ ++ size = asprintf(&p, PKCS11_URI_SCHEME "%s%s%s", ++ path != NULL ? path : "", ++ query != NULL ? "?" : "", ++ query != NULL ? query : ""); ++err: ++ free(query); ++ free(path); ++ if (size <= 0) ++ return NULL; ++ return p; ++} ++ ++struct pkcs11_uri * ++pkcs11_uri_init() ++{ ++ struct pkcs11_uri *d = calloc(1, sizeof(struct pkcs11_uri)); ++ return d; ++} ++ ++void ++pkcs11_uri_cleanup(struct pkcs11_uri *pkcs11) ++{ ++ free(pkcs11->id); ++ free(pkcs11->module_path); ++ free(pkcs11->token); ++ free(pkcs11->object); ++ free(pkcs11->lib_manuf); ++ free(pkcs11->manuf); ++ 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("%s: The '%s' does not look like PKCS#11 URI", ++ __func__, uri); ++ return -1; ++ } ++ ++ if (pkcs11 == NULL) { ++ error("%s: Bad arguments. The pkcs11 can't be null", __func__); ++ return -1; ++ } ++ ++ /* skip URI schema name */ ++ p = strdup(uri); ++ str1 = p; ++ ++ /* everything before ? */ ++ tok = strtok_r(str1, "?", &saveptr1); ++ if (tok == NULL) { ++ error("%s: pk11-path expected, got EOF", __func__); ++ 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("%s: The id already set in the PKCS#11 URI", ++ __func__); ++ rv = -1; ++ goto out; ++ } ++ len = percent_decode(arg, &pkcs11->id); ++ if (len <= 0) { ++ verbose("%s: Failed to percent-decode CKA_ID: %s", ++ __func__, arg); ++ rv = -1; ++ goto out; ++ } else ++ pkcs11->id_len = len; ++ debug3("%s: Setting CKA_ID = %s from PKCS#11 URI", ++ __func__, arg); ++ break; ++ case pToken: ++ /* CK_TOKEN_INFO -> label */ ++ charptr = &pkcs11->token; ++ parse_string: ++ if (*charptr != NULL) { ++ verbose("%s: The %s already set in the PKCS#11 URI", ++ keywords[opcode].name, __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, charptr); ++ debug3("%s: Setting %s = %s from PKCS#11 URI", ++ __func__, keywords[opcode].name, *charptr); ++ break; ++ ++ case pObject: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->object; ++ goto parse_string; ++ ++ case pManufacturer: ++ /* CK_TOKEN_INFO -> manufacturerID */ ++ charptr = &pkcs11->manuf; ++ goto parse_string; ++ ++ case pLibraryManufacturer: ++ /* CK_INFO -> manufacturerID */ ++ charptr = &pkcs11->lib_manuf; ++ goto parse_string; ++ ++ default: ++ /* Unrecognized attribute in the URI path SHOULD be error */ ++ verbose("%s: Unknown part of path in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++ ++ tok = strtok_r(NULL, "?", &saveptr1); ++ if (tok == NULL) { ++ 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("%s: Multiple module-path attributes are" ++ "not supported the PKCS#11 URI", __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, &pkcs11->module_path); ++ debug3("%s: Setting PKCS11Provider = %s from PKCS#11 URI", ++ __func__, pkcs11->module_path); ++ break; ++ ++ case pPinValue: ++ /* pin-value */ ++ if (pkcs11->pin != NULL) { ++ verbose("%s: Multiple pin-value attributes are" ++ "not supported the PKCS#11 URI", __func__); ++ rv = -1; ++ goto out; ++ } ++ percent_decode(arg, &pkcs11->pin); ++ debug3("%s: Setting PIN from PKCS#11 URI", __func__); ++ break; ++ ++ default: ++ /* Unrecognized attribute in the URI query SHOULD be ignored */ ++ verbose("%s: Unknown part of query in PKCS#11 URI: %s", ++ __func__, tok); ++ } ++ } ++out: ++ free(p); ++ return rv; ++} ++ ++#endif /* ENABLE_PKCS11 */ +diff --git a/ssh-pkcs11-uri.h b/ssh-pkcs11-uri.h +new file mode 100644 +index 00000000..942a5a5a +--- /dev/null ++++ b/ssh-pkcs11-uri.h +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2017 Red Hat ++ * ++ * Authors: Jakub Jelen ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#define PKCS11_URI_SCHEME "pkcs11:" ++#define PKCS11_URI_WHITELIST "abcdefghijklmnopqrstuvwxyz" \ ++ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ ++ "0123456789_-.()" ++ ++struct pkcs11_uri { ++ /* path */ ++ char *id; ++ size_t id_len; ++ char *token; ++ char *object; ++ char *lib_manuf; ++ char *manuf; ++ /* query */ ++ char *module_path; ++ 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/ssh-pkcs11.c b/ssh-pkcs11.c +index 70f06bff..59332945 100644 +--- a/ssh-pkcs11.c ++++ b/ssh-pkcs11.c +@@ -54,8 +54,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; +@@ -64,6 +64,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; + }; + +@@ -74,6 +81,7 @@ struct pkcs11_key { + CK_ULONG slotidx; + char *keyid; + int keyid_len; ++ char *label; + }; + + int pkcs11_interactive = 0; +@@ -106,26 +114,63 @@ pkcs11_init(int interactive) + * this is called when a provider gets unregistered. + */ + static void +-pkcs11_provider_finalize(struct pkcs11_provider *p) ++pkcs11_module_finalize(struct pkcs11_module *m) + { + CK_RV rv; + CK_ULONG i; + +- debug("pkcs11_provider_finalize: %p refcount %d valid %d", +- p, p->refcount, p->valid); +- if (!p->valid) ++ debug("%s: %p refcount %d valid %d", __func__, ++ m, m->refcount, m->valid); ++ if (!m->valid) + return; +- for (i = 0; i < p->nslots; i++) { +- if (p->slotinfo[i].session && +- (rv = p->function_list->C_CloseSession( +- p->slotinfo[i].session)) != CKR_OK) ++ for (i = 0; i < m->nslots; i++) { ++ if (m->slotinfo[i].session && ++ (rv = m->function_list->C_CloseSession( ++ m->slotinfo[i].session)) != CKR_OK) + error("C_CloseSession failed: %lu", rv); + } +- if ((rv = p->function_list->C_Finalize(NULL)) != CKR_OK) ++ if ((rv = m->function_list->C_Finalize(NULL)) != CKR_OK) + error("C_Finalize failed: %lu", rv); ++ m->valid = 0; ++ m->function_list = NULL; ++ dlclose(m->handle); ++} ++ ++/* ++ * remove a reference to the pkcs11 module. ++ * called when a provider is unregistered. ++ */ ++static void ++pkcs11_module_unref(struct pkcs11_module *m) ++{ ++ debug("%s: %p refcount %d", __func__, m, m->refcount); ++ if (--m->refcount <= 0) { ++ pkcs11_module_finalize(m); ++ if (m->valid) ++ error("%s: %p still valid", __func__, m); ++ free(m->slotlist); ++ free(m->slotinfo); ++ free(m->module_path); ++ free(m); ++ } ++} ++ ++/* ++ * finalize a provider shared libarary, it's no longer usable. ++ * however, there might still be keys referencing this provider, ++ * so the actuall freeing of memory is handled by pkcs11_provider_unref(). ++ * this is called when a provider gets unregistered. ++ */ ++static void ++pkcs11_provider_finalize(struct pkcs11_provider *p) ++{ ++ debug("%s: %p refcount %d valid %d", __func__, ++ p, p->refcount, p->valid); ++ if (!p->valid) ++ return; ++ pkcs11_module_unref(p->module); ++ p->module = NULL; + p->valid = 0; +- p->function_list = NULL; +- dlclose(p->handle); + } + + /* +@@ -135,13 +180,11 @@ pkcs11_provider_finalize(struct pkcs11_provider *p) + static void + pkcs11_provider_unref(struct pkcs11_provider *p) + { +- debug("pkcs11_provider_unref: %p refcount %d", p, p->refcount); ++ debug("%s: %p refcount %d", __func__, p, p->refcount); + if (--p->refcount <= 0) { +- if (p->valid) +- error("pkcs11_provider_unref: %p still valid", p); + free(p->name); +- free(p->slotlist); +- free(p->slotinfo); ++ if (p->module) ++ pkcs11_module_unref(p->module); + free(p); + } + } +@@ -159,6 +202,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 +230,52 @@ pkcs11_provider_lookup(char *provider_id) + return (NULL); + } + ++int pkcs11_del_provider_by_uri(struct pkcs11_uri *); ++ + /* unregister provider by name */ + int + pkcs11_del_provider(char *provider_id) ++{ ++ int rv; ++ struct pkcs11_uri *uri; ++ ++ debug("%s: called, provider_id = %s", __func__, provider_id); ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ rv = pkcs11_del_provider_by_uri(uri); ++ pkcs11_uri_cleanup(uri); ++ return rv; ++} ++ ++/* unregister provider by PKCS#11 URI */ ++int ++pkcs11_del_provider_by_uri(struct pkcs11_uri *uri) + { + struct pkcs11_provider *p; ++ int rv = -1; ++ char *provider_uri = pkcs11_uri_get(uri); + +- if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { ++ debug3("%s(%s): called", __func__, provider_uri); ++ ++ if ((p = pkcs11_provider_lookup(provider_uri)) != NULL) { + TAILQ_REMOVE(&pkcs11_providers, p, next); + pkcs11_provider_finalize(p); + pkcs11_provider_unref(p); +- return (0); ++ rv = 0; + } +- return (-1); ++ free(provider_uri); ++ return rv; + } + + static RSA_METHOD *rsa_method; +@@ -195,6 +285,55 @@ static EC_KEY_METHOD *ec_key_method; + static int ec_key_idx = 0; + #endif + ++/* ++ * 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 +347,7 @@ pkcs11_k11_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, int idx, + if (k11->provider) + pkcs11_provider_unref(k11->provider); + free(k11->keyid); ++ free(k11->label); + free(k11); + } + +@@ -222,8 +362,8 @@ pkcs11_find(struct pkcs11_provider *p, CK_ULONG slotidx, CK_ATTRIBUTE *attr, + CK_RV rv; + int ret = -1; + +- f = p->function_list; +- session = p->slotinfo[slotidx].session; ++ f = p->module->function_list; ++ session = p->module->slotinfo[slotidx].session; + if ((rv = f->C_FindObjectsInit(session, attr, nattr)) != CKR_OK) { + error("C_FindObjectsInit failed (nattr %lu): %lu", nattr, rv); + return (-1); +@@ -252,8 +392,8 @@ pkcs11_login(struct pkcs11_key *k11, CK_USER_TYPE type) + 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 (!pkcs11_interactive) { + error("need pin entry%s", +@@ -300,8 +440,8 @@ pkcs11_check_obj_bool_attrib(struct pkcs11_key *k11, CK_OBJECT_HANDLE obj, + 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; +@@ -332,13 +472,14 @@ pkcs11_get_key(struct pkcs11_key *k11, CK_MECHANISM_TYPE mech_type) + 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) { +@@ -415,8 +556,8 @@ pkcs11_rsa_private_encrypt(int flen, const u_char *from, u_char *to, RSA *rsa, + return (-1); + } + +- f = k11->provider->function_list; +- si = &k11->provider->slotinfo[k11->slotidx]; ++ f = k11->provider->module->function_list; ++ si = &k11->provider->module->slotinfo[k11->slotidx]; + tlen = RSA_size(rsa); + + /* XXX handle CKR_BUFFER_TOO_SMALL */ +@@ -460,7 +601,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; + +@@ -478,6 +619,12 @@ pkcs11_rsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + 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); +@@ -508,8 +655,8 @@ ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, + 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); +@@ -574,7 +721,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; + +@@ -590,6 +737,12 @@ pkcs11_ecdsa_wrap(struct pkcs11_provider *provider, CK_ULONG slotidx, + k11->keyid = xmalloc(k11->keyid_len); + memcpy(k11->keyid, keyid_attrib->pValue, k11->keyid_len); + ++ if (label_attrib->ulValueLen > 0 ) { ++ k11->label = xmalloc(label_attrib->ulValueLen+1); ++ memcpy(k11->label, label_attrib->pValue, label_attrib->ulValueLen); ++ k11->label[label_attrib->ulValueLen] = 0; ++ } ++ + EC_KEY_set_method(ec, ec_key_method); + EC_KEY_set_ex_data(ec, ec_key_idx, k11); + +@@ -624,47 +777,26 @@ pkcs11_open_session(struct pkcs11_provider *p, CK_ULONG slotidx, char *pin, + CK_FUNCTION_LIST *f; + CK_RV rv; + CK_SESSION_HANDLE session; +- int login_required, have_pinpad, ret; +- char prompt[1024], *xpin = NULL; ++ int login_required, ret; + +- f = p->function_list; +- si = &p->slotinfo[slotidx]; ++ f = p->module->function_list; ++ si = &p->module->slotinfo[slotidx]; + +- have_pinpad = si->token.flags & CKF_PROTECTED_AUTHENTICATION_PATH; + login_required = si->token.flags & CKF_LOGIN_REQUIRED; + + /* fail early before opening session */ +- if (login_required && !have_pinpad && !pkcs11_interactive && ++ if (login_required && !pkcs11_interactive && + (pin == NULL || strlen(pin) == 0)) { + 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) { +- if (have_pinpad && (pin == NULL || strlen(pin) == 0)) { +- /* defer PIN entry to the reader keypad */ +- rv = f->C_Login(session, CKU_USER, NULL_PTR, 0); +- } else { +- if (pkcs11_interactive) { +- snprintf(prompt, sizeof(prompt), +- "Enter PIN for '%s': ", si->token.label); +- if ((xpin = read_passphrase(prompt, +- RP_ALLOW_EOF)) == NULL) { +- debug("%s: no pin specified", +- __func__); +- return (-SSH_PKCS11_ERR_PIN_REQUIRED); +- } +- pin = xpin; +- } +- rv = f->C_Login(session, CKU_USER, +- (u_char *)pin, strlen(pin)); +- if (xpin != NULL) +- freezero(xpin, strlen(xpin)); +- } ++ if (login_required && pin != NULL && strlen(pin) != 0) { ++ rv = f->C_Login(session, user, (u_char *)pin, strlen(pin)); + if (rv != CKR_OK && rv != CKR_USER_ALREADY_LOGGED_IN) { + error("C_Login failed: %lu", rv); + ret = (rv == CKR_PIN_LOCKED) ? +@@ -696,7 +828,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; +@@ -710,14 +843,15 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + 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); +@@ -729,18 +863,19 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * XXX assumes CKA_ID is always first. + */ + if (key_attr[1].ulValueLen == 0 || +- key_attr[2].ulValueLen == 0) { ++ 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; +@@ -752,8 +887,8 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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; +@@ -764,13 +899,13 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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; +@@ -787,7 +922,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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); +@@ -803,7 +938,7 @@ pkcs11_fetch_ecdsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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); +@@ -820,7 +955,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; +@@ -831,14 +967,15 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + 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); +@@ -850,18 +987,19 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * XXX assumes CKA_ID is always first. + */ + if (key_attr[1].ulValueLen == 0 || +- key_attr[2].ulValueLen == 0) { ++ 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; +@@ -873,8 +1011,8 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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; +@@ -883,7 +1021,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + fatal("%s: set key", __func__); + 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); +@@ -898,7 +1036,7 @@ pkcs11_fetch_rsa_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + 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); + +@@ -909,7 +1047,8 @@ static struct sshkey * + pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + CK_OBJECT_HANDLE *obj) + { +- 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; +@@ -926,14 +1065,15 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + + 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 (NULL); +@@ -945,18 +1085,19 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + * 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 (NULL); + } + + /* 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 fail; +@@ -968,8 +1109,8 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- cp = cert_attr[2].pValue; +- if (d2i_X509(&x509, &cp, cert_attr[2].ulValueLen) == NULL) { ++ cp = cert_attr[3].pValue; ++ if (d2i_X509(&x509, &cp, cert_attr[3].ulValueLen) == NULL) { + error("d2i_x509 failed"); + goto fail; + } +@@ -990,7 +1131,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], rsa)) ++ if (pkcs11_rsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], rsa)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -1020,7 +1161,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + goto fail; + } + +- if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], ec)) ++ if (pkcs11_ecdsa_wrap(p, slotidx, &cert_attr[0], &cert_attr[1], ec)) + goto fail; + + key = sshkey_new(KEY_UNSPEC); +@@ -1039,7 +1180,7 @@ pkcs11_fetch_x509_pubkey(struct pkcs11_provider *p, CK_ULONG slotidx, + error("unknown certificate key type"); + + fail: +- for (i = 0; i < 3; i++) ++ for (i = 0; i < nattr; i++) + free(cert_attr[i].pValue); + X509_free(x509); + RSA_free(rsa); +@@ -1066,11 +1207,12 @@ have_rsa_key(const RSA *rsa) + */ + static int + pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, 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; +@@ -1086,10 +1228,23 @@ pkcs11_fetch_certs(struct pkcs11_provider *p, CK_ULONG slotidx, + 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; +@@ -1163,11 +1318,12 @@ fail: + */ + static int + pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, +- struct sshkey ***keysp, int *nkeys) ++ struct sshkey ***keysp, int *nkeys, struct pkcs11_uri *uri) + { + 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; +@@ -1183,10 +1339,23 @@ pkcs11_fetch_keys(struct pkcs11_provider *p, CK_ULONG slotidx, + 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; +@@ -1443,15 +1612,10 @@ pkcs11_ecdsa_generate_private_key(struct pkcs11_provider *p, CK_ULONG slotidx, + } + #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, +- 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; +@@ -1460,148 +1624,285 @@ pkcs11_register_provider(char *provider_id, char *pin, struct sshkey ***keyp, + CK_FUNCTION_LIST *f = NULL; + CK_TOKEN_INFO *token; + CK_ULONG i; +- +- if (providerp == NULL) ++ char *provider_module = NULL; ++ struct pkcs11_module *m; ++ ++ /* if no provider specified, fallback to p11-kit */ ++ if (uri->module_path == NULL) { ++#ifdef PKCS11_DEFAULT_PROVIDER ++ provider_module = strdup(PKCS11_DEFAULT_PROVIDER); ++#else ++ error("%s: No module path provided", __func__); + goto fail; +- *providerp = NULL; +- +- if (keyp != NULL) +- *keyp = NULL; ++#endif ++ } else { ++ provider_module = strdup(uri->module_path); ++ } + +- if (pkcs11_provider_lookup(provider_id) != NULL) { +- debug("%s: provider already registered: %s", +- __func__, provider_id); +- goto fail; ++ p = xcalloc(1, sizeof(*p)); ++ p->name = pkcs11_uri_get(uri); ++ ++ if ((m = pkcs11_provider_lookup_module(provider_module)) != NULL ++ && m->valid) { ++ debug("%s: provider module already initialized: %s", ++ __func__, provider_module); ++ free(provider_module); ++ /* Skip the initialization of PKCS#11 module */ ++ m->refcount++; ++ p->module = m; ++ p->valid = 1; ++ TAILQ_INSERT_TAIL(&pkcs11_providers, p, next); ++ p->refcount++; /* add to provider list */ ++ *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(p->info.manufacturerID, sizeof(p->info.manufacturerID)); +- rmspace(p->info.libraryDescription, sizeof(p->info.libraryDescription)); ++ rmspace(m->info.manufacturerID, sizeof(m->info.manufacturerID)); ++ if (uri->lib_manuf != NULL && ++ strcmp(uri->lib_manuf, m->info.manufacturerID)) { ++ debug("%s: Skipping provider %s not matching library_manufacturer", ++ __func__, m->info.manufacturerID); ++ goto fail; ++ } ++ rmspace(m->info.libraryDescription, sizeof(m->info.libraryDescription)); + debug("provider %s: manufacturerID <%s> cryptokiVersion %d.%d" + " libraryDescription <%s> libraryVersion %d.%d", +- provider_id, +- p->info.manufacturerID, +- p->info.cryptokiVersion.major, +- p->info.cryptokiVersion.minor, +- p->info.libraryDescription, +- p->info.libraryVersion.major, +- p->info.libraryVersion.minor); +- if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &p->nslots)) != CKR_OK) { ++ provider_module, ++ m->info.manufacturerID, ++ m->info.cryptokiVersion.major, ++ m->info.cryptokiVersion.minor, ++ m->info.libraryDescription, ++ m->info.libraryVersion.major, ++ m->info.libraryVersion.minor); ++ ++ if ((rv = f->C_GetSlotList(CK_TRUE, NULL, &m->nslots)) != CKR_OK) { + error("C_GetSlotList failed: %lu", rv); + goto fail; + } +- if (p->nslots == 0) { ++ if (m->nslots == 0) { + error("%s: provider %s returned no slots", __func__, +- provider_id); ++ 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, (unsigned long)i, rv); ++ "failed: %lu", provider_module, (unsigned 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, 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); ++ nkeys = 0; ++ for (i = 0; i < p->module->nslots; i++) { ++ token = &p->module->slotinfo[i].token; + if ((token->flags & CKF_TOKEN_INITIALIZED) == 0) { + debug2("%s: ignoring uninitialised token in " + "provider %s slot %lu", __func__, +- provider_id, (unsigned long)i); ++ provider_uri, (unsigned long)i); ++ continue; ++ } ++ if (uri->token != NULL && ++ strcmp(token->label, uri->token) != 0) { ++ debug2("%s: ignoring token not matching label (%s) " ++ "specified by PKCS#11 URI in slot %lu", __func__, ++ token->label, (unsigned long)i); ++ continue; ++ } ++ if (uri->manuf != NULL && ++ strcmp(token->manufacturerID, uri->manuf) != 0) { ++ debug2("%s: ignoring token not matching requrested " ++ "manufacturerID (%s) specified by PKCS#11 URI in " ++ "slot %lu", __func__, ++ token->manufacturerID, (unsigned long)i); + continue; + } +- rmspace(token->label, sizeof(token->label)); +- rmspace(token->manufacturerID, sizeof(token->manufacturerID)); +- rmspace(token->model, sizeof(token->model)); +- rmspace(token->serialNumber, sizeof(token->serialNumber)); + 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); ++ if (pin == NULL && uri->pin != NULL) { ++ pin = uri->pin; ++ } + /* +- * 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) { + if (keyp == NULL) + continue; +- pkcs11_fetch_keys(p, i, keyp, &nkeys); +- pkcs11_fetch_certs(p, i, keyp, &nkeys); ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); ++ pkcs11_fetch_certs(p, i, keyp, &nkeys, uri); ++ if (nkeys == 0 && uri->object != NULL) { ++ debug3("%s: No keys found. Retrying without label (%s) ", ++ __func__, token->label); ++ /* Try once more without the label filter */ ++ char *label = uri->object; ++ uri->object = NULL; /* XXX clone uri? */ ++ pkcs11_fetch_keys(p, i, keyp, &nkeys, uri); ++ pkcs11_fetch_certs(p, i, keyp, &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); + 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, ++ struct pkcs11_provider **providerp, CK_ULONG user) ++{ ++ struct pkcs11_uri *uri = NULL; ++ int r; ++ ++ debug("%s: called, provider_id = %s", __func__, 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, providerp, user); ++ pkcs11_uri_cleanup(uri); ++ ++ return r; ++} ++ + int +-pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) ++pkcs11_add_provider_by_uri(struct pkcs11_uri *uri, char *pin, ++ struct sshkey ***keyp) + { +- struct pkcs11_provider *p = NULL; + int nkeys; ++ struct pkcs11_provider *p = NULL; ++ char *provider_uri = pkcs11_uri_get(uri); + +- nkeys = pkcs11_register_provider(provider_id, pin, keyp, &p, CKU_USER); ++ debug("%s: called, provider_uri = %s", __func__, provider_uri); ++ ++ nkeys = pkcs11_register_provider_by_uri(uri, pin, keyp, &p, CKU_USER); + + /* no keys found or some other error, de-register provider */ + if (nkeys <= 0 && p != NULL) { +@@ -1611,7 +1912,36 @@ pkcs11_add_provider(char *provider_id, char *pin, struct sshkey ***keyp) + } + if (nkeys == 0) + debug("%s: provider %s returned no keys", __func__, +- provider_id); ++ 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) ++{ ++ struct pkcs11_uri *uri; ++ int nkeys; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (strlen(provider_id) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(provider_id, PKCS11_URI_SCHEME, strlen(PKCS11_URI_SCHEME)) == 0) { ++ if (pkcs11_uri_parse(provider_id, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI"); ++ } else { ++ uri->module_path = strdup(provider_id); ++ } ++ ++ nkeys = pkcs11_add_provider_by_uri(uri, pin, keyp); ++ pkcs11_uri_cleanup(uri); + + return (nkeys); + } +@@ -1633,8 +1963,7 @@ pkcs11_gakp(char *provider_id, char *pin, unsigned int slotidx, char *label, + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) + debug("%s: provider \"%s\" available", __func__, provider_id); +- else if ((ret = pkcs11_register_provider(provider_id, pin, NULL, &p, +- CKU_SO)) < 0) { ++ else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) { + debug("%s: could not register provider %s", __func__, + provider_id); + goto out; +@@ -1705,8 +2034,7 @@ pkcs11_destroy_keypair(char *provider_id, char *pin, unsigned long slotidx, + + if ((p = pkcs11_provider_lookup(provider_id)) != NULL) { + debug("%s: using provider \"%s\"", __func__, provider_id); +- } else if (pkcs11_register_provider(provider_id, pin, NULL, &p, +- CKU_SO) < 0) { ++ } else if ((rv = pkcs11_register_provider(provider_id, pin, NULL, &p, CKU_SO)) != 0) { + debug("%s: could not register provider %s", __func__, + provider_id); + goto out; +diff --git a/ssh-pkcs11.h b/ssh-pkcs11.h +index b9038450..5a855338 100644 +--- a/ssh-pkcs11.h ++++ b/ssh-pkcs11.h +@@ -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 ***); ++int pkcs11_add_provider_by_uri(struct pkcs11_uri *, char *, struct sshkey ***); + int pkcs11_del_provider(char *); ++int pkcs11_uri_write(const struct sshkey *, FILE *); + #ifdef WITH_PKCS11_KEYGEN + struct sshkey * + pkcs11_gakp(char *, char *, unsigned int, char *, unsigned int, +diff --git a/ssh.c b/ssh.c +index 91e7c351..47f4f299 100644 +--- a/ssh.c ++++ b/ssh.c +@@ -772,6 +772,14 @@ main(int ac, char **av) + options.gss_deleg_creds = 1; + break; + case 'i': ++#ifdef ENABLE_PKCS11 ++ if (strlen(optarg) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(optarg, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ add_identity_file(&options, NULL, optarg, 1); ++ break; ++ } ++#endif + p = tilde_expand_filename(optarg, getuid()); + if (stat(p, &st) < 0) + fprintf(stderr, "Warning: Identity file %s " +@@ -1521,6 +1529,7 @@ main(int ac, char **av) + free(options.certificate_files[i]); + options.certificate_files[i] = NULL; + } ++ pkcs11_terminate(); + + skip_connect: + exit_status = ssh_session2(ssh, pw); +@@ -1994,6 +2003,45 @@ ssh_session2(struct ssh *ssh, struct passwd *pw) + options.escape_char : SSH_ESCAPECHAR_NONE, id); + } + ++#ifdef ENABLE_PKCS11 ++static void ++load_pkcs11_identity(char *pkcs11_uri, char *identity_files[], ++ struct sshkey *identity_keys[], int *n_ids) ++{ ++ int nkeys, i; ++ struct sshkey **keys; ++ struct pkcs11_uri *uri; ++ ++ debug("identity file '%s' from pkcs#11", pkcs11_uri); ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ if (pkcs11_uri_parse(pkcs11_uri, uri) != 0) ++ fatal("Failed to parse PKCS#11 URI %s", pkcs11_uri); ++ ++ /* we need to merge URI and provider together */ ++ if (options.pkcs11_provider != NULL && uri->module_path == NULL) ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ if (options.num_identity_files < SSH_MAX_IDENTITY_FILES && ++ (nkeys = pkcs11_add_provider_by_uri(uri, NULL, &keys)) > 0) { ++ for (i = 0; i < nkeys; i++) { ++ if (*n_ids >= SSH_MAX_IDENTITY_FILES) { ++ sshkey_free(keys[i]); ++ continue; ++ } ++ identity_keys[*n_ids] = keys[i]; ++ identity_files[*n_ids] = pkcs11_uri_get(uri); ++ (*n_ids)++; ++ } ++ free(keys); ++ } ++ ++ pkcs11_uri_cleanup(uri); ++} ++#endif /* ENABLE_PKCS11 */ ++ + /* Loads all IdentityFile and CertificateFile keys */ + static void + load_public_identity_files(struct passwd *pw) +@@ -2008,10 +2056,6 @@ load_public_identity_files(struct passwd *pw) + char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; + struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; + int certificate_file_userprovided[SSH_MAX_CERTIFICATE_FILES]; +-#ifdef ENABLE_PKCS11 +- struct sshkey **keys; +- int nkeys; +-#endif /* PKCS11 */ + + n_ids = n_certs = 0; + memset(identity_files, 0, sizeof(identity_files)); +@@ -2024,32 +2068,46 @@ load_public_identity_files(struct passwd *pw) + sizeof(certificate_file_userprovided)); + + #ifdef ENABLE_PKCS11 +- if (options.pkcs11_provider != NULL && +- options.num_identity_files < SSH_MAX_IDENTITY_FILES && +- (pkcs11_init(!options.batch_mode) == 0) && +- (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, +- &keys)) > 0) { +- for (i = 0; i < nkeys; i++) { +- if (n_ids >= SSH_MAX_IDENTITY_FILES) { +- sshkey_free(keys[i]); +- continue; +- } +- identity_keys[n_ids] = keys[i]; +- identity_files[n_ids] = +- xstrdup(options.pkcs11_provider); /* XXX */ +- n_ids++; +- } +- free(keys); ++ /* handle fallback from PKCS11Provider option */ ++ pkcs11_init(!options.batch_mode); ++ ++ if (options.pkcs11_provider != NULL) { ++ struct pkcs11_uri *uri; ++ ++ uri = pkcs11_uri_init(); ++ if (uri == NULL) ++ fatal("Failed to init PCKS#11 URI"); ++ ++ /* Construct simple PKCS#11 URI to simplify access */ ++ uri->module_path = strdup(options.pkcs11_provider); ++ ++ /* Add it as any other IdentityFile */ ++ cp = pkcs11_uri_get(uri); ++ add_identity_file(&options, NULL, cp, 1); ++ free(cp); ++ ++ pkcs11_uri_cleanup(uri); + } + #endif /* ENABLE_PKCS11 */ + for (i = 0; i < options.num_identity_files; i++) { ++ char *name = options.identity_files[i]; + if (n_ids >= SSH_MAX_IDENTITY_FILES || +- strcasecmp(options.identity_files[i], "none") == 0) { ++ strcasecmp(name, "none") == 0) { + free(options.identity_files[i]); + options.identity_files[i] = NULL; + continue; + } +- cp = tilde_expand_filename(options.identity_files[i], getuid()); ++#ifdef ENABLE_PKCS11 ++ if (strlen(name) >= strlen(PKCS11_URI_SCHEME) && ++ strncmp(name, PKCS11_URI_SCHEME, ++ strlen(PKCS11_URI_SCHEME)) == 0) { ++ load_pkcs11_identity(name, identity_files, ++ identity_keys, &n_ids); ++ free(options.identity_files[i]); ++ continue; ++ } ++#endif /* ENABLE_PKCS11 */ ++ cp = tilde_expand_filename(name, getuid()); + filename = percent_expand(cp, "d", pw->pw_dir, + "u", pw->pw_name, "l", thishost, "h", host, + "r", options.user, (char *)NULL); +diff --git a/ssh_config.5 b/ssh_config.5 +index 41262963..a211034e 100644 +--- a/ssh_config.5 ++++ b/ssh_config.5 +@@ -952,6 +952,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 --git a/openssh.spec b/openssh.spec index 7794d31..1d72e29 100644 --- a/openssh.spec +++ b/openssh.spec @@ -65,10 +65,10 @@ %endif # Do not forget to bump pam_ssh_agent_auth release if you rewind the main package release to 1 -%global openssh_ver 7.9p1 -%global openssh_rel 5 +%global openssh_ver 8.0p1 +%global openssh_rel 1 %global pam_ssh_agent_ver 0.10.3 -%global pam_ssh_agent_rel 6 +%global pam_ssh_agent_rel 7 Summary: An open source implementation of SSH protocol version 2 Name: openssh @@ -150,8 +150,6 @@ Patch702: openssh-5.1p1-askpass-progress.patch Patch703: openssh-4.3p2-askpass-grab-info.patch #https://bugzilla.mindrot.org/show_bug.cgi?id=1635 (WONTFIX) Patch707: openssh-7.7p1-redhat.patch -#https://bugzilla.mindrot.org/show_bug.cgi?id=1640 (WONTFIX) -Patch709: openssh-6.2p1-vendor.patch # warn users for unsupported UsePAM=no (#757545) Patch711: openssh-7.8p1-UsePAM-warning.patch # make aes-ctr ciphers use EVP engines such as AES-NI from OpenSSL @@ -161,29 +159,21 @@ Patch713: openssh-6.6p1-ctr-cavstest.patch # add SSH KDF CAVS test driver Patch714: openssh-6.7p1-kdf-cavs.patch - -#http://www.sxw.org.uk/computing/patches/openssh.html -#changed cache storage type - #848228 -Patch800: openssh-7.8p1-gsskex.patch +# GSSAPI Key Exchange (RFC 4462 + draft-ietf-curdle-gss-keyex-sha2-08) +# from https://github.com/openssh-gsskex/openssh-gsskex/tree/fedora/master +Patch800: openssh-8.0p1-gssapi-keyex.patch #http://www.mail-archive.com/kerberos@mit.edu/msg17591.html Patch801: openssh-6.6p1-force_krb.patch # add new option GSSAPIEnablek5users and disable using ~/.k5users by default (#1169843) # CVE-2014-9278 Patch802: openssh-6.6p1-GSSAPIEnablek5users.patch -# Documentation about GSSAPI -# from https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=765655 -Patch803: openssh-7.1p1-gssapi-documentation.patch # Improve ccache handling in openssh (#991186, #1199363, #1566494) # https://bugzilla.mindrot.org/show_bug.cgi?id=2775 Patch804: openssh-7.7p1-gssapi-new-unique.patch # Respect k5login_directory option in krk5.conf (#1328243) Patch805: openssh-7.2p2-k5login_directory.patch -# Support SHA2 in GSS key exchanges from draft-ssorce-gss-keyex-sha2-02 -Patch807: openssh-7.5p1-gssapi-kex-with-ec.patch -# Do not break when using AuthenticationMethods with gssapi-keyex auth method (#1625366) -Patch808: openssh-7.9p1-gsskex-method.patch -Patch900: openssh-6.1p1-gssapi-canohost.patch + #https://bugzilla.mindrot.org/show_bug.cgi?id=1780 Patch901: openssh-6.6p1-kuserok.patch # Use tty allocation for a remote scp (#985650) @@ -194,16 +184,12 @@ Patch916: openssh-6.6.1p1-selinux-contexts.patch Patch918: openssh-6.6.1p1-log-in-chroot.patch # scp file into non-existing directory (#1142223) Patch919: openssh-6.6.1p1-scp-non-existing-directory.patch -# Config parser shouldn't accept ip/port syntax (#1130733) -Patch920: openssh-7.8p1-ip-port-config-parser.patch # apply upstream patch and make sshd -T more consistent (#1187521) Patch922: openssh-6.8p1-sshdT-output.patch # Add sftp option to force mode of created files (#1191055) Patch926: openssh-6.7p1-sftp-force-permission.patch # Restore compatible default (#89216) Patch929: openssh-6.9p1-permit-root-login.patch -# Add GSSAPIKexAlgorithms option for server and client application -Patch932: openssh-7.0p1-gssKexAlgorithms.patch # make s390 use /dev/ crypto devices -- ignore closefrom Patch939: openssh-7.2p2-s390-closefrom.patch # Move MAX_DISPLAYS to a configuration option (#1341302) @@ -215,28 +201,13 @@ Patch949: openssh-7.6p1-cleanup-selinux.patch # Sandbox adjustments for s390 and audit Patch950: openssh-7.5p1-sandbox.patch # PKCS#11 URIs (upstream #2817, 2nd iteration) -Patch951: openssh-7.6p1-pkcs11-uri.patch -# PKCS#11 ECDSA keys (upstream #2474, 8th iteration) -Patch952: openssh-7.6p1-pkcs11-ecdsa.patch +Patch951: openssh-8.0p1-pkcs11-uri.patch # Unbreak scp between two IPv6 hosts (#1620333) Patch953: openssh-7.8p1-scp-ipv6.patch -# Allow to disable RSA signatures with SHA-1 in server -# https://bugzilla.mindrot.org/show_bug.cgi?id=2746 -Patch954: openssh-7.9p1-disable-sha1.patch -# Backport Match final so the crypto-policies do not break canonicalization (#1630166) -# https://bugzilla.mindrot.org/show_bug.cgi?id=2906 -Patch955: openssh-7.9p1-match-final.patch -# Backport more after-release fixes (#1665611) -Patch956: openssh-7.9p1-backports.patch -# Backport patch for CVE-2018-20685 (#1665786) -Patch957: openssh-7.9p1-CVE-2018-20685.patch # ssh-copy-id is unmaintained: Aggreagete patches # - do not return 0 if the write fails (full disk) # - shellcheck reports (upstream #2902) Patch958: openssh-7.9p1-ssh-copy-id.patch -# log when a client requests an interactive session and only sftp is allowed -# https://bugzilla.mindrot.org/show_bug.cgi?id=2960 -Patch959: openssh-7.9p1-log-sftp-only-connections.patch # Update cached passwd structure after PAM authentication (#1674541) Patch960: openssh-7.9p1-updated-cached-pw.patch @@ -416,7 +387,6 @@ popd %patch702 -p1 -b .progress %patch703 -p1 -b .grab-info %patch707 -p1 -b .redhat -%patch709 -p1 -b .vendor %patch711 -p1 -b .log-usepam-no %patch712 -p1 -b .evp-ctr %patch713 -p1 -b .ctr-cavs @@ -424,38 +394,26 @@ popd # %patch800 -p1 -b .gsskex %patch801 -p1 -b .force_krb -%patch803 -p1 -b .gss-docs %patch804 -p1 -b .ccache_name %patch805 -p1 -b .k5login # -%patch900 -p1 -b .canohost %patch901 -p1 -b .kuserok %patch906 -p1 -b .fromto-remote %patch916 -p1 -b .contexts %patch918 -p1 -b .log-in-chroot %patch919 -p1 -b .scp -%patch920 -p1 -b .config %patch802 -p1 -b .GSSAPIEnablek5users %patch922 -p1 -b .sshdt %patch926 -p1 -b .sftp-force-mode %patch929 -p1 -b .root-login -%patch932 -p1 -b .gsskexalg %patch939 -p1 -b .s390-dev %patch944 -p1 -b .x11max %patch948 -p1 -b .systemd -%patch807 -p1 -b .gsskex-ec %patch949 -p1 -b .refactor %patch950 -p1 -b .sandbox %patch951 -p1 -b .pkcs11-uri -%patch952 -p1 -b .pkcs11-ecdsa %patch953 -p1 -b .scp-ipv6 -%patch808 -p1 -b .gsskex-method -%patch954 -p1 -b .disable-sha1 -%patch955 -p1 -b .match-final -%patch956 -p1 -b .backports -%patch957 -p1 -b .CVE-2018-20685 %patch958 -p1 -b .ssh-copy-id -%patch959 -p1 -b .log-sftp-only %patch960 -p1 -b .update-pw %patch200 -p1 -b .audit @@ -512,7 +470,6 @@ fi --with-default-path=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin \ --with-superuser-path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin \ --with-privsep-path=%{_var}/empty/sshd \ - --enable-vendor-patchlevel="FC-%{openssh_ver}-%{openssh_rel}" \ --disable-strip \ --without-zlib-version-check \ --with-ssl-engine \ @@ -760,6 +717,12 @@ getent passwd sshd >/dev/null || \ %endif %changelog +* Fri Apr 26 2019 Jakub Jelen - 8.0p1-1 + 0.10.3-7 +- New upstream release (#1701072) +- Removed support for VendroPatchLevel configuration option +- Significant rework of GSSAPI Key Exchange +- Significant rework of PKCS#11 URI support + * Mon Mar 11 2019 Jakub Jelen - 7.9p1-5 + 0.10.3.6 - Fix kerberos cleanup procedures with GSSAPI - Update cached passwd structure after PAM authentication diff --git a/sources b/sources index 61a7b55..849fc69 100644 --- a/sources +++ b/sources @@ -1,4 +1,4 @@ -SHA512 (openssh-7.9p1.tar.gz) = 0412c9c429c9287f0794023951469c8e6ec833cdb55821bfa0300dd90d0879ff60484f620cffd93372641ab69bf0b032c2d700ccc680950892725fb631b7708e -SHA512 (openssh-7.9p1.tar.gz.asc) = 881db1b541813136fabd9adb9f5430c4f0fae372c06c99cb049feb8526a573275fe80c129c89511dd4e65f73f41e29364fefaaf8b7c78835224691c488d5da32 +SHA512 (openssh-8.0p1.tar.gz) = e280fa2d56f550efd37c5d2477670326261aa8b94d991f9eb17aad90e0c6c9c939efa90fe87d33260d0f709485cb05c379f0fd1bd44fc0d5190298b6398c9982 +SHA512 (openssh-8.0p1.tar.gz.asc) = fe9e7383d9467e869762864f2b719165d9a3f2c5316c07067df1d45fc7819bd2cb8ef758454865595688804a4c160dd3d3aaee4c5f887859555d2c7bb8c4592b SHA512 (DJM-GPG-KEY.gpg) = db1191ed9b6495999e05eed2ef863fb5179bdb63e94850f192dad68eed8579836f88fbcfffd9f28524fe1457aff8cd248ee3e0afc112c8f609b99a34b80ecc0d SHA512 (pam_ssh_agent_auth-0.10.3.tar.bz2) = d75062c4e46b0b011f46aed9704a99049995fea8b5115ff7ee26dad7e93cbcf54a8af7efc6b521109d77dc03c6f5284574d2e1b84c6829cec25610f24fb4bd66