Coverage Report

Created: 2023-09-25 06:08

/src/dropbear/src/svr-authpubkey.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Dropbear - a SSH2 server
3
 *
4
 * Copyright (c) 2002,2003 Matt Johnston
5
 * All rights reserved.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 *
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE. */
24
/*
25
 * This file incorporates work covered by the following copyright and
26
 * permission notice:
27
 *
28
 *  Copyright (c) 2000 Markus Friedl.  All rights reserved.
29
 *
30
 *  Redistribution and use in source and binary forms, with or without
31
 *  modification, are permitted provided that the following conditions
32
 *  are met:
33
 *  1. Redistributions of source code must retain the above copyright
34
 *     notice, this list of conditions and the following disclaimer.
35
 *  2. Redistributions in binary form must reproduce the above copyright
36
 *     notice, this list of conditions and the following disclaimer in the
37
 *     documentation and/or other materials provided with the distribution.
38
 *
39
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
40
 *  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
42
 *  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
43
 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
44
 *  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
45
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
46
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
48
 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
49
 *
50
 * This copyright and permission notice applies to the code parsing public keys
51
 * options string which can also be found in OpenSSH auth2-pubkey.c file
52
 * (user_key_allowed2). It has been adapted to work with buffers.
53
 *
54
 */
55
56
/* Process a pubkey auth request */
57
58
#include "includes.h"
59
#include "session.h"
60
#include "dbutil.h"
61
#include "buffer.h"
62
#include "signkey.h"
63
#include "auth.h"
64
#include "ssh.h"
65
#include "packet.h"
66
#include "algo.h"
67
#include "runopts.h"
68
69
#if DROPBEAR_SVR_PUBKEY_AUTH
70
71
0
#define MIN_AUTHKEYS_LINE 10 /* "ssh-rsa AB" - short but doesn't matter */
72
0
#define MAX_AUTHKEYS_LINE 4200 /* max length of a line in authkeys */
73
74
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
75
    const unsigned char* keyblob, unsigned int keybloblen);
76
static int checkpubkeyperms(void);
77
static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
78
    const unsigned char* keyblob, unsigned int keybloblen);
79
static int checkfileperm(char * filename);
80
81
/* process a pubkey auth request, sending success or failure message as
82
 * appropriate */
