diff --git a/openssh-7.2p2-disable_short_DH_parameters.patch b/openssh-7.2p2-disable_short_DH_parameters.patch index ddd6ae4..dbd4a1f 100644 --- a/openssh-7.2p2-disable_short_DH_parameters.patch +++ b/openssh-7.2p2-disable_short_DH_parameters.patch @@ -1,5 +1,5 @@ # HG changeset patch -# Parent c924f46e3639b3646e42dd7505c206d43d7180fa +# Parent c40dce555117c740f3df867e9fc2b07b64b3ad96 Raise minimal size of DH group parameters to 2048 bits like upstream did in 7.2. 1024b values are believed to be in breaking range for state adversaries @@ -101,7 +101,7 @@ diff --git a/openssh-7.2p2/kexgexc.c b/openssh-7.2p2/kexgexc.c goto out; if ((bits = BN_num_bits(p)) < 0 || (u_int)bits < kex->min || (u_int)bits > kex->max) { -+ if (bits < kex->min && bits >= DH_GRP_MIN_RFC) ++ if ((u_int)bits < kex->min && (u_int)bits >= DH_GRP_MIN_RFC) + logit("DH parameter offered by the server (%d bits) " + "is considered insecure. " + "You can lower the accepted the minimum " @@ -115,6 +115,61 @@ diff --git a/openssh-7.2p2/kexgexc.c b/openssh-7.2p2/kexgexc.c goto out; } p = g = NULL; /* belong to kex->dh now */ +diff --git a/openssh-7.2p2/kexgexs.c b/openssh-7.2p2/kexgexs.c +--- a/openssh-7.2p2/kexgexs.c ++++ b/openssh-7.2p2/kexgexs.c +@@ -49,16 +49,19 @@ + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" + #include "dispatch.h" + #include "ssherr.h" + #include "sshbuf.h" + ++/* import from dh.c */ ++extern int dh_grp_min; ++ + static int input_kex_dh_gex_request(int, u_int32_t, void *); + static int input_kex_dh_gex_init(int, u_int32_t, void *); + + int + kexgex_server(struct ssh *ssh) + { + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, + &input_kex_dh_gex_request); +@@ -78,23 +81,29 @@ input_kex_dh_gex_request(int type, u_int + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || + (r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_u32(ssh, &max)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; + kex->min = min; + kex->max = max; +- min = MAX(DH_GRP_MIN, min); ++ min = MAX(dh_grp_min, min); + max = MIN(DH_GRP_MAX, max); +- nbits = MAX(DH_GRP_MIN, nbits); ++ nbits = MAX(dh_grp_min, nbits); + nbits = MIN(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits) { ++ if (kex->nbits < kex->min && kex->nbits >= DH_GRP_MIN_RFC) ++ logit("DH parameter requested by the client (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted minimum " ++ "via the KexDHMin option.", ++ kex->nbits); + r = SSH_ERR_DH_GEX_OUT_OF_RANGE; + goto out; + } + + /* Contact privileged parent */ + kex->dh = PRIVSEP(choose_dh(min, nbits, max)); + if (kex->dh == NULL) { + sshpkt_disconnect(ssh, "no matching DH grp found"); diff --git a/openssh-7.2p2/readconf.c b/openssh-7.2p2/readconf.c --- a/openssh-7.2p2/readconf.c +++ b/openssh-7.2p2/readconf.c @@ -147,7 +202,7 @@ diff --git a/openssh-7.2p2/readconf.c b/openssh-7.2p2/readconf.c oVisualHostKey, - oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, + oKexAlgorithms, oKexDHMin, -+ oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, ++ oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, @@ -243,7 +298,7 @@ diff --git a/openssh-7.2p2/readconf.c b/openssh-7.2p2/readconf.c if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; + if (options->kex_dhmin == -1) -+ options->kex_dhmin = DH_GRP_MIN; ++ options->kex_dhmin = DH_GRP_MIN_RFC; + else { + options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC); + options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX); @@ -278,10 +333,199 @@ diff --git a/openssh-7.2p2/readconf.h b/openssh-7.2p2/readconf.h int escape_char; /* Escape character; -2 = none */ u_int num_system_hostfiles; /* Paths for /etc/ssh/ssh_known_hosts */ +diff --git a/openssh-7.2p2/servconf.c b/openssh-7.2p2/servconf.c +--- a/openssh-7.2p2/servconf.c ++++ b/openssh-7.2p2/servconf.c +@@ -52,16 +52,20 @@ + #include "channels.h" + #include "groupaccess.h" + #include "canohost.h" + #include "packet.h" + #include "hostfile.h" + #include "auth.h" + #include "myproposal.h" + #include "digest.h" ++#include "dh.h" ++ ++/* import from dh.c */ ++extern int dh_grp_min; + + static void add_listen_addr(ServerOptions *, char *, int); + static void add_one_listen_addr(ServerOptions *, char *, int); + + /* Use of privilege separation or not */ + extern int use_privsep; + extern Buffer cfg; + +@@ -134,16 +138,17 @@ initialize_server_options(ServerOptions + options->allow_agent_forwarding = -1; + options->num_allow_users = 0; + options->num_deny_users = 0; + options->num_allow_groups = 0; + options->num_deny_groups = 0; + options->ciphers = NULL; + options->macs = NULL; + options->kex_algorithms = NULL; ++ options->kex_dhmin = -1; + options->protocol = SSH_PROTO_UNKNOWN; + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; + options->num_subsystems = 0; + options->max_startups_begin = -1; + options->max_startups_rate = -1; + options->max_startups = -1; +@@ -199,16 +204,23 @@ fill_default_server_options(ServerOption + int i; + + /* Portable-specific options */ + if (options->use_pam == -1) + options->use_pam = 0; + if (options->use_pam_check_locks == -1) + options->use_pam_check_locks = 0; + ++ if (options->kex_dhmin == -1) ++ options->kex_dhmin = DH_GRP_MIN_RFC; ++ else { ++ options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX); ++ } ++ dh_grp_min = options->kex_dhmin; + /* Standard Options */ + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_2; + if (options->num_host_key_files == 0) { + /* fill default hostkeys for protocols */ + if (options->protocol & SSH_PROTO_1) + options->host_key_files[options->num_host_key_files++] = + _PATH_HOST_KEY_FILE; +@@ -423,17 +435,18 @@ typedef enum { + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, + sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, +- sKexAlgorithms, sIPQoS, sVersionAddendum, ++ sKexAlgorithms, sKexDHMin, ++ sIPQoS, sVersionAddendum, + sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, + sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, + sStreamLocalBindMask, sStreamLocalBindUnlink, + sAllowStreamLocalForwarding, sFingerprintHash, + sDeprecated, sUnsupported + } ServerOpCodes; + + #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ +@@ -561,16 +574,17 @@ static struct { + { "permitopen", sPermitOpen, SSHCFG_ALL }, + { "forcecommand", sForceCommand, SSHCFG_ALL }, + { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, + { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, + { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, + { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, + { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, + { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, ++ { "kexdhmin", sKexDHMin }, + { "ipqos", sIPQoS, SSHCFG_ALL }, + { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, + { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, + { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL }, + { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL }, + { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, + { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, + { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, +@@ -1481,16 +1495,20 @@ process_server_config_line(ServerOptions + filename, linenum); + if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) + fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + if (options->kex_algorithms == NULL) + options->kex_algorithms = xstrdup(arg); + break; + ++ case sKexDHMin: ++ intptr = &options->kex_dhmin; ++ goto parse_int; ++ + case sProtocol: + intptr = &options->protocol; + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); + value = proto_spec(arg); + if (value == SSH_PROTO_UNKNOWN) + fatal("%s line %d: Bad protocol spec '%s'.", +@@ -2247,16 +2265,17 @@ dump_config(ServerOptions *o) + dump_cfg_int(sLoginGraceTime, o->login_grace_time); + dump_cfg_int(sKeyRegenerationTime, o->key_regeneration_time); + dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); + dump_cfg_int(sMaxAuthTries, o->max_authtries); + dump_cfg_int(sMaxSessions, o->max_sessions); + dump_cfg_int(sClientAliveInterval, o->client_alive_interval); + dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); + dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask); ++ dump_cfg_int(sKexDHMin, o->kex_dhmin); + + /* formatted integer arguments */ + dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); + dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); + dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); + dump_cfg_fmtint(sRhostsRSAAuthentication, o->rhosts_rsa_authentication); + dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); + dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, +diff --git a/openssh-7.2p2/servconf.h b/openssh-7.2p2/servconf.h +--- a/openssh-7.2p2/servconf.h ++++ b/openssh-7.2p2/servconf.h +@@ -88,16 +88,17 @@ typedef struct { + int permit_user_rc; /* If false, deny ~/.ssh/rc execution */ + int strict_modes; /* If true, require string home dir modes. */ + int tcp_keep_alive; /* If true, set SO_KEEPALIVE. */ + int ip_qos_interactive; /* IP ToS/DSCP/class for interactive */ + int ip_qos_bulk; /* IP ToS/DSCP/class for bulk traffic */ + char *ciphers; /* Supported SSH2 ciphers. */ + char *macs; /* Supported SSH2 macs. */ + char *kex_algorithms; /* SSH2 kex methods in order of preference. */ ++ int kex_dhmin; /* minimum bit length of the DH group parameter */ + int protocol; /* Supported protocol versions. */ + struct ForwardOptions fwd_opts; /* forwarding options */ + SyslogFacility log_facility; /* Facility for system logging. */ + LogLevel log_level; /* Level for system logging. */ + int rhosts_rsa_authentication; /* If true, permit rhosts RSA + * authentication. */ + int hostbased_authentication; /* If true, permit ssh2 hostbased auth */ + int hostbased_uses_name_from_packet_only; /* experimental */ +diff --git a/openssh-7.2p2/ssh_config b/openssh-7.2p2/ssh_config +--- a/openssh-7.2p2/ssh_config ++++ b/openssh-7.2p2/ssh_config +@@ -12,16 +12,21 @@ + # Any configuration value is only changed the first time it is set. + # Thus, host-specific definitions should be at the beginning of the + # configuration file, and defaults at the end. + + # Site-wide defaults for some commonly used options. For a comprehensive + # list of available options, their meanings and defaults, please see the + # ssh_config(5) man page. + ++# Minimum accepted size of the DH parameter p. By default this is set to 1024 ++# to maintain compatibility with RFC4419, but should be set higher. ++# Upstream default is identical to setting this to 2048. ++#KexDHMin 1024 ++ + Host * + # ForwardAgent no + # ForwardX11 no + + # If you do not trust your remote host (or its administrator), you + # should not forward X11 connections to your local X11-display for + # security reasons: Someone stealing the authentification data on the + # remote side (the "spoofed" X-server by the remote sshd) can read your diff --git a/openssh-7.2p2/ssh_config.0 b/openssh-7.2p2/ssh_config.0 --- a/openssh-7.2p2/ssh_config.0 +++ b/openssh-7.2p2/ssh_config.0 -@@ -606,16 +606,29 @@ DESCRIPTION +@@ -606,16 +606,33 @@ DESCRIPTION ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group-exchange-sha1, @@ -291,17 +535,21 @@ diff --git a/openssh-7.2p2/ssh_config.0 b/openssh-7.2p2/ssh_config.0 obtained using the -Q option of ssh(1) with an argument of M-bM-^@M-^\kexM-bM-^@M-^]. + KexDHMin -+ Specifies the minimum accepted bit length of the DH group parameter p. -+ As per RFC4419, this is 1024 bits however, this has increasingly ++ Specifies the minimum accepted bit length of the DH group ++ parameter p. ++ ++ As per RFC4419, this is 1024 bits, however this has increasingly + been seen as insecure, which prompted the change to 2048 bits. + Setting this option allows the client to accept parameters shorter + than the current minimum, down to the RFC specified 1024 bits. + Using this option may be needed when connecting to servers that + only know short DH group parameters. -+ -+ Note that using this option can severly impact security and thus -+ should be viewed as a temporary fix of last resort and all efforts -+ should be made to fix the server. ++ ++ Note, that while by default this option is set to 1024 to maintain ++ maximum backward compatibility, using it can severly impact ++ security and thus should be viewed as a temporary fix of last ++ resort and all efforts should be made to fix the (broken) ++ counterparty. + LocalCommand Specifies a command to execute on the local machine after @@ -314,7 +562,7 @@ diff --git a/openssh-7.2p2/ssh_config.0 b/openssh-7.2p2/ssh_config.0 diff --git a/openssh-7.2p2/ssh_config.5 b/openssh-7.2p2/ssh_config.5 --- a/openssh-7.2p2/ssh_config.5 +++ b/openssh-7.2p2/ssh_config.5 -@@ -1092,16 +1092,28 @@ diffie-hellman-group14-sha1 +@@ -1092,16 +1092,32 @@ diffie-hellman-group14-sha1 .Ed .Pp The list of available key exchange algorithms may also be obtained using the @@ -324,17 +572,21 @@ diff --git a/openssh-7.2p2/ssh_config.5 b/openssh-7.2p2/ssh_config.5 with an argument of .Dq kex . +.It Cm KexDHMin -+Specifies the minimum accepted bit length of the DH group parameter p. -+As per RFC4419, this is 1024 bits however, this has increasingly ++Specifies the minimum accepted bit length of the DH group ++parameter p. ++.Pp ++As per RFC4419, this is 1024 bits, however this has increasingly +been seen as insecure, which prompted the change to 2048 bits. +Setting this option allows the client to accept parameters shorter +than the current minimum, down to the RFC specified 1024 bits. +Using this option may be needed when connecting to servers that +only know short DH group parameters. -+ -+Note that using this option can severly impact security and thus -+should be viewed as a temporary fix of last resort and all efforts -+should be made to fix the server. ++.Pp ++Note, that while by default this option is set to 1024 to maintain ++maximum backward compatibility, using it can severly impact ++security and thus should be viewed as a temporary fix of last ++resort and all efforts should be made to fix the (broken) ++counterparty. .It Cm LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. @@ -343,3 +595,101 @@ diff --git a/openssh-7.2p2/ssh_config.5 b/openssh-7.2p2/ssh_config.5 The following escape character substitutions will be performed: .Ql %d (local user's home directory), +diff --git a/openssh-7.2p2/sshd_config b/openssh-7.2p2/sshd_config +--- a/openssh-7.2p2/sshd_config ++++ b/openssh-7.2p2/sshd_config +@@ -21,16 +21,21 @@ + # HostKey for protocol version 1 + #HostKey /etc/ssh/ssh_host_key + # HostKeys for protocol version 2 + #HostKey /etc/ssh/ssh_host_rsa_key + #HostKey /etc/ssh/ssh_host_dsa_key + #HostKey /etc/ssh/ssh_host_ecdsa_key + #HostKey /etc/ssh/ssh_host_ed25519_key + ++# Minimum accepted size of the DH parameter p. By default this is set to 1024 ++# to maintain compatibility with RFC4419, but should be set higher. ++# Upstream default is identical to setting this to 2048. ++#KexDHMin 1024 ++ + # Lifetime and size of ephemeral version 1 server key + #KeyRegenerationInterval 1h + #ServerKeyBits 1024 + + # Ciphers and keying + #RekeyLimit default none + + # Logging +diff --git a/openssh-7.2p2/sshd_config.0 b/openssh-7.2p2/sshd_config.0 +--- a/openssh-7.2p2/sshd_config.0 ++++ b/openssh-7.2p2/sshd_config.0 +@@ -539,16 +539,33 @@ DESCRIPTION + curve25519-sha256@libssh.org, + ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, + diffie-hellman-group-exchange-sha256, + diffie-hellman-group14-sha1 + + The list of available key exchange algorithms may also be + obtained using the -Q option of ssh(1) with an argument of M-bM-^@M-^\kexM-bM-^@M-^]. + ++ KexDHMin ++ Specifies the minimum accepted bit length of the DH group ++ parameter p. ++ ++ As per RFC4419, this is 1024 bits, however this has increasingly ++ been seen as insecure, which prompted the change to 2048 bits. ++ Setting this option allows the server to accept parameters shorter ++ than the current minimum, down to the RFC specified 1024 bits. ++ Using this option may be needed when some of the connectiong ++ clients only know short DH group parameters. ++ ++ Note, that while by default this option is set to 1024 to maintain ++ maximum backward compatibility, using it can severly impact ++ security and thus should be viewed as a temporary fix of last ++ resort and all efforts should be made to fix the (broken) ++ counterparty. ++ + KeyRegenerationInterval + In protocol version 1, the ephemeral server key is automatically + regenerated after this many seconds (if it has been used). The + purpose of regeneration is to prevent decrypting captured + sessions by later breaking into the machine and stealing the + keys. The key is never stored anywhere. If the value is 0, the + key is never regenerated. The default is 3600 (seconds). + +diff --git a/openssh-7.2p2/sshd_config.5 b/openssh-7.2p2/sshd_config.5 +--- a/openssh-7.2p2/sshd_config.5 ++++ b/openssh-7.2p2/sshd_config.5 +@@ -895,16 +895,32 @@ diffie-hellman-group14-sha1 + .Ed + .Pp + The list of available key exchange algorithms may also be obtained using the + .Fl Q + option of + .Xr ssh 1 + with an argument of + .Dq kex . ++.It Cm KexDHMin ++Specifies the minimum accepted bit length of the DH group ++parameter p. ++.Pp ++As per RFC4419, this is 1024 bits, however this has increasingly ++been seen as insecure, which prompted the change to 2048 bits. ++Setting this option allows the server to accept parameters shorter ++than the current minimum, down to the RFC specified 1024 bits. ++Using this option may be needed when some of the connectiong ++clients only know short DH group parameters. ++.Pp ++Note, that while by default this option is set to 1024 to maintain ++maximum backward compatibility, using it can severly impact ++security and thus should be viewed as a temporary fix of last ++resort and all efforts should be made to fix the (broken) ++counterparty. + .It Cm KeyRegenerationInterval + In protocol version 1, the ephemeral server key is automatically regenerated + after this many seconds (if it has been used). + The purpose of regeneration is to prevent + decrypting captured sessions by later breaking into the machine and + stealing the keys. + The key is never stored anywhere. + If the value is 0, the key is never regenerated. diff --git a/openssh-7.2p2-fips.patch b/openssh-7.2p2-fips.patch new file mode 100644 index 0000000..8251e5a --- /dev/null +++ b/openssh-7.2p2-fips.patch @@ -0,0 +1,1834 @@ +# HG changeset patch +# Parent 0dee2a3f80c2db73903388815fb4e311c8588a15 +FIPS 140-2 compliance. Perform selftests on start and use only FIPS approved +algorithms. + +diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in +--- a/openssh-7.2p2/Makefile.in ++++ b/openssh-7.2p2/Makefile.in +@@ -87,17 +87,17 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + 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 \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ + kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ + kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ + kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ +- platform-pledge.o ++ platform-pledge.o fips.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o mux.o + + SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ + audit.o audit-bsm.o audit-linux.o platform.o \ + sshpty.o sshlogin.o servconf.o serverloop.o \ + auth.o auth1.o auth2.o auth-options.o session.o \ +diff --git a/openssh-7.2p2/auth-rsa.c b/openssh-7.2p2/auth-rsa.c +--- a/openssh-7.2p2/auth-rsa.c ++++ b/openssh-7.2p2/auth-rsa.c +@@ -46,16 +46,18 @@ + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" + #include "ssh.h" + + #include "digest.h" + ++#include "fips.h" ++ + /* import */ + extern ServerOptions options; + + /* + * Session identifier that is used to bind key exchange and authentication + * responses to a particular session. + */ + extern u_char session_id[16]; +@@ -86,45 +88,52 @@ auth_rsa_generate_challenge(Key *key) + if (BN_mod(challenge, challenge, key->rsa->n, ctx) == 0) + fatal("auth_rsa_generate_challenge: BN_mod failed"); + BN_CTX_free(ctx); + + return challenge; + } + + int +-auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) ++auth_rsa_verify_response(Key *key, BIGNUM *challenge, ++ u_char response[SSH_DIGEST_MAX_LENGTH]) + { +- u_char buf[32], mdbuf[16]; ++ u_char buf[2 * SSH_DIGEST_MAX_LENGTH], mdbuf[SSH_DIGEST_MAX_LENGTH]; + struct ssh_digest_ctx *md; + int len; ++ int dgst; ++ size_t dgst_len; + + /* don't allow short keys */ + if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { + error("%s: RSA modulus too small: %d < minimum %d bits", + __func__, + BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); + return (0); + } + +- /* The response is MD5 of decrypted challenge plus session id. */ ++ dgst = fips_correct_dgst(SSH_DIGEST_MD5); ++ dgst_len = ssh_digest_bytes(dgst); ++ ++ /* The response is a hash of decrypted challenge plus session id. ++ * Normally this is MD5, in FIPS mode a stronger function is used. */ + len = BN_num_bytes(challenge); +- if (len <= 0 || len > 32) ++ if (len <= 0 || (unsigned int)len > (2 * dgst_len)) + fatal("%s: bad challenge length %d", __func__, len); +- memset(buf, 0, 32); +- BN_bn2bin(challenge, buf + 32 - len); +- if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || +- ssh_digest_update(md, buf, 32) < 0 || +- ssh_digest_update(md, session_id, 16) < 0 || ++ memset(buf, 0, sizeof(buf)); ++ BN_bn2bin(challenge, buf + 2 * dgst_len - len); ++ if ((md = ssh_digest_start(dgst)) == NULL || ++ ssh_digest_update(md, buf, 2 * dgst_len) < 0 || ++ ssh_digest_update(md, session_id, dgst_len) < 0 || + ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) + fatal("%s: md5 failed", __func__); + ssh_digest_free(md); + + /* Verify that the response is the original challenge. */ +- if (timingsafe_bcmp(response, mdbuf, 16) != 0) { ++ if (timingsafe_bcmp(response, mdbuf, dgst_len) != 0) { + /* Wrong answer. */ + return (0); + } + /* Correct answer. */ + return (1); + } + + /* +@@ -132,17 +141,17 @@ auth_rsa_verify_response(Key *key, BIGNU + * and returns true (non-zero) if the client gave the correct answer to + * our challenge; returns zero if the client gives a wrong answer. + */ + + int + auth_rsa_challenge_dialog(Key *key) + { + BIGNUM *challenge, *encrypted_challenge; +- u_char response[16]; ++ u_char response[SSH_DIGEST_MAX_LENGTH]; + int i, success; + + if ((encrypted_challenge = BN_new()) == NULL) + fatal("auth_rsa_challenge_dialog: BN_new() failed"); + + challenge = PRIVSEP(auth_rsa_generate_challenge(key)); + + /* Encrypt the challenge with the public key. */ +@@ -153,17 +162,17 @@ auth_rsa_challenge_dialog(Key *key) + packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); + packet_put_bignum(encrypted_challenge); + packet_send(); + BN_clear_free(encrypted_challenge); + packet_write_wait(); + + /* Wait for a response. */ + packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); +- for (i = 0; i < 16; i++) ++ for (i = 0; i < ssh_digest_bytes(fips_dgst_min()); i++) + response[i] = (u_char)packet_get_char(); + packet_check_eom(); + + success = PRIVSEP(auth_rsa_verify_response(key, challenge, response)); + BN_clear_free(challenge); + return (success); + } + +diff --git a/openssh-7.2p2/cipher-ctr.c b/openssh-7.2p2/cipher-ctr.c +--- a/openssh-7.2p2/cipher-ctr.c ++++ b/openssh-7.2p2/cipher-ctr.c +@@ -22,16 +22,18 @@ + #include + #include + + #include + + #include "xmalloc.h" + #include "log.h" + ++#include "fips.h" ++ + /* compatibility with old or broken OpenSSL versions */ + #include "openbsd-compat/openssl-compat.h" + + #ifndef USE_BUILTIN_RIJNDAEL + #include + #endif + + struct ssh_aes_ctr_ctx +@@ -134,13 +136,15 @@ evp_aes_128_ctr(void) + aes_ctr.iv_len = AES_BLOCK_SIZE; + aes_ctr.key_len = 16; + aes_ctr.init = ssh_aes_ctr_init; + aes_ctr.cleanup = ssh_aes_ctr_cleanup; + aes_ctr.do_cipher = ssh_aes_ctr; + #ifndef SSH_OLD_EVP + aes_ctr.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH | + EVP_CIPH_ALWAYS_CALL_INIT | EVP_CIPH_CUSTOM_IV; ++ if (fips_mode()) ++ aes_ctr.flags |= EVP_CIPH_FLAG_FIPS; + #endif + return (&aes_ctr); + } + + #endif /* defined(WITH_OPENSSL) && !defined(OPENSSL_HAVE_EVPCTR) */ +diff --git a/openssh-7.2p2/cipher.c b/openssh-7.2p2/cipher.c +--- a/openssh-7.2p2/cipher.c ++++ b/openssh-7.2p2/cipher.c +@@ -46,16 +46,19 @@ + #include "cipher.h" + #include "misc.h" + #include "sshbuf.h" + #include "ssherr.h" + #include "digest.h" + + #include "openbsd-compat/openssl-compat.h" + ++#include "fips.h" ++#include "log.h" ++ + #ifdef WITH_SSH1 + extern const EVP_CIPHER *evp_ssh1_bf(void); + extern const EVP_CIPHER *evp_ssh1_3des(void); + extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); + #endif + + struct sshcipher { + char *name; +@@ -72,17 +75,17 @@ struct sshcipher { + #define CFLAG_NONE (1<<3) + #ifdef WITH_OPENSSL + const EVP_CIPHER *(*evptype)(void); + #else + void *ignored; + #endif + }; + +-static const struct sshcipher ciphers[] = { ++static const struct sshcipher ciphers_all[] = { + #ifdef WITH_SSH1 + { "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, + { "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, + { "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf }, + #endif /* WITH_SSH1 */ + #ifdef WITH_OPENSSL + { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, + { "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc }, +@@ -114,27 +117,75 @@ static const struct sshcipher ciphers[] + { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, CFLAG_NONE, NULL }, + #endif /* WITH_OPENSSL */ + { "chacha20-poly1305@openssh.com", + SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL }, + + { NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL } + }; + ++static const struct sshcipher ciphers_fips140_2[] = { ++#ifdef WITH_SSH1 ++ { "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, ++#endif /* WITH_SSH1 */ ++#ifdef WITH_OPENSSL ++ { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, ++ { "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc }, ++ { "aes128-cbc", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 1, EVP_aes_128_cbc }, ++ { "aes192-cbc", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 1, EVP_aes_192_cbc }, ++ { "aes256-cbc", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc }, ++ { "rijndael-cbc@lysator.liu.se", ++ SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 1, EVP_aes_256_cbc }, ++ { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 0, EVP_aes_128_ctr }, ++ { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 0, EVP_aes_192_ctr }, ++ { "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 0, EVP_aes_256_ctr }, ++# ifdef OPENSSL_HAVE_EVPGCM ++ { "aes128-gcm@openssh.com", ++ SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm }, ++ { "aes256-gcm@openssh.com", ++ SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm }, ++# endif /* OPENSSL_HAVE_EVPGCM */ ++#else /* WITH_OPENSSL */ ++ { "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, CFLAG_AESCTR, NULL }, ++ { "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, CFLAG_AESCTR, NULL }, ++ { "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, CFLAG_AESCTR, NULL }, ++ { "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, CFLAG_NONE, NULL }, ++#endif /* WITH_OPENSSL */ ++ { NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL } ++}; ++ + /*--*/ + ++/* Returns array of ciphers available depending on selected FIPS mode */ ++static const struct sshcipher * ++fips_select_ciphers(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return ciphers_all; ++ case 1: ++ return ciphers_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ return NULL; ++ } ++} ++ + /* Returns a comma-separated list of supported ciphers. */ + char * + cipher_alg_list(char sep, int auth_only) + { + char *tmp, *ret = NULL; + size_t nlen, rlen = 0; + const struct sshcipher *c; + +- for (c = ciphers; c->name != NULL; c++) { ++ for (c = fips_select_ciphers(); c->name != NULL; c++) { + if (c->number != SSH_CIPHER_SSH2) + continue; + if (auth_only && c->auth_len == 0) + continue; + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(c->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { +@@ -208,27 +259,27 @@ cipher_mask_ssh1(int client) + } + return mask; + } + + const struct sshcipher * + cipher_by_name(const char *name) + { + const struct sshcipher *c; +- for (c = ciphers; c->name != NULL; c++) ++ for (c = fips_select_ciphers(); c->name != NULL; c++) + if (strcmp(c->name, name) == 0) + return c; + return NULL; + } + + const struct sshcipher * + cipher_by_number(int id) + { + const struct sshcipher *c; +- for (c = ciphers; c->name != NULL; c++) ++ for (c = fips_select_ciphers(); c->name != NULL; c++) + if (c->number == id) + return c; + return NULL; + } + + #define CIPHER_SEP "," + int + ciphers_valid(const char *names) +@@ -259,17 +310,17 @@ ciphers_valid(const char *names) + */ + + int + cipher_number(const char *name) + { + const struct sshcipher *c; + if (name == NULL) + return -1; +- for (c = ciphers; c->name != NULL; c++) ++ for (c = fips_select_ciphers(); c->name != NULL; c++) + if (strcasecmp(c->name, name) == 0) + return c->number; + return -1; + } + + char * + cipher_name(int id) + { +@@ -477,25 +528,26 @@ cipher_cleanup(struct sshcipher_ctx *cc) + /* + * Selects the cipher, and keys if by computing the MD5 checksum of the + * passphrase and using the resulting 16 bytes as the key. + */ + int + cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher, + const char *passphrase, int do_encrypt) + { +- u_char digest[16]; ++ u_char digest[SSH_DIGEST_MAX_LENGTH]; ++ int dgst = fips_correct_dgst(SSH_DIGEST_MD5); + int r = SSH_ERR_INTERNAL_ERROR; + +- if ((r = ssh_digest_memory(SSH_DIGEST_MD5, ++ if ((r = ssh_digest_memory(dgst, + passphrase, strlen(passphrase), + digest, sizeof(digest))) != 0) + goto out; + +- r = cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt); ++ r = cipher_init(cc, cipher, digest, ssh_digest_bytes(dgst), NULL, 0, do_encrypt); + out: + explicit_bzero(digest, sizeof(digest)); + return r; + } + + /* + * Exports an IV from the sshcipher_ctx required to export the key + * state back from the unprivileged child to the privileged parent +diff --git a/openssh-7.2p2/dh.h b/openssh-7.2p2/dh.h +--- a/openssh-7.2p2/dh.h ++++ b/openssh-7.2p2/dh.h +@@ -45,16 +45,17 @@ int dh_pub_is_valid(DH *, BIGNUM *); + u_int dh_estimate(int); + + /* + * Max value from RFC4419. + * Miniumum increased in light of DH precomputation attacks. + */ + #define DH_GRP_MIN_RFC 1024 + #define DH_GRP_MIN 2048 ++#define DH_GRP_MIN_FIPS 2048 + #define DH_GRP_MAX 8192 + + /* + * Values for "type" field of moduli(5) + * Specifies the internal structure of the prime modulus. + */ + #define MODULI_TYPE_UNKNOWN (0) + #define MODULI_TYPE_UNSTRUCTURED (1) +diff --git a/openssh-7.2p2/fips.c b/openssh-7.2p2/fips.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/fips.c +@@ -0,0 +1,237 @@ ++/* ++ * Copyright (c) 2012 Petr Cerny. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include "includes.h" ++ ++#include "fips.h" ++ ++#include "cipher.h" ++#include "dh.h" ++#include "digest.h" ++#include "kex.h" ++#include "key.h" ++#include "mac.h" ++#include "log.h" ++#include "xmalloc.h" ++ ++#include ++#include ++ ++/* import from dh.c */ ++extern int dh_grp_min; ++ ++static int fips_state = -1; ++ ++static int ++fips_check_required_env(void) ++{ ++ int fips_required = 0; ++ char *env = getenv(SSH_FORCE_FIPS_ENV); ++ ++ if (env) { ++ errno = 0; ++ fips_required = strtol(env, NULL, 10); ++ if (errno) { ++ debug("bogus value in the %s environment variable, ignoring\n" ++ , SSH_FORCE_FIPS_ENV); ++ fips_required = 0; ++ } else ++ fips_required = 1; ++ } ++ return fips_required; ++} ++ ++int ++fips_mode(void) ++{ ++ if (-1 == fips_state) { ++ fips_state = FIPS_mode(); ++ if (fips_state) ++ debug("FIPS mode initialized"); ++ else { ++ if (fips_check_required_env()) { ++ debug("FIPS mode requested through the environment variable '%s'" ++ , SSH_FORCE_FIPS_ENV); ++ if (!FIPS_mode_set(1)) ++ fatal("Unable to enter FIPS mode as requested through the environment variable '%s'" ++ , SSH_FORCE_FIPS_ENV); ++ fips_state = 1; ++ } ++ } ++ } ++ return fips_state; ++} ++ ++int ++fips_correct_dgst(int digest) ++{ ++ int fips; ++ int rv = -1; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ rv = digest; ++ break; ++ case 1: ++ switch (digest) { ++ case SSH_DIGEST_MD5: ++ case SSH_DIGEST_RIPEMD160: ++ debug("MD5/RIPEMD160 digests not allowed in FIPS 140-2 mode" ++ "using SHA-256 instead."); ++ rv = SSH_DIGEST_SHA256; ++ break; ++ default: ++ rv = digest; ++ break; ++ } ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ ++ return rv; ++} ++ ++/* ++ * filter out FIPS disallowed algorithms ++ * *crypto MUST be free()-able - it is assigned newly allocated memory and ++ * the previous one is freed ++ * ++ * returns zero if all algorithms were rejected, non-zero otherwise ++ */ ++int ++fips_filter_crypto(char **crypto, fips_filters filter) ++{ ++ char *token, *tmp, *tmp_sav, *new; ++ int plus = 0; ++ int valid; ++ int comma = 0; ++ int empty = 1; ++ size_t len; ++ ++ tmp = tmp_sav = xstrdup(*crypto); ++ ++ len = strlen(tmp) + 1; ++ new = xcalloc(1, len); ++ ++ if ('+' == *tmp) { ++ plus = 1; ++ tmp++; ++ } ++ ++ while ((token = strsep(&tmp, ",")) != NULL) { ++ switch(filter) { ++ case FIPS_FILTER_CIPHERS: ++ valid = ciphers_valid(token); ++ if (!valid) ++ debug("Cipher '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ case FIPS_FILTER_MACS: ++ valid = mac_valid(token); ++ if (!valid) ++ debug("MAC '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ case FIPS_FILTER_KEX_ALGS: ++ valid = kex_names_valid(token); ++ if (!valid) ++ debug("KEX '%s' is not allowed in FIPS mode", ++ token); ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS filter '%i' requested at %s:%u", ++ filter, __FILE__, __LINE__); ++ } ++ ++ if (valid) { ++ empty = 0; ++ if (plus) { ++ strlcat(new, "+", len); ++ plus = 0; ++ } ++ if (comma) ++ strlcat(new, ",", len); ++ else ++ comma = 1; ++ strlcat(new, token, len); ++ } ++ } ++ ++ /* free tmp and re-allocate shorter buffer for result if necessary */ ++ free(tmp_sav); ++ free(*crypto); ++ *crypto = new; ++ ++ return (!empty); ++} ++ ++int ++fips_dgst_min(void) ++{ ++ int fips; ++ int dgst; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ dgst = SSH_DIGEST_MD5; ++ break; ++ case 1: ++ dgst = SSH_DIGEST_SHA1; ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ return dgst; ++} ++ ++int ++fips_dh_grp_min(void) ++{ ++ int fips; ++ int dh; ++ ++ fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ dh = dh_grp_min; ++ break; ++ case 1: ++ dh = DH_GRP_MIN_FIPS; ++ break; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++ return dh; ++} ++ +diff --git a/openssh-7.2p2/fips.h b/openssh-7.2p2/fips.h +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/fips.h +@@ -0,0 +1,45 @@ ++/* ++ * Copyright (c) 2012 Petr Cerny. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ++ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++#ifndef FIPS_H ++#define FIPS_H ++ ++#include "key.h" ++ ++#define SSH_FORCE_FIPS_ENV "SSH_FORCE_FIPS" ++ ++typedef enum { ++ FIPS_FILTER_CIPHERS, ++ FIPS_FILTER_MACS, ++ FIPS_FILTER_KEX_ALGS ++} fips_filters; ++ ++int fips_mode(void); ++int fips_correct_dgst(int); ++int fips_dgst_min(void); ++int fips_dh_grp_min(void); ++enum fp_type fips_correct_fp_type(enum fp_type); ++int fips_filter_crypto(char **, fips_filters); ++ ++#endif ++ +diff --git a/openssh-7.2p2/hmac.c b/openssh-7.2p2/hmac.c +--- a/openssh-7.2p2/hmac.c ++++ b/openssh-7.2p2/hmac.c +@@ -139,17 +139,17 @@ ssh_hmac_free(struct ssh_hmac_ctx *ctx) + /* cc -DTEST hmac.c digest.c buffer.c cleanup.c fatal.c log.c xmalloc.c -lcrypto */ + static void + hmac_test(void *key, size_t klen, void *m, size_t mlen, u_char *e, size_t elen) + { + struct ssh_hmac_ctx *ctx; + size_t i; + u_char digest[16]; + +- if ((ctx = ssh_hmac_start(SSH_DIGEST_MD5)) == NULL) ++ if ((ctx = ssh_hmac_start(fips_correct_dgst(SSH_DIGEST_MD5))) == NULL) + printf("ssh_hmac_start failed"); + if (ssh_hmac_init(ctx, key, klen) < 0 || + ssh_hmac_update(ctx, m, mlen) < 0 || + ssh_hmac_final(ctx, digest, sizeof(digest)) < 0) + printf("ssh_hmac_xxx failed"); + ssh_hmac_free(ctx); + + if (memcmp(e, digest, elen)) { +diff --git a/openssh-7.2p2/kex.c b/openssh-7.2p2/kex.c +--- a/openssh-7.2p2/kex.c ++++ b/openssh-7.2p2/kex.c +@@ -49,16 +49,18 @@ + #include "misc.h" + #include "dispatch.h" + #include "monitor.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" + ++#include "fips.h" ++ + #if OPENSSL_VERSION_NUMBER >= 0x00907000L + # if defined(HAVE_EVP_SHA256) + # define evp_ssh_sha256 EVP_sha256 + # else + extern const EVP_MD *evp_ssh_sha256(void); + # endif + #endif + +@@ -80,17 +82,17 @@ static const char *proposal_names[PROPOS + }; + + struct kexalg { + char *name; + u_int type; + int ec_nid; + int hash_alg; + }; +-static const struct kexalg kexalgs[] = { ++static const struct kexalg kexalgs_all[] = { + #ifdef WITH_OPENSSL + { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, + #ifdef HAVE_EVP_SHA256 + { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, + #endif /* HAVE_EVP_SHA256 */ + #ifdef OPENSSL_HAS_ECC +@@ -105,24 +107,62 @@ static const struct kexalg kexalgs[] = { + #endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ + #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) + { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, + #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ + { NULL, -1, -1, -1}, + }; + ++static const struct kexalg kexalgs_fips140_2[] = { ++#ifdef WITH_OPENSSL ++ { KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++#ifdef HAVE_EVP_SHA256 ++ { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, ++#endif /* HAVE_EVP_SHA256 */ ++#ifdef OPENSSL_HAS_ECC ++ { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, ++ NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, ++ { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, ++ SSH_DIGEST_SHA384 }, ++# ifdef OPENSSL_HAS_NISTP521 ++ { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, ++ SSH_DIGEST_SHA512 }, ++# endif /* OPENSSL_HAS_NISTP521 */ ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ { NULL, -1, -1, -1}, ++}; ++ ++/* Returns array of macs available depending on selected FIPS mode */ ++static const struct kexalg * ++fips_select_kexalgs(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return kexalgs_all; ++ case 1: ++ return kexalgs_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++} ++ + char * + kex_alg_list(char sep) + { + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(k->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; +@@ -132,17 +172,17 @@ kex_alg_list(char sep) + return ret; + } + + static const struct kexalg * + kex_alg_by_name(const char *name) + { + const struct kexalg *k; + +- for (k = kexalgs; k->name != NULL; k++) { ++ for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; + } + return NULL; + } + + /* Validate KEX method name list */ + int +@@ -967,39 +1007,41 @@ kex_derive_keys_bn(struct ssh *ssh, u_ch + int + derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, + u_int8_t cookie[8], u_int8_t id[16]) + { + u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH]; + struct ssh_digest_ctx *hashctx = NULL; + size_t hlen, slen; + int r; ++ int digest; + + hlen = BN_num_bytes(host_modulus); + slen = BN_num_bytes(server_modulus); + if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) || + slen < (512 / 8) || (u_int)slen > sizeof(sbuf)) + return SSH_ERR_KEY_BITS_MISMATCH; + if (BN_bn2bin(host_modulus, hbuf) <= 0 || + BN_bn2bin(server_modulus, sbuf) <= 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) { ++ digest = fips_correct_dgst(SSH_DIGEST_MD5); ++ if ((hashctx = ssh_digest_start(digest)) == NULL) { + r = SSH_ERR_ALLOC_FAIL; + goto out; + } + if (ssh_digest_update(hashctx, hbuf, hlen) != 0 || + ssh_digest_update(hashctx, sbuf, slen) != 0 || + ssh_digest_update(hashctx, cookie, 8) != 0 || + ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) { + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } +- memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5)); ++ memcpy(id, obuf, ssh_digest_bytes(digest)); + r = 0; + out: + ssh_digest_free(hashctx); + explicit_bzero(hbuf, sizeof(hbuf)); + explicit_bzero(sbuf, sizeof(sbuf)); + explicit_bzero(obuf, sizeof(obuf)); + return r; + } +diff --git a/openssh-7.2p2/kexgexc.c b/openssh-7.2p2/kexgexc.c +--- a/openssh-7.2p2/kexgexc.c ++++ b/openssh-7.2p2/kexgexc.c +@@ -46,32 +46,31 @@ + #include "packet.h" + #include "dh.h" + #include "ssh2.h" + #include "compat.h" + #include "dispatch.h" + #include "ssherr.h" + #include "sshbuf.h" + +-/* import from dh.c */ +-extern int dh_grp_min; ++#include "fips.h" + + static int input_kex_dh_gex_group(int, u_int32_t, void *); + static int input_kex_dh_gex_reply(int, u_int32_t, void *); + + int + kexgex_client(struct ssh *ssh) + { + struct kex *kex = ssh->kex; + int r; + u_int nbits; + + nbits = dh_estimate(kex->dh_need * 8); + +- kex->min = dh_grp_min; ++ kex->min = fips_dh_grp_min(); + kex->max = DH_GRP_MAX; + kex->nbits = nbits; + if (datafellows & SSH_BUG_DHGEX_LARGE) + kex->nbits = MIN(kex->nbits, 4096); + /* New GEX request */ + if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 || + (r = sshpkt_put_u32(ssh, kex->min)) != 0 || + (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 || +diff --git a/openssh-7.2p2/kexgexs.c b/openssh-7.2p2/kexgexs.c +--- a/openssh-7.2p2/kexgexs.c ++++ b/openssh-7.2p2/kexgexs.c +@@ -49,18 +49,17 @@ + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" + #include "dispatch.h" + #include "ssherr.h" + #include "sshbuf.h" + +-/* import from dh.c */ +-extern int dh_grp_min; ++#include "fips.h" + + static int input_kex_dh_gex_request(int, u_int32_t, void *); + static int input_kex_dh_gex_init(int, u_int32_t, void *); + + int + kexgex_server(struct ssh *ssh) + { + ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST, +@@ -81,19 +80,19 @@ input_kex_dh_gex_request(int type, u_int + if ((r = sshpkt_get_u32(ssh, &min)) != 0 || + (r = sshpkt_get_u32(ssh, &nbits)) != 0 || + (r = sshpkt_get_u32(ssh, &max)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + goto out; + kex->nbits = nbits; + kex->min = min; + kex->max = max; +- min = MAX(dh_grp_min, min); ++ min = MAX(fips_dh_grp_min(), min); + max = MIN(DH_GRP_MAX, max); +- nbits = MAX(dh_grp_min, nbits); ++ nbits = MAX(fips_dh_grp_min(), nbits); + nbits = MIN(DH_GRP_MAX, nbits); + + if (kex->max < kex->min || kex->nbits < kex->min || + kex->max < kex->nbits) { + if (kex->nbits < kex->min && kex->nbits >= DH_GRP_MIN_RFC) + logit("DH parameter requested by the client (%d bits) " + "is considered insecure. " + "You can lower the accepted minimum " +diff --git a/openssh-7.2p2/mac.c b/openssh-7.2p2/mac.c +--- a/openssh-7.2p2/mac.c ++++ b/openssh-7.2p2/mac.c +@@ -35,31 +35,34 @@ + #include "umac.h" + #include "mac.h" + #include "misc.h" + #include "ssherr.h" + #include "sshbuf.h" + + #include "openbsd-compat/openssl-compat.h" + ++#include "fips.h" ++#include "log.h" ++ + #define SSH_DIGEST 1 /* SSH_DIGEST_XXX */ + #define SSH_UMAC 2 /* UMAC (not integrated with OpenSSL) */ + #define SSH_UMAC128 3 + + struct macalg { + char *name; + int type; + int alg; + int truncatebits; /* truncate digest if != 0 */ + int key_len; /* just for UMAC */ + int len; /* just for UMAC */ + int etm; /* Encrypt-then-MAC */ + }; + +-static const struct macalg macs[] = { ++static const struct macalg macs_all[] = { + /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ + { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, + { "hmac-sha1-96", SSH_DIGEST, SSH_DIGEST_SHA1, 96, 0, 0, 0 }, + #ifdef HAVE_EVP_SHA256 + { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, + { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, + #endif + { "hmac-md5", SSH_DIGEST, SSH_DIGEST_MD5, 0, 0, 0, 0 }, +@@ -80,25 +83,60 @@ static const struct macalg macs[] = { + { "hmac-md5-96-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_MD5, 96, 0, 0, 1 }, + { "hmac-ripemd160-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_RIPEMD160, 0, 0, 0, 1 }, + { "umac-64-etm@openssh.com", SSH_UMAC, 0, 0, 128, 64, 1 }, + { "umac-128-etm@openssh.com", SSH_UMAC128, 0, 0, 128, 128, 1 }, + + { NULL, 0, 0, 0, 0, 0, 0 } + }; + ++static const struct macalg macs_fips140_2[] = { ++ /* Encrypt-and-MAC (encrypt-and-authenticate) variants */ ++ { "hmac-sha1", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 0 }, ++#ifdef HAVE_EVP_SHA256 ++ { "hmac-sha2-256", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 0 }, ++ { "hmac-sha2-512", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 0 }, ++#endif ++ ++ /* Encrypt-then-MAC variants */ ++ { "hmac-sha1-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA1, 0, 0, 0, 1 }, ++#ifdef HAVE_EVP_SHA256 ++ { "hmac-sha2-256-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA256, 0, 0, 0, 1 }, ++ { "hmac-sha2-512-etm@openssh.com", SSH_DIGEST, SSH_DIGEST_SHA512, 0, 0, 0, 1 }, ++#endif ++ ++ { NULL, 0, 0, 0, 0, 0, 0 } ++}; ++ ++/* Returns array of macs available depending on selected FIPS mode */ ++static const struct macalg * ++fips_select_macs(void) ++{ ++ int fips = fips_mode(); ++ switch (fips) { ++ case 0: ++ return macs_all; ++ case 1: ++ return macs_fips140_2; ++ default: ++ /* should not be reached */ ++ fatal("Fatal error: incorrect FIPS mode '%i' at %s:%u", ++ fips, __FILE__, __LINE__); ++ } ++} ++ + /* Returns a list of supported MACs separated by the specified char. */ + char * + mac_alg_list(char sep) + { + char *ret = NULL, *tmp; + size_t nlen, rlen = 0; + const struct macalg *m; + +- for (m = macs; m->name != NULL; m++) { ++ for (m = fips_select_macs(); m->name != NULL; m++) { + if (ret != NULL) + ret[rlen++] = sep; + nlen = strlen(m->name); + if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { + free(ret); + return NULL; + } + ret = tmp; +@@ -127,17 +165,17 @@ mac_setup_by_alg(struct sshmac *mac, con + return 0; + } + + int + mac_setup(struct sshmac *mac, char *name) + { + const struct macalg *m; + +- for (m = macs; m->name != NULL; m++) { ++ for (m = fips_select_macs(); m->name != NULL; m++) { + if (strcmp(name, m->name) != 0) + continue; + if (mac != NULL) + return mac_setup_by_alg(mac, m); + return 0; + } + return SSH_ERR_INVALID_ARGUMENT; + } +diff --git a/openssh-7.2p2/myproposal.h b/openssh-7.2p2/myproposal.h +--- a/openssh-7.2p2/myproposal.h ++++ b/openssh-7.2p2/myproposal.h +@@ -128,16 +128,18 @@ + "hmac-sha2-256," \ + "hmac-sha2-512," \ + "hmac-sha1" + + #define KEX_CLIENT_MAC KEX_SERVER_MAC + + #else /* WITH_OPENSSL */ + ++#error "OpenSSL support is needed for FIPS mode to compile" ++ + #define KEX_SERVER_KEX \ + "curve25519-sha256@libssh.org" + #define KEX_DEFAULT_PK_ALG \ + "ssh-ed25519-cert-v01@openssh.com," \ + "ssh-ed25519" + #define KEX_SERVER_ENCRYPT \ + "chacha20-poly1305@openssh.com," \ + "aes128-ctr,aes192-ctr,aes256-ctr" +diff --git a/openssh-7.2p2/readconf.c b/openssh-7.2p2/readconf.c +--- a/openssh-7.2p2/readconf.c ++++ b/openssh-7.2p2/readconf.c +@@ -57,16 +57,17 @@ + #include "readconf.h" + #include "match.h" + #include "kex.h" + #include "mac.h" + #include "uidswap.h" + #include "myproposal.h" + #include "digest.h" + #include "dh.h" ++#include "fips.h" + + /* Format of the configuration file: + + # Configuration data is parsed as follows: + # 1. command line options + # 2. user-specific file + # 3. system-wide file + # Any configuration value is only changed the first time it is set. +@@ -1628,16 +1629,33 @@ read_config_file(const char *filename, s + + /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ + int + option_clear_or_none(const char *o) + { + return o == NULL || strcasecmp(o, "none") == 0; + } + ++/* remove algorithms not approved for use in FIPS mode, when running in FIPS ++ * mode ++ */ ++void ++filter_fips_algorithms(Options *o) ++{ ++ if (fips_mode()) { ++ if (!fips_filter_crypto(&o->ciphers, FIPS_FILTER_CIPHERS)) ++ fatal("None of selected ciphers can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->macs, FIPS_FILTER_MACS)) ++ fatal("None of selected MAC algorithms can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->kex_algorithms, FIPS_FILTER_KEX_ALGS)) ++ fatal("None of selected KEX algorithms can be used in FIPS mode"); ++ } ++ return; ++} ++ + /* + * Initializes options to special values that indicate that they have not yet + * been set. Read_config_file will only set options with this value. Options + * are processed in the following order: command line, user config file, + * system config file. Last, fill_default_options is called. + */ + + void +@@ -1817,19 +1835,20 @@ fill_default_options(Options * options) + if (options->connection_attempts == -1) + options->connection_attempts = 1; + if (options->number_of_password_prompts == -1) + options->number_of_password_prompts = 3; + /* Selected in ssh_login(). */ + if (options->cipher == -1) + options->cipher = SSH_CIPHER_NOT_SET; + if (options->kex_dhmin == -1) +- options->kex_dhmin = DH_GRP_MIN_RFC; ++ options->kex_dhmin = fips_dh_grp_min(); + else { +- options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MAX(options->kex_dhmin, ++ fips_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN_RFC); + options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX); + } + dh_grp_min = options->kex_dhmin; + /* options->hostkeyalgorithms, default set in myproposals.h */ + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_2; + if (options->add_keys_to_agent == -1) + options->add_keys_to_agent = 0; +@@ -1914,26 +1933,29 @@ fill_default_options(Options * options) + if (options->canonicalize_max_dots == -1) + options->canonicalize_max_dots = 1; + if (options->canonicalize_fallback_local == -1) + options->canonicalize_fallback_local = 1; + if (options->canonicalize_hostname == -1) + options->canonicalize_hostname = SSH_CANONICALISE_NO; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; ++ options->fingerprint_hash = ++ fips_correct_dgst(options->fingerprint_hash); + if (options->update_hostkeys == -1) + options->update_hostkeys = 0; + if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 || + kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 || + kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->hostbased_key_types) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &options->pubkey_key_types) != 0) + fatal("%s: kex_assemble_names failed", __func__); ++ filter_fips_algorithms(options); + + #define CLEAR_ON_NONE(v) \ + do { \ + if (option_clear_or_none(v)) { \ + free(v); \ + v = NULL; \ + } \ + } while(0) +diff --git a/openssh-7.2p2/readconf.h b/openssh-7.2p2/readconf.h +--- a/openssh-7.2p2/readconf.h ++++ b/openssh-7.2p2/readconf.h +@@ -180,16 +180,17 @@ 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 SSH_UPDATE_HOSTKEYS_NO 0 + #define SSH_UPDATE_HOSTKEYS_YES 1 + #define SSH_UPDATE_HOSTKEYS_ASK 2 + ++void filter_fips_algorithms(Options *o); + void initialize_options(Options *); + void fill_default_options(Options *); + 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); + int parse_forward(struct Forward *, const char *, int, int); +diff --git a/openssh-7.2p2/servconf.c b/openssh-7.2p2/servconf.c +--- a/openssh-7.2p2/servconf.c ++++ b/openssh-7.2p2/servconf.c +@@ -53,16 +53,17 @@ + #include "groupaccess.h" + #include "canohost.h" + #include "packet.h" + #include "hostfile.h" + #include "auth.h" + #include "myproposal.h" + #include "digest.h" + #include "dh.h" ++#include "fips.h" + + /* import from dh.c */ + extern int dh_grp_min; + + static void add_listen_addr(ServerOptions *, char *, int); + static void add_one_listen_addr(ServerOptions *, char *, int); + + /* Use of privilege separation or not */ +@@ -179,45 +180,65 @@ initialize_server_options(ServerOptions + + /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ + static int + option_clear_or_none(const char *o) + { + return o == NULL || strcasecmp(o, "none") == 0; + } + ++/* remove algorithms not approved for use in FIPS mode, when running in FIPS ++ * mode ++ */ ++static void ++filter_fips_algorithms_s(ServerOptions *o) ++{ ++ if (fips_mode()) { ++ if (!fips_filter_crypto(&o->ciphers, FIPS_FILTER_CIPHERS)) ++ fatal("None of selected ciphers can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->macs, FIPS_FILTER_MACS)) ++ fatal("None of selected MAC algorithms can be used in FIPS mode"); ++ if (!fips_filter_crypto(&o->kex_algorithms, FIPS_FILTER_KEX_ALGS)) ++ fatal("None of selected KEX algorithms can be used in FIPS mode"); ++ } ++ return; ++} ++ + static void + assemble_algorithms(ServerOptions *o) + { + if (kex_assemble_names(KEX_SERVER_ENCRYPT, &o->ciphers) != 0 || + kex_assemble_names(KEX_SERVER_MAC, &o->macs) != 0 || + kex_assemble_names(KEX_SERVER_KEX, &o->kex_algorithms) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &o->hostkeyalgorithms) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, + &o->hostbased_key_types) != 0 || + kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->pubkey_key_types) != 0) + fatal("kex_assemble_names failed"); ++ ++ filter_fips_algorithms_s(o); + } + + void + fill_default_server_options(ServerOptions *options) + { + int i; + + /* Portable-specific options */ + if (options->use_pam == -1) + options->use_pam = 0; + if (options->use_pam_check_locks == -1) + options->use_pam_check_locks = 0; + + if (options->kex_dhmin == -1) +- options->kex_dhmin = DH_GRP_MIN_RFC; ++ options->kex_dhmin = fips_dh_grp_min(); + else { +- options->kex_dhmin = MAX(options->kex_dhmin, DH_GRP_MIN_RFC); ++ options->kex_dhmin = MAX(options->kex_dhmin, ++ fips_mode() ? DH_GRP_MIN_FIPS : DH_GRP_MIN_RFC); + options->kex_dhmin = MIN(options->kex_dhmin, DH_GRP_MAX); + } + dh_grp_min = options->kex_dhmin; + /* Standard Options */ + if (options->protocol == SSH_PROTO_UNKNOWN) + options->protocol = SSH_PROTO_2; + if (options->num_host_key_files == 0) { + /* fill default hostkeys for protocols */ +@@ -363,16 +384,18 @@ fill_default_server_options(ServerOption + if (options->version_addendum == NULL) + options->version_addendum = xstrdup(""); + 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) + options->fwd_opts.streamlocal_bind_unlink = 0; + if (options->fingerprint_hash == -1) + options->fingerprint_hash = SSH_FP_HASH_DEFAULT; ++ options->fingerprint_hash = ++ fips_correct_dgst(options->fingerprint_hash); + + assemble_algorithms(options); + + /* Turn privilege separation and sandboxing on by default */ + if (use_privsep == -1) + use_privsep = PRIVSEP_ON; + + #define CLEAR_ON_NONE(v) \ +@@ -980,17 +1003,17 @@ static const struct multistate multistat + { NULL, -1 } + }; + + int + process_server_config_line(ServerOptions *options, char *line, + const char *filename, int linenum, int *activep, + struct connection_info *connectinfo) + { +- char *cp, **charptr, *arg, *p; ++ char *cp, **charptr, *arg, *p, *tmp; + int cmdline = 0, *intptr, value, value2, n, port; + SyslogFacility *log_facility_ptr; + LogLevel *log_level_ptr; + ServerOpCodes opcode; + u_int i, flags = 0; + size_t len; + long long val64; + const struct multistate *multistate_ptr; +@@ -1465,44 +1488,72 @@ process_server_config_line(ServerOptions + xstrdup(arg); + } + break; + + case sCiphers: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); +- if (!ciphers_valid(*arg == '+' ? arg + 1 : arg)) ++ tmp = xstrdup(arg); ++ if (fips_mode()) { ++ if(!fips_filter_crypto(&tmp, FIPS_FILTER_CIPHERS)) ++ error("All ciphers were rejected " ++ "by the FIPS filter: '%s'.", arg); ++ } ++ if (!ciphers_valid(*tmp == '+' ? tmp + 1 : tmp)) + fatal("%s line %d: Bad SSH2 cipher spec '%s'.", + filename, linenum, arg ? arg : ""); + if (options->ciphers == NULL) +- options->ciphers = xstrdup(arg); ++ options->ciphers = tmp; ++ else ++ free(tmp); + break; + + case sMacs: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", filename, linenum); +- if (!mac_valid(*arg == '+' ? arg + 1 : arg)) ++ tmp = xstrdup(arg); ++ if (fips_mode()) { ++ if(!fips_filter_crypto(&tmp, FIPS_FILTER_MACS)) ++ error("All ciphers were rejected " ++ "by the FIPS filter: '%s'.", arg); ++ } ++ if (!mac_valid(*tmp == '+' ? tmp + 1 : tmp)) ++ /* print the original line including any algorithms ++ * dropped by the FIPS filter */ + fatal("%s line %d: Bad SSH2 mac spec '%s'.", + filename, linenum, arg ? arg : ""); + if (options->macs == NULL) +- options->macs = xstrdup(arg); ++ options->macs = tmp; ++ else ++ free(tmp); + break; + + case sKexAlgorithms: + arg = strdelim(&cp); + if (!arg || *arg == '\0') + fatal("%s line %d: Missing argument.", + filename, linenum); +- if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) ++ tmp = xstrdup(arg); ++ if (fips_mode()) { ++ if(!fips_filter_crypto(&tmp, FIPS_FILTER_KEX_ALGS)) ++ error("All ciphers were rejected " ++ "by the FIPS filter: '%s'.", arg); ++ } ++ if (!kex_names_valid(*tmp == '+' ? tmp + 1 : tmp)) ++ /* print the original line including any algorithms ++ * dropped by the FIPS filter */ + fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", + filename, linenum, arg ? arg : ""); + if (options->kex_algorithms == NULL) +- options->kex_algorithms = xstrdup(arg); ++ options->kex_algorithms = tmp; ++ else ++ free(tmp); + break; + + case sKexDHMin: + intptr = &options->kex_dhmin; + goto parse_int; + + case sProtocol: + intptr = &options->protocol; +diff --git a/openssh-7.2p2/ssh-keygen.c b/openssh-7.2p2/ssh-keygen.c +--- a/openssh-7.2p2/ssh-keygen.c ++++ b/openssh-7.2p2/ssh-keygen.c +@@ -53,16 +53,18 @@ + #include "ssh.h" + #include "ssh2.h" + #include "ssherr.h" + #include "ssh-pkcs11.h" + #include "atomicio.h" + #include "krl.h" + #include "digest.h" + ++#include "fips.h" ++ + #ifdef WITH_OPENSSL + # define DEFAULT_KEY_TYPE_NAME "rsa" + #else + # define DEFAULT_KEY_TYPE_NAME "ed25519" + #endif + + /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ + #define DEFAULT_BITS 2048 +@@ -965,42 +967,61 @@ do_fingerprint(struct passwd *pw) + if (invalid) + fatal("%s is not a public key file.", path); + exit(0); + } + + static void + do_gen_all_hostkeys(struct passwd *pw) + { +- struct { ++ struct Key_types { + char *key_type; + char *key_type_display; + char *path; +- } key_types[] = { ++ }; ++ ++ struct Key_types key_types_all[] = { + #ifdef WITH_OPENSSL + #ifdef WITH_SSH1 + { "rsa1", "RSA1", _PATH_HOST_KEY_FILE }, + #endif /* WITH_SSH1 */ + { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, + { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, + #ifdef OPENSSL_HAS_ECC + { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, + #endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ + { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, + { NULL, NULL, NULL } + }; + ++ struct Key_types key_types_fips140_2[] = { ++#ifdef WITH_OPENSSL ++ { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, ++#ifdef OPENSSL_HAS_ECC ++ { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, ++#endif /* OPENSSL_HAS_ECC */ ++#endif /* WITH_OPENSSL */ ++ { NULL, NULL, NULL } ++ }; ++ ++ struct Key_types *key_types; + int first = 0; + struct stat st; + struct sshkey *private, *public; + char comment[1024]; + int i, type, fd, r; + FILE *f; + ++ if (fips_mode()) { ++ key_types = key_types_fips140_2; ++ } else { ++ key_types = key_types_all; ++ } ++ + for (i = 0; key_types[i].key_type; i++) { + if (stat(key_types[i].path, &st) == 0) + continue; + if (errno != ENOENT) { + error("Could not stat %s: %s", key_types[i].path, + strerror(errno)); + first = 0; + continue; +@@ -2610,16 +2631,23 @@ main(int argc, char **argv) + do_gen_all_hostkeys(pw); + return (0); + } + + if (key_type_name == NULL) + key_type_name = DEFAULT_KEY_TYPE_NAME; + + type = sshkey_type_from_name(key_type_name); ++ ++ /* protocol v1 is not allowed in FIPS mode, DSA is not acceptable because ++ * it has to be 1024 bit due to RFC 4253 using SHA-1 which implies 1024 bit ++ * keys due to FIPS-186 specification for DSS */ ++ if (fips_mode() && (type == KEY_RSA1 || type == KEY_DSA)) ++ fatal("Key type %s not alowed in FIPS mode", key_type_name); ++ + type_bits_valid(type, key_type_name, &bits); + + if (!quiet) + printf("Generating public/private %s key pair.\n", + key_type_name); + if ((r = sshkey_generate(type, bits, &private)) != 0) + fatal("key_generate failed"); + if ((r = sshkey_from_private(private, &public)) != 0) +diff --git a/openssh-7.2p2/ssh.c b/openssh-7.2p2/ssh.c +--- a/openssh-7.2p2/ssh.c ++++ b/openssh-7.2p2/ssh.c +@@ -104,16 +104,18 @@ + #include "sshpty.h" + #include "match.h" + #include "msg.h" + #include "uidswap.h" + #include "version.h" + #include "ssherr.h" + #include "myproposal.h" + ++#include "fips.h" ++ + #ifdef ENABLE_PKCS11 + #include "ssh-pkcs11.h" + #endif + + extern char *__progname; + + /* Saves a copy of argv for setproctitle emulation */ + #ifndef HAVE_SETPROCTITLE +@@ -604,16 +606,18 @@ main(int ac, char **av) + logfile = NULL; + argv0 = av[0]; + + again: + while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" + "ACD:E:F:GI:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { + switch (opt) { + case '1': ++ if (fips_mode()) ++ fatal("Protocol 1 not allowed in the FIPS mode."); + options.protocol = SSH_PROTO_1; + break; + case '2': + options.protocol = SSH_PROTO_2; + break; + case '4': + options.address_family = AF_INET; + break; +@@ -1073,16 +1077,22 @@ main(int ac, char **av) + */ + if (addrs != NULL && options.port > 0) + set_addrinfo_port(addrs, options.port); + } + + /* Fill configuration defaults. */ + fill_default_options(&options); + ++ if (fips_mode()) { ++ options.protocol &= SSH_PROTO_2; ++ if (options.protocol == 0) ++ fatal("Protocol 2 disabled by configuration but required in the FIPS mode"); ++ } ++ + if (options.port == 0) + options.port = default_ssh_port(); + channel_set_af(options.address_family); + + /* Tidy and check options */ + if (options.host_key_alias != NULL) + lowercase(options.host_key_alias); + if (options.proxy_command != NULL && +diff --git a/openssh-7.2p2/ssh_config.0 b/openssh-7.2p2/ssh_config.0 +--- a/openssh-7.2p2/ssh_config.0 ++++ b/openssh-7.2p2/ssh_config.0 +@@ -376,16 +376,18 @@ DESCRIPTION + ultimate forwarding destination fail. The argument must be M-bM-^@M-^\yesM-bM-^@M-^] + or M-bM-^@M-^\noM-bM-^@M-^]. The default is M-bM-^@M-^\noM-bM-^@M-^]. + + FingerprintHash + Specifies the hash algorithm used when displaying key + fingerprints. Valid options are: M-bM-^@M-^\md5M-bM-^@M-^] and M-bM-^@M-^\sha256M-bM-^@M-^]. The + default is M-bM-^@M-^\sha256M-bM-^@M-^]. + ++ In the FIPS mode the minimum of SHA-1 is enforced. ++ + ForwardAgent + Specifies whether the connection to the authentication agent (if + any) will be forwarded to the remote machine. The argument must + be M-bM-^@M-^\yesM-bM-^@M-^] or M-bM-^@M-^\noM-bM-^@M-^]. The default is M-bM-^@M-^\noM-bM-^@M-^]. + + Agent forwarding should be enabled with caution. Users with the + ability to bypass file permissions on the remote host (for the + agent's Unix-domain socket) can access the local agent through +@@ -623,16 +625,19 @@ DESCRIPTION + only know short DH group parameters. + + Note, that while by default this option is set to 1024 to maintain + maximum backward compatibility, using it can severly impact + security and thus should be viewed as a temporary fix of last + resort and all efforts should be made to fix the (broken) + counterparty. + ++ In the FIPS mode the FIPS standard takes precedence over RFC and ++ forces the minimum to a higher value, currently 2048 bits. ++ + LocalCommand + Specifies a command to execute on the local machine after + successfully connecting to the server. The command string + extends to the end of the line, and is executed with the user's + shell. The following escape character substitutions will be + performed: M-bM-^@M-^X%dM-bM-^@M-^Y (local user's home directory), M-bM-^@M-^X%hM-bM-^@M-^Y (remote host + name), M-bM-^@M-^X%lM-bM-^@M-^Y (local host name), M-bM-^@M-^X%nM-bM-^@M-^Y (host name as provided on the + command line), M-bM-^@M-^X%pM-bM-^@M-^Y (remote port), M-bM-^@M-^X%rM-bM-^@M-^Y (remote user name) or +diff --git a/openssh-7.2p2/ssh_config.5 b/openssh-7.2p2/ssh_config.5 +--- a/openssh-7.2p2/ssh_config.5 ++++ b/openssh-7.2p2/ssh_config.5 +@@ -727,16 +727,18 @@ The default is + .It Cm FingerprintHash + Specifies the hash algorithm used when displaying key fingerprints. + Valid options are: + .Dq md5 + and + .Dq sha256 . + The default is + .Dq sha256 . ++.Pp ++In the FIPS mode the minimum of SHA-1 is enforced. + .It Cm ForwardAgent + Specifies whether the connection to the authentication agent (if any) + will be forwarded to the remote machine. + The argument must be + .Dq yes + or + .Dq no . + The default is +@@ -1108,16 +1110,19 @@ than the current minimum, down to the RF + Using this option may be needed when connecting to servers that + only know short DH group parameters. + .Pp + Note, that while by default this option is set to 1024 to maintain + maximum backward compatibility, using it can severly impact + security and thus should be viewed as a temporary fix of last + resort and all efforts should be made to fix the (broken) + counterparty. ++.Pp ++In the FIPS mode the FIPS standard takes precedence over RFC and ++forces the minimum to a higher value, currently 2048 bits. + .It Cm LocalCommand + Specifies a command to execute on the local machine after successfully + connecting to the server. + The command string extends to the end of the line, and is executed with + the user's shell. + The following escape character substitutions will be performed: + .Ql %d + (local user's home directory), +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -120,16 +120,18 @@ + #ifdef GSSAPI + #include "ssh-gss.h" + #endif + #include "monitor_wrap.h" + #include "ssh-sandbox.h" + #include "version.h" + #include "ssherr.h" + ++#include "fips.h" ++ + #ifndef O_NOCTTY + #define O_NOCTTY 0 + #endif + + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) + #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) +@@ -1824,16 +1826,20 @@ main(int ac, char **av) + if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash, + SSH_FP_DEFAULT)) == NULL) + fatal("sshkey_fingerprint failed"); + debug("%s host key #%d: %s %s", + key ? "private" : "agent", i, keytype == KEY_RSA1 ? + sshkey_type(pubkey) : sshkey_ssh_name(pubkey), fp); + free(fp); + } ++ if ((options.protocol & SSH_PROTO_1) && fips_mode()) { ++ logit("Disabling protocol version 1. Not allowed in the FIPS mode."); ++ options.protocol &= ~SSH_PROTO_1; ++ } + if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { + logit("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + logit("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } +diff --git a/openssh-7.2p2/sshd_config.0 b/openssh-7.2p2/sshd_config.0 +--- a/openssh-7.2p2/sshd_config.0 ++++ b/openssh-7.2p2/sshd_config.0 +@@ -344,16 +344,18 @@ DESCRIPTION + AllowUsers, DenyGroups, and finally AllowGroups. + + See PATTERNS in ssh_config(5) for more information on patterns. + + FingerprintHash + Specifies the hash algorithm used when logging key fingerprints. + Valid options are: M-bM-^@M-^\md5M-bM-^@M-^] and M-bM-^@M-^\sha256M-bM-^@M-^]. The default is M-bM-^@M-^\sha256M-bM-^@M-^]. + ++ In the FIPS mode the minimum of SHA-1 is enforced. ++ + ForceCommand + Forces the execution of the command specified by ForceCommand, + ignoring any command supplied by the client and ~/.ssh/rc if + present. The command is invoked by using the user's login shell + with the -c option. This applies to shell, command, or subsystem + execution. It is most useful inside a Match block. The command + originally supplied by the client is available in the + SSH_ORIGINAL_COMMAND environment variable. Specifying a command +@@ -556,16 +558,19 @@ DESCRIPTION + clients only know short DH group parameters. + + Note, that while by default this option is set to 1024 to maintain + maximum backward compatibility, using it can severly impact + security and thus should be viewed as a temporary fix of last + resort and all efforts should be made to fix the (broken) + counterparty. + ++ In the FIPS mode the FIPS standard takes precedence over RFC and ++ forces the minimum to a higher value, currently 2048 bits. ++ + KeyRegenerationInterval + In protocol version 1, the ephemeral server key is automatically + regenerated after this many seconds (if it has been used). The + purpose of regeneration is to prevent decrypting captured + sessions by later breaking into the machine and stealing the + keys. The key is never stored anywhere. If the value is 0, the + key is never regenerated. The default is 3600 (seconds). + +diff --git a/openssh-7.2p2/sshd_config.5 b/openssh-7.2p2/sshd_config.5 +--- a/openssh-7.2p2/sshd_config.5 ++++ b/openssh-7.2p2/sshd_config.5 +@@ -572,16 +572,18 @@ for more information on patterns. + .It Cm FingerprintHash + Specifies the hash algorithm used when logging key fingerprints. + Valid options are: + .Dq md5 + and + .Dq sha256 . + The default is + .Dq sha256 . ++.Pp ++In the FIPS mode the minimum of SHA-1 is enforced. + .It Cm ForceCommand + Forces the execution of the command specified by + .Cm ForceCommand , + ignoring any command supplied by the client and + .Pa ~/.ssh/rc + if present. + The command is invoked by using the user's login shell with the -c option. + This applies to shell, command, or subsystem execution. +@@ -911,16 +913,19 @@ than the current minimum, down to the RF + Using this option may be needed when some of the connectiong + clients only know short DH group parameters. + .Pp + Note, that while by default this option is set to 1024 to maintain + maximum backward compatibility, using it can severly impact + security and thus should be viewed as a temporary fix of last + resort and all efforts should be made to fix the (broken) + counterparty. ++.Pp ++In the FIPS mode the FIPS standard takes precedence over RFC and ++forces the minimum to a higher value, currently 2048 bits. + .It Cm KeyRegenerationInterval + In protocol version 1, the ephemeral server key is automatically regenerated + after this many seconds (if it has been used). + The purpose of regeneration is to prevent + decrypting captured sessions by later breaking into the machine and + stealing the keys. + The key is never stored anywhere. + If the value is 0, the key is never regenerated. diff --git a/openssh-7.2p2-gssapi_key_exchange.patch b/openssh-7.2p2-gssapi_key_exchange.patch new file mode 100644 index 0000000..481a8de --- /dev/null +++ b/openssh-7.2p2-gssapi_key_exchange.patch @@ -0,0 +1,3963 @@ +# HG changeset patch +# Parent c2049622cf75dbab61a8f49b53a13dc1de6695fd +GSSAPI Key Exchange implementation + +diff --git a/openssh-7.2p2/ChangeLog.gssapi b/openssh-7.2p2/ChangeLog.gssapi +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/ChangeLog.gssapi +@@ -0,0 +1,113 @@ ++20110101 ++ - Finally update for OpenSSH 5.6p1 ++ - Add GSSAPIServerIdentity option from Jim Basney ++ ++20100308 ++ - [ Makefile.in, key.c, key.h ] ++ Updates for OpenSSH 5.4p1 ++ - [ servconf.c ] ++ Include GSSAPI options in the sshd -T configuration dump, and flag ++ some older configuration options as being unsupported. Thanks to Colin ++ Watson. ++ - ++ ++20100124 ++ - [ sshconnect2.c ] ++ Adapt to deal with additional element in Authmethod structure. Thanks to ++ Colin Watson ++ ++20090615 ++ - [ gss-genr.c gss-serv.c kexgssc.c kexgsss.c monitor.c sshconnect2.c ++ sshd.c ] ++ Fix issues identified by Greg Hudson following a code review ++ Check return value of gss_indicate_mechs ++ Protect GSSAPI calls in monitor, so they can only be used if enabled ++ Check return values of bignum functions in key exchange ++ Use BN_clear_free to clear other side's DH value ++ Make ssh_gssapi_id_kex more robust ++ Only configure kex table pointers if GSSAPI is enabled ++ Don't leak mechanism list, or gss mechanism list ++ Cast data.length before printing ++ If serverkey isn't provided, use an empty string, rather than NULL ++ ++20090201 ++ - [ gss-genr.c gss-serv.c kex.h kexgssc.c readconf.c readconf.h ssh-gss.h ++ ssh_config.5 sshconnet2.c ] ++ Add support for the GSSAPIClientIdentity option, which allows the user ++ to specify which GSSAPI identity to use to contact a given server ++ ++20080404 ++ - [ gss-serv.c ] ++ Add code to actually implement GSSAPIStrictAcceptCheck, which had somehow ++ been omitted from a previous version of this patch. Reported by Borislav ++ Stoichkov ++ ++20070317 ++ - [ gss-serv-krb5.c ] ++ Remove C99ism, where new_ccname was being declared in the middle of a ++ function ++ ++20061220 ++ - [ servconf.c ] ++ Make default for GSSAPIStrictAcceptorCheck be Yes, to match previous, and ++ documented, behaviour. Reported by Dan Watson. ++ ++20060910 ++ - [ gss-genr.c kexgssc.c kexgsss.c kex.h monitor.c sshconnect2.c sshd.c ++ ssh-gss.h ] ++ add support for gss-group14-sha1 key exchange mechanisms ++ - [ gss-serv.c servconf.c servconf.h sshd_config sshd_config.5 ] ++ Add GSSAPIStrictAcceptorCheck option to allow the disabling of ++ acceptor principal checking on multi-homed machines. ++ ++ - [ sshd_config ssh_config ] ++ Add settings for GSSAPIKeyExchange and GSSAPITrustDNS to the sample ++ configuration files ++ - [ kexgss.c kegsss.c sshconnect2.c sshd.c ] ++ Code cleanup. Replace strlen/xmalloc/snprintf sequences with xasprintf() ++ Limit length of error messages displayed by client ++ ++20060909 ++ - [ gss-genr.c gss-serv.c ] ++ move ssh_gssapi_acquire_cred() and ssh_gssapi_server_ctx to be server ++ only, where they belong ++ ++ ++20060829 ++ - [ gss-serv-krb5.c ] ++ Fix CCAPI credentials cache name when creating KRB5CCNAME environment ++ variable ++ ++20060828 ++ - [ gss-genr.c ] ++ Avoid Heimdal context freeing problem ++ ++ ++20060818 ++ - [ gss-genr.c ssh-gss.h sshconnect2.c ] ++ Make sure that SPENGO is disabled ++ ++ ++20060421 ++ - [ gssgenr.c, sshconnect2.c ] ++ a few type changes (signed versus unsigned, int versus size_t) to ++ fix compiler errors/warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ kexgssc.c, sshconnect2.c ] ++ fix uninitialized variable warnings ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ gssgenr.c ] ++ pass oid to gss_display_status (helpful when using GSSAPI mechglue) ++ (from jbasney AT ncsa.uiuc.edu) ++ ++ - [ gss-serv-krb5.c ] ++ #ifdef HAVE_GSSAPI_KRB5 should be #ifdef HAVE_GSSAPI_KRB5_H ++ (from jbasney AT ncsa.uiuc.edu) ++ ++ - [ readconf.c, readconf.h, ssh_config.5, sshconnect2.c ++ add client-side GssapiKeyExchange option ++ (from jbasney AT ncsa.uiuc.edu) ++ - [ sshconnect2.c ] ++ add support for GssapiTrustDns option for gssapi-with-mic ++ (from jbasney AT ncsa.uiuc.edu) ++ +diff --git a/openssh-7.2p2/Makefile.in b/openssh-7.2p2/Makefile.in +--- a/openssh-7.2p2/Makefile.in ++++ b/openssh-7.2p2/Makefile.in +@@ -85,18 +85,18 @@ LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ + atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o \ + monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ + 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 \ + ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ + sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ + 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 \ ++ kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o kexgssc.o \ ++ kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o kexgsss.o \ + platform-pledge.o fips.o + + SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ + sshconnect.o sshconnect1.o sshconnect2.o mux.o + + SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ + audit.o audit-bsm.o audit-linux.o platform.o \ + sshpty.o sshlogin.o servconf.o serverloop.o \ +diff --git a/openssh-7.2p2/auth-krb5.c b/openssh-7.2p2/auth-krb5.c +--- a/openssh-7.2p2/auth-krb5.c ++++ b/openssh-7.2p2/auth-krb5.c +@@ -178,18 +178,23 @@ auth_krb5_password(Authctxt *authctxt, c + if (problem) + goto out; + #endif + + authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); + + len = strlen(authctxt->krb5_ticket_file) + 6; + authctxt->krb5_ccname = xmalloc(len); ++#ifdef USE_CCAPI ++ snprintf(authctxt->krb5_ccname, len, "API:%s", ++ authctxt->krb5_ticket_file); ++#else + snprintf(authctxt->krb5_ccname, len, "FILE:%s", + authctxt->krb5_ticket_file); ++#endif + + #ifdef USE_PAM + if (options.use_pam) + do_pam_putenv("KRB5CCNAME", authctxt->krb5_ccname); + #endif + + out: + restore_uid(); +@@ -239,35 +244,42 @@ krb5_cleanup_proc(Authctxt *authctxt) + } + + #ifndef HEIMDAL + krb5_error_code + ssh_krb5_cc_gen(krb5_context ctx, krb5_ccache *ccache) { + int tmpfd, ret, oerrno; + char ccname[40]; + mode_t old_umask; ++#ifdef USE_CCAPI ++ char cctemplate[] = "API:krb5cc_%d"; ++#else ++ char cctemplate[] = "FILE:/tmp/krb5cc_%d_XXXXXXXXXX"; ++#endif + + ret = snprintf(ccname, sizeof(ccname), +- "FILE:/tmp/krb5cc_%d_XXXXXXXXXX", geteuid()); ++ cctemplate, geteuid()); + if (ret < 0 || (size_t)ret >= sizeof(ccname)) + return ENOMEM; + ++#ifndef USE_CCAPI + old_umask = umask(0177); + tmpfd = mkstemp(ccname + strlen("FILE:")); + oerrno = errno; + umask(old_umask); + if (tmpfd == -1) { + logit("mkstemp(): %.100s", strerror(oerrno)); + return oerrno; + } + + if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { + oerrno = errno; + logit("fchmod(): %.100s", strerror(oerrno)); + close(tmpfd); + return oerrno; + } + close(tmpfd); ++#endif + + return (krb5_cc_resolve(ctx, ccname, ccache)); + } + #endif /* !HEIMDAL */ + #endif /* KRB5 */ +diff --git a/openssh-7.2p2/auth2-gss.c b/openssh-7.2p2/auth2-gss.c +--- a/openssh-7.2p2/auth2-gss.c ++++ b/openssh-7.2p2/auth2-gss.c +@@ -1,12 +1,12 @@ + /* $OpenBSD: auth2-gss.c,v 1.22 2015/01/19 20:07:45 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 + * 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 +@@ -48,16 +48,50 @@ + + extern ServerOptions options; + + static int input_gssapi_token(int type, u_int32_t plen, void *ctxt); + static int input_gssapi_mic(int type, u_int32_t plen, void *ctxt); + static int input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt); + static int input_gssapi_errtok(int, u_int32_t, void *); + ++/* ++ * The 'gssapi_keyex' userauth mechanism. ++ */ ++static int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ int authenticated = 0; ++ Buffer b; ++ gss_buffer_desc mic, gssbuf; ++ u_int len; ++ ++ mic.value = packet_get_string(&len); ++ mic.length = len; ++ ++ packet_check_eom(); ++ ++ ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_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)); ++ ++ buffer_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) + */ + static int + userauth_gssapi(Authctxt *authctxt) + { + gss_OID_desc goid = {0, NULL}; +@@ -233,17 +267,18 @@ input_gssapi_exchange_complete(int type, + + /* + * We don't need to check the status, because we're only enabled in + * the dispatcher once the exchange is complete + */ + + packet_check_eom(); + +- authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user)); ++ authenticated = PRIVSEP(ssh_gssapi_userok(authctxt->user, ++ authctxt->pw)); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); + return 0; +@@ -269,31 +304,38 @@ input_gssapi_mic(int type, u_int32_t ple + + ssh_gssapi_buildmic(&b, authctxt->user, authctxt->service, + "gssapi-with-mic"); + + gssbuf.value = buffer_ptr(&b); + gssbuf.length = buffer_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"); + + buffer_free(&b); + free(mic.value); + + authctxt->postponed = 0; + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); + dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); + userauth_finish(authctxt, authenticated, "gssapi-with-mic", NULL); + return 0; + } + ++Authmethod method_gsskeyex = { ++ "gssapi-keyex", ++ userauth_gsskeyex, ++ &options.gss_authentication ++}; ++ + Authmethod method_gssapi = { + "gssapi-with-mic", + userauth_gssapi, + &options.gss_authentication + }; + + #endif /* GSSAPI */ +diff --git a/openssh-7.2p2/auth2.c b/openssh-7.2p2/auth2.c +--- a/openssh-7.2p2/auth2.c ++++ b/openssh-7.2p2/auth2.c +@@ -65,23 +65,25 @@ extern Buffer loginmsg; + /* methods */ + + extern Authmethod method_none; + extern Authmethod method_pubkey; + extern Authmethod method_passwd; + extern Authmethod method_kbdint; + extern Authmethod method_hostbased; + #ifdef GSSAPI ++extern Authmethod method_gsskeyex; + extern Authmethod method_gssapi; + #endif + + Authmethod *authmethods[] = { + &method_none, + &method_pubkey, + #ifdef GSSAPI ++ &method_gsskeyex, + &method_gssapi, + #endif + &method_passwd, + &method_kbdint, + &method_hostbased, + NULL + }; + +diff --git a/openssh-7.2p2/clientloop.c b/openssh-7.2p2/clientloop.c +--- a/openssh-7.2p2/clientloop.c ++++ b/openssh-7.2p2/clientloop.c +@@ -109,16 +109,20 @@ + #include "authfd.h" + #include "atomicio.h" + #include "sshpty.h" + #include "match.h" + #include "msg.h" + #include "ssherr.h" + #include "hostfile.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + /* import options */ + extern Options options; + + /* Flag indicating that stdin should be redirected from /dev/null. */ + extern int stdin_null_flag; + + /* Flag indicating that no shell has been requested */ + extern int no_shell_flag; +@@ -1657,19 +1661,28 @@ client_loop(int have_pty, int escape_cha + max_fd2 = max_fd; + client_wait_until_can_do_something(&readset, &writeset, + &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state)); + + if (quit_pending) + break; + + /* Do channel operations unless rekeying in progress. */ +- if (!ssh_packet_is_rekeying(active_state)) ++ if (!ssh_packet_is_rekeying(active_state)) { + channel_after_select(readset, writeset); + ++#ifdef GSSAPI ++ if (options.gss_renewal_rekey && ++ ssh_gssapi_credentials_updated((Gssctxt *)NULL)) { ++ debug("credentials updated - forcing rekey"); ++ need_rekeying = 1; ++ } ++#endif ++ } ++ + /* Buffer input from the connection. */ + client_process_net_input(readset); + + if (quit_pending) + break; + + if (!compat20) { + /* Buffer data from stdin */ +diff --git a/openssh-7.2p2/configure.ac b/openssh-7.2p2/configure.ac +--- a/openssh-7.2p2/configure.ac ++++ b/openssh-7.2p2/configure.ac +@@ -627,16 +627,40 @@ main() { if (NSVersionOfRunTimeLibrary(" + AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) + AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], + [Define if your resolver libs need this for getrrsetbyname]) + AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) + AC_DEFINE([SSH_TUN_COMPAT_AF], [1], + [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]) + [#include ] + AC_DEFINE([LASTLOG_WRITE_PUTUTXLINE], [1], + [Define if pututxline updates lastlog too]) + ) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], +diff --git a/openssh-7.2p2/gss-genr.c b/openssh-7.2p2/gss-genr.c +--- a/openssh-7.2p2/gss-genr.c ++++ b/openssh-7.2p2/gss-genr.c +@@ -1,12 +1,12 @@ + /* $OpenBSD: gss-genr.c,v 1.23 2015/01/20 23:14:00 deraadt 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 + * 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 +@@ -36,22 +36,177 @@ + #include + #include + #include + + #include "xmalloc.h" + #include "buffer.h" + #include "log.h" + #include "ssh2.h" ++#include "cipher.h" ++#include "key.h" ++#include "kex.h" ++#include + + #include "ssh-gss.h" + + extern u_char *session_id2; + extern u_int session_id2_len; + ++typedef struct { ++ char *encoded; ++ gss_OID oid; ++} ssh_gss_kex_mapping; ++ ++/* ++ * XXX - It would be nice to find a more elegant way of handling the ++ * XXX passing of the key exchange context to the userauth routines ++ */ ++ ++Gssctxt *gss_kex_context = NULL; ++ ++static ssh_gss_kex_mapping *gss_enc2oid = NULL; ++ ++int ++ssh_gssapi_oid_table_ok() { ++ return (gss_enc2oid != NULL); ++} ++ ++/* ++ * Return a list of the gss-group1-sha1 mechanisms supported by this program ++ * ++ * We test mechanisms to ensure that we can use them, to avoid starting ++ * a key exchange with a bad mechanism ++ */ ++ ++char * ++ssh_gssapi_client_mechanisms(const char *host, const char *client) { ++ 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) { ++ Buffer buf; ++ size_t i; ++ int oidpos, enclen; ++ 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)); ++ ++ buffer_init(&buf); ++ ++ 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) ++ buffer_put_char(&buf, ','); ++ ++ buffer_append(&buf, KEX_GSS_GEX_SHA1_ID, ++ sizeof(KEX_GSS_GEX_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP1_SHA1_ID, ++ sizeof(KEX_GSS_GRP1_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ buffer_put_char(&buf, ','); ++ buffer_append(&buf, KEX_GSS_GRP14_SHA1_ID, ++ sizeof(KEX_GSS_GRP14_SHA1_ID) - 1); ++ buffer_append(&buf, encoded, enclen); ++ ++ gss_enc2oid[oidpos].oid = &(gss_supported->elements[i]); ++ gss_enc2oid[oidpos].encoded = encoded; ++ oidpos++; ++ } ++ } ++ gss_enc2oid[oidpos].oid = NULL; ++ gss_enc2oid[oidpos].encoded = NULL; ++ ++ buffer_put_char(&buf, '\0'); ++ ++ mechs = xmalloc(buffer_len(&buf)); ++ buffer_get(&buf, mechs, buffer_len(&buf)); ++ buffer_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; ++} ++ + /* 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) + { + return (ctx != NULL && ctx->oid != GSS_C_NO_OID && + ctx->oid->length == len && + memcmp(ctx->oid->elements, data, len) == 0); + } +@@ -194,17 +349,17 @@ ssh_gssapi_init_ctx(Gssctxt *ctx, int de + int deleg_flag = 0; + + if (deleg_creds) { + deleg_flag = GSS_C_DELEG_FLAG; + debug("Delegating credentials"); + } + + 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); + + if (GSS_ERROR(ctx->major)) + ssh_gssapi_error(ctx); + + return (ctx->major); + } +@@ -224,60 +379,175 @@ ssh_gssapi_import_name(Gssctxt *ctx, con + &gssbuf, GSS_C_NT_HOSTBASED_SERVICE, &ctx->name))) + ssh_gssapi_error(ctx); + + free(gssbuf.value); + 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); + + 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(Buffer *b, const char *user, const char *service, + const char *context) + { + buffer_init(b); + buffer_put_string(b, session_id2, session_id2_len); + buffer_put_char(b, SSH2_MSG_USERAUTH_REQUEST); + buffer_put_cstring(b, user); + buffer_put_cstring(b, service); + buffer_put_cstring(b, context); + } + + 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 && + (memcmp(oid->elements, spnego_oid.elements, oid->length) == 0)) + return 0; /* false */ + + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + major = ssh_gssapi_import_name(*ctx, host); ++ ++ if (!GSS_ERROR(major) && client) ++ major = ssh_gssapi_client_identity(*ctx, client); ++ + if (!GSS_ERROR(major)) { + major = ssh_gssapi_init_ctx(*ctx, 0, GSS_C_NO_BUFFER, &token, + NULL); + gss_release_buffer(&minor, &token); + if ((*ctx)->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor, &(*ctx)->context, + 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; ++ /* TODO: recheck ++ gss_cred_usage_t usage = GSS_C_INITIATE; ++ */ ++ ++ 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/openssh-7.2p2/gss-serv-krb5.c b/openssh-7.2p2/gss-serv-krb5.c +--- a/openssh-7.2p2/gss-serv-krb5.c ++++ b/openssh-7.2p2/gss-serv-krb5.c +@@ -1,12 +1,12 @@ + /* $OpenBSD: gss-serv-krb5.c,v 1.8 2013/07/20 01:55:13 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 + * 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 +@@ -116,18 +116,21 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client + + static void + ssh_gssapi_krb5_storecreds(ssh_gssapi_client *client) + { + krb5_ccache ccache; + krb5_error_code problem; + krb5_principal princ; + OM_uint32 maj_status, min_status; ++ /* TODO: + int len; ++ */ + const char *errmsg; ++ const char *new_ccname; + + if (client->creds == NULL) { + debug("No credentials stored"); + return; + } + + if (ssh_gssapi_krb5_init() == 0) + return; +@@ -176,37 +179,108 @@ ssh_gssapi_krb5_storecreds(ssh_gssapi_cl + + if ((maj_status = gss_krb5_copy_ccache(&min_status, + client->creds, ccache))) { + logit("gss_krb5_copy_ccache() failed"); + krb5_cc_destroy(krb_context, ccache); + return; + } + +- client->store.filename = xstrdup(krb5_cc_get_name(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 ++ xasprintf(&client->store.envval, "FILE:%s", new_ccname); ++ client->store.filename = xstrdup(new_ccname); ++#endif + + #ifdef USE_PAM + if (options.use_pam) + do_pam_putenv(client->store.envvar, client->store.envval); + #endif + + krb5_cc_close(krb_context, ccache); + + 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", + {9, "\x2A\x86\x48\x86\xF7\x12\x01\x02\x02"}, + NULL, + &ssh_gssapi_krb5_userok, + NULL, +- &ssh_gssapi_krb5_storecreds ++ &ssh_gssapi_krb5_storecreds, ++ &ssh_gssapi_krb5_updatecreds + }; + + #endif /* KRB5 */ + + #endif /* GSSAPI */ +diff --git a/openssh-7.2p2/gss-serv.c b/openssh-7.2p2/gss-serv.c +--- a/openssh-7.2p2/gss-serv.c ++++ b/openssh-7.2p2/gss-serv.c +@@ -1,12 +1,12 @@ + /* $OpenBSD: gss-serv.c,v 1.29 2015/05/22 03:50:02 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 + * 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 +@@ -40,27 +40,29 @@ + #include "key.h" + #include "hostfile.h" + #include "auth.h" + #include "log.h" + #include "channels.h" + #include "session.h" + #include "misc.h" + #include "servconf.h" ++#include "uidswap.h" + + #include "ssh-gss.h" ++#include "monitor_wrap.h" + + extern ServerOptions options; + + static ssh_gssapi_client gssapi_client = + { GSS_C_EMPTY_BUFFER, GSS_C_EMPTY_BUFFER, +- GSS_C_NO_CREDENTIAL, NULL, {NULL, NULL, NULL, NULL}}; ++ GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME, NULL, {NULL, NULL, NULL, NULL, 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; + #endif + + ssh_gssapi_mech* supported_mechs[]= { + #ifdef KRB5 + &gssapi_kerberos_mech, +@@ -137,26 +139,51 @@ ssh_gssapi_server_ctx(Gssctxt **ctx, gss + if (*ctx) + ssh_gssapi_delete_ctx(ctx); + ssh_gssapi_build_ctx(ctx); + ssh_gssapi_set_oid(*ctx, oid); + return (ssh_gssapi_acquire_cred(*ctx)); + } + + /* Unprivileged */ ++char * ++ssh_gssapi_server_mechanisms() { ++ gss_OID_set supported; ++ ++ ssh_gssapi_supported_oids(&supported); ++ return (ssh_gssapi_kex_mechs(supported, &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) + { + int i = 0; + OM_uint32 min_status; + int present; + 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, + &supported_mechs[i]->oid, supported, &present))) + present = 0; + if (present) + gss_add_oid_set_member(&min_status, + &supported_mechs[i]->oid, oidset); +@@ -272,32 +299,79 @@ ssh_gssapi_parse_ename(Gssctxt *ctx, gss + /* Extract the client details from a given context. This can only reliably + * be called once for a context */ + + /* Privileged (called from accept_secure_ctx) */ + 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; + +- gss_buffer_desc ename; ++ 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_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; + + while (supported_mechs[i]->name != NULL) { + if (supported_mechs[i]->oid.length == ctx->oid->length && + (memcmp(supported_mechs[i]->oid.elements, + ctx->oid->elements, ctx->oid->length) == 0)) + client->mech = supported_mechs[i]; + i++; + } + + 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); + return (ctx->major); + } + + if ((ctx->major = gss_export_name(&ctx->minor, ctx->client, + &ename))) { +@@ -305,16 +379,18 @@ ssh_gssapi_getclient(Gssctxt *ctx, ssh_g + return (ctx->major); + } + + if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename, + &client->exportedname))) { + 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; + return (ctx->major); + } + + /* As user - called on fatal/exit */ + void +@@ -352,45 +428,124 @@ ssh_gssapi_do_child(char ***envp, u_int + gssapi_client.store.envval); + child_set_env(envp, envsizep, gssapi_client.store.envvar, + gssapi_client.store.envval); + } + } + + /* Privileged */ + int +-ssh_gssapi_userok(char *user) ++ssh_gssapi_userok(char *user, struct passwd *pw) + { + OM_uint32 lmin; + + 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); + gss_release_cred(&lmin, &gssapi_client.creds); + explicit_bzero(&gssapi_client, + sizeof(ssh_gssapi_client)); + return 0; + } + else + debug("ssh_gssapi_userok: Unknown GSSAPI mechanism"); + return (0); + } + + /* Privileged */ +-OM_uint32 +-ssh_gssapi_checkmic(Gssctxt *ctx, gss_buffer_t gssbuf, gss_buffer_t gssmic) ++/* These bits are only used for rekeying. The unpriviledged child is running ++ * as the user, the monitor is root. ++ * ++ * In the child, we want to : ++ * *) Ask the monitor to store our credentials into the store we specify ++ * *) If it succeeds, maybe do a PAM update ++ */ ++ ++/* Stuff for PAM */ ++ ++#ifdef USE_PAM ++static int ssh_gssapi_simple_conv(int n, const struct pam_message **msg, ++ struct pam_response **resp, void *data) + { +- ctx->major = gss_verify_mic(&ctx->minor, ctx->context, +- gssbuf, gssmic, NULL); ++ return (PAM_CONV_ERR); ++} ++#endif + +- return (ctx->major); ++void ++ssh_gssapi_rekey_creds() { ++ int ok; ++ int ret; ++#ifdef USE_PAM ++ pam_handle_t *pamh = NULL; ++ struct pam_conv pamconv = {ssh_gssapi_simple_conv, NULL}; ++ char *envstr; ++#endif ++ ++ if (gssapi_client.store.filename == NULL && ++ gssapi_client.store.envval == NULL && ++ gssapi_client.store.envvar == NULL) ++ return; ++ ++ ok = PRIVSEP(ssh_gssapi_update_creds(&gssapi_client.store)); ++ ++ if (!ok) ++ return; ++ ++ debug("Rekeyed credentials stored successfully"); ++ ++ /* Actually managing to play with the ssh pam stack from here will ++ * be next to impossible. In any case, we may want different options ++ * for rekeying. So, use our own :) ++ */ ++#ifdef USE_PAM ++ if (!use_privsep) { ++ debug("Not even going to try and do PAM with privsep disabled"); ++ return; ++ } ++ ++ ret = pam_start("sshd-rekey", gssapi_client.store.owner->pw_name, ++ &pamconv, &pamh); ++ if (ret) ++ return; ++ ++ xasprintf(&envstr, "%s=%s", gssapi_client.store.envvar, ++ gssapi_client.store.envval); ++ ++ ret = pam_putenv(pamh, envstr); ++ if (!ret) ++ pam_setcred(pamh, PAM_REINITIALIZE_CRED); ++ pam_end(pamh, PAM_SUCCESS); ++#endif ++} ++ ++int ++ssh_gssapi_update_creds(ssh_gssapi_ccache *store) { ++ int ok = 0; ++ ++ /* Check we've got credentials to store */ ++ if (!gssapi_client.updated) ++ return 0; ++ ++ gssapi_client.updated = 0; ++ ++ temporarily_use_uid(gssapi_client.store.owner); ++ if (gssapi_client.mech && gssapi_client.mech->updatecreds) ++ ok = (*gssapi_client.mech->updatecreds)(store, &gssapi_client); ++ else ++ debug("No update function for this mechanism"); ++ ++ restore_uid(); ++ ++ return ok; + } + + #endif +diff --git a/openssh-7.2p2/kex.c b/openssh-7.2p2/kex.c +--- a/openssh-7.2p2/kex.c ++++ b/openssh-7.2p2/kex.c +@@ -51,16 +51,20 @@ + #include "monitor.h" + + #include "ssherr.h" + #include "sshbuf.h" + #include "digest.h" + + #include "fips.h" + ++#ifdef GSSAPI ++#include "ssh-gss.h" ++#endif ++ + #if OPENSSL_VERSION_NUMBER >= 0x00907000L + # if defined(HAVE_EVP_SHA256) + # define evp_ssh_sha256 EVP_sha256 + # else + extern const EVP_MD *evp_ssh_sha256(void); + # endif + #endif + +@@ -104,16 +108,21 @@ static const struct kexalg kexalgs_all[] + { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, + SSH_DIGEST_SHA512 }, + # endif /* OPENSSL_HAS_NISTP521 */ + #endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ + #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) + { KEX_CURVE25519_SHA256, 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}, + }; + + static const struct kexalg kexalgs_fips140_2[] = { + #ifdef WITH_OPENSSL + { KEX_DH14, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, + { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, + #ifdef HAVE_EVP_SHA256 +@@ -125,16 +134,20 @@ static const struct kexalg kexalgs_fips1 + { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, + SSH_DIGEST_SHA384 }, + # ifdef OPENSSL_HAS_NISTP521 + { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, + SSH_DIGEST_SHA512 }, + # endif /* OPENSSL_HAS_NISTP521 */ + #endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++#ifdef GSSAPI ++ { KEX_GSS_GEX_SHA1_ID, KEX_GSS_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, ++ { KEX_GSS_GRP14_SHA1_ID, KEX_GSS_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, ++#endif + { NULL, -1, -1, -1}, + }; + + /* Returns array of macs available depending on selected FIPS mode */ + static const struct kexalg * + fips_select_kexalgs(void) + { + int fips = fips_mode(); +@@ -175,16 +188,22 @@ kex_alg_list(char sep) + static const struct kexalg * + kex_alg_by_name(const char *name) + { + const struct kexalg *k; + + for (k = fips_select_kexalgs(); k->name != NULL; k++) { + if (strcmp(k->name, name) == 0) + return k; ++#ifdef GSSAPI ++ if (strncmp(name, "gss-", 4) == 0) { ++ if (strncmp(k->name, name, strlen(k->name)) == 0) ++ return k; ++ } ++#endif + } + return NULL; + } + + /* Validate KEX method name list */ + int + kex_names_valid(const char *names) + { +diff --git a/openssh-7.2p2/kex.h b/openssh-7.2p2/kex.h +--- a/openssh-7.2p2/kex.h ++++ b/openssh-7.2p2/kex.h +@@ -87,16 +87,19 @@ enum kex_modes { + + enum kex_exchange { + KEX_DH_GRP1_SHA1, + KEX_DH_GRP14_SHA1, + KEX_DH_GEX_SHA1, + KEX_DH_GEX_SHA256, + KEX_ECDH_SHA2, + KEX_C25519_SHA256, ++ KEX_GSS_GRP1_SHA1, ++ KEX_GSS_GRP14_SHA1, ++ KEX_GSS_GEX_SHA1, + KEX_MAX + }; + + #define KEX_INIT_SENT 0x0001 + + struct sshenc { + char *name; + const struct sshcipher *cipher; +@@ -135,16 +138,22 @@ struct kex { + int rsa_sha2; + int ext_info_c; + struct sshbuf *my; + struct sshbuf *peer; + sig_atomic_t done; + 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; + int (*verify_host_key)(struct sshkey *, struct ssh *); + struct sshkey *(*load_host_public_key)(int, int, struct ssh *); + struct sshkey *(*load_host_private_key)(int, int, struct ssh *); + int (*host_key_index)(struct sshkey *, int, struct ssh *); + int (*sign)(struct sshkey *, struct sshkey *, u_char **, size_t *, +@@ -184,16 +193,21 @@ int kex_start_rekex(struct ssh *); + int kexdh_client(struct ssh *); + int kexdh_server(struct ssh *); + int kexgex_client(struct ssh *); + int kexgex_server(struct ssh *); + 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(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 *); + + int kexgex_hash(int, const char *, const char *, + const u_char *, size_t, const u_char *, size_t, const u_char *, size_t, + int, int, int, +diff --git a/openssh-7.2p2/kexgssc.c b/openssh-7.2p2/kexgssc.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/kexgssc.c +@@ -0,0 +1,368 @@ ++/* ++ * 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 "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "digest.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++ ++#include "ssh-gss.h" ++ ++#include "fips.h" ++ ++int ++kexgss_client(struct ssh *ssh) ++{ ++ struct kex *kex = ssh->kex; ++ 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; ++ size_t hashlen; ++ BIGNUM *dh_server_pub = NULL; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *p = NULL; ++ BIGNUM *g = NULL; ++ u_char *kbuf; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ u_char *serverhostkey = NULL; ++ u_char *empty = ""; ++ char *msg; ++ /* TODO: ++ char *lang; ++ */ ++ int type = 0; ++ int first = 1; ++ int nbits = 0, min = fips_dh_grp_min(), max = DH_GRP_MAX; ++ int p_bitlen; ++ 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"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ kex->dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ kex->dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange\n"); ++ nbits = dh_estimate(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); ++ p_bitlen = BN_num_bits(p); ++ if ((g = BN_new()) == NULL) ++ fatal("BN_new() failed"); ++ packet_get_bignum2(g); ++ packet_check_eom(); ++ ++ if (p_bitlen < min || p_bitlen > max) { ++ if (p_bitlen < min && p_bitlen >= DH_GRP_MIN_RFC) ++ logit("DH parameter offered by the server (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted minimum " ++ "via the KexDHMin option.", ++ p_bitlen); ++ fatal("GSSGRP_GEX group out of range: %d !< %d !< %d", ++ min, p_bitlen, max); ++ } ++ ++ kex->dh = dh_new_group(g, p); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ /* Step 1 - e is dh->pub_key */ ++ dh_gen_key(kex->dh, kex->we_need * 8); ++ ++ /* This is f, we initialise it now to make life easier */ ++ dh_server_pub = BN_new(); ++ if (dh_server_pub == NULL) ++ fatal("dh_server_pub == NULL"); ++ ++ token_ptr = GSS_C_NO_BUFFER; ++ ++ do { ++ debug("Calling gss_init_sec_context"); ++ ++ maj_status = ssh_gssapi_init_ctx(ctxt, ++ 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(kex->dh->pub_key); ++ first = 0; ++ } else { ++ packet_start(SSH2_MSG_KEXGSS_CONTINUE); ++ packet_put_string(send_tok.value, ++ send_tok.length); ++ } ++ packet_send(); ++ gss_release_buffer(&min_status, &send_tok); ++ ++ /* If we've sent them data, they should reply */ ++ do { ++ type = packet_read(); ++ if (type == SSH2_MSG_KEXGSS_HOSTKEY) { ++ debug("Received KEXGSS_HOSTKEY"); ++ if (serverhostkey) ++ fatal("Server host key received more than once"); ++ serverhostkey = ++ packet_get_string(&slen); ++ } ++ } while (type == SSH2_MSG_KEXGSS_HOSTKEY); ++ ++ switch (type) { ++ case SSH2_MSG_KEXGSS_CONTINUE: ++ debug("Received GSSAPI_CONTINUE"); ++ if (maj_status == GSS_S_COMPLETE) ++ fatal("GSSAPI Continue received from server when complete"); ++ recv_tok.value = packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ break; ++ case SSH2_MSG_KEXGSS_COMPLETE: ++ debug("Received GSSAPI_COMPLETE"); ++ packet_get_bignum2(dh_server_pub); ++ msg_tok.value = packet_get_string(&strlen); ++ msg_tok.length = strlen; ++ ++ /* Is there a token included? */ ++ if (packet_get_char()) { ++ recv_tok.value= ++ packet_get_string(&strlen); ++ recv_tok.length = strlen; ++ /* If we're already complete - protocol error */ ++ if (maj_status == GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: received token when complete"); ++ } else { ++ /* No token included */ ++ if (maj_status != GSS_S_COMPLETE) ++ packet_disconnect("Protocol error: did not receive final token"); ++ } ++ break; ++ case SSH2_MSG_KEXGSS_ERROR: ++ debug("Received Error"); ++ maj_status = packet_get_int(); ++ min_status = packet_get_int(); ++ msg = packet_get_string(NULL); ++ /* TODO: ++ lang = packet_get_string(NULL); ++ */ ++ fatal("GSSAPI Error: \n%.400s",msg); ++ default: ++ packet_disconnect("Protocol error: didn't expect packet type %d", ++ type); ++ } ++ token_ptr = &recv_tok; ++ } else { ++ /* No data, and not complete */ ++ if (maj_status != GSS_S_COMPLETE) ++ fatal("Not complete, and no token output"); ++ } ++ } while (maj_status & GSS_S_CONTINUE_NEEDED); ++ ++ /* ++ * We _must_ have received a COMPLETE message in reply from the ++ * server, which will have set dh_server_pub and msg_tok ++ */ ++ ++ if (type != SSH2_MSG_KEXGSS_COMPLETE) ++ fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it"); ++ ++ /* Check f in range [1, p-1] */ ++ if (!dh_pub_is_valid(kex->dh, dh_server_pub)) ++ packet_disconnect("bad server public DH value"); ++ ++ /* compute K=f^x mod p */ ++ klen = DH_size(kex->dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_server_pub, kex->dh); ++ if (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); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(kex->my), buffer_len(kex->my), ++ buffer_ptr(kex->peer), buffer_len(kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ kex->dh->pub_key, /* e */ ++ dh_server_pub, /* f */ ++ shared_secret, /* K */ ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->hash_alg, ++ kex->client_version_string, ++ kex->server_version_string, ++ buffer_ptr(kex->my), buffer_len(kex->my), ++ buffer_ptr(kex->peer), buffer_len(kex->peer), ++ (serverhostkey ? serverhostkey : empty), slen, ++ min, nbits, max, ++ kex->dh->p, kex->dh->g, ++ kex->dh->pub_key, ++ dh_server_pub, ++ 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); ++ ++ if (serverhostkey) ++ free(serverhostkey); ++ BN_clear_free(dh_server_pub); ++ ++ /* 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); ++ ++ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++/* TODO: ++out: ++*/ ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ if (dh_server_pub) ++ BN_clear_free(dh_server_pub); ++ if (kbuf) { ++ explicit_bzero(kbuf, klen); ++ free(kbuf); ++ } ++ if (shared_secret) ++ BN_clear_free(shared_secret); ++ /* any errors should have finished as fatal */ ++ return r; ++} ++ ++#endif /* GSSAPI */ +diff --git a/openssh-7.2p2/kexgsss.c b/openssh-7.2p2/kexgsss.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/kexgsss.c +@@ -0,0 +1,317 @@ ++/* ++ * 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 "buffer.h" ++#include "ssh2.h" ++#include "key.h" ++#include "cipher.h" ++#include "digest.h" ++#include "kex.h" ++#include "log.h" ++#include "packet.h" ++#include "dh.h" ++#include "ssh-gss.h" ++#include "monitor_wrap.h" ++#include "servconf.h" ++ ++#include "fips.h" ++ ++extern ServerOptions options; ++ ++void ssh_gssapi_rekey_creds(); ++ ++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; ++ u_int slen, klen, kout; ++ size_t hashlen; ++ u_char *kbuf; ++ u_char hash[SSH_DIGEST_MAX_LENGTH]; ++ int min = -1, max = -1, nbits = -1; ++ BIGNUM *shared_secret = NULL; ++ BIGNUM *dh_client_pub = NULL; ++ int type = 0; ++ gss_OID oid; ++ char *mechs; ++ 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"); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ kex->dh = dh_new_group1(); ++ break; ++ case KEX_GSS_GRP14_SHA1: ++ kex->dh = dh_new_group14(); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ debug("Doing group exchange"); ++ packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ); ++ min = packet_get_int(); ++ nbits = packet_get_int(); ++ max = packet_get_int(); ++ min = MAX(fips_dh_grp_min(), min); ++ max = MIN(DH_GRP_MAX, max); ++ packet_check_eom(); ++ if (max < min || nbits < min || max < nbits) { ++ if (nbits < min && nbits >= DH_GRP_MIN_RFC) ++ logit("DH parameter requested by the client (%d bits) " ++ "is considered insecure. " ++ "You can lower the accepted minimum " ++ "via the KexDHMin option.", ++ nbits); ++ fatal("GSS_GEX, bad parameters: %d !< %d !< %d", ++ min, nbits, max); ++ } ++ kex->dh = PRIVSEP(choose_dh(min, nbits, max)); ++ if (kex->dh == NULL) ++ packet_disconnect("Protocol error: no matching group found"); ++ ++ packet_start(SSH2_MSG_KEXGSS_GROUP); ++ packet_put_bignum2(kex->dh->p); ++ packet_put_bignum2(kex->dh->g); ++ packet_send(); ++ ++ packet_write_wait(); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ dh_gen_key(kex->dh, 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(kex->dh, dh_client_pub)) ++ packet_disconnect("bad client public DH value"); ++ ++ klen = DH_size(kex->dh); ++ kbuf = xmalloc(klen); ++ kout = DH_compute_key(kbuf, dh_client_pub, kex->dh); ++ if (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); ++ ++ switch (kex->kex_type) { ++ case KEX_GSS_GRP1_SHA1: ++ case KEX_GSS_GRP14_SHA1: ++ kex_dh_hash( ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(kex->peer), buffer_len(kex->peer), ++ buffer_ptr(kex->my), buffer_len(kex->my), ++ NULL, 0, /* Change this if we start sending host keys */ ++ dh_client_pub, kex->dh->pub_key, shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ case KEX_GSS_GEX_SHA1: ++ kexgex_hash( ++ kex->hash_alg, ++ kex->client_version_string, kex->server_version_string, ++ buffer_ptr(kex->peer), buffer_len(kex->peer), ++ buffer_ptr(kex->my), buffer_len(kex->my), ++ NULL, 0, ++ min, nbits, max, ++ kex->dh->p, kex->dh->g, ++ dh_client_pub, ++ kex->dh->pub_key, ++ shared_secret, ++ hash, &hashlen ++ ); ++ break; ++ default: ++ fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type); ++ } ++ ++ BN_clear_free(dh_client_pub); ++ ++ 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); ++ packet_put_bignum2(kex->dh->pub_key); ++ packet_put_string(msg_tok.value,msg_tok.length); ++ ++ if (send_tok.length != 0) { ++ packet_put_char(1); /* true */ ++ packet_put_string(send_tok.value, send_tok.length); ++ } else { ++ packet_put_char(0); /* false */ ++ } ++ packet_send(); ++ ++ gss_release_buffer(&min_status, &send_tok); ++ gss_release_buffer(&min_status, &msg_tok); ++ ++ if (gss_kex_context == NULL) ++ gss_kex_context = ctxt; ++ else ++ ssh_gssapi_delete_ctx(&ctxt); ++ ++ if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0) ++ r = kex_send_newkeys(ssh); ++ ++/* TODO: ++out: ++*/ ++ explicit_bzero(hash, sizeof(hash)); ++ DH_free(kex->dh); ++ kex->dh = NULL; ++ if (dh_client_pub) ++ BN_clear_free(dh_client_pub); ++ if (kbuf) { ++ explicit_bzero(kbuf, klen); ++ free(kbuf); ++ } ++ if (shared_secret) ++ BN_clear_free(shared_secret); ++ ++ /* If this was a rekey, then save out any delegated credentials we ++ * just exchanged. */ ++ if (options.gss_store_rekey) ++ ssh_gssapi_rekey_creds(); ++ ++ return r; ++} ++#endif /* GSSAPI */ +diff --git a/openssh-7.2p2/monitor.c b/openssh-7.2p2/monitor.c +--- a/openssh-7.2p2/monitor.c ++++ b/openssh-7.2p2/monitor.c +@@ -151,16 +151,18 @@ int mm_answer_pam_respond(int, Buffer *) + int mm_answer_pam_free_ctx(int, Buffer *); + #endif + + #ifdef GSSAPI + int mm_answer_gss_setup_ctx(int, Buffer *); + int mm_answer_gss_accept_ctx(int, Buffer *); + int mm_answer_gss_userok(int, Buffer *); + int mm_answer_gss_checkmic(int, Buffer *); ++int mm_answer_gss_sign(int, Buffer *); ++int mm_answer_gss_updatecreds(int, Buffer *); + #endif + + #ifdef SSH_AUDIT_EVENTS + int mm_answer_audit_event(int, Buffer *); + int mm_answer_audit_command(int, Buffer *); + #endif + + static int monitor_read_log(struct monitor *); +@@ -228,21 +230,28 @@ struct mon_table mon_dispatch_proto20[] + #endif + {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, + {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, + #ifdef GSSAPI + {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, + {MONITOR_REQ_GSSSTEP, MON_ISAUTH, mm_answer_gss_accept_ctx}, + {MONITOR_REQ_GSSUSEROK, MON_AUTH, mm_answer_gss_userok}, + {MONITOR_REQ_GSSCHECKMIC, MON_ISAUTH, 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 + {MONITOR_REQ_SIGN, 0, mm_answer_sign}, + {MONITOR_REQ_PTY, 0, mm_answer_pty}, + {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, + {MONITOR_REQ_TERM, 0, mm_answer_term}, + #ifdef SSH_AUDIT_EVENTS +@@ -347,16 +356,20 @@ monitor_child_preauth(Authctxt *_authctx + authctxt->loginmsg = &loginmsg; + + if (compat20) { + mon_dispatch = mon_dispatch_proto20; + + /* 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 + } else { + mon_dispatch = mon_dispatch_proto15; + + monitor_permit(mon_dispatch, MONITOR_REQ_SESSKEY, 1); + } + + /* The first few requests do not require asynchronous access */ + while (!authenticated) { +@@ -455,16 +468,20 @@ monitor_child_postauth(struct monitor *p + + if (compat20) { + mon_dispatch = mon_dispatch_postauth20; + + /* Permit requests for moduli and signatures */ + 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 + } else { + mon_dispatch = mon_dispatch_postauth15; + monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); + } + if (!no_pty_flag) { + monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); + monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); + } +@@ -1856,16 +1873,23 @@ monitor_apply_keystate(struct monitor *p + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kexecdh_server; + # 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; + kex->sign = sshd_hostkey_sign; + } + + /* Update with new address */ + if (options.compression) { +@@ -1955,16 +1979,19 @@ monitor_reinit(struct monitor *mon) + #ifdef GSSAPI + int + mm_answer_gss_setup_ctx(int sock, Buffer *m) + { + gss_OID_desc goid; + OM_uint32 major; + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + goid.elements = buffer_get_string(m, &len); + goid.length = len; + + major = ssh_gssapi_server_ctx(&gsscontext, &goid); + + free(goid.elements); + + buffer_clear(m); +@@ -1982,16 +2009,19 @@ int + mm_answer_gss_accept_ctx(int sock, Buffer *m) + { + gss_buffer_desc in; + gss_buffer_desc out = GSS_C_EMPTY_BUFFER; + OM_uint32 major, minor; + OM_uint32 flags = 0; /* GSI needs this */ + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + in.value = buffer_get_string(m, &len); + in.length = len; + major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); + free(in.value); + + buffer_clear(m); + buffer_put_int(m, major); + buffer_put_string(m, out.value, out.length); +@@ -1999,27 +2029,31 @@ mm_answer_gss_accept_ctx(int sock, Buffe + mm_request_send(sock, MONITOR_ANS_GSSSTEP, m); + + gss_release_buffer(&minor, &out); + + if (major == GSS_S_COMPLETE) { + 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); + } + + int + mm_answer_gss_checkmic(int sock, Buffer *m) + { + gss_buffer_desc gssbuf, mic; + OM_uint32 ret; + u_int len; + ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ + gssbuf.value = buffer_get_string(m, &len); + gssbuf.length = len; + mic.value = buffer_get_string(m, &len); + mic.length = len; + + ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic); + + free(gssbuf.value); +@@ -2036,23 +2070,95 @@ mm_answer_gss_checkmic(int sock, Buffer + return (0); + } + + int + mm_answer_gss_userok(int sock, Buffer *m) + { + int authenticated; + +- authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ ++ authenticated = authctxt->valid && ++ ssh_gssapi_userok(authctxt->user, authctxt->pw); + + buffer_clear(m); + buffer_put_int(m, authenticated); + + debug3("%s: sending result %d", __func__, authenticated); + mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); + + auth_method = "gssapi-with-mic"; + + /* Monitor loop will terminate if authenticated */ + return (authenticated); + } ++ ++int ++mm_answer_gss_sign(int socket, Buffer *m) ++{ ++ gss_buffer_desc data; ++ gss_buffer_desc hash = GSS_C_EMPTY_BUFFER; ++ OM_uint32 major, minor; ++ u_int len; ++ ++ if (!options.gss_authentication && !options.gss_keyex) ++ fatal("In GSSAPI monitor when GSSAPI is disabled"); ++ ++ data.value = buffer_get_string(m, &len); ++ data.length = len; ++ 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); ++ ++ buffer_clear(m); ++ buffer_put_int(m, major); ++ buffer_put_string(m, hash.value, hash.length); ++ ++ 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, Buffer *m) { ++ ssh_gssapi_ccache store; ++ int ok; ++ ++ store.filename = buffer_get_string(m, NULL); ++ store.envvar = buffer_get_string(m, NULL); ++ store.envval = buffer_get_string(m, NULL); ++ ++ ok = ssh_gssapi_update_creds(&store); ++ ++ free(store.filename); ++ free(store.envvar); ++ free(store.envval); ++ ++ buffer_clear(m); ++ buffer_put_int(m, ok); ++ ++ mm_request_send(socket, MONITOR_ANS_GSSUPCREDS, m); ++ ++ return(0); ++} ++ + #endif /* GSSAPI */ + +diff --git a/openssh-7.2p2/monitor.h b/openssh-7.2p2/monitor.h +--- a/openssh-7.2p2/monitor.h ++++ b/openssh-7.2p2/monitor.h +@@ -60,16 +60,19 @@ enum monitor_reqtype { + MONITOR_REQ_PAM_START = 100, + MONITOR_REQ_PAM_ACCOUNT = 102, MONITOR_ANS_PAM_ACCOUNT = 103, + MONITOR_REQ_PAM_INIT_CTX = 104, MONITOR_ANS_PAM_INIT_CTX = 105, + MONITOR_REQ_PAM_QUERY = 106, MONITOR_ANS_PAM_QUERY = 107, + MONITOR_REQ_PAM_RESPOND = 108, MONITOR_ANS_PAM_RESPOND = 109, + MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, + MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, + ++ MONITOR_REQ_GSSSIGN = 201, MONITOR_ANS_GSSSIGN = 202, ++ MONITOR_REQ_GSSUPCREDS = 203, MONITOR_ANS_GSSUPCREDS = 204, ++ + }; + + struct mm_master; + struct monitor { + int m_recvfd; + int m_sendfd; + int m_log_recvfd; + int m_log_sendfd; +diff --git a/openssh-7.2p2/monitor_wrap.c b/openssh-7.2p2/monitor_wrap.c +--- a/openssh-7.2p2/monitor_wrap.c ++++ b/openssh-7.2p2/monitor_wrap.c +@@ -1063,27 +1063,72 @@ mm_ssh_gssapi_checkmic(Gssctxt *ctx, gss + &m); + + major = buffer_get_int(&m); + buffer_free(&m); + return(major); + } + + int +-mm_ssh_gssapi_userok(char *user) ++mm_ssh_gssapi_userok(char *user, struct passwd *pw) + { + Buffer m; + int authenticated = 0; + + buffer_init(&m); + + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUSEROK, &m); + mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUSEROK, + &m); + + authenticated = buffer_get_int(&m); + + buffer_free(&m); + 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) ++{ ++ Buffer m; ++ OM_uint32 major; ++ u_int len; ++ ++ buffer_init(&m); ++ buffer_put_string(&m, data->value, data->length); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSSIGN, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSSIGN, &m); ++ ++ major = buffer_get_int(&m); ++ hash->value = buffer_get_string(&m, &len); ++ hash->length = len; ++ ++ buffer_free(&m); ++ ++ return(major); ++} ++ ++int ++mm_ssh_gssapi_update_creds(ssh_gssapi_ccache *store) ++{ ++ Buffer m; ++ int ok; ++ ++ buffer_init(&m); ++ ++ buffer_put_cstring(&m, store->filename ? store->filename : ""); ++ buffer_put_cstring(&m, store->envvar ? store->envvar : ""); ++ buffer_put_cstring(&m, store->envval ? store->envval : ""); ++ ++ mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_GSSUPCREDS, &m); ++ mm_request_receive_expect(pmonitor->m_recvfd, MONITOR_ANS_GSSUPCREDS, &m); ++ ++ ok = buffer_get_int(&m); ++ ++ buffer_free(&m); ++ ++ return (ok); ++} ++ + #endif /* GSSAPI */ + +diff --git a/openssh-7.2p2/monitor_wrap.h b/openssh-7.2p2/monitor_wrap.h +--- a/openssh-7.2p2/monitor_wrap.h ++++ b/openssh-7.2p2/monitor_wrap.h +@@ -53,18 +53,20 @@ int mm_key_verify(Key *, u_char *, u_int + int mm_auth_rsa_key_allowed(struct passwd *, BIGNUM *, Key **); + int mm_auth_rsa_verify_response(Key *, BIGNUM *, u_char *); + BIGNUM *mm_auth_rsa_generate_challenge(Key *); + + #ifdef GSSAPI + 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 + void mm_start_pam(struct Authctxt *); + u_int mm_do_pam_account(void); + void *mm_sshpam_init_ctx(struct Authctxt *); + int mm_sshpam_query(void *, char **, char **, u_int *, char ***, u_int **); + int mm_sshpam_respond(void *, u_int, char **); +diff --git a/openssh-7.2p2/readconf.c b/openssh-7.2p2/readconf.c +--- a/openssh-7.2p2/readconf.c ++++ b/openssh-7.2p2/readconf.c +@@ -145,16 +145,18 @@ typedef enum { + oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, + oPubkeyAuthentication, + oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, + oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, + oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, + oClearAllForwardings, oNoHostAuthenticationForLocalhost, + oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, + oAddressFamily, oGssAuthentication, oGssDelegateCreds, ++ oGssTrustDns, oGssKeyEx, oGssClientIdentity, oGssRenewalRekey, ++ oGssServerIdentity, + oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, + oSendEnv, oControlPath, oControlMaster, oControlPersist, + oHashKnownHosts, + oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, + oVisualHostKey, + oKexAlgorithms, oKexDHMin, + oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, + oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, +@@ -191,20 +193,30 @@ static struct { + { "challengeresponseauthentication", oChallengeResponseAuthentication }, + { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ + { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ + { "kerberosauthentication", oUnsupported }, + { "kerberostgtpassing", oUnsupported }, + { "afstokenpassing", oUnsupported }, + #if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, ++ { "gssapikeyexchange", oGssKeyEx }, + { "gssapidelegatecredentials", oGssDelegateCreds }, ++ { "gssapitrustdns", oGssTrustDns }, ++ { "gssapiclientidentity", oGssClientIdentity }, ++ { "gssapiserveridentity", oGssServerIdentity }, ++ { "gssapirenewalforcesrekey", oGssRenewalRekey }, + #else + { "gssapiauthentication", oUnsupported }, ++ { "gssapikeyexchange", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, ++ { "gssapitrustdns", oUnsupported }, ++ { "gssapiclientidentity", oUnsupported }, ++ { "gssapiserveridentity", oUnsupported }, ++ { "gssapirenewalforcesrekey", oUnsupported }, + #endif + { "fallbacktorsh", oDeprecated }, + { "usersh", oDeprecated }, + { "identityfile", oIdentityFile }, + { "identityfile2", oIdentityFile }, /* obsolete */ + { "identitiesonly", oIdentitiesOnly }, + { "certificatefile", oCertificateFile }, + { "addkeystoagent", oAddKeysToAgent }, +@@ -928,20 +940,40 @@ parse_time: + case oChallengeResponseAuthentication: + intptr = &options->challenge_response_authentication; + goto parse_flag; + + case oGssAuthentication: + 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; + + case oCheckHostIP: + intptr = &options->check_host_ip; + goto parse_flag; + +@@ -1671,17 +1703,22 @@ initialize_options(Options * options) + options->fwd_opts.gateway_ports = -1; + options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; + options->fwd_opts.streamlocal_bind_unlink = -1; + options->use_privileged_port = -1; + options->rsa_authentication = -1; + 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; + options->rhosts_rsa_authentication = -1; + options->hostbased_authentication = -1; + options->batch_mode = -1; + options->check_host_ip = -1; + options->strict_host_key_checking = -1; +@@ -1801,18 +1838,24 @@ fill_default_options(Options * options) + if (options->rsa_authentication == -1) + options->rsa_authentication = 1; + if (options->pubkey_authentication == -1) + options->pubkey_authentication = 1; + if (options->challenge_response_authentication == -1) + 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) + options->kbd_interactive_authentication = 1; + if (options->rhosts_rsa_authentication == -1) + options->rhosts_rsa_authentication = 0; + if (options->hostbased_authentication == -1) + options->hostbased_authentication = 0; +diff --git a/openssh-7.2p2/readconf.h b/openssh-7.2p2/readconf.h +--- a/openssh-7.2p2/readconf.h ++++ b/openssh-7.2p2/readconf.h +@@ -40,17 +40,22 @@ typedef struct { + int rhosts_rsa_authentication; /* Try rhosts with RSA + * authentication. */ + int rsa_authentication; /* Try RSA authentication. */ + int pubkey_authentication; /* Try ssh2 pubkey authentication. */ + int hostbased_authentication; /* ssh2's rhosts_rsa */ + 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. */ + char *kbd_interactive_devices; /* Keyboard-interactive auth devices. */ + int batch_mode; /* Batch mode: do not ask for passwords. */ + int check_host_ip; /* Also keep track of keys for IP address */ + int strict_host_key_checking; /* Strict host key checking. */ + int compression; /* Compress packets in both directions. */ +diff --git a/openssh-7.2p2/servconf.c b/openssh-7.2p2/servconf.c +--- a/openssh-7.2p2/servconf.c ++++ b/openssh-7.2p2/servconf.c +@@ -118,18 +118,20 @@ initialize_server_options(ServerOptions + options->rsa_authentication = -1; + options->pubkey_authentication = -1; + options->pubkey_key_types = NULL; + options->kerberos_authentication = -1; + options->kerberos_or_local_passwd = -1; + 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; + options->permit_empty_passwd = -1; + options->permit_user_env = -1; + options->use_login = -1; + options->compression = -1; + options->rekey_limit = -1; +@@ -318,20 +320,24 @@ fill_default_server_options(ServerOption + if (options->kerberos_or_local_passwd == -1) + options->kerberos_or_local_passwd = 1; + if (options->kerberos_ticket_cleanup == -1) + options->kerberos_ticket_cleanup = 1; + if (options->kerberos_get_afs_token == -1) + 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 = 0; ++ 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) + options->kbd_interactive_authentication = 0; + if (options->challenge_response_authentication == -1) + options->challenge_response_authentication = 1; + if (options->permit_empty_passwd == -1) + options->permit_empty_passwd = 0; +@@ -452,16 +458,17 @@ typedef enum { + sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, + sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedKeyTypes, + sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, + sBanner, sUseDNS, sHostbasedAuthentication, + sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, + sHostKeyAlgorithms, + sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, + sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, ++ sGssKeyEx, sGssStoreRekey, + sAcceptEnv, sPermitTunnel, + sMatch, sPermitOpen, sForceCommand, sChrootDirectory, + sUsePrivilegeSeparation, sAllowAgentForwarding, + sHostCertificate, + sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, + sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, + sKexAlgorithms, sKexDHMin, + sIPQoS, sVersionAddendum, +@@ -529,21 +536,27 @@ static struct { + { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, + #endif + { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, + { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, + #ifdef GSSAPI + { "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 }, + { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ + { "checkmail", sDeprecated, SSHCFG_GLOBAL }, + { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, + { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, + { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, +@@ -1282,24 +1295,32 @@ process_server_config_line(ServerOptions + case sKerberosGetAFSToken: + intptr = &options->kerberos_get_afs_token; + goto parse_flag; + + case sGssAuthentication: + 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; + + case sGssStrictAcceptor: + 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; + + case sKbdInteractiveAuthentication: + intptr = &options->kbd_interactive_authentication; + goto parse_flag; + +@@ -2051,16 +2072,20 @@ copy_set_server_options(ServerOptions *d + { + #define M_CP_INTOPT(n) do {\ + if (src->n != -1) \ + dst->n = src->n; \ + } while (0) + + M_CP_INTOPT(password_authentication); + M_CP_INTOPT(gss_authentication); ++ M_CP_INTOPT(gss_keyex); ++ M_CP_INTOPT(gss_cleanup_creds); ++ M_CP_INTOPT(gss_strict_acceptor); ++ M_CP_INTOPT(gss_store_rekey); + M_CP_INTOPT(rsa_authentication); + M_CP_INTOPT(pubkey_authentication); + M_CP_INTOPT(kerberos_authentication); + M_CP_INTOPT(hostbased_authentication); + M_CP_INTOPT(hostbased_uses_name_from_packet_only); + M_CP_INTOPT(kbd_interactive_authentication); + M_CP_INTOPT(permit_root_login); + M_CP_INTOPT(permit_empty_passwd); +@@ -2338,17 +2363,20 @@ dump_config(ServerOptions *o) + dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); + dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); + # ifdef USE_AFS + dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); + # endif + #endif + #ifdef GSSAPI + dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); ++ dump_cfg_fmtint(sGssKeyEx, o->gss_keyex); + dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); ++ dump_cfg_fmtint(sGssStrictAcceptor, o->gss_strict_acceptor); ++ dump_cfg_fmtint(sGssStoreRekey, o->gss_store_rekey); + #endif + dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); + dump_cfg_fmtint(sKbdInteractiveAuthentication, + o->kbd_interactive_authentication); + dump_cfg_fmtint(sChallengeResponseAuthentication, + o->challenge_response_authentication); + dump_cfg_fmtint(sPrintMotd, o->print_motd); + #ifndef DISABLE_LASTLOG +diff --git a/openssh-7.2p2/servconf.h b/openssh-7.2p2/servconf.h +--- a/openssh-7.2p2/servconf.h ++++ b/openssh-7.2p2/servconf.h +@@ -11,16 +11,18 @@ + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + + #ifndef SERVCONF_H + #define SERVCONF_H + ++#include "misc.h" ++ + #define MAX_PORTS 256 /* Max # ports. */ + + #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ + #define MAX_DENY_USERS 256 /* Max # users on deny list. */ + #define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ + #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ + #define MAX_SUBSYSTEMS 256 /* Max # subsystems. */ + #define MAX_HOSTKEYS 256 /* Max # hostkeys. */ +@@ -114,18 +116,20 @@ typedef struct { + * authentication mechanism, + * such as SecurID or + * /etc/passwd */ + int kerberos_ticket_cleanup; /* If true, destroy ticket + * file on logout. */ + 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 */ + int challenge_response_authentication; + int permit_empty_passwd; /* If false, do not permit empty + * passwords. */ + int permit_user_env; /* If true, read ~/.ssh/environment */ + int use_login; /* If true, login(1) is used */ +diff --git a/openssh-7.2p2/ssh-gss.h b/openssh-7.2p2/ssh-gss.h +--- a/openssh-7.2p2/ssh-gss.h ++++ b/openssh-7.2p2/ssh-gss.h +@@ -1,11 +1,11 @@ + /* $OpenBSD: ssh-gss.h,v 1.11 2014/02/26 20:28:44 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 + * 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 +@@ -56,53 +56,70 @@ + #define SSH2_MSG_USERAUTH_GSSAPI_TOKEN 61 + #define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE 63 + #define SSH2_MSG_USERAUTH_GSSAPI_ERROR 64 + #define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK 65 + #define SSH2_MSG_USERAUTH_GSSAPI_MIC 66 + + #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; + + 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 { + char *enc_name; + char *name; + gss_OID_desc oid; + int (*dochild) (ssh_gssapi_client *); + 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 { + OM_uint32 major; /* both */ + OM_uint32 minor; /* both */ + gss_ctx_id_t context; /* both */ + gss_name_t name; /* both */ + 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); + void ssh_gssapi_set_oid(Gssctxt *, gss_OID); + void ssh_gssapi_supported_oids(gss_OID_set *); + ssh_gssapi_mech *ssh_gssapi_get_ctype(Gssctxt *); + void ssh_gssapi_prepare_supported_oids(void); + OM_uint32 ssh_gssapi_test_oid_supported(OM_uint32 *, gss_OID, int *); +@@ -114,21 +131,35 @@ OM_uint32 ssh_gssapi_accept_ctx(Gssctxt + gss_buffer_desc *, gss_buffer_desc *, OM_uint32 *); + OM_uint32 ssh_gssapi_getclient(Gssctxt *, ssh_gssapi_client *); + void ssh_gssapi_error(Gssctxt *); + char *ssh_gssapi_last_error(Gssctxt *, OM_uint32 *, OM_uint32 *); + void ssh_gssapi_build_ctx(Gssctxt **); + void ssh_gssapi_delete_ctx(Gssctxt **); + OM_uint32 ssh_gssapi_sign(Gssctxt *, gss_buffer_t, gss_buffer_t); + void ssh_gssapi_buildmic(Buffer *, 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); + ++char *ssh_gssapi_server_mechanisms(void); ++int ssh_gssapi_oid_table_ok(); ++ ++int ssh_gssapi_update_creds(ssh_gssapi_ccache *store); + #endif /* GSSAPI */ + + #endif /* _SSH_GSS_H */ +diff --git a/openssh-7.2p2/ssh_config b/openssh-7.2p2/ssh_config +--- a/openssh-7.2p2/ssh_config ++++ b/openssh-7.2p2/ssh_config +@@ -42,16 +42,18 @@ Host * + SendEnv LC_IDENTIFICATION LC_ALL + + # RhostsRSAAuthentication no + # RSAAuthentication yes + # PasswordAuthentication yes + # HostbasedAuthentication no + # GSSAPIAuthentication no + # GSSAPIDelegateCredentials no ++# GSSAPIKeyExchange no ++# GSSAPITrustDNS no + # BatchMode no + # CheckHostIP yes + # AddressFamily any + # ConnectTimeout 0 + # StrictHostKeyChecking ask + # IdentityFile ~/.ssh/identity + # IdentityFile ~/.ssh/id_rsa + # IdentityFile ~/.ssh/id_dsa +diff --git a/openssh-7.2p2/ssh_config.0 b/openssh-7.2p2/ssh_config.0 +--- a/openssh-7.2p2/ssh_config.0 ++++ b/openssh-7.2p2/ssh_config.0 +@@ -445,20 +445,51 @@ DESCRIPTION + Specifies one or more files to use for the global host key + database, separated by whitespace. The default is + /etc/ssh/ssh_known_hosts, /etc/ssh/ssh_known_hosts2. + + GSSAPIAuthentication + Specifies whether user authentication based on GSSAPI is allowed. + The default is M-bM-^@M-^\noM-bM-^@M-^]. + ++ GSSAPIKeyExchange ++ Specifies whether key exchange based on GSSAPI may be used. When ++ using GSSAPI key exchange the server need not have a host key. ++ The default is no. ++ Note that this option applies to protocol version 2 only. ++ ++ GSSAPIClientIdentity ++ If set, specifies the GSSAPI client identity that ssh should use ++ when connecting to the server. The default is unset, which means ++ that the default identity will be used. ++ ++ GSSAPIServerIdentity ++ If set, specifies the GSSAPI server identity that ssh should expect ++ when connecting to the server. The default is unset, which means ++ that the expected GSSAPI server identity will be determined from ++ the target hostname. ++ + GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. The default is + M-bM-^@M-^\noM-bM-^@M-^]. + ++ GSSAPIRenewalForcesRekey ++ If set to yes then renewal of the client's GSSAPI credentials will ++ force the rekeying of the ssh connection. With a compatible server, ++ this can delegate the renewed credentials to a session on the ++ server. The default is no. ++ ++ GSSAPITrustDns ++ Set to yes to indicate that the DNS is trusted to securely ++ canonicalize the name of the host being connected to. If no, the ++ hostname entered on the command line will be passed untouched to ++ the GSSAPI library. The default is no. ++ This option only applies to protocol version 2 connections using ++ GSSAPI. ++ + HashKnownHosts + Indicates that ssh(1) should hash host names and addresses when + they are added to ~/.ssh/known_hosts. These hashed names may be + used normally by ssh(1) and sshd(8), but they do not reveal + identifying information should the file's contents be disclosed. + The default is M-bM-^@M-^\noM-bM-^@M-^]. Note that existing names and addresses in + known hosts files will not be converted automatically, but may be + manually hashed using ssh-keygen(1). +diff --git a/openssh-7.2p2/ssh_config.5 b/openssh-7.2p2/ssh_config.5 +--- a/openssh-7.2p2/ssh_config.5 ++++ b/openssh-7.2p2/ssh_config.5 +@@ -823,20 +823,52 @@ Specifies one or more files to use for t + host key database, separated by whitespace. + The default is + .Pa /etc/ssh/ssh_known_hosts , + .Pa /etc/ssh/ssh_known_hosts2 . + .It Cm GSSAPIAuthentication + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Dq 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 . ++Note that this option applies to protocol version 2 only. ++.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 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 GSSAPIDelegateCredentials + Forward (delegate) credentials to the server. + 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 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 . ++This option only applies to protocol version 2 connections using GSSAPI. + .It Cm HashKnownHosts + Indicates that + .Xr ssh 1 + should hash host names and addresses when they are added to + .Pa ~/.ssh/known_hosts . + These hashed names may be used normally by + .Xr ssh 1 + and +diff --git a/openssh-7.2p2/sshconnect2.c b/openssh-7.2p2/sshconnect2.c +--- a/openssh-7.2p2/sshconnect2.c ++++ b/openssh-7.2p2/sshconnect2.c +@@ -155,20 +155,44 @@ order_hostkeyalgs(char *host, struct soc + + void + ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) + { + char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; + char *s; + 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 = myproposal[PROPOSAL_KEX_ALGS]; ++ ++ if (options.gss_trust_dns) ++ gss_host = (char *)get_canonical_hostname(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(&myproposal[PROPOSAL_KEX_ALGS], ++ "%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); + myproposal[PROPOSAL_ENC_ALGS_CTOS] = + compat_cipher_proposal(options.ciphers); + myproposal[PROPOSAL_ENC_ALGS_STOC] = + compat_cipher_proposal(options.ciphers); + if (options.compression) { +@@ -190,16 +214,27 @@ ssh_kex2(char *host, struct sockaddr *ho + /* Enforce default */ + options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); + /* Prefer algorithms that we already have keys for */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal( + order_hostkeyalgs(host, hostaddr, port)); + } + ++#ifdef GSSAPI ++ /* If we've got GSSAPI algorithms, then we also support the ++ * 'null' hostkey, as a last resort */ ++ if (options.gss_keyex && gss) { ++ orig = myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; ++ xasprintf(&myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS], ++ "%s,null", orig); ++ free(gss); ++ } ++#endif ++ + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits((u_int32_t)options.rekey_limit, + (time_t)options.rekey_interval); + + /* start key exchange */ + if ((r = kex_setup(active_state, myproposal)) != 0) + fatal("kex_setup: %s", ssh_err(r)); + kex = active_state->kex; +@@ -208,20 +243,40 @@ ssh_kex2(char *host, struct sockaddr *ho + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kexecdh_client; + # endif + #endif + kex->kex[KEX_C25519_SHA256] = kexc25519_client; ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GRP14_SHA1] = kexgss_client; ++ kex->kex[KEX_GSS_GEX_SHA1] = kexgss_client; ++ } ++#endif + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->verify_host_key=&verify_host_key_callback; + ++#ifdef GSSAPI ++ if (options.gss_keyex) { ++ kex->gss_deleg_creds = options.gss_deleg_creds; ++ kex->gss_trust_dns = options.gss_trust_dns; ++ kex->gss_client = options.gss_client_identity; ++ if (options.gss_server_identity) { ++ kex->gss_host = options.gss_server_identity; ++ } else { ++ kex->gss_host = gss_host; ++ } ++ } ++#endif ++ + dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); + + /* remove ext-info from the KEX proposals for rekeying */ + myproposal[PROPOSAL_KEX_ALGS] = + compat_kex_proposal(options.kex_algorithms); + if ((r = kex_prop2buf(kex->my, myproposal)) != 0) + fatal("kex_prop2buf: %s", ssh_err(r)); + +@@ -306,31 +361,37 @@ int userauth_hostbased(Authctxt *); + + #ifdef GSSAPI + int userauth_gssapi(Authctxt *authctxt); + int input_gssapi_response(int type, u_int32_t, void *); + int input_gssapi_token(int type, u_int32_t, void *); + int input_gssapi_hash(int type, u_int32_t, void *); + int input_gssapi_error(int, u_int32_t, void *); + int input_gssapi_errtok(int, u_int32_t, void *); ++int userauth_gsskeyex(Authctxt *authctxt); + #endif + + void userauth(Authctxt *, char *); + + static int sign_and_send_pubkey(Authctxt *, Identity *); + static void pubkey_prepare(Authctxt *); + static void pubkey_cleanup(Authctxt *); + static Key *load_identity_file(Identity *); + + static Authmethod *authmethod_get(char *authlist); + static Authmethod *authmethod_lookup(const char *name); + static char *authmethods_get(void); + + Authmethod authmethods[] = { + #ifdef GSSAPI ++ {"gssapi-keyex", ++ userauth_gsskeyex, ++ NULL, ++ &options.gss_authentication, ++ NULL}, + {"gssapi-with-mic", + userauth_gssapi, + NULL, + &options.gss_authentication, + NULL}, + #endif + {"hostbased", + userauth_hostbased, +@@ -651,29 +712,41 @@ done: + int + userauth_gssapi(Authctxt *authctxt) + { + Gssctxt *gssctxt = NULL; + static gss_OID_set gss_supported = NULL; + static u_int mech = 0; + OM_uint32 min; + int 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(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++; + } + } + + if (!ok) + return 0; +@@ -760,18 +833,18 @@ process_gssapi_token(void *ctxt, gss_buf + } + + /* ARGSUSED */ + int + input_gssapi_response(int type, u_int32_t plen, void *ctxt) + { + Authctxt *authctxt = ctxt; + Gssctxt *gssctxt; +- int oidlen; +- char *oidv; ++ u_int oidlen; ++ u_char *oidv; + + if (authctxt == NULL) + fatal("input_gssapi_response: no authentication context"); + gssctxt = authctxt->methoddata; + + /* Setup our OID */ + oidv = packet_get_string(&oidlen); + +@@ -874,16 +947,58 @@ input_gssapi_error(int type, u_int32_t p + + packet_check_eom(); + + debug("Server GSSAPI Error:\n%s", msg); + free(msg); + free(lang); + return 0; + } ++ ++int ++userauth_gsskeyex(Authctxt *authctxt) ++{ ++ Buffer b; ++ 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); ++ } ++ ++ ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, ++ "gssapi-keyex"); ++ ++ gssbuf.value = buffer_ptr(&b); ++ gssbuf.length = buffer_len(&b); ++ ++ if (GSS_ERROR(ssh_gssapi_sign(gss_kex_context, &gssbuf, &mic))) { ++ buffer_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(); ++ ++ buffer_free(&b); ++ gss_release_buffer(&ms, &mic); ++ ++ return (1); ++} ++ + #endif /* GSSAPI */ + + int + userauth_none(Authctxt *authctxt) + { + /* initial userauth request */ + packet_start(SSH2_MSG_USERAUTH_REQUEST); + packet_put_cstring(authctxt->server_user); +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -124,16 +124,20 @@ + #endif + #include "monitor_wrap.h" + #include "ssh-sandbox.h" + #include "version.h" + #include "ssherr.h" + + #include "fips.h" + ++#ifdef USE_SECURITY_SESSION_API ++#include ++#endif ++ + #ifndef O_NOCTTY + #define O_NOCTTY 0 + #endif + + /* Re-exec fds */ + #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) + #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) + #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) +@@ -1847,20 +1851,23 @@ main(int ac, char **av) + if ((options.protocol & SSH_PROTO_1) && fips_mode()) { + logit("Disabling protocol version 1. Not allowed in the FIPS mode."); + options.protocol &= ~SSH_PROTO_1; + } + if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { + logit("Disabling protocol version 1. Could not load host key"); + options.protocol &= ~SSH_PROTO_1; + } ++#ifndef GSSAPI ++ /* The GSSAPI key exchange can run without a host key */ + if ((options.protocol & SSH_PROTO_2) && !sensitive_data.have_ssh2_key) { + logit("Disabling protocol version 2. Could not load host key"); + options.protocol &= ~SSH_PROTO_2; + } ++#endif + if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { + logit("sshd: no hostkeys available -- exiting."); + exit(1); + } + + /* + * Load certificates. They are stored in an array at identical + * indices to the public keys that they relate to. +@@ -2055,16 +2062,70 @@ main(int ac, char **av) + /* Accept a connection and return in a forked child */ + server_accept_loop(&sock_in, &sock_out, + &newsock, config_s); + } + + /* This is the child processing a new connection. */ + setproctitle("%s", "[accepted]"); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * Create a new session and process group since the 4.4BSD + * setlogin() affects the entire process group. We don't + * want the child to be able to affect the parent. + */ + #if !defined(SSHD_ACQUIRES_CTTY) + /* + * If setsid is called, on some platforms sshd will later acquire a +@@ -2165,16 +2226,70 @@ main(int ac, char **av) + #endif + + /* Log the connection. */ + laddr = get_local_ipaddr(sock_in); + verbose("Connection from %s port %d on %s port %d", + remote_ip, remote_port, laddr, get_local_port()); + free(laddr); + ++#ifdef USE_SECURITY_SESSION_API ++ /* ++ * Create a new security session for use by the new user login if ++ * the current session is the root session or we are not launched ++ * by inetd (eg: debugging mode or server mode). We do not ++ * necessarily need to create a session if we are launched from ++ * inetd because Panther xinetd will create a session for us. ++ * ++ * The only case where this logic will fail is if there is an ++ * inetd running in a non-root session which is not creating ++ * new sessions for us. Then all the users will end up in the ++ * same session (bad). ++ * ++ * When the client exits, the session will be destroyed for us ++ * automatically. ++ * ++ * We must create the session before any credentials are stored ++ * (including AFS pags, which happens a few lines below). ++ */ ++ { ++ OSStatus err = 0; ++ SecuritySessionId sid = 0; ++ SessionAttributeBits sattrs = 0; ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("Current Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ ++ if (inetd_flag && !(sattrs & sessionIsRoot)) ++ debug("Running in inetd mode in a non-root session... " ++ "assuming inetd created the session for us."); ++ else { ++ debug("Creating new security session..."); ++ err = SessionCreate(0, sessionHasTTY | sessionIsRemote); ++ if (err) ++ error("SessionCreate() failed with error %.8X", ++ (unsigned) err); ++ ++ err = SessionGetInfo(callerSecuritySession, &sid, ++ &sattrs); ++ if (err) ++ error("SessionGetInfo() failed with error %.8X", ++ (unsigned) err); ++ else ++ debug("New Session ID is %.8X / Session Attributes are %.8X", ++ (unsigned) sid, (unsigned) sattrs); ++ } ++ } ++#endif ++ + /* + * We don't want to listen forever unless the other side + * successfully authenticates itself. So we set up an alarm which is + * cleared after successful authentication. A limit of zero + * indicates no limit. Note that we don't set the alarm in debugging + * mode; it is just annoying to have the server exit just when you + * are about to discover the bug. + */ +@@ -2585,30 +2700,79 @@ do_ssh2_kex(void) + + if (options.rekey_limit || options.rekey_interval) + packet_set_rekey_limits(options.rekey_limit, + (time_t)options.rekey_interval); + + 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)); + kex = active_state->kex; + #ifdef WITH_OPENSSL + kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; + kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; + kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; + kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; + # ifdef OPENSSL_HAS_ECC + kex->kex[KEX_ECDH_SHA2] = kexecdh_server; + # 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; + 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; + kex->sign = sshd_hostkey_sign; + +diff --git a/openssh-7.2p2/sshd_config b/openssh-7.2p2/sshd_config +--- a/openssh-7.2p2/sshd_config ++++ b/openssh-7.2p2/sshd_config +@@ -84,16 +84,18 @@ PasswordAuthentication no + #KerberosAuthentication no + #KerberosOrLocalPasswd yes + #KerberosTicketCleanup yes + #KerberosGetAFSToken no + + # GSSAPI options + #GSSAPIAuthentication no + #GSSAPICleanupCredentials yes ++#GSSAPIStrictAcceptorCheck yes ++#GSSAPIKeyExchange no + + # Set this to 'yes' to enable PAM authentication, account processing, + # and session processing. If this is enabled, PAM authentication will + # be allowed through the ChallengeResponseAuthentication and + # PasswordAuthentication. Depending on your PAM configuration, + # PAM authentication via ChallengeResponseAuthentication may bypass + # the setting of "PermitRootLogin without-password". + # If you just want the PAM account and session checks to run without +diff --git a/openssh-7.2p2/sshd_config.0 b/openssh-7.2p2/sshd_config.0 +--- a/openssh-7.2p2/sshd_config.0 ++++ b/openssh-7.2p2/sshd_config.0 +@@ -375,29 +375,41 @@ DESCRIPTION + force remote port forwardings to bind to the wildcard address, or + M-bM-^@M-^\clientspecifiedM-bM-^@M-^] to allow the client to select the address to + which the forwarding is bound. The default is M-bM-^@M-^\noM-bM-^@M-^]. + + GSSAPIAuthentication + Specifies whether user authentication based on GSSAPI is allowed. + The default is M-bM-^@M-^\noM-bM-^@M-^]. + ++ GSSAPIKeyExchange ++ Specifies whether key exchange based on GSSAPI is allowed. GSSAPI ++ key exchange doesn't rely on ssh keys to verify host identity. The ++ default is no. ++ Note that this option applies to protocol version 2 only. ++ + GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials + cache on logout. The default is M-bM-^@M-^\yesM-bM-^@M-^]. + + GSSAPIStrictAcceptorCheck + Determines whether to be strict about the identity of the GSSAPI + acceptor a client authenticates against. If set to M-bM-^@M-^\yesM-bM-^@M-^] then + the client must authenticate against the host service on the + current hostname. If set to M-bM-^@M-^\noM-bM-^@M-^] then the client may + authenticate against any service key stored in the machine's + default store. This facility is provided to assist with + operation on multi homed machines. The default is M-bM-^@M-^\yesM-bM-^@M-^]. + ++ GSSAPIStoreCredentialsOnRekey ++ Controls whether the user's GSSAPI credentials should be updated ++ following a successful connection rekeying. This option can be used ++ to accepted renewed or updated credentials from a compatible ++ client. The default is no. ++ + HostbasedAcceptedKeyTypes + Specifies the key types that will be accepted for hostbased + authentication as a comma-separated pattern list. Alternately if + the specified value begins with a M-bM-^@M-^X+M-bM-^@M-^Y character, then the + specified key types will be appended to the default set instead + of replacing them. The default for this option is: + + ecdsa-sha2-nistp256-cert-v01@openssh.com, +diff --git a/openssh-7.2p2/sshd_config.5 b/openssh-7.2p2/sshd_config.5 +--- a/openssh-7.2p2/sshd_config.5 ++++ b/openssh-7.2p2/sshd_config.5 +@@ -619,16 +619,22 @@ to force remote port forwardings to bind + .Dq clientspecified + to allow the client to select the address to which the forwarding is bound. + The default is + .Dq no . + .It Cm GSSAPIAuthentication + Specifies whether user authentication based on GSSAPI is allowed. + The default is + .Dq no . ++.It Cm GSSAPIKeyExchange ++Specifies whether key exchange based on GSSAPI is allowed. GSSAPI key exchange ++doesn't rely on ssh keys to verify host identity. ++The default is ++.Dq no . ++Note that this option applies to protocol version 2 only. + .It Cm GSSAPICleanupCredentials + Specifies whether to automatically destroy the user's credentials cache + on logout. + The default is + .Dq yes . + .It Cm GSSAPIStrictAcceptorCheck + Determines whether to be strict about the identity of the GSSAPI acceptor + a client authenticates against. +@@ -639,16 +645,21 @@ then the client must authenticate agains + service on the current hostname. + If set to + .Dq no + then the client may authenticate against any service key stored in the + machine's default store. + This facility is provided to assist with operation on multi homed machines. + The default is + .Dq 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 comma-separated pattern list. + Alternately if the specified value begins with a + .Sq + + character, then the specified key types will be appended to the default set + instead of replacing them. + The default for this option is: +diff --git a/openssh-7.2p2/sshkey.c b/openssh-7.2p2/sshkey.c +--- a/openssh-7.2p2/sshkey.c ++++ b/openssh-7.2p2/sshkey.c +@@ -110,16 +110,18 @@ static const struct keytype keytypes[] = + { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", + KEY_ECDSA_CERT, NID_secp384r1, 1, 0 }, + # ifdef OPENSSL_HAS_NISTP521 + { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", + KEY_ECDSA_CERT, NID_secp521r1, 1, 0 }, + # endif /* OPENSSL_HAS_NISTP521 */ + # endif /* OPENSSL_HAS_ECC */ + #endif /* WITH_OPENSSL */ ++ { "null", "null", ++ KEY_NULL, 0, 0 }, + { NULL, NULL, -1, -1, 0, 0 } + }; + + const char * + sshkey_type(const struct sshkey *k) + { + const struct keytype *kt; + +diff --git a/openssh-7.2p2/sshkey.h b/openssh-7.2p2/sshkey.h +--- a/openssh-7.2p2/sshkey.h ++++ b/openssh-7.2p2/sshkey.h +@@ -57,16 +57,17 @@ enum sshkey_types { + KEY_RSA, + KEY_DSA, + KEY_ECDSA, + KEY_ED25519, + KEY_RSA_CERT, + KEY_DSA_CERT, + KEY_ECDSA_CERT, + KEY_ED25519_CERT, ++ KEY_NULL, + KEY_UNSPEC + }; + + /* Default fingerprint hash */ + #define SSH_FP_HASH_DEFAULT SSH_DIGEST_SHA256 + + /* Fingerprint representation formats */ + enum sshkey_fp_rep { diff --git a/openssh-7.2p2-seed-prng.patch b/openssh-7.2p2-seed-prng.patch new file mode 100644 index 0000000..3c30b92 --- /dev/null +++ b/openssh-7.2p2-seed-prng.patch @@ -0,0 +1,461 @@ +# HG changeset patch +# Parent 36ab4b78afea8cea4e3bed1291a49ba05cbb9115 +# extended support for (re-)seeding the OpenSSL PRNG from /dev/random +# bnc#703221, FATE#312172 + +diff --git a/openssh-7.2p2/entropy.c b/openssh-7.2p2/entropy.c +--- a/openssh-7.2p2/entropy.c ++++ b/openssh-7.2p2/entropy.c +@@ -49,16 +49,17 @@ + + #include "ssh.h" + #include "misc.h" + #include "xmalloc.h" + #include "atomicio.h" + #include "pathnames.h" + #include "log.h" + #include "buffer.h" ++#include "openbsd-compat/port-linux.h" + + /* + * Portable OpenSSH PRNG seeding: + * If OpenSSL has not "internally seeded" itself (e.g. pulled data from + * /dev/random), then collect RANDOM_SEED_SIZE bytes of randomness from + * PRNGd. + */ + #ifndef OPENSSL_PRNG_ONLY +@@ -224,16 +225,19 @@ seed_rng(void) + } + + if (seed_from_prngd(buf, sizeof(buf)) == -1) + fatal("Could not obtain seed from PRNGd"); + RAND_add(buf, sizeof(buf), sizeof(buf)); + memset(buf, '\0', sizeof(buf)); + + #endif /* OPENSSL_PRNG_ONLY */ ++ ++ linux_seed(); ++ + if (RAND_status() != 1) + fatal("PRNG is not seeded"); + } + + #else /* WITH_OPENSSL */ + + /* Handled in arc4random() */ + void +diff --git a/openssh-7.2p2/openbsd-compat/Makefile.in b/openssh-7.2p2/openbsd-compat/Makefile.in +--- a/openssh-7.2p2/openbsd-compat/Makefile.in ++++ b/openssh-7.2p2/openbsd-compat/Makefile.in +@@ -15,17 +15,17 @@ AR=@AR@ + RANLIB=@RANLIB@ + INSTALL=@INSTALL@ + LDFLAGS=-L. @LDFLAGS@ + + OPENBSD=base64.o basename.o bcrypt_pbkdf.o bindresvport.o blowfish.o daemon.o dirname.o fmt_scaled.o getcwd.o getgrouplist.o getopt_long.o getrrsetbyname.o glob.o inet_aton.o inet_ntoa.o inet_ntop.o mktemp.o pwcache.o readpassphrase.o reallocarray.o realpath.o rresvport.o setenv.o setproctitle.o sha1.o sha2.o rmd160.o md5.o sigact.o strlcat.o strlcpy.o strmode.o strnlen.o strptime.o strsep.o strtonum.o strtoll.o strtoul.o strtoull.o timingsafe_bcmp.o vis.o blowfish.o bcrypt_pbkdf.o explicit_bzero.o + + COMPAT=arc4random.o bsd-asprintf.o bsd-closefrom.o bsd-cray.o bsd-cygwin_util.o bsd-getpeereid.o getrrsetbyname-ldns.o bsd-misc.o bsd-nextstep.o bsd-openpty.o bsd-poll.o bsd-setres_id.o bsd-snprintf.o bsd-statvfs.o bsd-waitpid.o fake-rfc2553.o openssl-compat.o xmmap.o xcrypt.o kludge-fd_set.o + +-PORTS=port-aix.o port-irix.o port-linux.o port-solaris.o port-tun.o port-uw.o ++PORTS=port-aix.o port-irix.o port-linux.o port-linux-prng.o port-solaris.o port-tun.o port-uw.o + + .c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $< + + all: libopenbsd-compat.a + + $(COMPAT): ../config.h + $(OPENBSD): ../config.h +diff --git a/openssh-7.2p2/openbsd-compat/port-linux-prng.c b/openssh-7.2p2/openbsd-compat/port-linux-prng.c +new file mode 100644 +--- /dev/null ++++ b/openssh-7.2p2/openbsd-compat/port-linux-prng.c +@@ -0,0 +1,81 @@ ++/* ++ * Copyright (c) 2011 Jan F. Chadima ++ * (c) 2011 Petr Cerny ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++/* ++ * Linux-specific portability code - prng support ++ */ ++ ++#include "includes.h" ++#include "defines.h" ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "log.h" ++#include "port-linux.h" ++#include "fips.h" ++ ++#define RNG_BYTES_DEFAULT 6L ++#define RNG_ENV_VAR "SSH_USE_STRONG_RNG" ++ ++long rand_bytes = 0; ++char *rand_file = NULL; ++ ++static void ++linux_seed_init(void) ++{ ++ long elen = 0; ++ char *env = getenv(RNG_ENV_VAR); ++ ++ if (env) { ++ errno = 0; ++ elen = strtol(env, NULL, 10); ++ if (errno) { ++ elen = RNG_BYTES_DEFAULT; ++ debug("bogus value in the %s environment variable, " ++ "using %li bytes from /dev/random\n", ++ RNG_ENV_VAR, RNG_BYTES_DEFAULT); ++ } ++ } ++ ++ if (elen || fips_mode()) ++ rand_file = "/dev/random"; ++ else ++ rand_file = "/dev/urandom"; ++ ++ rand_bytes = MAX(elen, RNG_BYTES_DEFAULT); ++} ++ ++void ++linux_seed(void) ++{ ++ long len; ++ if (!rand_file) ++ linux_seed_init(); ++ ++ errno = 0; ++ len = RAND_load_file(rand_file, rand_bytes); ++ if (len != rand_bytes) { ++ if (errno) ++ fatal ("cannot read from %s, %s", rand_file, strerror(errno)); ++ else ++ fatal ("EOF reading %s", rand_file); ++ } ++} +diff --git a/openssh-7.2p2/openbsd-compat/port-linux.h b/openssh-7.2p2/openbsd-compat/port-linux.h +--- a/openssh-7.2p2/openbsd-compat/port-linux.h ++++ b/openssh-7.2p2/openbsd-compat/port-linux.h +@@ -14,16 +14,20 @@ + * 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. + */ + + #ifndef _PORT_LINUX_H + #define _PORT_LINUX_H + ++extern long rand_bytes; ++extern char *rand_file; ++void linux_seed(void); ++ + #ifdef WITH_SELINUX + int ssh_selinux_enabled(void); + void ssh_selinux_setup_pty(char *, const char *); + void ssh_selinux_setup_exec_context(char *); + void ssh_selinux_change_context(const char *); + void ssh_selinux_setfscreatecon(const char *); + #endif + +diff --git a/openssh-7.2p2/ssh-add.1 b/openssh-7.2p2/ssh-add.1 +--- a/openssh-7.2p2/ssh-add.1 ++++ b/openssh-7.2p2/ssh-add.1 +@@ -166,16 +166,30 @@ or related script. + (Note that on some machines it + may be necessary to redirect the input from + .Pa /dev/null + to make this work.) + .It Ev SSH_AUTH_SOCK + Identifies the path of a + .Ux Ns -domain + socket used to communicate with the agent. ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .El + .Sh FILES + .Bl -tag -width Ds + .It Pa ~/.ssh/identity + Contains the protocol version 1 RSA authentication identity of the user. + .It Pa ~/.ssh/id_dsa + Contains the protocol version 2 DSA authentication identity of the user. + .It Pa ~/.ssh/id_ecdsa +diff --git a/openssh-7.2p2/ssh-agent.1 b/openssh-7.2p2/ssh-agent.1 +--- a/openssh-7.2p2/ssh-agent.1 ++++ b/openssh-7.2p2/ssh-agent.1 +@@ -196,16 +196,33 @@ line terminates. + .Sh FILES + .Bl -tag -width Ds + .It Pa $TMPDIR/ssh-XXXXXXXXXX/agent.\*(Ltppid\*(Gt + .Ux Ns -domain + sockets used to contain the connection to the authentication agent. + These sockets should only be readable by the owner. + The sockets should get automatically removed when the agent exits. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , + .Xr ssh-keygen 1 , + .Xr sshd 8 + .Sh AUTHORS + OpenSSH is a derivative of the original and free + ssh 1.2.12 release by Tatu Ylonen. +diff --git a/openssh-7.2p2/ssh-keygen.1 b/openssh-7.2p2/ssh-keygen.1 +--- a/openssh-7.2p2/ssh-keygen.1 ++++ b/openssh-7.2p2/ssh-keygen.1 +@@ -841,16 +841,33 @@ on all machines + where the user wishes to log in using public key authentication. + There is no need to keep the contents of this file secret. + .Pp + .It Pa /etc/moduli + Contains Diffie-Hellman groups used for DH-GEX. + The file format is described in + .Xr moduli 5 . + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , + .Xr moduli 5 , + .Xr sshd 8 + .Rs + .%R RFC 4716 +diff --git a/openssh-7.2p2/ssh-keysign.8 b/openssh-7.2p2/ssh-keysign.8 +--- a/openssh-7.2p2/ssh-keysign.8 ++++ b/openssh-7.2p2/ssh-keysign.8 +@@ -75,16 +75,33 @@ must be set-uid root if host-based authe + .Pp + .It Pa /etc/ssh/ssh_host_dsa_key-cert.pub + .It Pa /etc/ssh/ssh_host_ecdsa_key-cert.pub + .It Pa /etc/ssh/ssh_host_ed25519_key-cert.pub + .It Pa /etc/ssh/ssh_host_rsa_key-cert.pub + If these files exist they are assumed to contain public certificate + information corresponding with the private keys above. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr ssh 1 , + .Xr ssh-keygen 1 , + .Xr ssh_config 5 , + .Xr sshd 8 + .Sh HISTORY + .Nm + first appeared in +diff --git a/openssh-7.2p2/ssh.1 b/openssh-7.2p2/ssh.1 +--- a/openssh-7.2p2/ssh.1 ++++ b/openssh-7.2p2/ssh.1 +@@ -1411,16 +1411,30 @@ reads + and adds lines of the format + .Dq VARNAME=value + to the environment if the file exists and users are allowed to + change their environment. + For more information, see the + .Cm PermitUserEnvironment + option in + .Xr sshd_config 5 . ++.It Ev SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh FILES + .Bl -tag -width Ds -compact + .It Pa ~/.rhosts + This file is used for host-based authentication (see above). + On some machines this file may need to be + world-readable if the user's home directory is on an NFS partition, + because + .Xr sshd 8 +diff --git a/openssh-7.2p2/sshd.8 b/openssh-7.2p2/sshd.8 +--- a/openssh-7.2p2/sshd.8 ++++ b/openssh-7.2p2/sshd.8 +@@ -972,16 +972,33 @@ and not group or world-writable. + .It Pa /var/run/sshd.pid + Contains the process ID of the + .Nm + listening for connections (if there are several daemons running + concurrently for different ports, this contains the process ID of the one + started last). + The content of this file is not sensitive; it can be world-readable. + .El ++.Sh ENVIRONMENT ++.Bl -tag -width Ds -compact ++.Pp ++.It Pa SSH_USE_STRONG_RNG ++The reseeding of the OpenSSL random generator is usually done from ++.Cm /dev/urandom . ++If the ++.Cm SSH_USE_STRONG_RNG ++environment variable is set to value other than ++.Cm 0 ++the OpenSSL random generator is reseeded from ++.Cm /dev/random . ++The number of bytes read is defined by the SSH_USE_STRONG_RNG value. ++Minimum is 6 bytes. ++This setting is not recommended on the computers without the hardware ++random generator because insufficient entropy causes the connection to ++be blocked until enough entropy is available. + .Sh SEE ALSO + .Xr scp 1 , + .Xr sftp 1 , + .Xr ssh 1 , + .Xr ssh-add 1 , + .Xr ssh-agent 1 , + .Xr ssh-keygen 1 , + .Xr ssh-keyscan 1 , +diff --git a/openssh-7.2p2/sshd.c b/openssh-7.2p2/sshd.c +--- a/openssh-7.2p2/sshd.c ++++ b/openssh-7.2p2/sshd.c +@@ -50,16 +50,18 @@ + #ifdef HAVE_SYS_STAT_H + # include + #endif + #ifdef HAVE_SYS_TIME_H + # include + #endif + #include "openbsd-compat/sys-tree.h" + #include "openbsd-compat/sys-queue.h" ++#include "openbsd-compat/port-linux.h" ++ + #include + + #include + #include + #include + #ifdef HAVE_PATHS_H + #include + #endif +@@ -209,16 +211,23 @@ struct { + Key **host_pubkeys; /* all public host keys */ + Key **host_certificates; /* all public host certificates */ + int have_ssh1_key; + int have_ssh2_key; + u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; + } sensitive_data; + + /* ++ * Every RESEED_AFTERth connection triggers call to linux_seed() to re-seed the ++ * random pool. ++ */ ++#define RESEED_AFTER 100 ++static int re_seeding_counter = RESEED_AFTER; ++ ++/* + * Flag indicating whether the RSA server key needs to be regenerated. + * Is set in the SIGALRM handler and cleared when the key is regenerated. + */ + static volatile sig_atomic_t key_do_regen = 0; + + /* This is set to true when a signal is received. */ + static volatile sig_atomic_t received_sighup = 0; + static volatile sig_atomic_t received_sigterm = 0; +@@ -1343,16 +1352,20 @@ server_accept_loop(int *sock_in, int *so + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] == -1) { + startup_pipes[j] = startup_p[0]; + if (maxfd < startup_p[0]) + maxfd = startup_p[0]; + startups++; + break; + } ++ if(!(--re_seeding_counter)) { ++ re_seeding_counter = RESEED_AFTER; ++ linux_seed(); ++ } + + /* + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. + */ + if (debug_flag) { + /* + * In debugging mode. Close the listening diff --git a/openssh-askpass-gnome.spec b/openssh-askpass-gnome.spec index 7e43bd5..e76d15a 100644 --- a/openssh-askpass-gnome.spec +++ b/openssh-askpass-gnome.spec @@ -1,7 +1,7 @@ # # spec file for package openssh-askpass-gnome # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed diff --git a/openssh.changes b/openssh.changes index 4aa8d32..a4e526e 100644 --- a/openssh.changes +++ b/openssh.changes @@ -1,3 +1,13 @@ +------------------------------------------------------------------- +Fri Sep 16 12:45:11 UTC 2016 - pcerny@suse.com + +- FIPS compatibility (no selfchecks, only crypto restrictions) + [openssh-7.2p2-fips.patch] +- PRNG re-seeding + [openssh-7.2p2-seed-prng.patch] +- preliminary version of GSSAPI KEX + [openssh-7.2p2-gssapi_key_exchange.patch] + ------------------------------------------------------------------- Mon Jul 25 13:46:06 UTC 2016 - meissner@suse.com diff --git a/openssh.spec b/openssh.spec index 58d6f12..4bcc000 100644 --- a/openssh.spec +++ b/openssh.spec @@ -1,7 +1,7 @@ # # spec file for package openssh # -# Copyright (c) 2016 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2016 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -125,6 +125,9 @@ Patch12: openssh-7.2p2-pam_check_locks.patch Patch13: openssh-7.2p2-disable_short_DH_parameters.patch Patch14: openssh-7.2p2-seccomp_getuid.patch Patch15: openssh-7.2p2-seccomp_stat.patch +Patch16: openssh-7.2p2-fips.patch +Patch17: openssh-7.2p2-seed-prng.patch +Patch18: openssh-7.2p2-gssapi_key_exchange.patch BuildRoot: %{_tmppath}/%{name}-%{version}-build Conflicts: nonfreessh Recommends: audit @@ -192,6 +195,9 @@ FIPS140 CAVS tests related parts of the OpenSSH package %patch13 -p2 %patch14 -p2 %patch15 -p2 +%patch16 -p2 +%patch17 -p2 +%patch18 -p2 cp %{SOURCE3} %{SOURCE4} %{SOURCE11} . %build