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