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