[PATCH] allow user to update changed key in known_hosts

Jirka Bohac jbohac at jikos.cz
Sun Feb 5 03:11:41 EST 2006


Hi list,


I use ssh a lot and I often need to connect to hosts whose host key has
changed. If a host key of the remote host changes ssh terminates and the
user has to manually delete the offending host key from known_hosts. I
had to do this so many times that I no longer like the idea ;-)
I would really like ssh to ask me if the new host key is OK and if I
want to add it to known_hosts.

I talked to other people and they also seemed to be bothered by this
behaviour, so I have just written a small patch that introduces a new
config option: OffendingKeyOverride

When OffendingKeyOverride is "true" and ssh finds an offending key in
known_hosts it behaves just as if the key for the host were not there
at all. I.e. If StrictHostKeyChecking is set to "ask" the user is
prompted to accept the new key and so on...

As a result, known_hosts may contain multiple keys for the same host - I
find that very useful, for example when connecting to dualboot remote
hosts, with different host key in each of their OSes.

Is it possible that a similar patch could make it into openssh, or are
there any major objections?

The patch follows (it looks bigger than it is, because a large chunk of
code is moved into a separate function...). Comments appreciated. I am
willing to change the patch in any way or do whatever to get this
functionality into openssh.


Regards,


Jirka Bohac



diff -aur openssh-4.3p1/readconf.c openssh-4.3p1-patch/readconf.c
--- openssh-4.3p1/readconf.c	2005-12-13 09:33:20.000000000 +0100
+++ openssh-4.3p1-patch/readconf.c	2006-02-04 16:41:10.000000000 +0100
@@ -112,7 +112,7 @@
 	oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly,
 	oSendEnv, oControlPath, oControlMaster, oHashKnownHosts,
 	oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand,
-	oDeprecated, oUnsupported
+	oDeprecated, oUnsupported, oOffendingKeyOverride
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -175,6 +175,7 @@
 	{ "batchmode", oBatchMode },
 	{ "checkhostip", oCheckHostIP },
 	{ "stricthostkeychecking", oStrictHostKeyChecking },
+	{ "offendingkeyoverride", oOffendingKeyOverride },
 	{ "compression", oCompression },
 	{ "compressionlevel", oCompressionLevel },
 	{ "tcpkeepalive", oTCPKeepAlive },
@@ -434,6 +435,7 @@
 
 	case oStrictHostKeyChecking:
 		intptr = &options->strict_host_key_checking;
+		
 parse_yesnoask:
 		arg = strdelim(&s);
 		if (!arg || *arg == '\0')
@@ -452,6 +454,10 @@
 			*intptr = value;
 		break;
 
+	case oOffendingKeyOverride: 
+		intptr = &options->offending_key_override;
+		goto parse_flag;
+		
 	case oCompression:
 		intptr = &options->compression;
 		goto parse_flag;
@@ -979,6 +985,7 @@
 	options->batch_mode = -1;
 	options->check_host_ip = -1;
 	options->strict_host_key_checking = -1;
+	options->offending_key_override = -1;
 	options->compression = -1;
 	options->tcp_keep_alive = -1;
 	options->compression_level = -1;
@@ -1073,6 +1080,8 @@
 		options->check_host_ip = 1;
 	if (options->strict_host_key_checking == -1)
 		options->strict_host_key_checking = 2;	/* 2 is default */
+	if (options->offending_key_override == -1) 
+		options->offending_key_override = 0;
 	if (options->compression == -1)
 		options->compression = 0;
 	if (options->tcp_keep_alive == -1)
diff -aur openssh-4.3p1/readconf.h openssh-4.3p1-patch/readconf.h
--- openssh-4.3p1/readconf.h	2005-12-13 09:29:02.000000000 +0100
+++ openssh-4.3p1-patch/readconf.h	2006-02-04 15:07:38.000000000 +0100
@@ -53,6 +53,7 @@
 	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     offending_key_override;	/* Allow adding changed keys to hostfile */
 	int     compression;	/* Compress packets in both directions. */
 	int     compression_level;	/* Compression level 1 (fast) to 9
 					 * (best). */
diff -aur openssh-4.3p1/sshconnect.c openssh-4.3p1-patch/sshconnect.c
--- openssh-4.3p1/sshconnect.c	2005-12-13 09:29:03.000000000 +0100
+++ openssh-4.3p1-patch/sshconnect.c	2006-02-04 16:42:04.000000000 +0100
@@ -51,6 +51,9 @@
 
 static int show_other_keys(const char *, Key *);
 static void warn_changed_key(Key *);