83
819
void svr_auth_pubkey(int valid_user) {
84
85
819
  unsigned char testkey; /* whether we're just checking if a key is usable */
86
819
  char* sigalgo = NULL;
87
819
  unsigned int sigalgolen;
88
819
  const char* keyalgo;
89
819
  unsigned int keyalgolen;
90
819
  unsigned char* keyblob = NULL;
91
819
  unsigned int keybloblen;
92
819
  unsigned int sign_payload_length;
93
819
  buffer * signbuf = NULL;
94
819
  sign_key * key = NULL;
95
819
  char* fp = NULL;
96
819
  enum signature_type sigtype;
97
819
  enum signkey_type keytype;
98
819
    int auth_failure = 1;
99
100
819
  TRACE(("enter pubkeyauth"))
101
102
  /* 0 indicates user just wants to check if key can be used, 1 is an
103
   * actual attempt*/
104
819
  testkey = (buf_getbool(ses.payload) == 0);
105
106
819
  sigalgo = buf_getstring(ses.payload, &sigalgolen);
107
819
  keybloblen = buf_getint(ses.payload);
108
819
  keyblob = buf_getptr(ses.payload, keybloblen);
109
110
819
  if (!valid_user) {
111
    /* Return failure once we have read the contents of the packet
112
    required to validate a public key.
113
    Avoids blind user enumeration though it isn't possible to prevent
114
    testing for user existence if the public key is known */
115
81
    send_msg_userauth_failure(0, 0);
116
81
    goto out;
117
81
  }
118
119
738
  sigtype = signature_type_from_name(sigalgo, sigalgolen);
120
738
  if (sigtype == DROPBEAR_SIGNATURE_NONE) {
121
667
    send_msg_userauth_failure(0, 0);
122
667
    goto out;
123
667
  }
124
125
71
  keytype = signkey_type_from_signature(sigtype);
126
71
  keyalgo = signkey_name_from_type(keytype, &keyalgolen);
127
128
#if DROPBEAR_PLUGIN
129
        if (svr_ses.plugin_instance != NULL) {
130
            char *options_buf;
131
            if (svr_ses.plugin_instance->checkpubkey(
132
                        svr_ses.plugin_instance,
133
                        &ses.plugin_session,
134
                        keyalgo,
135
                        keyalgolen,
136
                        keyblob,
137
                        keybloblen,
138
                        ses.authstate.username) == DROPBEAR_SUCCESS) {
139
                /* Success */
140
                auth_failure = 0;
141
142
                /* Options provided? */
143
                options_buf = ses.plugin_session->get_options(ses.plugin_session);
144
                if (options_buf) {
145
                    struct buf temp_buf = {
146
                        .data = (unsigned char *)options_buf,
147
                        .len = strlen(options_buf),
148
                        .pos = 0,
149
                        .size = 0
150
                    };
151
                    int ret = svr_add_pubkey_options(&temp_buf, 0, "N/A");
152
                    if (ret == DROPBEAR_FAILURE) {
153
                        /* Fail immediately as the plugin provided wrong options */
154
                        send_msg_userauth_failure(0, 0);
155
                        goto out;
156
                    }
157
                }
158
            }
159
        }
160
#endif
161
  /* check if the key is valid */
162
71
        if (auth_failure) {
163
0
            auth_failure = checkpubkey(keyalgo, keyalgolen, keyblob, keybloblen) == DROPBEAR_FAILURE;
164
0
        }
165
166
71
        if (auth_failure) {
167
0
    send_msg_userauth_failure(0, 0);
168
0
    goto out;
169
0
  }
170
171
  /* let them know that the key is ok to use */
172
71
  if (testkey) {
173
0
    send_msg_userauth_pk_ok(sigalgo, sigalgolen, keyblob, keybloblen);
174
0
    goto out;
175
0
  }
176
177
  /* now we can actually verify the signature */
178
179
  /* get the key */
180
71
  key = new_sign_key();
181
71
  if (buf_get_pub_key(ses.payload, key, &keytype) == DROPBEAR_FAILURE) {
182
0
    send_msg_userauth_failure(0, 1);
183
0
    goto out;
184
0
  }
185
186
71
#if DROPBEAR_SK_ECDSA || DROPBEAR_SK_ED25519
187
71
  key->sk_flags_mask = SSH_SK_USER_PRESENCE_REQD;
188
71
  if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->no_touch_required_flag) {
189
0
    key->sk_flags_mask &= ~SSH_SK_USER_PRESENCE_REQD;
190
0
  }
191
71
  if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->verify_required_flag) {
192
0
    key->sk_flags_mask |= SSH_SK_USER_VERIFICATION_REQD;
193
0
  }
194
71
#endif
195
196
  /* create the data which has been signed - this a string containing
197
   * session_id, concatenated with the payload packet up to the signature */
198
71
  assert(ses.payload_beginning <= ses.payload->pos);
199
0
  sign_payload_length = ses.payload->pos - ses.payload_beginning;
200
0
  signbuf = buf_new(ses.payload->pos + 4 + ses.session_id->len);
201
0
  buf_putbufstring(signbuf, ses.session_id);
202
203
  /* The entire contents of the payload prior. */
204
0
  buf_setpos(ses.payload, ses.payload_beginning);
205
0
  buf_putbytes(signbuf,
206
0
    buf_getptr(ses.payload, sign_payload_length),
207
0
    sign_payload_length);
208
0
  buf_incrpos(ses.payload, sign_payload_length);
209
210
0
  buf_setpos(signbuf, 0);
211
212
  /* ... and finally verify the signature */
213
0
  fp = sign_key_fingerprint(keyblob, keybloblen);
