1 /* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 djm Exp $ */ 2 /* 3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5 * All rights reserved 6 * As far as I am concerned, the code I have written for this software 7 * can be used freely for any purpose. Any derived versions of this 8 * software must be clearly marked as such, and if the derived work is 9 * incompatible with the protocol description in the RFC file, it must be 10 * called by a name other than "ssh" or "Secure Shell". 11 */ 12 13 #include "includes.h" 14 15 #include <sys/types.h> 16 17 #include <netdb.h> 18 #include <pwd.h> 19 #include <string.h> 20 #include <stdio.h> 21 #include <stdarg.h> 22 23 #include "openbsd-compat/sys-queue.h" 24 25 #include "key.h" /* XXX for typedef */ 26 #include "buffer.h" /* XXX for typedef */ 27 #include "xmalloc.h" 28 #include "match.h" 29 #include "ssherr.h" 30 #include "log.h" 31 #include "canohost.h" 32 #include "packet.h" 33 #include "sshbuf.h" 34 #include "misc.h" 35 #include "channels.h" 36 #include "servconf.h" 37 #include "sshkey.h" 38 #include "auth-options.h" 39 #include "hostfile.h" 40 #include "auth.h" 41 42 /* Flags set authorized_keys flags */ 43 int no_port_forwarding_flag = 0; 44 int no_agent_forwarding_flag = 0; 45 int no_x11_forwarding_flag = 0; 46 int no_pty_flag = 0; 47 int no_user_rc = 0; 48 int key_is_cert_authority = 0; 49 50 /* "command=" option. */ 51 char *forced_command = NULL; 52 53 /* "environment=" options. */ 54 struct envstring *custom_environment = NULL; 55 56 /* "tunnel=" option. */ 57 int forced_tun_device = -1; 58 59 /* "principals=" option. */ 60 char *authorized_principals = NULL; 61 62 extern ServerOptions options; 63 64 /* XXX refactor to be stateless */ 65 66 void 67 auth_clear_options(void) 68 { 69 struct ssh *ssh = active_state; /* XXX */ 70 71 no_agent_forwarding_flag = 0; 72 no_port_forwarding_flag = 0; 73 no_pty_flag = 0; 74 no_x11_forwarding_flag = 0; 75 no_user_rc = 0; 76 key_is_cert_authority = 0; 77 while (custom_environment) { 78 struct envstring *ce = custom_environment; 79 custom_environment = ce->next; 80 free(ce->s); 81 free(ce); 82 } 83 free(forced_command); 84 forced_command = NULL; 85 free(authorized_principals); 86 authorized_principals = NULL; 87 forced_tun_device = -1; 88 channel_clear_permitted_opens(ssh); 89 } 90 91 /* 92 * Match flag 'opt' in *optsp, and if allow_negate is set then also match 93 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 94 * if negated option matches. 95 * If the option or negated option matches, then *optsp is updated to 96 * point to the first character after the option and, if 'msg' is not NULL 97 * then a message based on it added via auth_debug_add(). 98 */ 99 static int 100 match_flag(const char *opt, int allow_negate, char **optsp, const char *msg) 101 { 102 size_t opt_len = strlen(opt); 103 char *opts = *optsp; 104 int negate = 0; 105 106 if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { 107 opts += 3; 108 negate = 1; 109 } 110 if (strncasecmp(opts, opt, opt_len) == 0) { 111 *optsp = opts + opt_len; 112 if (msg != NULL) { 113 auth_debug_add("%s %s.", msg, 114 negate ? "disabled" : "enabled"); 115 } 116 return negate ? 0 : 1; 117 } 118 return -1; 119 } 120 121 /* 122 * return 1 if access is granted, 0 if not. 123 * side effect: sets key option flags 124 * XXX remove side effects; fill structure instead. 125 */ 126 int 127 auth_parse_options(struct passwd *pw, char *opts, const char *file, 128 u_long linenum) 129 { 130 struct ssh *ssh = active_state; /* XXX */ 131 const char *cp; 132 int i, r; 133 134 /* reset options */ 135 auth_clear_options(); 136 137 if (!opts) 138 return 1; 139 140 while (*opts && *opts != ' ' && *opts != '\t') { 141 if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) { 142 key_is_cert_authority = r; 143 goto next_option; 144 } 145 if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) { 146 auth_debug_add("Key is restricted."); 147 no_port_forwarding_flag = 1; 148 no_agent_forwarding_flag = 1; 149 no_x11_forwarding_flag = 1; 150 no_pty_flag = 1; 151 no_user_rc = 1; 152 goto next_option; 153 } 154 if ((r = match_flag("port-forwarding", 1, &opts, 155 "Port forwarding")) != -1) { 156 no_port_forwarding_flag = r != 1; 157 goto next_option; 158 } 159 if ((r = match_flag("agent-forwarding", 1, &opts, 160 "Agent forwarding")) != -1) { 161 no_agent_forwarding_flag = r != 1; 162 goto next_option; 163 } 164 if ((r = match_flag("x11-forwarding", 1, &opts, 165 "X11 forwarding")) != -1) { 166 no_x11_forwarding_flag = r != 1; 167 goto next_option; 168 } 169 if ((r = match_flag("pty", 1, &opts, 170 "PTY allocation")) != -1) { 171 no_pty_flag = r != 1; 172 goto next_option; 173 } 174 if ((r = match_flag("user-rc", 1, &opts, 175 "User rc execution")) != -1) { 176 no_user_rc = r != 1; 177 goto next_option; 178 } 179 cp = "command=\""; 180 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 181 opts += strlen(cp); 182 free(forced_command); 183 forced_command = xmalloc(strlen(opts) + 1); 184 i = 0; 185 while (*opts) { 186 if (*opts == '"') 187 break; 188 if (*opts == '\\' && opts[1] == '"') { 189 opts += 2; 190 forced_command[i++] = '"'; 191 continue; 192 } 193 forced_command[i++] = *opts++; 194 } 195 if (!*opts) { 196 debug("%.100s, line %lu: missing end quote", 197 file, linenum); 198 auth_debug_add("%.100s, line %lu: missing end quote", 199 file, linenum); 200 free(forced_command); 201 forced_command = NULL; 202 goto bad_option; 203 } 204 forced_command[i] = '\0'; 205 auth_debug_add("Forced command."); 206 opts++; 207 goto next_option; 208 } 209 cp = "principals=\""; 210 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 211 opts += strlen(cp); 212 free(authorized_principals); 213 authorized_principals = xmalloc(strlen(opts) + 1); 214 i = 0; 215 while (*opts) { 216 if (*opts == '"') 217 break; 218 if (*opts == '\\' && opts[1] == '"') { 219 opts += 2; 220 authorized_principals[i++] = '"'; 221 continue; 222 } 223 authorized_principals[i++] = *opts++; 224 } 225 if (!*opts) { 226 debug("%.100s, line %lu: missing end quote", 227 file, linenum); 228 auth_debug_add("%.100s, line %lu: missing end quote", 229 file, linenum); 230 free(authorized_principals); 231 authorized_principals = NULL; 232 goto bad_option; 233 } 234 authorized_principals[i] = '\0'; 235 auth_debug_add("principals: %.900s", 236 authorized_principals); 237 opts++; 238 goto next_option; 239 } 240 cp = "environment=\""; 241 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 242 char *s; 243 struct envstring *new_envstring; 244 245 opts += strlen(cp); 246 s = xmalloc(strlen(opts) + 1); 247 i = 0; 248 while (*opts) { 249 if (*opts == '"') 250 break; 251 if (*opts == '\\' && opts[1] == '"') { 252 opts += 2; 253 s[i++] = '"'; 254 continue; 255 } 256 s[i++] = *opts++; 257 } 258 if (!*opts) { 259 debug("%.100s, line %lu: missing end quote", 260 file, linenum); 261 auth_debug_add("%.100s, line %lu: missing end quote", 262 file, linenum); 263 free(s); 264 goto bad_option; 265 } 266 s[i] = '\0'; 267 opts++; 268 if (options.permit_user_env) { 269 auth_debug_add("Adding to environment: " 270 "%.900s", s); 271 debug("Adding to environment: %.900s", s); 272 new_envstring = xcalloc(1, 273 sizeof(*new_envstring)); 274 new_envstring->s = s; 275 new_envstring->next = custom_environment; 276 custom_environment = new_envstring; 277 s = NULL; 278 } 279 free(s); 280 goto next_option; 281 } 282 cp = "from=\""; 283 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 284 const char *remote_ip = ssh_remote_ipaddr(ssh); 285 const char *remote_host = auth_get_canonical_hostname( 286 ssh, options.use_dns); 287 char *patterns = xmalloc(strlen(opts) + 1); 288 289 opts += strlen(cp); 290 i = 0; 291 while (*opts) { 292 if (*opts == '"') 293 break; 294 if (*opts == '\\' && opts[1] == '"') { 295 opts += 2; 296 patterns[i++] = '"'; 297 continue; 298 } 299 patterns[i++] = *opts++; 300 } 301 if (!*opts) { 302 debug("%.100s, line %lu: missing end quote", 303 file, linenum); 304 auth_debug_add("%.100s, line %lu: missing end quote", 305 file, linenum); 306 free(patterns); 307 goto bad_option; 308 } 309 patterns[i] = '\0'; 310 opts++; 311 switch (match_host_and_ip(remote_host, remote_ip, 312 patterns)) { 313 case 1: 314 free(patterns); 315 /* Host name matches. */ 316 goto next_option; 317 case -1: 318 debug("%.100s, line %lu: invalid criteria", 319 file, linenum); 320 auth_debug_add("%.100s, line %lu: " 321 "invalid criteria", file, linenum); 322 /* FALLTHROUGH */ 323 case 0: 324 free(patterns); 325 logit("Authentication tried for %.100s with " 326 "correct key but not from a permitted " 327 "host (host=%.200s, ip=%.200s).", 328 pw->pw_name, remote_host, remote_ip); 329 auth_debug_add("Your host '%.200s' is not " 330 "permitted to use this key for login.", 331 remote_host); 332 break; 333 } 334 /* deny access */ 335 return 0; 336 } 337 cp = "permitopen=\""; 338 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 339 char *host, *p; 340 int port; 341 char *patterns = xmalloc(strlen(opts) + 1); 342 343 opts += strlen(cp); 344 i = 0; 345 while (*opts) { 346 if (*opts == '"') 347 break; 348 if (*opts == '\\' && opts[1] == '"') { 349 opts += 2; 350 patterns[i++] = '"'; 351 continue; 352 } 353 patterns[i++] = *opts++; 354 } 355 if (!*opts) { 356 debug("%.100s, line %lu: missing end quote", 357 file, linenum); 358 auth_debug_add("%.100s, line %lu: missing " 359 "end quote", file, linenum); 360 free(patterns); 361 goto bad_option; 362 } 363 patterns[i] = '\0'; 364 opts++; 365 p = patterns; 366 /* XXX - add streamlocal support */ 367 host = hpdelim(&p); 368 if (host == NULL || strlen(host) >= NI_MAXHOST) { 369 debug("%.100s, line %lu: Bad permitopen " 370 "specification <%.100s>", file, linenum, 371 patterns); 372 auth_debug_add("%.100s, line %lu: " 373 "Bad permitopen specification", file, 374 linenum); 375 free(patterns); 376 goto bad_option; 377 } 378 host = cleanhostname(host); 379 if (p == NULL || (port = permitopen_port(p)) < 0) { 380 debug("%.100s, line %lu: Bad permitopen port " 381 "<%.100s>", file, linenum, p ? p : ""); 382 auth_debug_add("%.100s, line %lu: " 383 "Bad permitopen port", file, linenum); 384 free(patterns); 385 goto bad_option; 386 } 387 if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) 388 channel_add_permitted_opens(ssh, host, port); 389 free(patterns); 390 goto next_option; 391 } 392 cp = "tunnel=\""; 393 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 394 char *tun = NULL; 395 opts += strlen(cp); 396 tun = xmalloc(strlen(opts) + 1); 397 i = 0; 398 while (*opts) { 399 if (*opts == '"') 400 break; 401 tun[i++] = *opts++; 402 } 403 if (!*opts) { 404 debug("%.100s, line %lu: missing end quote", 405 file, linenum); 406 auth_debug_add("%.100s, line %lu: missing end quote", 407 file, linenum); 408 free(tun); 409 forced_tun_device = -1; 410 goto bad_option; 411 } 412 tun[i] = '\0'; 413 forced_tun_device = a2tun(tun, NULL); 414 free(tun); 415 if (forced_tun_device == SSH_TUNID_ERR) { 416 debug("%.100s, line %lu: invalid tun device", 417 file, linenum); 418 auth_debug_add("%.100s, line %lu: invalid tun device", 419 file, linenum); 420 forced_tun_device = -1; 421 goto bad_option; 422 } 423 auth_debug_add("Forced tun device: %d", forced_tun_device); 424 opts++; 425 goto next_option; 426 } 427 next_option: 428 /* 429 * Skip the comma, and move to the next option 430 * (or break out if there are no more). 431 */ 432 if (!*opts) 433 fatal("Bugs in auth-options.c option processing."); 434 if (*opts == ' ' || *opts == '\t') 435 break; /* End of options. */ 436 if (*opts != ',') 437 goto bad_option; 438 opts++; 439 /* Process the next option. */ 440 } 441 442 /* grant access */ 443 return 1; 444 445 bad_option: 446 logit("Bad options in %.100s file, line %lu: %.50s", 447 file, linenum, opts); 448 auth_debug_add("Bad options in %.100s file, line %lu: %.50s", 449 file, linenum, opts); 450 451 /* deny access */ 452 return 0; 453 } 454 455 #define OPTIONS_CRITICAL 1 456 #define OPTIONS_EXTENSIONS 2 457 static int 458 parse_option_list(struct sshbuf *oblob, struct passwd *pw, 459 u_int which, int crit, 460 int *cert_no_port_forwarding_flag, 461 int *cert_no_agent_forwarding_flag, 462 int *cert_no_x11_forwarding_flag, 463 int *cert_no_pty_flag, 464 int *cert_no_user_rc, 465 char **cert_forced_command, 466 int *cert_source_address_done) 467 { 468 struct ssh *ssh = active_state; /* XXX */ 469 char *command, *allowed; 470 const char *remote_ip; 471 char *name = NULL; 472 struct sshbuf *c = NULL, *data = NULL; 473 int r, ret = -1, result, found; 474 475 if ((c = sshbuf_fromb(oblob)) == NULL) { 476 error("%s: sshbuf_fromb failed", __func__); 477 goto out; 478 } 479 480 while (sshbuf_len(c) > 0) { 481 sshbuf_free(data); 482 data = NULL; 483 if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || 484 (r = sshbuf_froms(c, &data)) != 0) { 485 error("Unable to parse certificate options: %s", 486 ssh_err(r)); 487 goto out; 488 } 489 debug3("found certificate option \"%.100s\" len %zu", 490 name, sshbuf_len(data)); 491 found = 0; 492 if ((which & OPTIONS_EXTENSIONS) != 0) { 493 if (strcmp(name, "permit-X11-forwarding") == 0) { 494 *cert_no_x11_forwarding_flag = 0; 495 found = 1; 496 } else if (strcmp(name, 497 "permit-agent-forwarding") == 0) { 498 *cert_no_agent_forwarding_flag = 0; 499 found = 1; 500 } else if (strcmp(name, 501 "permit-port-forwarding") == 0) { 502 *cert_no_port_forwarding_flag = 0; 503 found = 1; 504 } else if (strcmp(name, "permit-pty") == 0) { 505 *cert_no_pty_flag = 0; 506 found = 1; 507 } else if (strcmp(name, "permit-user-rc") == 0) { 508 *cert_no_user_rc = 0; 509 found = 1; 510 } 511 } 512 if (!found && (which & OPTIONS_CRITICAL) != 0) { 513 if (strcmp(name, "force-command") == 0) { 514 if ((r = sshbuf_get_cstring(data, &command, 515 NULL)) != 0) { 516 error("Unable to parse \"%s\" " 517 "section: %s", name, ssh_err(r)); 518 goto out; 519 } 520 if (*cert_forced_command != NULL) { 521 error("Certificate has multiple " 522 "force-command options"); 523 free(command); 524 goto out; 525 } 526 *cert_forced_command = command; 527 found = 1; 528 } 529 if (strcmp(name, "source-address") == 0) { 530 if ((r = sshbuf_get_cstring(data, &allowed, 531 NULL)) != 0) { 532 error("Unable to parse \"%s\" " 533 "section: %s", name, ssh_err(r)); 534 goto out; 535 } 536 if ((*cert_source_address_done)++) { 537 error("Certificate has multiple " 538 "source-address options"); 539 free(allowed); 540 goto out; 541 } 542 remote_ip = ssh_remote_ipaddr(ssh); 543 result = addr_match_cidr_list(remote_ip, 544 allowed); 545 free(allowed); 546 switch (result) { 547 case 1: 548 /* accepted */ 549 break; 550 case 0: 551 /* no match */ 552 logit("Authentication tried for %.100s " 553 "with valid certificate but not " 554 "from a permitted host " 555 "(ip=%.200s).", pw->pw_name, 556 remote_ip); 557 auth_debug_add("Your address '%.200s' " 558 "is not permitted to use this " 559 "certificate for login.", 560 remote_ip); 561 goto out; 562 case -1: 563 default: 564 error("Certificate source-address " 565 "contents invalid"); 566 goto out; 567 } 568 found = 1; 569 } 570 } 571 572 if (!found) { 573 if (crit) { 574 error("Certificate critical option \"%s\" " 575 "is not supported", name); 576 goto out; 577 } else { 578 logit("Certificate extension \"%s\" " 579 "is not supported", name); 580 } 581 } else if (sshbuf_len(data) != 0) { 582 error("Certificate option \"%s\" corrupt " 583 "(extra data)", name); 584 goto out; 585 } 586 free(name); 587 name = NULL; 588 } 589 /* successfully parsed all options */ 590 ret = 0; 591 592 out: 593 if (ret != 0 && 594 cert_forced_command != NULL && 595 *cert_forced_command != NULL) { 596 free(*cert_forced_command); 597 *cert_forced_command = NULL; 598 } 599 free(name); 600 sshbuf_free(data); 601 sshbuf_free(c); 602 return ret; 603 } 604 605 /* 606 * Set options from critical certificate options. These supersede user key 607 * options so this must be called after auth_parse_options(). 608 */ 609 int 610 auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason) 611 { 612 int cert_no_port_forwarding_flag = 1; 613 int cert_no_agent_forwarding_flag = 1; 614 int cert_no_x11_forwarding_flag = 1; 615 int cert_no_pty_flag = 1; 616 int cert_no_user_rc = 1; 617 char *cert_forced_command = NULL; 618 int cert_source_address_done = 0; 619 620 *reason = "invalid certificate options"; 621 622 /* Separate options and extensions for v01 certs */ 623 if (parse_option_list(k->cert->critical, pw, 624 OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, 625 &cert_forced_command, 626 &cert_source_address_done) == -1) 627 return -1; 628 if (parse_option_list(k->cert->extensions, pw, 629 OPTIONS_EXTENSIONS, 0, 630 &cert_no_port_forwarding_flag, 631 &cert_no_agent_forwarding_flag, 632 &cert_no_x11_forwarding_flag, 633 &cert_no_pty_flag, 634 &cert_no_user_rc, 635 NULL, NULL) == -1) 636 return -1; 637 638 no_port_forwarding_flag |= cert_no_port_forwarding_flag; 639 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 640 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 641 no_pty_flag |= cert_no_pty_flag; 642 no_user_rc |= cert_no_user_rc; 643 /* 644 * Only permit both CA and key option forced-command if they match. 645 * Otherwise refuse the certificate. 646 */ 647 if (cert_forced_command != NULL && forced_command != NULL) { 648 if (strcmp(forced_command, cert_forced_command) == 0) { 649 free(forced_command); 650 forced_command = cert_forced_command; 651 } else { 652 *reason = "certificate and key options forced command " 653 "do not match"; 654 free(cert_forced_command); 655 return -1; 656 } 657 } else if (cert_forced_command != NULL) 658 forced_command = cert_forced_command; 659 /* success */ 660 *reason = NULL; 661 return 0; 662 } 663 664