diff options
Diffstat (limited to 'src/gensignkey.c')
-rw-r--r-- | src/gensignkey.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/src/gensignkey.c b/src/gensignkey.c new file mode 100644 index 0000000..cfe0a80 --- /dev/null +++ b/src/gensignkey.c @@ -0,0 +1,193 @@ +#include "includes.h" +#include "dbutil.h" +#include "buffer.h" +#include "ecdsa.h" +#include "genrsa.h" +#include "gendss.h" +#include "gened25519.h" +#include "signkey.h" +#include "dbrandom.h" + +/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +static int buf_writefile(buffer * buf, const char * filename, int skip_exist) { + int ret = DROPBEAR_FAILURE; + int fd = -1; + + fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fd < 0) { + /* If generating keys on connection (skip_exist) it's OK to get EEXIST + - we probably just lost a race with another connection to generate the key */ + if (skip_exist && errno == EEXIST) { + ret = DROPBEAR_SUCCESS; + } else { + dropbear_log(LOG_ERR, "Couldn't create new file %s: %s", + filename, strerror(errno)); + } + + goto out; + } + + /* write the file now */ + while (buf->pos != buf->len) { + int len = write(fd, buf_getptr(buf, buf->len - buf->pos), + buf->len - buf->pos); + if (len == -1 && errno == EINTR) { + continue; + } + if (len <= 0) { + dropbear_log(LOG_ERR, "Failed writing file %s: %s", + filename, strerror(errno)); + goto out; + } + buf_incrpos(buf, len); + } + + ret = DROPBEAR_SUCCESS; + +out: + if (fd >= 0) { + if (fsync(fd) != 0) { + dropbear_log(LOG_ERR, "fsync of %s failed: %s", filename, strerror(errno)); + } + m_close(fd); + } + return ret; +} + +/* returns 0 on failure */ +static int get_default_bits(enum signkey_type keytype) +{ + switch (keytype) { +#if DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + return DROPBEAR_DEFAULT_RSA_SIZE; +#endif +#if DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + /* DSS for SSH only defines 1024 bits */ + return 1024; +#endif +#if DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + return ECDSA_DEFAULT_SIZE; + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + return 521; + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + return 384; + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + return 256; +#endif +#if DROPBEAR_ED25519 + case DROPBEAR_SIGNKEY_ED25519: + return 256; +#endif + default: + return 0; + } +} + +int signkey_generate_get_bits(enum signkey_type keytype, int bits) { + if (bits == 0) + { + bits = get_default_bits(keytype); + } + return bits; +} + +/* 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; + bits = signkey_generate_get_bits(keytype, bits); + + /* now we can generate the key */ + key = new_sign_key(); + + seedrandom(); + + switch(keytype) { +#if DROPBEAR_RSA + case DROPBEAR_SIGNKEY_RSA: + key->rsakey = gen_rsa_priv_key(bits); + break; +#endif +#if DROPBEAR_DSS + case DROPBEAR_SIGNKEY_DSS: + key->dsskey = gen_dss_priv_key(bits); + break; +#endif +#if DROPBEAR_ECDSA + case DROPBEAR_SIGNKEY_ECDSA_KEYGEN: + case DROPBEAR_SIGNKEY_ECDSA_NISTP521: + case DROPBEAR_SIGNKEY_ECDSA_NISTP384: + case DROPBEAR_SIGNKEY_ECDSA_NISTP256: + { + ecc_key *ecckey = gen_ecdsa_priv_key(bits); + keytype = ecdsa_signkey_type(ecckey); + *signkey_key_ptr(key, keytype) = ecckey; + } + break; +#endif +#if DROPBEAR_ED25519 + case DROPBEAR_SIGNKEY_ED25519: + key->ed25519key = gen_ed25519_priv_key(bits); + break; +#endif + default: + dropbear_exit("Internal error"); + } + + seedrandom(); + + buf = buf_new(MAX_PRIVKEY_SIZE); + + buf_put_priv_key(buf, key, keytype); + sign_key_free(key); + key = NULL; + buf_setpos(buf, 0); + + fn_temp = m_malloc(strlen(filename) + 30); + snprintf(fn_temp, strlen(filename)+30, "%s.tmp%d", filename, getpid()); + ret = buf_writefile(buf, fn_temp, 0); + + if (ret == DROPBEAR_FAILURE) { + goto out; + } + + 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)) { + if (errno == EPERM || errno == EACCES) { + /* Non-atomic fallback when hard-links not allowed or unsupported */ + buf_setpos(buf, 0); + ret = buf_writefile(buf, filename, skip_exist); + } else { + dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", filename, + strerror(errno)); + ret = DROPBEAR_FAILURE; + } + + goto out; + } + } + + /* ensure directory update is flushed to disk, otherwise we can end up + with zero-byte hostkey files if the power goes off */ + fsync_parent_dir(filename); + +out: + if (buf) { + buf_burn_free(buf); + } + + if (fn_temp) { + unlink(fn_temp); + m_free(fn_temp); + } + + return ret; +} |