From b7727ac11601d06e63fa67c8975994cfdbb7e62f Mon Sep 17 00:00:00 2001
From: Alexey Makhalov <amakhalov@vmware.com>
Date: Sat, 20 May 2017 05:19:04 +0000
Subject: [PATCH] Configure FIPS

New parameter: FipsMode yes/no

As soon as FipsMode option parsed FIPS_mode_set(1) will be called.
See Bug #1872327 for details.
---
 readconf.c    | 38 +++++++++++++++++++++++++++++++++++++-
 readconf.h    |  1 +
 servconf.c    | 34 +++++++++++++++++++++++++++++++++-
 servconf.h    |  1 +
 ssh_config    |  1 +
 ssh_config.0  |  4 ++++
 ssh_config.5  |  4 ++++
 sshd_config   |  2 ++
 sshd_config.0 |  4 ++++
 sshd_config.5 |  4 ++++
 10 files changed, 91 insertions(+), 2 deletions(-)

diff --git a/readconf.c b/readconf.c
index 7f401d6..2c970e2 100644
--- a/readconf.c
+++ b/readconf.c
@@ -171,7 +171,8 @@ typedef enum {
 	oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys,
 	oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes,
 	oPubkeyAcceptedKeyTypes, oProxyJump,
-	oIgnoredUnknownOption, oDeprecated, oUnsupported
+	oIgnoredUnknownOption, oDeprecated, oUnsupported,
+	oFipsMode
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -291,6 +292,7 @@ static struct {
 	{ "streamlocalbindunlink", oStreamLocalBindUnlink },
 	{ "revokedhostkeys", oRevokedHostKeys },
 	{ "fingerprinthash", oFingerprintHash },
+	{ "fipsmode", oFipsMode },
 	{ "updatehostkeys", oUpdateHostkeys },
 	{ "hostbasedkeytypes", oHostbasedKeyTypes },
 	{ "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes },
@@ -965,6 +967,35 @@ parse_time:
 		intptr = &options->gss_deleg_creds;
 		goto parse_flag;
 
+        case oFipsMode:
+		if (options->ciphers != NULL || options->cipher != -1)
+			fatal("%.200s line %d: FipsMode should be set before "
+			    "Ciphers option", filename, linenum);
+		intptr = &options->fips_mode;
+		multistate_ptr = multistate_flag;
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: missing argument.",
+			    filename, linenum);
+		value = -1;
+		for (i = 0; multistate_ptr[i].key != NULL; i++) {
+			if (strcasecmp(arg, multistate_ptr[i].key) == 0) {
+				value = multistate_ptr[i].value;
+				break;
+			}
+		}
+		if (value == -1)
+			fatal("%s line %d: unsupported option \"%s\".",
+			    filename, linenum, arg);
+		if (*activep && *intptr == -1) {
+			*intptr = value;
+			/* Call FIPS_mode_set as soon as possible */
+			if (*intptr == 1)
+				if (!FIPS_mode_set(1))
+					fatal("FIPS mode could not be set");
+		}
+		break;
+
 	case oBatchMode:
 		intptr = &options->batch_mode;
 		goto parse_flag;
@@ -1857,6 +1888,7 @@ initialize_options(Options * options)
 	options->update_hostkeys = -1;
 	options->hostbased_key_types = NULL;
 	options->pubkey_key_types = NULL;
+	options->fips_mode = -1;
 }
 
 /*
@@ -2044,6 +2076,9 @@ fill_default_options(Options * options)
 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
 	if (options->update_hostkeys == -1)
 		options->update_hostkeys = 0;
+        if (options->fips_mode == -1)
+                options->fips_mode = 0;
+
 	if (kex_assemble_names((FIPS_mode() ? KEX_FIPS_ENCRYPT
 	        : KEX_CLIENT_ENCRYPT), &options->ciphers) != 0 ||
 	    kex_assemble_names((FIPS_mode() ? KEX_FIPS_MAC
@@ -2535,6 +2570,7 @@ dump_client_config(Options *o, const char *host)
 	dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns);
 	dump_cfg_fmtint(oVisualHostKey, o->visual_host_key);
 	dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys);
+	dump_cfg_fmtint(oFipsMode, o->fips_mode);
 
 	/* Integer options */
 	dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots);
diff --git a/readconf.h b/readconf.h
index cef55f7..875931e 100644
--- a/readconf.h
+++ b/readconf.h
@@ -157,6 +157,7 @@ typedef struct {
 	char	*revoked_host_keys;
 
 	int	 fingerprint_hash;
+	int	 fips_mode;
 
 	int	 update_hostkeys; /* one of SSH_UPDATE_HOSTKEYS_* */
 
diff --git a/servconf.c b/servconf.c
index 4e5401c..107647a 100644
--- a/servconf.c
+++ b/servconf.c
@@ -164,6 +164,7 @@ initialize_server_options(ServerOptions *options)
 	options->version_addendum = NULL;
 	options->fingerprint_hash = -1;
 	options->disable_forwarding = -1;
+	options->fips_mode = -1;
 }
 
 /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */
@@ -336,6 +337,8 @@ fill_default_server_options(ServerOptions *options)
 		options->fingerprint_hash = SSH_FP_HASH_DEFAULT;
 	if (options->disable_forwarding == -1)
 		options->disable_forwarding = 0;
+	if (options->fips_mode == -1)
+		options->fips_mode = 0;
 
 	assemble_algorithms(options);
 
