Coverage Report

Created: 2025-10-10 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */