1 /* $OpenBSD: auth-options.c,v 1.56 2011/10/18 04:58:26 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 = permitopen_port(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 found = 0; 454 if ((which & OPTIONS_EXTENSIONS) != 0) { 455 if (strcmp(name, "permit-X11-forwarding") == 0) { 456 *cert_no_x11_forwarding_flag = 0; 457 found = 1; 458 } else if (strcmp(name, 459 "permit-agent-forwarding") == 0) { 460 *cert_no_agent_forwarding_flag = 0; 461 found = 1; 462 } else if (strcmp(name, 463 "permit-port-forwarding") == 0) { 464 *cert_no_port_forwarding_flag = 0; 465 found = 1; 466 } else if (strcmp(name, "permit-pty") == 0) { 467 *cert_no_pty_flag = 0; 468 found = 1; 469 } else if (strcmp(name, "permit-user-rc") == 0) { 470 *cert_no_user_rc = 0; 471 found = 1; 472 } 473 } 474 if (!found && (which & OPTIONS_CRITICAL) != 0) { 475 if (strcmp(name, "force-command") == 0) { 476 if ((command = buffer_get_cstring_ret(&data, 477 &clen)) == NULL) { 478 error("Certificate constraint \"%s\" " 479 "corrupt", name); 480 goto out; 481 } 482 if (*cert_forced_command != NULL) { 483 error("Certificate has multiple " 484 "force-command options"); 485 xfree(command); 486 goto out; 487 } 488 *cert_forced_command = command; 489 found = 1; 490 } 491 if (strcmp(name, "source-address") == 0) { 492 if ((allowed = buffer_get_cstring_ret(&data, 493 &clen)) == NULL) { 494 error("Certificate constraint " 495 "\"%s\" corrupt", name); 496 goto out; 497 } 498 if ((*cert_source_address_done)++) { 499 error("Certificate has multiple " 500 "source-address options"); 501 xfree(allowed); 502 goto out; 503 } 504 remote_ip = get_remote_ipaddr(); 505 switch (addr_match_cidr_list(remote_ip, 506 allowed)) { 507 case 1: 508 /* accepted */ 509 xfree(allowed); 510 break; 511 case 0: 512 /* no match */ 513 logit("Authentication tried for %.100s " 514 "with valid certificate but not " 515 "from a permitted host " 516 "(ip=%.200s).", pw->pw_name, 517 remote_ip); 518 auth_debug_add("Your address '%.200s' " 519 "is not permitted to use this " 520 "certificate for login.", 521 remote_ip); 522 xfree(allowed); 523 goto out; 524 case -1: 525 error("Certificate source-address " 526 "contents invalid"); 527 xfree(allowed); 528 goto out; 529 } 530 found = 1; 531 } 532 } 533 534 if (!found) { 535 if (crit) { 536 error("Certificate critical option \"%s\" " 537 "is not supported", name); 538 goto out; 539 } else { 540 logit("Certificate extension \"%s\" " 541 "is not supported", name); 542 } 543 } else if (buffer_len(&data) != 0) { 544 error("Certificate option \"%s\" corrupt " 545 "(extra data)", name); 546 goto out; 547 } 548 buffer_clear(&data); 549 xfree(name); 550 xfree(data_blob); 551 name = data_blob = NULL; 552 } 553 /* successfully parsed all options */ 554 ret = 0; 555 556 out: 557 if (ret != 0 && 558 cert_forced_command != NULL && 559 *cert_forced_command != NULL) { 560 xfree(*cert_forced_command); 561 *cert_forced_command = NULL; 562 } 563 if (name != NULL) 564 xfree(name); 565 if (data_blob != NULL) 566 xfree(data_blob); 567 buffer_free(&data); 568 buffer_free(&c); 569 return ret; 570 } 571 572 /* 573 * Set options from critical certificate options. These supersede user key 574 * options so this must be called after auth_parse_options(). 575 */ 576 int 577 auth_cert_options(Key *k, struct passwd *pw) 578 { 579 int cert_no_port_forwarding_flag = 1; 580 int cert_no_agent_forwarding_flag = 1; 581 int cert_no_x11_forwarding_flag = 1; 582 int cert_no_pty_flag = 1; 583 int cert_no_user_rc = 1; 584 char *cert_forced_command = NULL; 585 int cert_source_address_done = 0; 586 587 if (key_cert_is_legacy(k)) { 588 /* All options are in the one field for v00 certs */ 589 if (parse_option_list(buffer_ptr(&k->cert->critical), 590 buffer_len(&k->cert->critical), pw, 591 OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, 592 &cert_no_port_forwarding_flag, 593 &cert_no_agent_forwarding_flag, 594 &cert_no_x11_forwarding_flag, 595 &cert_no_pty_flag, 596 &cert_no_user_rc, 597 &cert_forced_command, 598 &cert_source_address_done) == -1) 599 return -1; 600 } else { 601 /* Separate options and extensions for v01 certs */ 602 if (parse_option_list(buffer_ptr(&k->cert->critical), 603 buffer_len(&k->cert->critical), pw, 604 OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, 605 &cert_forced_command, 606 &cert_source_address_done) == -1) 607 return -1; 608 if (parse_option_list(buffer_ptr(&k->cert->extensions), 609 buffer_len(&k->cert->extensions), pw, 610 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 NULL, NULL) == -1) 617 return -1; 618 } 619 620 no_port_forwarding_flag |= cert_no_port_forwarding_flag; 621 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 622 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 623 no_pty_flag |= cert_no_pty_flag; 624 no_user_rc |= cert_no_user_rc; 625 /* CA-specified forced command supersedes key option */ 626 if (cert_forced_command != NULL) { 627 if (forced_command != NULL) 628 xfree(forced_command); 629 forced_command = cert_forced_command; 630 } 631 return 0; 632 } 633 634