Coverage Report

Created: 2023-03-20 06:28

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