@@ -421,7 +424,8 @@ typedef enum {
 	sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
 	sStreamLocalBindMask, sStreamLocalBindUnlink,
 	sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding,
-	sDeprecated, sIgnore, sUnsupported
+	sDeprecated, sIgnore, sUnsupported,
+	sFipsMode
 } ServerOpCodes;
 
 #define SSHCFG_GLOBAL	0x01	/* allowed in main section of sshd_config */
@@ -564,6 +568,7 @@ static struct {
 	{ "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL },
 	{ "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL },
 	{ "disableforwarding", sDisableForwarding, SSHCFG_ALL },
+	{ "fipsmode", sFipsMode, SSHCFG_GLOBAL },
 	{ NULL, sBadOption, 0 }
 };
 
@@ -1839,6 +1844,32 @@ process_server_config_line(ServerOptions *options, char *line,
 			options->fingerprint_hash = value;
 		break;
 
+	case sFipsMode:
+		if (options->ciphers != NULL)
+			fatal("%.200s line %d: FipsMode should be set before "
+			    "Ciphers option", filename, linenum);
+		intptr = &options->fips_mode;
+		arg = strdelim(&cp);
+		if (!arg || *arg == '\0')
+			fatal("%s line %d: missing yes/no argument.",
+			    filename, linenum);
+		value = 0;	/* silence compiler */
+		if (strcmp(arg, "yes") == 0)
+			value = 1;
+		else if (strcmp(arg, "no") == 0)
+			value = 0;
+		else
+			fatal("%s line %d: Bad yes/no argument: %s",
+				filename, linenum, arg);
+		if (*activep && *intptr == -1) {
+			*intptr = value;
+			/* Call FIPS_mode_set as soon as possible */
+			if (*intptr == 1)
+				if (!FIPS_mode_set(1))
+					fatal("FIPS mode could not be set");
+		}
+		break;
+
 	case sDeprecated:
 	case sIgnore:
 	case sUnsupported:
@@ -2280,6 +2311,7 @@ dump_config(ServerOptions *o)
 	dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding);
 	dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink);
 	dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash);
+	dump_cfg_fmtint(sFipsMode, o->fips_mode);
 
 	/* string arguments */
 	dump_cfg_string(sPidFile, o->pid_file);
diff --git a/servconf.h b/servconf.h
index 5853a97..a9ec1a2 100644
--- a/servconf.h
+++ b/servconf.h
@@ -189,6 +189,7 @@ typedef struct {
 	char   *auth_methods[MAX_AUTH_METHODS];
 
 	int	fingerprint_hash;
+	int fips_mode;
 }       ServerOptions;
 
 /* Information about the incoming connection as used by Match */
diff --git a/ssh_config b/ssh_config
index 90fb63f..fd6ab39 100644
--- a/ssh_config
+++ b/ssh_config
@@ -37,6 +37,7 @@
 #   IdentityFile ~/.ssh/id_ecdsa
 #   IdentityFile ~/.ssh/id_ed25519
 #   Port 22
+#   FipsMode no
 #   Protocol 2
 #   Cipher 3des
 #   Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc
diff --git a/ssh_config.0 b/ssh_config.0
index 4ca9a5f..33ac338 100644
--- a/ssh_config.0
+++ b/ssh_config.0
@@ -362,6 +362,10 @@ DESCRIPTION
              Specifies the hash algorithm used when displaying key
              fingerprints.  Valid options are: md5 and sha256 (the default).
 
+     FipsMode
+             Enables or disables FIPS mode. Requires FIPS capable ssl modules.
+             The default is no.
+
      ForwardAgent
              Specifies whether the connection to the authentication agent (if
              any) will be forwarded to the remote machine.  The argument must
diff --git a/ssh_config.5 b/ssh_config.5
index 591365f..df46e0d 100644
--- a/ssh_config.5
+++ b/ssh_config.5
@@ -658,6 +658,10 @@ Valid options are:
 and
 .Cm sha256
 (the default).
+.It Cm FipsMode
+Enables or disables FIPS mode. Requires FIPS capable ssl modules.
+The default is
+.Cm no .
 .It Cm ForwardAgent
 Specifies whether the connection to the authentication agent (if any)
 will be forwarded to the remote machine.
diff --git a/sshd_config b/sshd_config
index 9f09e4a..1a0d68a 100644
--- a/sshd_config
+++ b/sshd_config
@@ -105,6 +105,8 @@ AuthorizedKeysFile	.ssh/authorized_keys
 #ChrootDirectory none
 #VersionAddendum none
 
+#FipsMode no
+
 # no default banner path
 #Banner none
 
diff --git a/sshd_config.0 b/sshd_config.0
index 022c052..af813b2 100644
--- a/sshd_config.0
+++ b/sshd_config.0
@@ -331,6 +331,10 @@ DESCRIPTION
              Specifies the hash algorithm used when logging key fingerprints.
              Valid options are: md5 and sha256.  The default is sha256.
 
+     FipsMode
+             Enables or disables FIPS mode. Requires FIPS capable ssl modules.
+             The default is no.
+
      ForceCommand
              Forces the execution of the command specified by ForceCommand,
              ignoring any command supplied by the client and ~/.ssh/rc if
diff --git a/sshd_config.5 b/sshd_config.5
index 32b29d2..c618359 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -578,6 +578,10 @@ and
 .Cm sha256 .
 The default is
 .Cm sha256 .
+.It Cm FipsMode
+Enables or disables FIPS mode. Requires FIPS capable ssl modules.
+The default is
+.Cm no .
 .It Cm ForceCommand
 Forces the execution of the command specified by
 .Cm ForceCommand ,
-- 
2.8.1