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