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