1 /* $OpenBSD: auth2-pubkeyfile.c,v 1.3 2022/07/01 03:52:57 djm Exp $ */
3 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Copyright (c) 2010 Damien Miller. All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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.
29 #include <sys/types.h>
50 #include "auth-options.h"
56 auth_authorise_keyopts(struct passwd
*pw
, struct sshauthopt
*opts
,
57 int allow_cert_authority
, const char *remote_ip
, const char *remote_host
,
60 time_t now
= time(NULL
);
64 * Check keys/principals file expiry time.
65 * NB. validity interval in certificate is handled elsewhere.
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
);
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
);
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
);
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
)) {
94 /* Host name matches. */
98 debug("%s: invalid from criteria", loc
);
99 auth_debug_add("%s: invalid from criteria", loc
);
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.",
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
)) {
124 error("%s: Certificate source-address invalid", loc
);
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.",
138 * XXX this is spammy. We should report remotely only for keys
139 * that are successful in actual auth attempts, and not PK_OK
142 auth_log_authopts(loc
, opts
, 1);
148 match_principals_option(const char *principal_list
, struct sshkey_cert
*cert
)
153 /* XXX percent_expand() sequences for authorized_principals? */
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\"",
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.
173 auth_check_principals_line(char *cp
, const struct sshkey_cert
*cert
,
174 const char *loc
, struct sshauthopt
**authoptsp
)
177 char *ep
, *line_opts
;
178 const char *reason
= NULL
;
179 struct sshauthopt
*opts
= NULL
;
181 if (authoptsp
!= NULL
)
184 /* Trim trailing whitespace. */
185 ep
= cp
+ strlen(cp
) - 1;
186 while (ep
> cp
&& (*ep
== '\n' || *ep
== ' ' || *ep
== '\t'))
190 * If the line has internal whitespace then assume it has
194 if ((ep
= strrchr(cp
, ' ')) != NULL
||
195 (ep
= strrchr(cp
, '\t')) != NULL
) {
196 for (; *ep
== ' ' || *ep
== '\t'; ep
++)
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
);
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)
210 debug3("%s: matched principal \"%.100s\"",
211 loc
, cert
->principals
[i
]);
214 if (found
&& authoptsp
!= NULL
) {
218 sshauthopt_free(opts
);
219 return found
? 0 : -1;
223 auth_process_principals(FILE *f
, const char *file
,
224 const struct sshkey_cert
*cert
, struct sshauthopt
**authoptsp
)
226 char loc
[256], *line
= NULL
, *cp
, *ep
;
228 u_long linenum
= 0, nonblank
= 0;
229 u_int found_principal
= 0;
231 if (authoptsp
!= NULL
)
234 while (getline(&line
, &linesize
, f
) != -1) {
236 /* Always consume entire input */
240 /* Skip leading whitespace. */
241 for (cp
= line
; *cp
== ' ' || *cp
== '\t'; cp
++)
243 /* Skip blank and comment lines. */
244 if ((ep
= strchr(cp
, '#')) != NULL
)
246 if (!*cp
|| *cp
== '\n')
250 snprintf(loc
, sizeof(loc
), "%.200s:%lu", file
, linenum
);
251 if (auth_check_principals_line(cp
, cert
, loc
, authoptsp
) == 0)
254 debug2_f("%s: processed %lu/%lu lines", file
, nonblank
, linenum
);
256 return found_principal
;
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.
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
)
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
;
276 if (authoptsp
!= NULL
)
279 if ((found
= sshkey_new(want_keytype
)) == NULL
) {
280 debug3_f("keytype %d failed", want_keytype
);
284 /* XXX djm: peek at key type in line and skip if unwanted */
286 if (sshkey_read(found
, &cp
) != 0) {
287 /* no key? check for options */
288 debug2("%s: check options: '%s'", loc
, cp
);
290 if (sshkey_advance_past_options(&cp
) != 0) {
291 reason
= "invalid key option string";
295 if (sshkey_read(found
, &cp
) != 0) {
296 /* still no key? advance to next line*/
297 debug2("%s: advance: '%s'", loc
, cp
);
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
);
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
)
314 /* Plain key: check it against key found in file */
315 if (!sshkey_equal(found
, key
) || keyopts
->cert_authority
)
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");
324 debug("%s: matching %s found: %s %s", loc
,
325 sshkey_is_cert(key
) ? "CA" : "key", sshkey_type(found
), fp
);
327 if (auth_authorise_keyopts(pw
, keyopts
,
328 sshkey_is_cert(key
), remote_ip
, remote_host
, loc
) != 0) {
329 reason
= "Refused by key options";
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
);
342 * Additional authorisation for certificates.
345 /* Parse and check options present in certificate */
346 if ((certopts
= sshauthopt_from_cert(key
)) == NULL
) {
347 reason
= "Invalid certificate options";
350 if (auth_authorise_keyopts(pw
, certopts
, 0,
351 remote_ip
, remote_host
, loc
) != 0) {
352 reason
= "Refused by certificate options";
355 if ((finalopts
= sshauthopt_merge(keyopts
, certopts
, &reason
)) == NULL
)
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.
363 if (keyopts
->cert_principals
!= NULL
&&
364 !match_principals_option(keyopts
->cert_principals
, key
->cert
)) {
365 reason
= "Certificate does not contain an authorized principal";
368 if (sshkey_cert_check_authority_now(key
, 0, 0, 0,
369 keyopts
->cert_principals
== NULL
? pw
->pw_name
: NULL
,
373 verbose("Accepted certificate ID \"%s\" (serial %llu) "
374 "signed by CA %s %s found at %s",
376 (unsigned long long)key
->cert
->serial
,
377 sshkey_type(found
), fp
, loc
);
380 if (finalopts
== NULL
)
381 fatal_f("internal error: missing options");
382 if (authoptsp
!= NULL
) {
383 *authoptsp
= finalopts
;
392 auth_debug_add("%s", reason
);
395 sshauthopt_free(keyopts
);
396 sshauthopt_free(certopts
);
397 sshauthopt_free(finalopts
);
403 * Checks whether key is allowed in authorized_keys-format file,
404 * returns 1 if the key is allowed or 0 otherwise.
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
)
411 char *cp
, *line
= NULL
, loc
[256];
414 u_long linenum
= 0, nonblank
= 0;
416 if (authoptsp
!= NULL
)
419 while (getline(&line
, &linesize
, f
) != -1) {
421 /* Always consume entire file */
425 /* Skip leading whitespace, empty and comment lines. */
428 if (!*cp
|| *cp
== '\n' || *cp
== '#')
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)
438 debug2_f("%s: processed %lu/%lu lines", file
, nonblank
, linenum
);
443 auth_openfile(const char *file
, struct passwd
*pw
, int strict_modes
,
444 int log_missing
, char *file_type
)
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
));
462 if (fstat(fd
, &st
) == -1) {
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
);
473 if ((f
= fdopen(fd
, "r")) == NULL
) {
478 safe_path_fd(fileno(f
), file
, pw
, line
, sizeof(line
)) != 0) {
480 logit("Authentication refused: %s", line
);
481 auth_debug_add("Ignored %s: %s", file_type
, line
);
490 auth_openkeyfile(const char *file
, struct passwd
*pw
, int strict_modes
)
492 return auth_openfile(file
, pw
, strict_modes
, 1, "authorized keys");
496 auth_openprincipals(const char *file
, struct passwd
*pw
, int strict_modes
)
498 return auth_openfile(file
, pw
, strict_modes
, 0,
499 "authorized principals");