diff gensignkey.c @ 1329:185c14fa504d

Use atomic key generation in all cases
author Matt Johnston <matt@ucc.asn.au>
date Sat, 19 Nov 2016 00:31:21 +0800
parents 750ec4ec4cbe
children bbc0a0ee3843
line wrap: on
line diff
--- a/gensignkey.c	Fri Nov 18 23:56:22 2016 +0800
+++ b/gensignkey.c	Sat Nov 19 00:31:21 2016 +0800
@@ -76,10 +76,12 @@
 	}
 }
 
-int signkey_generate(enum signkey_type keytype, int bits, const char* filename)
+/* if skip_exist is set it will silently return if the key file exists */
+int signkey_generate(enum signkey_type keytype, int bits, const char* filename, int skip_exist)
 {
 	sign_key * key = NULL;
 	buffer *buf = NULL;
+	char *fn_temp = NULL;
 	int ret = DROPBEAR_FAILURE;
 	if (bits == 0)
 	{
@@ -126,10 +128,37 @@
 	sign_key_free(key);
 	key = NULL;
 	buf_setpos(buf, 0);
-	ret = buf_writefile(buf, filename);
+
+	fn_temp = m_malloc(strlen(filename) + 30);
+	snprintf(fn_temp, strlen(filename)+30, "%s.tmp%d", filename, getpid());
+	ret = buf_writefile(buf, fn_temp);
+
+	if (ret == DROPBEAR_FAILURE) {
+		goto out;
+	}
 
-	buf_burn(buf);
-	buf_free(buf);
-	buf = NULL;
+	if (link(fn_temp, filename) < 0) {
+		/* If generating keys on connection (skipexist) it's OK to get EEXIST 
+		- we probably just lost a race with another connection to generate the key */
+		if (!(skip_exist && errno == EEXIST)) {
+			dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", filename,
+				strerror(errno));
+			/* XXX fallback to non-atomic copy for some filesystems? */
+			ret = DROPBEAR_FAILURE;
+			goto out;
+		}
+	}
+
+out:
+	if (buf) {
+		buf_burn(buf);
+		buf_free(buf);
+	}
+	
+	if (fn_temp) {
+		unlink(fn_temp);
+		m_free(fn_temp);
+	}
+
 	return ret;
 }