]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/openssh/auth2-pubkeyfile.c
zfs: merge openzfs/zfs@03e9caaec
[FreeBSD/FreeBSD.git] / crypto / openssh / auth2-pubkeyfile.c
1 /* $OpenBSD: auth2-pubkeyfile.c,v 1.4 2023/03/05 05:34:09 dtucker Exp $ */
2 /*
3  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
4  * Copyright (c) 2010 Damien Miller.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "includes.h"
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <fcntl.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 #include <stdarg.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.h>
41
42 #include "ssh.h"
43 #include "log.h"
44 #include "misc.h"
45 #include "sshkey.h"
46 #include "digest.h"
47 #include "hostfile.h"
48 #include "auth.h"
49 #include "auth-options.h"
50 #include "authfile.h"
51 #include "match.h"
52 #include "ssherr.h"
53
54 int
55 auth_authorise_keyopts(struct passwd *pw, struct sshauthopt *opts,
56     int allow_cert_authority, const char *remote_ip, const char *remote_host,
57     const char *loc)
58 {
59         time_t now = time(NULL);
60         char buf[64];
61
62         /*
63          * Check keys/principals file expiry time.
64          * NB. validity interval in certificate is handled elsewhere.
65          */
66         if (opts->valid_before && now > 0 &&
67             opts->valid_before < (uint64_t)now) {
68                 format_absolute_time(opts->valid_before, buf, sizeof(buf));
69                 debug("%s: entry expired at %s", loc, buf);
70                 auth_debug_add("%s: entry expired at %s", loc, buf);
71                 return -1;
72         }
73         /* Consistency checks */
74         if (opts->cert_principals != NULL && !opts->cert_authority) {
75                 debug("%s: principals on non-CA key", loc);
76                 auth_debug_add("%s: principals on non-CA key", loc);
77                 /* deny access */
78                 return -1;
79         }
80         /* cert-authority flag isn't valid in authorized_principals files */
81         if (!allow_cert_authority && opts->cert_authority) {
82                 debug("%s: cert-authority flag invalid here", loc);
83                 auth_debug_add("%s: cert-authority flag invalid here", loc);
84                 /* deny access */
85                 return -1;
86         }
87
88         /* Perform from= checks */
89         if (opts->required_from_host_keys != NULL) {
90                 switch (match_host_and_ip(remote_host, remote_ip,
91                     opts->required_from_host_keys )) {
92                 case 1:
93                         /* Host name matches. */
94                         break;
95                 case -1:
96                 default:
97                         debug("%s: invalid from criteria", loc);
98                         auth_debug_add("%s: invalid from criteria", loc);
99                         /* FALLTHROUGH */
100                 case 0:
101                         logit("%s: Authentication tried for %.100s with "
102                             "correct key but not from a permitted "
103                             "host (host=%.200s, ip=%.200s, required=%.200s).",
104                             loc, pw->pw_name, remote_host, remote_ip,
105                             opts->required_from_host_keys);
106                         auth_debug_add("%s: Your host '%.200s' is not "
107                             "permitted to use this key for login.",
108                             loc, remote_host);
109                         /* deny access */
110                         return -1;
111                 }
112         }
113         /* Check source-address restriction from certificate */
114         if (opts->required_from_host_cert != NULL) {
115                 switch (addr_match_cidr_list(remote_ip,
116                     opts->required_from_host_cert)) {
117                 case 1:
118                         /* accepted */
119                         break;
120                 case -1:
121                 default:
122                         /* invalid */
123                         error("%s: Certificate source-address invalid", loc);
124                         /* FALLTHROUGH */
125                 case 0:
126                         logit("%s: Authentication tried for %.100s with valid "
127                             "certificate but not from a permitted source "
128                             "address (%.200s).", loc, pw->pw_name, remote_ip);
129                         auth_debug_add("%s: Your address '%.200s' is not "
130                             "permitted to use this certificate for login.",
131                             loc, remote_ip);
132                         return -1;
133                 }
134         }
135         /*
136          *
137          * XXX this is spammy. We should report remotely only for keys
138          *     that are successful in actual auth attempts, and not PK_OK
139          *     tests.
140          */
141         auth_log_authopts(loc, opts, 1);
142
143         return 0;
144 }
145
146 static int
147 match_principals_option(const char *principal_list, struct sshkey_cert *cert)
148 {
149         char *result;
150         u_int i;
151
152         /* XXX percent_expand() sequences for authorized_principals? */
153
154         for (i = 0; i < cert->nprincipals; i++) {
155                 if ((result = match_list(cert->principals[i],
156                     principal_list, NULL)) != NULL) {
157                         debug3("matched principal from key options \"%.100s\"",
158                             result);
159                         free(result);
160                         return 1;
161                 }
162         }
163         return 0;
164 }
165
166 /*
167  * Process a single authorized_principals format line. Returns 0 and sets
168  * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a
169  * log preamble for file/line information.
170  */
171 int
172 auth_check_principals_line(char *cp, const struct sshkey_cert *cert,
173     const char *loc, struct sshauthopt **authoptsp)
174 {
175         u_int i, found = 0;
176         char *ep, *line_opts;
177         const char *reason = NULL;
178         struct sshauthopt *opts = NULL;
179
180         if (authoptsp != NULL)
181                 *authoptsp = NULL;
182
183         /* Trim trailing whitespace. */
184         ep = cp + strlen(cp) - 1;
185         while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t'))
186                 *ep-- = '\0';
187
188         /*
189          * If the line has internal whitespace then assume it has
190          * key options.
191          */
192         line_opts = NULL;
193         if ((ep = strrchr(cp, ' ')) != NULL ||
194             (ep = strrchr(cp, '\t')) != NULL) {
195                 for (; *ep == ' ' || *ep == '\t'; ep++)
196                         ;
197                 line_opts = cp;
198                 cp = ep;
199         }
200         if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) {
201                 debug("%s: bad principals options: %s", loc, reason);
202                 auth_debug_add("%s: bad principals options: %s", loc, reason);
203                 return -1;
204         }
205         /* Check principals in cert against those on line */
206         for (i = 0; i < cert->nprincipals; i++) {
207                 if (strcmp(cp, cert->principals[i]) != 0)
208                         continue;
209                 debug3("%s: matched principal \"%.100s\"",
210                     loc, cert->principals[i]);
211                 found = 1;
212         }
213         if (found && authoptsp != NULL) {
214                 *authoptsp = opts;
215                 opts = NULL;
216         }
217         sshauthopt_free(opts);
218         return found ? 0 : -1;
219 }
220
221 int
222 auth_process_principals(FILE *f, const char *file,
223     const struct sshkey_cert *cert, struct sshauthopt **authoptsp)
224 {
225         char loc[256], *line = NULL, *cp, *ep;
226         size_t linesize = 0;
227         u_long linenum = 0, nonblank = 0;
228         u_int found_principal = 0;
229
230         if (authoptsp != NULL)
231                 *authoptsp = NULL;
232
233         while (getline(&line, &linesize, f) != -1) {
234                 linenum++;
235                 /* Always consume entire input */
236                 if (found_principal)
237                         continue;
238
239                 /* Skip leading whitespace. */
240                 for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
241                         ;
242                 /* Skip blank and comment lines. */
243                 if ((ep = strchr(cp, '#')) != NULL)
244                         *ep = '\0';
245                 if (!*cp || *cp == '\n')
246                         continue;
247
248                 nonblank++;
249                 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
250                 if (auth_check_principals_line(cp, cert, loc, authoptsp) == 0)
251                         found_principal = 1;
252         }
253         debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
254         free(line);
255         return found_principal;
256 }
257
258 /*
259  * Check a single line of an authorized_keys-format file. Returns 0 if key
260  * matches, -1 otherwise. Will return key/cert options via *authoptsp
261  * on success. "loc" is used as file/line location in log messages.
262  */
263 int
264 auth_check_authkey_line(struct passwd *pw, struct sshkey *key,
265     char *cp, const char *remote_ip, const char *remote_host, const char *loc,
266     struct sshauthopt **authoptsp)
267 {
268         int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type;
269         struct sshkey *found = NULL;
270         struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL;
271         char *key_options = NULL, *fp = NULL;
272         const char *reason = NULL;
273         int ret = -1;
274
275         if (authoptsp != NULL)
276                 *authoptsp = NULL;
277
278         if ((found = sshkey_new(want_keytype)) == NULL) {
279                 debug3_f("keytype %d failed", want_keytype);
280                 goto out;
281         }
282
283         /* XXX djm: peek at key type in line and skip if unwanted */
284
285         if (sshkey_read(found, &cp) != 0) {
286                 /* no key?  check for options */
287                 debug2("%s: check options: '%s'", loc, cp);
288                 key_options = cp;
289                 if (sshkey_advance_past_options(&cp) != 0) {
290                         reason = "invalid key option string";
291                         goto fail_reason;
292                 }
293                 skip_space(&cp);
294                 if (sshkey_read(found, &cp) != 0) {
295                         /* still no key?  advance to next line*/
296                         debug2("%s: advance: '%s'", loc, cp);
297                         goto out;
298                 }
299         }
300         /* Parse key options now; we need to know if this is a CA key */
301         if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) {
302                 debug("%s: bad key options: %s", loc, reason);
303                 auth_debug_add("%s: bad key options: %s", loc, reason);
304                 goto out;
305         }
306         /* Ignore keys that don't match or incorrectly marked as CAs */
307         if (sshkey_is_cert(key)) {
308                 /* Certificate; check signature key against CA */
309                 if (!sshkey_equal(found, key->cert->signature_key) ||
310                     !keyopts->cert_authority)
311                         goto out;
312         } else {
313                 /* Plain key: check it against key found in file */
314                 if (!sshkey_equal(found, key) || keyopts->cert_authority)
315                         goto out;
316         }
317
318         /* We have a candidate key, perform authorisation checks */
319         if ((fp = sshkey_fingerprint(found,
320             SSH_FP_HASH_DEFAULT, SSH_FP_DEFAULT)) == NULL)
321                 fatal_f("fingerprint failed");
322
323         debug("%s: matching %s found: %s %s", loc,
324             sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp);
325
326         if (auth_authorise_keyopts(pw, keyopts,
327             sshkey_is_cert(key), remote_ip, remote_host, loc) != 0) {
328                 reason = "Refused by key options";
329                 goto fail_reason;
330         }
331         /* That's all we need for plain keys. */
332         if (!sshkey_is_cert(key)) {
333                 verbose("Accepted key %s %s found at %s",
334                     sshkey_type(found), fp, loc);
335                 finalopts = keyopts;
336                 keyopts = NULL;
337                 goto success;
338         }
339
340         /*
341          * Additional authorisation for certificates.
342          */
343
344         /* Parse and check options present in certificate */
345         if ((certopts = sshauthopt_from_cert(key)) == NULL) {
346                 reason = "Invalid certificate options";
347                 goto fail_reason;
348         }
349         if (auth_authorise_keyopts(pw, certopts, 0,
350             remote_ip, remote_host, loc) != 0) {
351                 reason = "Refused by certificate options";
352                 goto fail_reason;
353         }
354         if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL)
355                 goto fail_reason;
356
357         /*
358          * If the user has specified a list of principals as
359          * a key option, then prefer that list to matching
360          * their username in the certificate principals list.
361          */
362         if (keyopts->cert_principals != NULL &&
363             !match_principals_option(keyopts->cert_principals, key->cert)) {
364                 reason = "Certificate does not contain an authorized principal";
365                 goto fail_reason;
366         }
367         if (sshkey_cert_check_authority_now(key, 0, 0, 0,
368             keyopts->cert_principals == NULL ? pw->pw_name : NULL,
369             &reason) != 0)
370                 goto fail_reason;
371
372         verbose("Accepted certificate ID \"%s\" (serial %llu) "
373             "signed by CA %s %s found at %s",
374             key->cert->key_id,
375             (unsigned long long)key->cert->serial,
376             sshkey_type(found), fp, loc);
377
378  success:
379         if (finalopts == NULL)
380                 fatal_f("internal error: missing options");
381         if (authoptsp != NULL) {
382                 *authoptsp = finalopts;
383                 finalopts = NULL;
384         }
385         /* success */
386         ret = 0;
387         goto out;
388
389  fail_reason:
390         error("%s", reason);
391         auth_debug_add("%s", reason);
392  out:
393         free(fp);
394         sshauthopt_free(keyopts);
395         sshauthopt_free(certopts);
396         sshauthopt_free(finalopts);
397         sshkey_free(found);
398         return ret;
399 }
400
401 /*
402  * Checks whether key is allowed in authorized_keys-format file,
403  * returns 1 if the key is allowed or 0 otherwise.
404  */
405 int
406 auth_check_authkeys_file(struct passwd *pw, FILE *f, char *file,
407     struct sshkey *key, const char *remote_ip,
408     const char *remote_host, struct sshauthopt **authoptsp)
409 {
410         char *cp, *line = NULL, loc[256];
411         size_t linesize = 0;
412         int found_key = 0;
413         u_long linenum = 0, nonblank = 0;
414
415         if (authoptsp != NULL)
416                 *authoptsp = NULL;
417
418         while (getline(&line, &linesize, f) != -1) {
419                 linenum++;
420                 /* Always consume entire file */
421                 if (found_key)
422                         continue;
423
424                 /* Skip leading whitespace, empty and comment lines. */
425                 cp = line;
426                 skip_space(&cp);
427                 if (!*cp || *cp == '\n' || *cp == '#')
428                         continue;
429
430                 nonblank++;
431                 snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum);
432                 if (auth_check_authkey_line(pw, key, cp,
433                     remote_ip, remote_host, loc, authoptsp) == 0)
434                         found_key = 1;
435         }
436         free(line);
437         debug2_f("%s: processed %lu/%lu lines", file, nonblank, linenum);
438         return found_key;
439 }
440
441 static FILE *
442 auth_openfile(const char *file, struct passwd *pw, int strict_modes,
443     int log_missing, char *file_type)
444 {
445         char line[1024];
446         struct stat st;
447         int fd;
448         FILE *f;
449
450         if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
451                 if (errno != ENOENT) {
452                         logit("Could not open user '%s' %s '%s': %s",
453                             pw->pw_name, file_type, file, strerror(errno));
454                 } else if (log_missing) {
455                         debug("Could not open user '%s' %s '%s': %s",
456                             pw->pw_name, file_type, file, strerror(errno));
457                 }
458                 return NULL;
459         }
460
461         if (fstat(fd, &st) == -1) {
462                 close(fd);
463                 return NULL;
464         }
465         if (!S_ISREG(st.st_mode)) {
466                 logit("User '%s' %s '%s' is not a regular file",
467                     pw->pw_name, file_type, file);
468                 close(fd);
469                 return NULL;
470         }
471         unset_nonblock(fd);
472         if ((f = fdopen(fd, "r")) == NULL) {
473                 close(fd);
474                 return NULL;
475         }
476         if (strict_modes &&
477             safe_path_fd(fileno(f), file, pw, line, sizeof(line)) != 0) {
478                 fclose(f);
479                 logit("Authentication refused: %s", line);
480                 auth_debug_add("Ignored %s: %s", file_type, line);
481                 return NULL;
482         }
483
484         return f;
485 }
486
487
488 FILE *
489 auth_openkeyfile(const char *file, struct passwd *pw, int strict_modes)
490 {
491         return auth_openfile(file, pw, strict_modes, 1, "authorized keys");
492 }
493
494 FILE *
495 auth_openprincipals(const char *file, struct passwd *pw, int strict_modes)
496 {
497         return auth_openfile(file, pw, strict_modes, 0,
498             "authorized principals");
499 }
500