4 * Copyright (c) 2002,2003 Matt Johnston
5 * Copyright (c) 2004 by Mihnea Stoenescu
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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
35 #ifdef ENABLE_CLI_PUBKEY_AUTH
36 static void send_msg_userauth_pubkey(sign_key
*key
, int type
, int realsign
);
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 void cli_pubkeyfail() {
42 for (iter
= cli_opts
.privkeys
->first
; iter
; iter
= iter
->next
) {
43 sign_key
*iter_key
= (sign_key
*)iter
->item
;
45 if (iter_key
== cli_ses
.lastprivkey
)
47 /* found the failing key */
49 sign_key_free(iter_key
);
50 cli_ses
.lastprivkey
= NULL
;
56 void recv_msg_userauth_pk_ok() {
58 buffer
* keybuf
= NULL
;
59 char* algotype
= NULL
;
62 unsigned int remotelen
;
64 TRACE(("enter recv_msg_userauth_pk_ok"))
66 algotype
= buf_getstring(ses
.payload
, &algolen
);
67 keytype
= signkey_type_from_name(algotype
, algolen
);
68 TRACE(("recv_msg_userauth_pk_ok: type %d", keytype
))
71 keybuf
= buf_new(MAX_PUBKEY_SIZE
);
73 remotelen
= buf_getint(ses
.payload
);
75 /* Iterate through our keys, find which one it was that matched, and
76 * send a real request with that key */
77 for (iter
= cli_opts
.privkeys
->first
; iter
; iter
= iter
->next
) {
78 sign_key
*key
= (sign_key
*)iter
->item
;
79 if (key
->type
!= keytype
) {
81 TRACE(("types differed"))
85 /* Now we compare the contents of the key */
86 keybuf
->pos
= keybuf
->len
= 0;
87 buf_put_pub_key(keybuf
, key
, keytype
);
88 buf_setpos(keybuf
, 0);
89 buf_incrpos(keybuf
, 4); /* first int is the length of the remainder (ie
90 remotelen) which has already been taken from
94 if (keybuf
->len
-4 != remotelen
) {
95 TRACE(("lengths differed: localh %d remote %d", keybuf
->len
, remotelen
))
96 /* Lengths differed */
99 if (memcmp(buf_getptr(keybuf
, remotelen
),
100 buf_getptr(ses
.payload
, remotelen
), remotelen
) != 0) {
101 /* Data didn't match this key */
102 TRACE(("data differed"))
112 TRACE(("matching key"))
113 /* XXX TODO: if it's an encrypted key, here we ask for their
115 send_msg_userauth_pubkey((sign_key
*)iter
->item
, keytype
, 1);
117 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"))
120 TRACE(("leave recv_msg_userauth_pk_ok"))
123 void cli_buf_put_sign(buffer
* buf
, sign_key
*key
, int type
,
124 const unsigned char *data
, unsigned int len
)
126 if (key
->source
== SIGNKEY_SOURCE_AGENT
) {
127 /* Format the agent signature ourselves, as buf_put_sign would. */
129 sigblob
= buf_new(MAX_PUBKEY_SIZE
);
130 agent_buf_sign(sigblob
, key
, data
, len
);
131 buf_setpos(sigblob
, 0);
132 buf_putstring(buf
, buf_getptr(sigblob
, sigblob
->len
),
137 buf_put_sign(buf
, key
, type
, data
, len
);
142 /* TODO: make it take an agent reference to use as well */
143 static void send_msg_userauth_pubkey(sign_key
*key
, int type
, int realsign
) {
145 const char *algoname
= NULL
;
147 buffer
* sigbuf
= NULL
;
149 TRACE(("enter send_msg_userauth_pubkey"))
152 buf_putbyte(ses
.writepayload
, SSH_MSG_USERAUTH_REQUEST
);
154 buf_putstring(ses
.writepayload
, cli_opts
.username
,
155 strlen(cli_opts
.username
));
157 buf_putstring(ses
.writepayload
, SSH_SERVICE_CONNECTION
,
158 SSH_SERVICE_CONNECTION_LEN
);
160 buf_putstring(ses
.writepayload
, AUTH_METHOD_PUBKEY
,
161 AUTH_METHOD_PUBKEY_LEN
);
163 buf_putbyte(ses
.writepayload
, realsign
);
165 algoname
= signkey_name_from_type(type
, &algolen
);
167 buf_putstring(ses
.writepayload
, algoname
, algolen
);
168 buf_put_pub_key(ses
.writepayload
, key
, type
);
172 /* We put the signature as well - this contains string(session id), then
173 * the contents of the write payload to this point */
174 sigbuf
= buf_new(4 + SHA1_HASH_SIZE
+ ses
.writepayload
->len
);
175 buf_putstring(sigbuf
, ses
.session_id
, SHA1_HASH_SIZE
);
176 buf_putbytes(sigbuf
, ses
.writepayload
->data
, ses
.writepayload
->len
);
177 cli_buf_put_sign(ses
.writepayload
, key
, type
, sigbuf
->data
, sigbuf
->len
);
178 buf_free(sigbuf
); /* Nothing confidential in the buffer */
182 TRACE(("leave send_msg_userauth_pubkey"))
185 /* Returns 1 if a key was tried */
186 int cli_auth_pubkey() {
188 TRACE(("enter cli_auth_pubkey"))
190 if (!cli_opts
.agent_keys_loaded
) {
191 /* get the list of available keys from the agent */
192 cli_load_agent_keys(cli_opts
.privkeys
);
193 cli_opts
.agent_keys_loaded
= 1;
196 if (cli_opts
.privkeys
->first
) {
197 sign_key
* key
= (sign_key
*)cli_opts
.privkeys
->first
->item
;
198 /* Send a trial request */
199 send_msg_userauth_pubkey(key
, key
->type
, 0);
200 cli_ses
.lastprivkey
= key
;
201 TRACE(("leave cli_auth_pubkey-success"))
204 /* no more keys left */
205 TRACE(("leave cli_auth_pubkey-failure"))
210 void cli_auth_pubkey_cleanup() {
212 #ifdef ENABLE_CLI_AGENTFWD
213 m_close(cli_opts
.agent_fd
);
214 cli_opts
.agent_fd
= -1;
217 while (cli_opts
.privkeys
->first
) {
218 sign_key
* key
= list_remove(cli_opts
.privkeys
->first
);
222 #endif /* Pubkey auth */