1 /* $OpenBSD: auth-options.c,v 1.70 2015/12/10 17:08:40 mmcc 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 "sshbuf.h" 33 #include "misc.h" 34 #include "channels.h" 35 #include "servconf.h" 36 #include "sshkey.h" 37 #include "auth-options.h" 38 #include "hostfile.h" 39 #include "auth.h" 40 41 /* Flags set authorized_keys flags */ 42 int no_port_forwarding_flag = 0; 43 int no_agent_forwarding_flag = 0; 44 int no_x11_forwarding_flag = 0; 45 int no_pty_flag = 0; 46 int no_user_rc = 0; 47 int key_is_cert_authority = 0; 48 49 /* "command=" option. */ 50 char *forced_command = NULL; 51 52 /* "environment=" options. */ 53 struct envstring *custom_environment = NULL; 54 55 /* "tunnel=" option. */ 56 int forced_tun_device = -1; 57 58 /* "principals=" option. */ 59 char *authorized_principals = NULL; 60 61 extern ServerOptions options; 62 63 void 64 auth_clear_options(void) 65 { 66 no_agent_forwarding_flag = 0; 67 no_port_forwarding_flag = 0; 68 no_pty_flag = 0; 69 no_x11_forwarding_flag = 0; 70 no_user_rc = 0; 71 key_is_cert_authority = 0; 72 while (custom_environment) { 73 struct envstring *ce = custom_environment; 74 custom_environment = ce->next; 75 free(ce->s); 76 free(ce); 77 } 78 free(forced_command); 79 forced_command = NULL; 80 free(authorized_principals); 81 authorized_principals = NULL; 82 forced_tun_device = -1; 83 channel_clear_permitted_opens(); 84 } 85 86 /* 87 * Match flag 'opt' in *optsp, and if allow_negate is set then also match 88 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0 89 * if negated option matches. 90 * If the option or negated option matches, then *optsp is updated to 91 * point to the first character after the option and, if 'msg' is not NULL 92 * then a message based on it added via auth_debug_add(). 93 */ 94 static int 95 match_flag(const char *opt, int allow_negate, char **optsp, const char *msg) 96 { 97 size_t opt_len = strlen(opt); 98 char *opts = *optsp; 99 int negate = 0; 100 101 if (allow_negate && strncasecmp(opts, "no-", 3) == 0) { 102 opts += 3; 103 negate = 1; 104 } 105 if (strncasecmp(opts, opt, opt_len) == 0) { 106 *optsp = opts + opt_len; 107 if (msg != NULL) { 108 auth_debug_add("%s %s.", msg, 109 negate ? "disabled" : "enabled"); 110 } 111 return negate ? 0 : 1; 112 } 113 return -1; 114 } 115 116 /* 117 * return 1 if access is granted, 0 if not. 118 * side effect: sets key option flags 119 */ 120 int 121 auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) 122 { 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 = get_remote_ipaddr(); 277 const char *remote_host = get_canonical_hostname( 278 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 char *command, *allowed; 461 const char *remote_ip; 462 char *name = NULL; 463 struct sshbuf *c = NULL, *data = NULL; 464 int r, ret = -1, result, found; 465 466 if ((c = sshbuf_fromb(oblob)) == NULL) { 467 error("%s: sshbuf_fromb failed", __func__); 468 goto out; 469 } 470 471 while (sshbuf_len(c) > 0) { 472 sshbuf_free(data); 473 data = NULL; 474 if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || 475 (r = sshbuf_froms(c, &data)) != 0) { 476 error("Unable to parse certificate options: %s", 477 ssh_err(r)); 478 goto out; 479 } 480 debug3("found certificate option \"%.100s\" len %zu", 481 name, sshbuf_len(data)); 482 found = 0; 483 if ((which & OPTIONS_EXTENSIONS) != 0) { 484 if (strcmp(name, "permit-X11-forwarding") == 0) { 485 *cert_no_x11_forwarding_flag = 0; 486 found = 1; 487 } else if (strcmp(name, 488 "permit-agent-forwarding") == 0) { 489 *cert_no_agent_forwarding_flag = 0; 490 found = 1; 491 } else if (strcmp(name, 492 "permit-port-forwarding") == 0) { 493 *cert_no_port_forwarding_flag = 0; 494 found = 1; 495 } else if (strcmp(name, "permit-pty") == 0) { 496 *cert_no_pty_flag = 0; 497 found = 1; 498 } else if (strcmp(name, "permit-user-rc") == 0) { 499 *cert_no_user_rc = 0; 500 found = 1; 501 } 502 } 503 if (!found && (which & OPTIONS_CRITICAL) != 0) { 504 if (strcmp(name, "force-command") == 0) { 505 if ((r = sshbuf_get_cstring(data, &command, 506 NULL)) != 0) { 507 error("Unable to parse \"%s\" " 508 "section: %s", name, ssh_err(r)); 509 goto out; 510 } 511 if (*cert_forced_command != NULL) { 512 error("Certificate has multiple " 513 "force-command options"); 514 free(command); 515 goto out; 516 } 517 *cert_forced_command = command; 518 found = 1; 519 } 520 if (strcmp(name, "source-address") == 0) { 521 if ((r = sshbuf_get_cstring(data, &allowed, 522 NULL)) != 0) { 523 error("Unable to parse \"%s\" " 524 "section: %s", name, ssh_err(r)); 525 goto out; 526 } 527 if ((*cert_source_address_done)++) { 528 error("Certificate has multiple " 529 "source-address options"); 530 free(allowed); 531 goto out; 532 } 533 remote_ip = get_remote_ipaddr(); 534 result = addr_match_cidr_list(remote_ip, 535 allowed); 536 free(allowed); 537 switch (result) { 538 case 1: 539 /* accepted */ 540 break; 541 case 0: 542 /* no match */ 543 logit("Authentication tried for %.100s " 544 "with valid certificate but not " 545 "from a permitted host " 546 "(ip=%.200s).", pw->pw_name, 547 remote_ip); 548 auth_debug_add("Your address '%.200s' " 549 "is not permitted to use this " 550 "certificate for login.", 551 remote_ip); 552 goto out; 553 case -1: 554 default: 555 error("Certificate source-address " 556 "contents invalid"); 557 goto out; 558 } 559 found = 1; 560 } 561 } 562 563 if (!found) { 564 if (crit) { 565 error("Certificate critical option \"%s\" " 566 "is not supported", name); 567 goto out; 568 } else { 569 logit("Certificate extension \"%s\" " 570 "is not supported", name); 571 } 572 } else if (sshbuf_len(data) != 0) { 573 error("Certificate option \"%s\" corrupt " 574 "(extra data)", name); 575 goto out; 576 } 577 free(name); 578 name = NULL; 579 } 580 /* successfully parsed all options */ 581 ret = 0; 582 583 out: 584 if (ret != 0 && 585 cert_forced_command != NULL && 586 *cert_forced_command != NULL) { 587 free(*cert_forced_command); 588 *cert_forced_command = NULL; 589 } 590 free(name); 591 sshbuf_free(data); 592 sshbuf_free(c); 593 return ret; 594 } 595 596 /* 597 * Set options from critical certificate options. These supersede user key 598 * options so this must be called after auth_parse_options(). 599 */ 600 int 601 auth_cert_options(struct sshkey *k, struct passwd *pw) 602 { 603 int cert_no_port_forwarding_flag = 1; 604 int cert_no_agent_forwarding_flag = 1; 605 int cert_no_x11_forwarding_flag = 1; 606 int cert_no_pty_flag = 1; 607 int cert_no_user_rc = 1; 608 char *cert_forced_command = NULL; 609 int cert_source_address_done = 0; 610 611 /* Separate options and extensions for v01 certs */ 612 if (parse_option_list(k->cert->critical, pw, 613 OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, 614 &cert_forced_command, 615 &cert_source_address_done) == -1) 616 return -1; 617 if (parse_option_list(k->cert->extensions, pw, 618 OPTIONS_EXTENSIONS, 0, 619 &cert_no_port_forwarding_flag, 620 &cert_no_agent_forwarding_flag, 621 &cert_no_x11_forwarding_flag, 622 &cert_no_pty_flag, 623 &cert_no_user_rc, 624 NULL, NULL) == -1) 625 return -1; 626 627 no_port_forwarding_flag |= cert_no_port_forwarding_flag; 628 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 629 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 630 no_pty_flag |= cert_no_pty_flag; 631 no_user_rc |= cert_no_user_rc; 632 /* CA-specified forced command supersedes key option */ 633 if (cert_forced_command != NULL) { 634 free(forced_command); 635 forced_command = cert_forced_command; 636 } 637 return 0; 638 } 639 640