gensignkey.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "includes.h"
  2. #include "dbutil.h"
  3. #include "buffer.h"
  4. #include "ecdsa.h"
  5. #include "genrsa.h"
  6. #include "gendss.h"
  7. #include "gened25519.h"
  8. #include "signkey.h"
  9. #include "dbrandom.h"
  10. /* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
  11. static int buf_writefile(buffer * buf, const char * filename, int skip_exist) {
  12. int ret = DROPBEAR_FAILURE;
  13. int fd = -1;
  14. fd = open(filename, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
  15. if (fd < 0) {
  16. /* If generating keys on connection (skip_exist) it's OK to get EEXIST
  17. - we probably just lost a race with another connection to generate the key */
  18. if (skip_exist && errno == EEXIST) {
  19. ret = DROPBEAR_SUCCESS;
  20. } else {
  21. dropbear_log(LOG_ERR, "Couldn't create new file %s: %s",
  22. filename, strerror(errno));
  23. }
  24. goto out;
  25. }
  26. /* write the file now */
  27. while (buf->pos != buf->len) {
  28. int len = write(fd, buf_getptr(buf, buf->len - buf->pos),
  29. buf->len - buf->pos);
  30. if (len == -1 && errno == EINTR) {
  31. continue;
  32. }
  33. if (len <= 0) {
  34. dropbear_log(LOG_ERR, "Failed writing file %s: %s",
  35. filename, strerror(errno));
  36. goto out;
  37. }
  38. buf_incrpos(buf, len);
  39. }
  40. ret = DROPBEAR_SUCCESS;
  41. out:
  42. if (fd >= 0) {
  43. if (fsync(fd) != 0) {
  44. dropbear_log(LOG_ERR, "fsync of %s failed: %s", filename, strerror(errno));
  45. }
  46. m_close(fd);
  47. }
  48. return ret;
  49. }
  50. /* returns 0 on failure */
  51. static int get_default_bits(enum signkey_type keytype)
  52. {
  53. switch (keytype) {
  54. #if DROPBEAR_RSA
  55. case DROPBEAR_SIGNKEY_RSA:
  56. return DROPBEAR_DEFAULT_RSA_SIZE;
  57. #endif
  58. #if DROPBEAR_DSS
  59. case DROPBEAR_SIGNKEY_DSS:
  60. /* DSS for SSH only defines 1024 bits */
  61. return 1024;
  62. #endif
  63. #if DROPBEAR_ECDSA
  64. case DROPBEAR_SIGNKEY_ECDSA_KEYGEN:
  65. return ECDSA_DEFAULT_SIZE;
  66. case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
  67. return 521;
  68. case DROPBEAR_SIGNKEY_ECDSA_NISTP384:
  69. return 384;
  70. case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
  71. return 256;
  72. #endif
  73. #if DROPBEAR_ED25519
  74. case DROPBEAR_SIGNKEY_ED25519:
  75. return 256;
  76. #endif
  77. default:
  78. return 0;
  79. }
  80. }
  81. int signkey_generate_get_bits(enum signkey_type keytype, int bits) {
  82. if (bits == 0)
  83. {
  84. bits = get_default_bits(keytype);
  85. }
  86. return bits;
  87. }
  88. /* if skip_exist is set it will silently return if the key file exists */
  89. int signkey_generate(enum signkey_type keytype, int bits, const char* filename, int skip_exist)
  90. {
  91. sign_key * key = NULL;
  92. buffer *buf = NULL;
  93. char *fn_temp = NULL;
  94. int ret = DROPBEAR_FAILURE;
  95. bits = signkey_generate_get_bits(keytype, bits);
  96. /* now we can generate the key */
  97. key = new_sign_key();
  98. seedrandom();
  99. switch(keytype) {
  100. #if DROPBEAR_RSA
  101. case DROPBEAR_SIGNKEY_RSA:
  102. key->rsakey = gen_rsa_priv_key(bits);
  103. break;
  104. #endif
  105. #if DROPBEAR_DSS
  106. case DROPBEAR_SIGNKEY_DSS:
  107. key->dsskey = gen_dss_priv_key(bits);
  108. break;
  109. #endif
  110. #if DROPBEAR_ECDSA
  111. case DROPBEAR_SIGNKEY_ECDSA_KEYGEN:
  112. case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
  113. case DROPBEAR_SIGNKEY_ECDSA_NISTP384:
  114. case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
  115. {
  116. ecc_key *ecckey = gen_ecdsa_priv_key(bits);
  117. keytype = ecdsa_signkey_type(ecckey);
  118. *signkey_key_ptr(key, keytype) = ecckey;
  119. }
  120. break;
  121. #endif
  122. #if DROPBEAR_ED25519
  123. case DROPBEAR_SIGNKEY_ED25519:
  124. key->ed25519key = gen_ed25519_priv_key(bits);
  125. break;
  126. #endif
  127. default:
  128. dropbear_exit("Internal error");
  129. }
  130. seedrandom();
  131. buf = buf_new(MAX_PRIVKEY_SIZE);
  132. buf_put_priv_key(buf, key, keytype);
  133. sign_key_free(key);
  134. key = NULL;
  135. buf_setpos(buf, 0);
  136. fn_temp = m_malloc(strlen(filename) + 30);
  137. snprintf(fn_temp, strlen(filename)+30, "%s.tmp%d", filename, getpid());
  138. ret = buf_writefile(buf, fn_temp, 0);
  139. if (ret == DROPBEAR_FAILURE) {
  140. goto out;
  141. }
  142. if (link(fn_temp, filename) < 0) {
  143. /* If generating keys on connection (skipexist) it's OK to get EEXIST
  144. - we probably just lost a race with another connection to generate the key */
  145. if (!(skip_exist && errno == EEXIST)) {
  146. if (errno == EPERM || errno == EACCES) {
  147. /* Non-atomic fallback when hard-links not allowed or unsupported */
  148. buf_setpos(buf, 0);
  149. ret = buf_writefile(buf, filename, skip_exist);
  150. } else {
  151. dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", filename,
  152. strerror(errno));
  153. ret = DROPBEAR_FAILURE;
  154. }
  155. goto out;
  156. }
  157. }
  158. /* ensure directory update is flushed to disk, otherwise we can end up
  159. with zero-byte hostkey files if the power goes off */
  160. fsync_parent_dir(filename);
  161. out:
  162. if (buf) {
  163. buf_burn_free(buf);
  164. }
  165. if (fn_temp) {
  166. unlink(fn_temp);
  167. m_free(fn_temp);
  168. }
  169. return ret;
  170. }