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