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