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