cli-authpubkey.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Dropbear SSH
  3. *
  4. * Copyright (c) 2002,2003 Matt Johnston
  5. * Copyright (c) 2004 by Mihnea Stoenescu
  6. * All rights reserved.
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. * SOFTWARE. */
  25. #include "includes.h"
  26. #include "buffer.h"
  27. #include "dbutil.h"
  28. #include "session.h"
  29. #include "ssh.h"
  30. #include "runopts.h"
  31. #include "auth.h"
  32. #include "agentfwd.h"
  33. #if DROPBEAR_CLI_PUBKEY_AUTH
  34. static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign);
  35. /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
  36. * We use it to remove the key we tried from the list */
  37. void cli_pubkeyfail() {
  38. m_list_elem *iter;
  39. for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
  40. sign_key *iter_key = (sign_key*)iter->item;
  41. if (iter_key == cli_ses.lastprivkey)
  42. {
  43. /* found the failing key */
  44. list_remove(iter);
  45. sign_key_free(iter_key);
  46. cli_ses.lastprivkey = NULL;
  47. return;
  48. }
  49. }
  50. }
  51. void recv_msg_userauth_pk_ok() {
  52. m_list_elem *iter;
  53. buffer* keybuf = NULL;
  54. char* algotype = NULL;
  55. unsigned int algolen;
  56. enum signkey_type keytype;
  57. enum signature_type sigtype;
  58. unsigned int remotelen;
  59. TRACE(("enter recv_msg_userauth_pk_ok"))
  60. algotype = buf_getstring(ses.payload, &algolen);
  61. sigtype = signature_type_from_name(algotype, algolen);
  62. keytype = signkey_type_from_signature(sigtype);
  63. TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype))
  64. m_free(algotype);
  65. keybuf = buf_new(MAX_PUBKEY_SIZE);
  66. remotelen = buf_getint(ses.payload);
  67. /* Iterate through our keys, find which one it was that matched, and
  68. * send a real request with that key */
  69. for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
  70. sign_key *key = (sign_key*)iter->item;
  71. if (key->type != keytype) {
  72. /* Types differed */
  73. TRACE(("types differed"))
  74. continue;
  75. }
  76. /* Now we compare the contents of the key */
  77. keybuf->pos = keybuf->len = 0;
  78. buf_put_pub_key(keybuf, key, keytype);
  79. buf_setpos(keybuf, 0);
  80. buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
  81. remotelen) which has already been taken from
  82. the remote buffer */
  83. if (keybuf->len-4 != remotelen) {
  84. TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen))
  85. /* Lengths differed */
  86. continue;
  87. }
  88. if (memcmp(buf_getptr(keybuf, remotelen),
  89. buf_getptr(ses.payload, remotelen), remotelen) != 0) {
  90. /* Data didn't match this key */
  91. TRACE(("data differed"))
  92. continue;
  93. }
  94. /* Success */
  95. break;
  96. }
  97. buf_free(keybuf);
  98. if (iter != NULL) {
  99. TRACE(("matching key"))
  100. /* XXX TODO: if it's an encrypted key, here we ask for their
  101. * password */
  102. send_msg_userauth_pubkey((sign_key*)iter->item, sigtype, 1);
  103. } else {
  104. TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
  105. }
  106. TRACE(("leave recv_msg_userauth_pk_ok"))
  107. }
  108. static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype,
  109. const buffer *data_buf) {
  110. #if DROPBEAR_CLI_AGENTFWD
  111. /* TODO: rsa-sha256 agent */
  112. if (key->source == SIGNKEY_SOURCE_AGENT) {
  113. /* Format the agent signature ourselves, as buf_put_sign would. */
  114. buffer *sigblob;
  115. sigblob = buf_new(MAX_PUBKEY_SIZE);
  116. agent_buf_sign(sigblob, key, data_buf, sigtype);
  117. buf_putbufstring(buf, sigblob);
  118. buf_free(sigblob);
  119. } else
  120. #endif /* DROPBEAR_CLI_AGENTFWD */
  121. {
  122. buf_put_sign(buf, key, sigtype, data_buf);
  123. }
  124. }
  125. static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign) {
  126. const char *algoname = NULL;
  127. unsigned int algolen;
  128. buffer* sigbuf = NULL;
  129. enum signkey_type keytype = signkey_type_from_signature(sigtype);
  130. DEBUG1(("enter send_msg_userauth_pubkey %s", signature_name_from_type(sigtype, NULL)))
  131. CHECKCLEARTOWRITE();
  132. buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
  133. buf_putstring(ses.writepayload, cli_opts.username,
  134. strlen(cli_opts.username));
  135. buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
  136. SSH_SERVICE_CONNECTION_LEN);
  137. buf_putstring(ses.writepayload, AUTH_METHOD_PUBKEY,
  138. AUTH_METHOD_PUBKEY_LEN);
  139. buf_putbyte(ses.writepayload, realsign);
  140. algoname = signature_name_from_type(sigtype, &algolen);
  141. buf_putstring(ses.writepayload, algoname, algolen);
  142. buf_put_pub_key(ses.writepayload, key, keytype);
  143. if (realsign) {
  144. TRACE(("realsign"))
  145. /* We put the signature as well - this contains string(session id), then
  146. * the contents of the write payload to this point */
  147. sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len);
  148. buf_putbufstring(sigbuf, ses.session_id);
  149. buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
  150. cli_buf_put_sign(ses.writepayload, key, sigtype, sigbuf);
  151. buf_free(sigbuf); /* Nothing confidential in the buffer */
  152. cli_ses.is_trivial_auth = 0;
  153. }
  154. encrypt_packet();
  155. TRACE(("leave send_msg_userauth_pubkey"))
  156. }
  157. /* Returns 1 if a key was tried */
  158. int cli_auth_pubkey() {
  159. enum signature_type sigtype = DROPBEAR_SIGNATURE_NONE;
  160. TRACE(("enter cli_auth_pubkey"))
  161. #if DROPBEAR_CLI_AGENTFWD
  162. if (!cli_opts.agent_keys_loaded) {
  163. /* get the list of available keys from the agent */
  164. cli_load_agent_keys(cli_opts.privkeys);
  165. cli_opts.agent_keys_loaded = 1;
  166. TRACE(("cli_auth_pubkey: agent keys loaded"))
  167. }
  168. #endif
  169. /* iterate through privkeys to remove ones not allowed in server-sig-algs */
  170. while (cli_opts.privkeys->first) {
  171. sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
  172. if (cli_ses.server_sig_algs) {
  173. #if DROPBEAR_RSA
  174. if (key->type == DROPBEAR_SIGNKEY_RSA) {
  175. #if DROPBEAR_RSA_SHA256
  176. if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNATURE_RSA_SHA256)
  177. == DROPBEAR_SUCCESS) {
  178. sigtype = DROPBEAR_SIGNATURE_RSA_SHA256;
  179. TRACE(("server-sig-algs allows rsa sha256"))
  180. break;
  181. }
  182. #endif /* DROPBEAR_RSA_SHA256 */
  183. #if DROPBEAR_RSA_SHA1
  184. if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNKEY_RSA)
  185. == DROPBEAR_SUCCESS) {
  186. sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
  187. TRACE(("server-sig-algs allows rsa sha1"))
  188. break;
  189. }
  190. #endif /* DROPBEAR_RSA_SHA256 */
  191. } else
  192. #endif /* DROPBEAR_RSA */
  193. {
  194. /* Not RSA */
  195. const char *name = NULL;
  196. sigtype = signature_type_from_signkey(key->type);
  197. name = signature_name_from_type(sigtype, NULL);
  198. if (buf_has_algo(cli_ses.server_sig_algs, name)
  199. == DROPBEAR_SUCCESS) {
  200. TRACE(("server-sig-algs allows %s", name))
  201. break;
  202. }
  203. }
  204. /* No match, skip this key */
  205. TRACE(("server-sig-algs no match keytype %d, skipping", key->type))
  206. key = list_remove(cli_opts.privkeys->first);
  207. sign_key_free(key);
  208. continue;
  209. } else {
  210. /* Server didn't provide a server-sig-algs list, we'll
  211. assume all except rsa-sha256 are OK. */
  212. #if DROPBEAR_RSA
  213. if (key->type == DROPBEAR_SIGNKEY_RSA) {
  214. #if DROPBEAR_RSA_SHA1
  215. sigtype = DROPBEAR_SIGNATURE_RSA_SHA1;
  216. TRACE(("no server-sig-algs, using rsa sha1"))
  217. break;
  218. #else
  219. /* only support rsa-sha256, skip this key */
  220. TRACE(("no server-sig-algs, skipping rsa sha256"))
  221. key = list_remove(cli_opts.privkeys->first);
  222. sign_key_free(key);
  223. continue;
  224. #endif
  225. } /* key->type == DROPBEAR_SIGNKEY_RSA */
  226. #endif /* DROPBEAR_RSA */
  227. sigtype = signature_type_from_signkey(key->type);
  228. TRACE(("no server-sig-algs, using key"))
  229. break;
  230. }
  231. }
  232. if (cli_opts.privkeys->first) {
  233. sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
  234. /* Send a trial request */
  235. send_msg_userauth_pubkey(key, sigtype, 0);
  236. cli_ses.lastprivkey = key;
  237. TRACE(("leave cli_auth_pubkey-success"))
  238. return 1;
  239. } else {
  240. /* no more keys left */
  241. TRACE(("leave cli_auth_pubkey-failure"))
  242. return 0;
  243. }
  244. }
  245. void cli_auth_pubkey_cleanup() {
  246. #if DROPBEAR_CLI_AGENTFWD
  247. m_close(cli_opts.agent_fd);
  248. cli_opts.agent_fd = -1;
  249. #endif
  250. while (cli_opts.privkeys->first) {
  251. sign_key * key = list_remove(cli_opts.privkeys->first);
  252. sign_key_free(key);
  253. }
  254. }
  255. #endif /* Pubkey auth */