214
0
  if (buf_verify(ses.payload, key, sigtype, signbuf) == DROPBEAR_SUCCESS) {
215
0
    if (svr_opts.multiauthmethod && (ses.authstate.authtypes & ~AUTH_TYPE_PUBKEY)) {
216
      /* successful pubkey authentication, but extra auth required */
217
0
      dropbear_log(LOG_NOTICE,
218
0
          "Pubkey auth succeeded for '%s' with %s key %s from %s, extra auth required",
219
0
          ses.authstate.pw_name,
220
0
          signkey_name_from_type(keytype, NULL), fp,
221
0
          svr_ses.addrstring);
222
0
      ses.authstate.authtypes &= ~AUTH_TYPE_PUBKEY; /* pubkey auth ok, delete the method flag */
223
0
      send_msg_userauth_failure(1, 0); /* Send partial success */
224
0
    } else {
225
      /* successful authentication */
226
0
      dropbear_log(LOG_NOTICE,
227
0
          "Pubkey auth succeeded for '%s' with %s key %s from %s",
228
0
          ses.authstate.pw_name,
229
0
          signkey_name_from_type(keytype, NULL), fp,
230
0
          svr_ses.addrstring);
231
0
      send_msg_userauth_success();
232
0
    }
233
#if DROPBEAR_PLUGIN
234
                if ((ses.plugin_session != NULL) && (svr_ses.plugin_instance->auth_success != NULL)) {
235
                    /* Was authenticated through the external plugin. tell plugin that signature verification was ok */
236
                    svr_ses.plugin_instance->auth_success(ses.plugin_session);
237
                }
238
#endif
239
0
  } else {
240
0
    dropbear_log(LOG_WARNING,
241
0
        "Pubkey auth bad signature for '%s' with key %s from %s",
242
0
        ses.authstate.pw_name, fp, svr_ses.addrstring);
243
0
    send_msg_userauth_failure(0, 1);
244
0
  }
245
0
  m_free(fp);
246
247
748
out:
248
  /* cleanup stuff */
249
748
  if (signbuf) {
250
0
    buf_free(signbuf);
251
0
  }
252
748
  if (sigalgo) {
253
748
    m_free(sigalgo);
254
748
  }
255
748
  if (key) {
256
0
    sign_key_free(key);
257
0
    key = NULL;
258
0
  }
259
  /* Retain pubkey options only if auth succeeded */
260
748
  if (!ses.authstate.authdone) {
261
748
    svr_pubkey_options_cleanup();
262
748
  }
263
748
  TRACE(("leave pubkeyauth"))
264
748
}
265
266
/* Reply that the key is valid for auth, this is sent when the user sends
267
 * a straight copy of their pubkey to test, to avoid having to perform
268
 * expensive signing operations with a worthless key */
269
static void send_msg_userauth_pk_ok(const char* sigalgo, unsigned int sigalgolen,
270
0
    const unsigned char* keyblob, unsigned int keybloblen) {
271
272
0
  TRACE(("enter send_msg_userauth_pk_ok"))
273
0
  CHECKCLEARTOWRITE();
274
275
0
  buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_PK_OK);
276
0
  buf_putstring(ses.writepayload, sigalgo, sigalgolen);
277
0
  buf_putstring(ses.writepayload, (const char*)keyblob, keybloblen);
278
279
0
  encrypt_packet();
280
0
  TRACE(("leave send_msg_userauth_pk_ok"))
281
282
0
}
283
284
/* Content for SSH_PUBKEYINFO is optionally returned malloced in ret_info (will be
285
   freed if already set */
286
static int checkpubkey_line(buffer* line, int line_num, const char* filename,
287
    const char* algo, unsigned int algolen,
288
    const unsigned char* keyblob, unsigned int keybloblen,
289
0
    char ** ret_info) {
290
0
  buffer *options_buf = NULL;
291
0
  char *info_str = NULL;
292
0
  unsigned int pos, len, infopos, infolen;
293
0
  int ret = DROPBEAR_FAILURE;
294
295
0
  if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) {
296
0
    TRACE(("checkpubkey_line: bad line length %d", line->len))
297
0
    goto out;
298
0
  }
