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