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