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 502