299
300
0
  if (memchr(line->data, 0x0, line->len) != NULL) {
301
0
    TRACE(("checkpubkey_line: bad line has null char"))
302
0
    goto out;
303
0
  }
304
305
  /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
306
0
  if (line->pos + algolen+3 > line->len) {
307
0
    goto out;
308
0
  }
309
  /* check the key type */
310
0
  if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
311
0
    int is_comment = 0;
312
0
    unsigned char *options_start = NULL;
313
0
    int options_len = 0;
314
0
    int escape, quoted;
315
316
    /* skip over any comments or leading whitespace */
317
0
    while (line->pos < line->len) {
318
0
      const char c = buf_getbyte(line);
319
0
      if (c == ' ' || c == '\t') {
320
0
        continue;
321
0
      } else if (c == '#') {
322
0
        is_comment = 1;
323
0
        break;
324
0
      }
325
0
      buf_decrpos(line, 1);
326
0
      break;
327
0
    }
328
0
    if (is_comment) {
329
      /* next line */
330
0
      goto out;
331
0
    }
332
333
    /* remember start of options */
334
0
    options_start = buf_getptr(line, 1);
335
0
    quoted = 0;
336
0
    escape = 0;
337
0
    options_len = 0;
338
339
    /* figure out where the options are */
340
0
    while (line->pos < line->len) {
341
0
      const char c = buf_getbyte(line);
342
0
      if (!quoted && (c == ' ' || c == '\t')) {
343
0
        break;
344
0
      }
345
0
      escape = (!escape && c == '\\');
346
0
      if (!escape && c == '"') {
347
0
        quoted = !quoted;
348
0
      }
349
0
      options_len++;
350
0
    }
351
0
    options_buf = buf_new(options_len);
352
0
    buf_putbytes(options_buf, options_start, options_len);
353
354
    /* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
355
0
    if (line->pos + algolen+3 > line->len) {
356
0
      goto out;
357
0
    }
358
0
    if (strncmp((const char *) buf_getptr(line, algolen), algo, algolen) != 0) {
359
0
      goto out;
360
0
    }
361
0
  }
362
0
  buf_incrpos(line, algolen);
363
364
  /* check for space (' ') character */
365
0
  if (buf_getbyte(line) != ' ') {
366
0
    TRACE(("checkpubkey_line: space character expected, isn't there"))
367
0
    goto out;
368
0
  }
369
370
  /* find the length of base64 data */
371
0
  pos = line->pos;
372
0
  for (len = 0; line->pos < line->len; len++) {
373
0
    if (buf_getbyte(line) == ' ') {
374
0
      break;
375
0
    }
376
0
  }
377
378
  /* find out the length of the public key info, stop at the first space */
379
0
  infopos = line->pos;
380
0
  for (infolen = 0; line->pos < line->len; infolen++) {
381
0
    const char c = buf_getbyte(line);
382
0
    if (c == ' ') {
383
0
      break;
384
0
    }
385
    /* We have an allowlist - authorized_keys lines can't be fully trusted,
386
    some shell scripts may do unsafe things with env var values */
387
0
    if (!(isalnum(c) || strchr(".,_-+@", c))) {
388
0
      TRACE(("Not setting SSH_PUBKEYINFO, special characters"))
389
0
      infolen = 0;
390
0
      break;
391
0
    }
392
0
  }
393
0
  if (infolen > 0) {
394
0
    info_str = m_malloc(infolen + 1);
395
0
    buf_setpos(line, infopos);
396
0
        strncpy(info_str, buf_getptr(line, infolen), infolen);
397
0
  }
398
399
  /* truncate to base64 data length */
400
0
  buf_setpos(line, pos);
401
0
  buf_setlen(line, line->pos + len);
402
403
0
  TRACE(("checkpubkey_line: line pos = %d len = %d", line->pos, line->len))