+static int ask_connect_with_new_key(const char *host, Key *host_key, 
+	const char* ip, const char* type, HostStatus ip_status, 
+	const char *user_hostfile);
 
 /*
  * Connect to the given ssh server using a proxy command.
@@ -524,10 +527,9 @@
 	Key *file_key;
 	const char *type = key_type(host_key);
 	char *ip = NULL;
-	char hostline[1000], *hostp, *fp;
 	HostStatus host_status;
 	HostStatus ip_status;
-	int r, local = 0, host_ip_differ = 0;
+	int local = 0, host_ip_differ = 0;
 	int salen;
 	char ntop[NI_MAXHOST];
 	char msg[1024];
@@ -674,71 +676,10 @@
 			error("No %s host key is known for %.200s and you "
 			    "have requested strict checking.", type, host);
 			goto fail;
-		} else if (options.strict_host_key_checking == 2) {
-			char msg1[1024], msg2[1024];
-
-			if (show_other_keys(host, host_key))
-				snprintf(msg1, sizeof(msg1),
-				    "\nbut keys of different type are already"
-				    " known for this host.");
-			else
-				snprintf(msg1, sizeof(msg1), ".");
-			/* The default */
-			fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
-			msg2[0] = '\0';
-			if (options.verify_host_key_dns) {
-				if (matching_host_key_dns)
-					snprintf(msg2, sizeof(msg2),
-					    "Matching host key fingerprint"
-					    " found in DNS.\n");
-				else
-					snprintf(msg2, sizeof(msg2),
-					    "No matching host key fingerprint"
-					    " found in DNS.\n");
-			}
-			snprintf(msg, sizeof(msg),
-			    "The authenticity of host '%.200s (%s)' can't be "
-			    "established%s\n"
-			    "%s key fingerprint is %s.\n%s"
-			    "Are you sure you want to continue connecting "
-			    "(yes/no)? ",
-			    host, ip, msg1, type, fp, msg2);
-			xfree(fp);
-			if (!confirm(msg))
-				goto fail;
-		}
-		/*
-		 * If not in strict mode, add the key automatically to the
-		 * local known_hosts file.
-		 */
-		if (options.check_host_ip && ip_status == HOST_NEW) {
-			snprintf(hostline, sizeof(hostline), "%s,%s",
-			    host, ip);
-			hostp = hostline;
-			if (options.hash_known_hosts) {
-				/* Add hash of host and IP separately */
-				r = add_host_to_hostfile(user_hostfile, host,
-				    host_key, options.hash_known_hosts) &&
-				    add_host_to_hostfile(user_hostfile, ip,
-				    host_key, options.hash_known_hosts);
-			} else {
-				/* Add unhashed "host,ip" */
-				r = add_host_to_hostfile(user_hostfile,
-				    hostline, host_key,
-				    options.hash_known_hosts);
-			}
-		} else {
-			r = add_host_to_hostfile(user_hostfile, host, host_key,
-			    options.hash_known_hosts);
-			hostp = host;
-		}
+		} 
+		if (ask_connect_with_new_key(host, host_key, ip, type, ip_status, user_hostfile))
+			goto fail;
 
-		if (!r)
-			logit("Failed to add the host to the list of known "
-			    "hosts (%.500s).", user_hostfile);
-		else
-			logit("Warning: Permanently added '%.200s' (%s) to the "
-			    "list of known hosts.", hostp, type);
 		break;
 	case HOST_CHANGED:
 		if (options.check_host_ip && host_ip_differ) {
@@ -760,21 +701,30 @@
 			if (ip_status != HOST_NEW)
 				error("Offending key for IP in %s:%d", ip_file, ip_line);
 		}
+		
 		/* The host key has changed. */
 		warn_changed_key(host_key);
 		error("Add correct host key in %.100s to get rid of this message.",
 		    user_hostfile);
 		error("Offending key in %s:%d", host_file, host_line);
