1 /* $NetBSD: auth-options.c,v 1.7 2013/11/08 19:18:24 christos Exp $ */ 2 /* $OpenBSD: auth-options.c,v 1.59.2.1 2013/11/08 01:33:56 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.7 2013/11/08 19:18:24 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 free(ce->s); 78 free(ce); 79 } 80 if (forced_command) { 81 free(forced_command); 82 forced_command = NULL; 83 } 84 if (authorized_principals) { 85 free(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 free(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 free(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 free(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 free(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 free(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 = xcalloc(1, 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 free(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 free(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 free(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 free(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 free(patterns); 344 goto bad_option; 345 } 346 host = cleanhostname(host); 347 if (p == NULL || (port = permitopen_port(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 free(patterns); 353 goto bad_option; 354 } 355 if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) 356 channel_add_permitted_opens(host, port); 357 free(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 free(tun); 377 forced_tun_device = -1; 378 goto bad_option; 379 } 380 tun[i] = '\0'; 381 forced_tun_device = a2tun(tun, NULL); 382 free(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 char *name = NULL; 439 u_char *data_blob = NULL; 440 u_int nlen, dlen, clen; 441 Buffer c, data; 442 int ret = -1, found; 443 444 buffer_init(&data); 445 446 /* Make copy to avoid altering original */ 447 buffer_init(&c); 448 buffer_append(&c, optblob, optblob_len); 449 450 while (buffer_len(&c) > 0) { 451 if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL || 452 (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { 453 error("Certificate options corrupt"); 454 goto out; 455 } 456 buffer_append(&data, data_blob, dlen); 457 debug3("found certificate option \"%.100s\" len %u", 458 name, dlen); 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_cstring_ret(&data, 483 &clen)) == NULL) { 484 error("Certificate constraint \"%s\" " 485 "corrupt", name); 486 goto out; 487 } 488 if (*cert_forced_command != NULL) { 489 error("Certificate has multiple " 490 "force-command options"); 491 free(command); 492 goto out; 493 } 494 *cert_forced_command = command; 495 found = 1; 496 } 497 if (strcmp(name, "source-address") == 0) { 498 if ((allowed = buffer_get_cstring_ret(&data, 499 &clen)) == NULL) { 500 error("Certificate constraint " 501 "\"%s\" corrupt", name); 502 goto out; 503 } 504 if ((*cert_source_address_done)++) { 505 error("Certificate has multiple " 506 "source-address options"); 507 free(allowed); 508 goto out; 509 } 510 remote_ip = get_remote_ipaddr(); 511 switch (addr_match_cidr_list(remote_ip, 512 allowed)) { 513 case 1: 514 /* accepted */ 515 free(allowed); 516 break; 517 case 0: 518 /* no match */ 519 logit("Authentication tried for %.100s " 520 "with valid certificate but not " 521 "from a permitted host " 522 "(ip=%.200s).", pw->pw_name, 523 remote_ip); 524 auth_debug_add("Your address '%.200s' " 525 "is not permitted to use this " 526 "certificate for login.", 527 remote_ip); 528 free(allowed); 529 goto out; 530 case -1: 531 error("Certificate source-address " 532 "contents invalid"); 533 free(allowed); 534 goto out; 535 } 536 found = 1; 537 } 538 } 539 540 if (!found) { 541 if (crit) { 542 error("Certificate critical option \"%s\" " 543 "is not supported", name); 544 goto out; 545 } else { 546 logit("Certificate extension \"%s\" " 547 "is not supported", name); 548 } 549 } else if (buffer_len(&data) != 0) { 550 error("Certificate option \"%s\" corrupt " 551 "(extra data)", name); 552 goto out; 553 } 554 buffer_clear(&data); 555 free(name); 556 free(data_blob); 557 name = NULL; 558 data_blob = NULL; 559 } 560 /* successfully parsed all options */ 561 ret = 0; 562 563 out: 564 if (ret != 0 && 565 cert_forced_command != NULL && 566 *cert_forced_command != NULL) { 567 free(*cert_forced_command); 568 *cert_forced_command = NULL; 569 } 570 if (name != NULL) 571 free(name); 572 if (data_blob != NULL) 573 free(data_blob); 574 buffer_free(&data); 575 buffer_free(&c); 576 return ret; 577 } 578 579 /* 580 * Set options from critical certificate options. These supersede user key 581 * options so this must be called after auth_parse_options(). 582 */ 583 int 584 auth_cert_options(Key *k, struct passwd *pw) 585 { 586 int cert_no_port_forwarding_flag = 1; 587 int cert_no_agent_forwarding_flag = 1; 588 int cert_no_x11_forwarding_flag = 1; 589 int cert_no_pty_flag = 1; 590 int cert_no_user_rc = 1; 591 char *cert_forced_command = NULL; 592 int cert_source_address_done = 0; 593 594 if (key_cert_is_legacy(k)) { 595 /* All options are in the one field for v00 certs */ 596 if (parse_option_list(buffer_ptr(&k->cert->critical), 597 buffer_len(&k->cert->critical), pw, 598 OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1, 599 &cert_no_port_forwarding_flag, 600 &cert_no_agent_forwarding_flag, 601 &cert_no_x11_forwarding_flag, 602 &cert_no_pty_flag, 603 &cert_no_user_rc, 604 &cert_forced_command, 605 &cert_source_address_done) == -1) 606 return -1; 607 } else { 608 /* Separate options and extensions for v01 certs */ 609 if (parse_option_list(buffer_ptr(&k->cert->critical), 610 buffer_len(&k->cert->critical), pw, 611 OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL, 612 &cert_forced_command, 613 &cert_source_address_done) == -1) 614 return -1; 615 if (parse_option_list(buffer_ptr(&k->cert->extensions), 616 buffer_len(&k->cert->extensions), pw, 617 OPTIONS_EXTENSIONS, 1, 618 &cert_no_port_forwarding_flag, 619 &cert_no_agent_forwarding_flag, 620 &cert_no_x11_forwarding_flag, 621 &cert_no_pty_flag, 622 &cert_no_user_rc, 623 NULL, NULL) == -1) 624 return -1; 625 } 626 627 no_port_forwarding_flag |= cert_no_port_forwarding_flag; 628 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 629 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 630 no_pty_flag |= cert_no_pty_flag; 631 no_user_rc |= cert_no_user_rc; 632 /* CA-specified forced command supersedes key option */ 633 if (cert_forced_command != NULL) { 634 if (forced_command != NULL) 635 free(forced_command); 636 forced_command = cert_forced_command; 637 } 638 return 0; 639 } 640 641