404
405
0
  ret = cmp_base64_key(keyblob, keybloblen, (const unsigned char *) algo, algolen, line, NULL);
406
407
  /* free pubkey_info if it is filled */
408
0
  if (ret_info && *ret_info) {
409
0
    m_free(*ret_info);
410
0
    *ret_info = NULL;
411
0
  }
412
413
0
  if (ret == DROPBEAR_SUCCESS) {
414
0
    if (options_buf) {
415
0
      ret = svr_add_pubkey_options(options_buf, line_num, filename);
416
0
    }
417
0
    if (ret_info) {
418
      /* take the (optional) public key information */
419
0
      *ret_info = info_str;
420
0
      info_str = NULL;
421
0
    }
422
0
  }
423
424
0
out:
425
0
  if (options_buf) {
426
0
    buf_free(options_buf);
427
0
  }
428
0
  if (info_str) {
429
0
    m_free(info_str);
430
0
  }
431
0
  return ret;
432
0
}
433
434
435
/* Checks whether a specified publickey (and associated algorithm) is an
436
 * acceptable key for authentication */
437
/* Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
438
static int checkpubkey(const char* keyalgo, unsigned int keyalgolen,
439
0
    const unsigned char* keyblob, unsigned int keybloblen) {
440
441
0
  FILE * authfile = NULL;
442
0
  char * filename = NULL;
443
0
  int ret = DROPBEAR_FAILURE;
444
0
  buffer * line = NULL;
445
0
  unsigned int len;
446
0
  int line_num;
447
0
  uid_t origuid;
448
0
  gid_t origgid;
449
450
0
  TRACE(("enter checkpubkey"))
451
452
0
#if DROPBEAR_SVR_MULTIUSER
453
  /* access the file as the authenticating user. */
454
0
  origuid = getuid();
455
0
  origgid = getgid();
456
0
  if ((setegid(ses.authstate.pw_gid)) < 0 ||
457
0
    (seteuid(ses.authstate.pw_uid)) < 0) {
458
0
    dropbear_exit("Failed to set euid");
459
0
  }
460
0
#endif
461
  /* check file permissions, also whether file exists */
462
0
  if (checkpubkeyperms() == DROPBEAR_FAILURE) {
463
0
    TRACE(("bad authorized_keys permissions, or file doesn't exist"))
464
0
  } else {
465
    /* we don't need to check pw and pw_dir for validity, since
466
     * its been done in checkpubkeyperms. */
467
0
    len = strlen(ses.authstate.pw_dir);
468
    /* allocate max required pathname storage,
469
     * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
470
0
    filename = m_malloc(len + 22);
471
0
    snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
472
0
          ses.authstate.pw_dir);
473
474
0
    authfile = fopen(filename, "r");
475
0
    if (!authfile) {
476
0
      TRACE(("checkpubkey: failed opening %s: %s", filename, strerror(errno)))
477
0
    }
478
0
  }
479
0
#if DROPBEAR_SVR_MULTIUSER
480
0
  if ((seteuid(origuid)) < 0 ||
481
0
    (setegid(origgid)) < 0) {
482
0
    dropbear_exit("Failed to revert euid");
483
0
  }
484
0
#endif
485
486
0
  if (authfile == NULL) {
487
0
    goto out;
488
0
  }
489
0
  TRACE(("checkpubkey: opened authorized_keys OK"))
490
491
0
  line = buf_new(MAX_AUTHKEYS_LINE);
492
0
  line_num = 0;
493
494
  /* iterate through the lines */
495
0
  do {
496
0
    if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
497
      /* EOF reached */
498
0
      TRACE(("checkpubkey: authorized_keys EOF reached"))
499
0
      break;
500
0
    }
501
0
    line_num++;
502
503
0
    ret = checkpubkey_line(line, line_num, filename, keyalgo, keyalgolen,
504
0
      keyblob, keybloblen, &ses.authstate.pubkey_info);
505
0
    if (ret == DROPBEAR_SUCCESS) {
506
0
      break;
507
0
    }
508
509
    /* We continue to the next line otherwise */
510
0
  } while (1);
