123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193 |
- #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;
- }
|