/src/dropbear/src/cli-authpubkey.c
Line | Count | Source |
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 | | |
26 | | #include "includes.h" |
27 | | #include "buffer.h" |
28 | | #include "dbutil.h" |
29 | | #include "session.h" |
30 | | #include "ssh.h" |
31 | | #include "runopts.h" |
32 | | #include "auth.h" |
33 | | #include "agentfwd.h" |
34 | | |
35 | | #if DROPBEAR_CLI_PUBKEY_AUTH |
36 | | static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign); |
37 | | |
38 | | /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request. |
39 | | * We use it to remove the key we tried from the list */ |
40 | 0 | void cli_pubkeyfail() { |
41 | 0 | m_list_elem *iter; |
42 | 0 | for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { |
43 | 0 | sign_key *iter_key = (sign_key*)iter->item; |
44 | | |
45 | 0 | if (iter_key == cli_ses.lastprivkey) |
46 | 0 | { |
47 | | /* found the failing key */ |
48 | 0 | list_remove(iter); |
49 | 0 | sign_key_free(iter_key); |
50 | 0 | cli_ses.lastprivkey = NULL; |
51 | 0 | return; |
52 | 0 | } |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | 0 | void recv_msg_userauth_pk_ok() { |
57 | 0 | m_list_elem *iter; |
58 | 0 | buffer* keybuf = NULL; |
59 | 0 | char* algotype = NULL; |
60 | 0 | unsigned int algolen; |
61 | 0 | enum signkey_type keytype; |
62 | 0 | enum signature_type sigtype; |
63 | 0 | unsigned int remotelen; |
64 | |
|
65 | 0 | TRACE(("enter recv_msg_userauth_pk_ok")) |
66 | |
|
67 | 0 | algotype = buf_getstring(ses.payload, &algolen); |
68 | 0 | sigtype = signature_type_from_name(algotype, algolen); |
69 | 0 | if (sigtype == DROPBEAR_SIGNATURE_NONE) { |
70 | | /* Server replied with an algorithm that we didn't send */ |
71 | 0 | dropbear_exit("Bad pk_ok"); |
72 | 0 | } |
73 | 0 | keytype = signkey_type_from_signature(sigtype); |
74 | 0 | TRACE(("recv_msg_userauth_pk_ok: type %d", sigtype)) |
75 | 0 | m_free(algotype); |
76 | |
|
77 | 0 | keybuf = buf_new(MAX_PUBKEY_SIZE); |
78 | |
|
79 | 0 | remotelen = buf_getint(ses.payload); |
80 | | |
81 | | /* Iterate through our keys, find which one it was that matched, and |
82 | | * send a real request with that key */ |
83 | 0 | for (iter = cli_opts.privkeys->first; iter; iter = iter->next) { |
84 | 0 | sign_key *key = (sign_key*)iter->item; |
85 | 0 | if (key->type != keytype) { |
86 | | /* Types differed */ |
87 | 0 | TRACE(("types differed")) |
88 | 0 | continue; |
89 | 0 | } |
90 | | |
91 | | /* Now we compare the contents of the key */ |
92 | 0 | keybuf->pos = keybuf->len = 0; |
93 | 0 | buf_put_pub_key(keybuf, key, keytype); |
94 | 0 | buf_setpos(keybuf, 0); |
95 | 0 | buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie |
96 | | remotelen) which has already been taken from |
97 | | the remote buffer */ |
98 | | |
99 | |
|
100 | 0 | if (keybuf->len-4 != remotelen) { |
101 | 0 | TRACE(("lengths differed: localh %d remote %d", keybuf->len, remotelen)) |
102 | | /* Lengths differed */ |
103 | 0 | continue; |
104 | 0 | } |
105 | 0 | if (memcmp(buf_getptr(keybuf, remotelen), |
106 | 0 | buf_getptr(ses.payload, remotelen), remotelen) != 0) { |
107 | | /* Data didn't match this key */ |
108 | 0 | TRACE(("data differed")) |
109 | 0 | continue; |
110 | 0 | } |
111 | | |
112 | | /* Success */ |
113 | 0 | break; |
114 | 0 | } |
115 | 0 | buf_free(keybuf); |
116 | |
|
117 | 0 | if (iter != NULL) { |
118 | 0 | TRACE(("matching key")) |
119 | | /* XXX TODO: if it's an encrypted key, here we ask for their |
120 | | * password */ |
121 | 0 | send_msg_userauth_pubkey((sign_key*)iter->item, sigtype, 1); |
122 | 0 | } else { |
123 | 0 | 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")) |
124 | 0 | } |
125 | | |
126 | 0 | TRACE(("leave recv_msg_userauth_pk_ok")) |
127 | 0 | } |
128 | | |
129 | | static void cli_buf_put_sign(buffer* buf, sign_key *key, enum signature_type sigtype, |
130 | 0 | const buffer *data_buf) { |
131 | 0 | #if DROPBEAR_CLI_AGENTFWD |
132 | | /* TODO: rsa-sha256 agent */ |
133 | 0 | if (key->source == SIGNKEY_SOURCE_AGENT) { |
134 | | /* Format the agent signature ourselves, as buf_put_sign would. */ |
135 | 0 | buffer *sigblob; |
136 | 0 | sigblob = buf_new(MAX_PUBKEY_SIZE); |
137 | 0 | agent_buf_sign(sigblob, key, data_buf, sigtype); |
138 | 0 | buf_putbufstring(buf, sigblob); |
139 | 0 | buf_free(sigblob); |
140 | 0 | } else |
141 | 0 | #endif /* DROPBEAR_CLI_AGENTFWD */ |
142 | 0 | { |
143 | 0 | buf_put_sign(buf, key, sigtype, data_buf); |
144 | 0 | } |
145 | 0 | } |
146 | | |
147 | 0 | static void send_msg_userauth_pubkey(sign_key *key, enum signature_type sigtype, int realsign) { |
148 | |
|
149 | 0 | const char *algoname = NULL; |
150 | 0 | unsigned int algolen; |
151 | 0 | buffer* sigbuf = NULL; |
152 | 0 | enum signkey_type keytype = signkey_type_from_signature(sigtype); |
153 | |
|
154 | 0 | DEBUG1(("enter send_msg_userauth_pubkey %s", signature_name_from_type(sigtype, NULL))) |
155 | 0 | CHECKCLEARTOWRITE(); |
156 | |
|
157 | 0 | buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST); |
158 | |
|
159 | 0 | buf_putstring(ses.writepayload, cli_opts.username, |
160 | 0 | strlen(cli_opts.username)); |
161 | |
|
162 | 0 | buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, |
163 | 0 | SSH_SERVICE_CONNECTION_LEN); |
164 | |
|
165 | 0 | buf_putstring(ses.writepayload, AUTH_METHOD_PUBKEY, |
166 | 0 | AUTH_METHOD_PUBKEY_LEN); |
167 | |
|
168 | 0 | buf_putbyte(ses.writepayload, realsign); |
169 | |
|
170 | 0 | algoname = signature_name_from_type(sigtype, &algolen); |
171 | 0 | buf_putstring(ses.writepayload, algoname, algolen); |
172 | 0 | buf_put_pub_key(ses.writepayload, key, keytype); |
173 | |
|
174 | 0 | if (realsign) { |
175 | 0 | TRACE(("realsign")) |
176 | | /* We put the signature as well - this contains string(session id), then |
177 | | * the contents of the write payload to this point */ |
178 | 0 | sigbuf = buf_new(4 + ses.session_id->len + ses.writepayload->len); |
179 | 0 | buf_putbufstring(sigbuf, ses.session_id); |
180 | 0 | buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len); |
181 | 0 | cli_buf_put_sign(ses.writepayload, key, sigtype, sigbuf); |
182 | 0 | buf_free(sigbuf); /* Nothing confidential in the buffer */ |
183 | 0 | cli_ses.is_trivial_auth = 0; |
184 | 0 | } |
185 | |
|
186 | 0 | encrypt_packet(); |
187 | 0 | TRACE(("leave send_msg_userauth_pubkey")) |
188 | 0 | } |
189 | | |
190 | | /* Returns 1 if a key was tried */ |
191 | 0 | int cli_auth_pubkey() { |
192 | 0 | enum signature_type sigtype = DROPBEAR_SIGNATURE_NONE; |
193 | 0 | TRACE(("enter cli_auth_pubkey")) |
194 | |
|
195 | 0 | #if DROPBEAR_CLI_AGENTFWD |
196 | 0 | if (!cli_opts.agent_keys_loaded) { |
197 | | /* get the list of available keys from the agent */ |
198 | 0 | cli_load_agent_keys(cli_opts.privkeys); |
199 | 0 | cli_opts.agent_keys_loaded = 1; |
200 | 0 | TRACE(("cli_auth_pubkey: agent keys loaded")) |
201 | 0 | } |
202 | 0 | #endif |
203 | | |
204 | | /* iterate through privkeys to remove ones not allowed in server-sig-algs */ |
205 | 0 | while (cli_opts.privkeys->first) { |
206 | 0 | sign_key * key = (sign_key*)cli_opts.privkeys->first->item; |
207 | 0 | if (cli_ses.server_sig_algs) { |
208 | 0 | #if DROPBEAR_RSA |
209 | 0 | if (key->type == DROPBEAR_SIGNKEY_RSA) { |
210 | 0 | #if DROPBEAR_RSA_SHA256 |
211 | 0 | if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNATURE_RSA_SHA256) |
212 | 0 | == DROPBEAR_SUCCESS) { |
213 | 0 | sigtype = DROPBEAR_SIGNATURE_RSA_SHA256; |
214 | 0 | TRACE(("server-sig-algs allows rsa sha256")) |
215 | 0 | break; |
216 | 0 | } |
217 | 0 | #endif /* DROPBEAR_RSA_SHA256 */ |
218 | 0 | #if DROPBEAR_RSA_SHA1 |
219 | 0 | if (buf_has_algo(cli_ses.server_sig_algs, SSH_SIGNKEY_RSA) |
220 | 0 | == DROPBEAR_SUCCESS) { |
221 | 0 | sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; |
222 | 0 | TRACE(("server-sig-algs allows rsa sha1")) |
223 | 0 | break; |
224 | 0 | } |
225 | 0 | #endif /* DROPBEAR_RSA_SHA256 */ |
226 | 0 | } else |
227 | 0 | #endif /* DROPBEAR_RSA */ |
228 | 0 | { |
229 | | /* Not RSA */ |
230 | 0 | const char *name = NULL; |
231 | 0 | sigtype = signature_type_from_signkey(key->type); |
232 | 0 | name = signature_name_from_type(sigtype, NULL); |
233 | 0 | if (buf_has_algo(cli_ses.server_sig_algs, name) |
234 | 0 | == DROPBEAR_SUCCESS) { |
235 | 0 | TRACE(("server-sig-algs allows %s", name)) |
236 | 0 | break; |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | /* No match, skip this key */ |
241 | 0 | TRACE(("server-sig-algs no match keytype %d, skipping", key->type)) |
242 | 0 | key = list_remove(cli_opts.privkeys->first); |
243 | 0 | sign_key_free(key); |
244 | 0 | continue; |
245 | 0 | } else { |
246 | | /* Server didn't provide a server-sig-algs list, we'll |
247 | | assume all except rsa-sha256 are OK. */ |
248 | 0 | #if DROPBEAR_RSA |
249 | 0 | if (key->type == DROPBEAR_SIGNKEY_RSA) { |
250 | 0 | #if DROPBEAR_RSA_SHA1 |
251 | 0 | sigtype = DROPBEAR_SIGNATURE_RSA_SHA1; |
252 | 0 | TRACE(("no server-sig-algs, using rsa sha1")) |
253 | 0 | break; |
254 | | #else |
255 | | /* only support rsa-sha256, skip this key */ |
256 | | TRACE(("no server-sig-algs, skipping rsa sha256")) |
257 | | key = list_remove(cli_opts.privkeys->first); |
258 | | sign_key_free(key); |
259 | | continue; |
260 | | #endif |
261 | 0 | } /* key->type == DROPBEAR_SIGNKEY_RSA */ |
262 | 0 | #endif /* DROPBEAR_RSA */ |
263 | 0 | sigtype = signature_type_from_signkey(key->type); |
264 | 0 | TRACE(("no server-sig-algs, using key")) |
265 | 0 | break; |
266 | 0 | } |
267 | 0 | } |
268 | |
|
269 | 0 | if (cli_opts.privkeys->first) { |
270 | 0 | sign_key * key = (sign_key*)cli_opts.privkeys->first->item; |
271 | | /* Send a trial request */ |
272 | 0 | send_msg_userauth_pubkey(key, sigtype, 0); |
273 | 0 | cli_ses.lastprivkey = key; |
274 | 0 | TRACE(("leave cli_auth_pubkey-success")) |
275 | 0 | return 1; |
276 | 0 | } else { |
277 | | /* no more keys left */ |
278 | 0 | TRACE(("leave cli_auth_pubkey-failure")) |
279 | 0 | return 0; |
280 | 0 | } |
281 | 0 | } |
282 | | |
283 | 0 | void cli_auth_pubkey_cleanup() { |
284 | |
|
285 | 0 | #if DROPBEAR_CLI_AGENTFWD |
286 | 0 | m_close(cli_opts.agent_fd); |
287 | 0 | cli_opts.agent_fd = -1; |
288 | 0 | #endif |
289 | |
|
290 | 0 | while (cli_opts.privkeys->first) { |
291 | 0 | sign_key * key = list_remove(cli_opts.privkeys->first); |
292 | 0 | sign_key_free(key); |
293 | 0 | } |
294 | 0 | } |
295 | | #endif /* Pubkey auth */ |