511
512
0
out:
513
0
  if (authfile) {
514
0
    fclose(authfile);
515
0
  }
516
0
  if (line) {
517
0
    buf_free(line);
518
0
  }
519
0
  m_free(filename);
520
0
  TRACE(("leave checkpubkey: ret=%d", ret))
521
0
  return ret;
522
0
}
523
524
525
/* Returns DROPBEAR_SUCCESS if file permissions for pubkeys are ok,
526
 * DROPBEAR_FAILURE otherwise.
527
 * Checks that the user's homedir, ~/.ssh, and
528
 * ~/.ssh/authorized_keys are all owned by either root or the user, and are
529
 * g-w, o-w */
530
0
static int checkpubkeyperms() {
531
532
0
  char* filename = NULL;
533
0
  int ret = DROPBEAR_FAILURE;
534
0
  unsigned int len;
535
536
0
  TRACE(("enter checkpubkeyperms"))
537
538
0
  if (ses.authstate.pw_dir == NULL) {
539
0
    goto out;
540
0
  }
541
542
0
  if ((len = strlen(ses.authstate.pw_dir)) == 0) {
543
0
    goto out;
544
0
  }
545
546
  /* allocate max required pathname storage,
547
   * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
548
0
  len += 22;
549
0
  filename = m_malloc(len);
550
0
  strlcpy(filename, ses.authstate.pw_dir, len);
551
552
  /* check ~ */
553
0
  if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
554
0
    goto out;
555
0
  }
556
557
  /* check ~/.ssh */
558
0
  strlcat(filename, "/.ssh", len);
559
0
  if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
560
0
    goto out;
561
0
  }
562
563
  /* now check ~/.ssh/authorized_keys */
564
0
  strlcat(filename, "/authorized_keys", len);
565
0
  if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
566
0
    goto out;
567
0
  }
568
569
  /* file looks ok, return success */
570
0
  ret = DROPBEAR_SUCCESS;
571
572
0
out:
573
0
  m_free(filename);
574
575
0
  TRACE(("leave checkpubkeyperms"))
576
0
  return ret;
577
0
}
578
579
/* Checks that a file is owned by the user or root, and isn't writable by
580
 * group or other */
581
/* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
582
0
static int checkfileperm(char * filename) {
583
0
  struct stat filestat;
584
0
  int badperm = 0;
585
586
0
  TRACE(("enter checkfileperm(%s)", filename))
587
588
0
  if (stat(filename, &filestat) != 0) {
589
0
    TRACE(("leave checkfileperm: stat() != 0"))
590
0
    return DROPBEAR_FAILURE;
591
0
  }
592
  /* check ownership - user or root only*/
593
0
  if (filestat.st_uid != ses.authstate.pw_uid
594
0
      && filestat.st_uid != 0) {
595
0
    badperm = 1;
596
0
    TRACE(("wrong ownership"))
597
0
  }
598
  /* check permissions - don't want group or others +w */
599
0
  if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
600
0
    badperm = 1;
601
0
    TRACE(("wrong perms"))
602
0
  }
603
0
  if (badperm) {
604
0
    if (!ses.authstate.perm_warn) {
605
0
      ses.authstate.perm_warn = 1;
606
0
      dropbear_log(LOG_INFO, "%s must be owned by user or root, and not writable by group or others", filename);
607
0
    }
608
0
    TRACE(("leave checkfileperm: failure perms/owner"))
609
0
    return DROPBEAR_FAILURE;
610
0
  }
611
612
0
  TRACE(("leave checkfileperm: success"))
613
0
  return DROPBEAR_SUCCESS;
614
0
}
615
616
#if DROPBEAR_FUZZ
617
int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
618
    const char* algo, unsigned int algolen,
619
0
    const unsigned char* keyblob, unsigned int keybloblen) {
620
0
  return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen, NULL);
621
0
}
622
#endif
623
624
#endif