-
-		/*
-		 * If strict host key checking is in use, the user will have
-		 * to edit the key manually and we can only abort.
-		 */
-		if (options.strict_host_key_checking) {
+		
+		/* Ask the user whether to accept the new host key */
+		if (options.offending_key_override && options.strict_host_key_checking != 1)
+		{
+			if (ask_connect_with_new_key(host, host_key, ip, type, ip_status, user_hostfile))
+				goto fail;
+			break;
+		} else if (options.strict_host_key_checking) {
+			/*
+			 * If strict host key checking is in use, the user will have
+			 * to edit the key manually and we can only abort.
+			 */
 			error("%s host key for %.200s has changed and you have "
 			    "requested strict checking.", type, host);
 			goto fail;
 		}
+		
+
 
 		/*
 		 * If strict host key checking has not been requested, allow
@@ -814,13 +764,6 @@
 			options.num_local_forwards =
 			    options.num_remote_forwards = 0;
 		}
-		/*
-		 * XXX Should permit the user to change to use the new id.
-		 * This could be done by converting the host key to an
-		 * identifying sentence, tell that the host identifies itself
-		 * by that sentence, and ask the user if he/she whishes to
-		 * accept the authentication.
-		 */
 		break;
 	case HOST_FOUND:
 		fatal("internal error");
@@ -1014,6 +957,83 @@
 	return (found);
 }
 
+static int 
+ask_connect_with_new_key(const char *host, Key *host_key, const char* ip,
+	const char* type, HostStatus ip_status, const char *user_hostfile)
+{
+	char *fp;
+	const char *hostp;
+	int r;
+	char hostline[1000];
+	char msg[1024];
+
+	if (options.strict_host_key_checking == 2) {
+		char msg1[1024], msg2[1024];
+
+		if (show_other_keys(host, host_key))
+			snprintf(msg1, sizeof(msg1),
+			    "\nbut keys of different type are already"
+			    " known for this host.");
+		else
+			snprintf(msg1, sizeof(msg1), ".");
+		/* The default */
+		fp = key_fingerprint(host_key, SSH_FP_MD5, SSH_FP_HEX);
+		msg2[0] = '\0';
+		if (options.verify_host_key_dns) {
+			if (matching_host_key_dns)
+				snprintf(msg2, sizeof(msg2),
+				    "Matching host key fingerprint"
+				    " found in DNS.\n");
+			else
+				snprintf(msg2, sizeof(msg2),
+				    "No matching host key fingerprint"
+				    " found in DNS.\n");
+		}
+		snprintf(msg, sizeof(msg),
+		    "The authenticity of host '%.200s (%s)' can't be "
+		    "established%s\n"
+		    "%s key fingerprint is %s.\n%s"
+		    "Are you sure you want to continue connecting "
+		    "(yes/no)? ",
+		    host, ip, msg1, type, fp, msg2);
+		xfree(fp);
+		if (!confirm(msg))
+			return -1;
+	}
+	/*
+	 * If not in strict mode, add the key automatically to the
+	 * local known_hosts file.
+	 */
+	if (options.check_host_ip && ip_status == HOST_NEW) {
+		snprintf(hostline, sizeof(hostline), "%s,%s",
+		    host, ip);
+		hostp = hostline;
+		if (options.hash_known_hosts) {
+			/* Add hash of host and IP separately */
+			r = add_host_to_hostfile(user_hostfile, host,
+			    host_key, options.hash_known_hosts) &&
+			    add_host_to_hostfile(user_hostfile, ip,
+			    host_key, options.hash_known_hosts);
+		} else {
+			/* Add unhashed "host,ip" */
+			r = add_host_to_hostfile(user_hostfile,
+			    hostline, host_key,
+			    options.hash_known_hosts);
+		}
+	} else {
+		r = add_host_to_hostfile(user_hostfile, host, host_key,
+		    options.hash_known_hosts);
+		hostp = host;
+	}
+
+	if (!r)
+		logit("Failed to add the host to the list of known "
+		    "hosts (%.500s).", user_hostfile);
+	else
+		logit("Warning: Permanently added '%.200s' (%s) to the "
+		    "list of known hosts.", hostp, type);
+	return 0;
+}	
 static void
 warn_changed_key(Key *host_key)
 {
@@ -1030,7 +1050,6 @@
 	error("It is also possible that the %s host key has just been changed.", type);
 	error("The fingerprint for the %s key sent by the remote host is\n%s.",
 	    type, fp);
-	error("Please contact your system administrator.");
 
 	xfree(fp);
 }




More information about the openssh-unix-dev mailing list