1 /* $OpenBSD: auth-options.c,v 1.62 2013/12/19 00:27:57 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 "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 "channels.h" 30 #include "servconf.h" 31 #include "misc.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 host = hpdelim(&p); 329 if (host == NULL || strlen(host) >= NI_MAXHOST) { 330 debug("%.100s, line %lu: Bad permitopen " 331 "specification <%.100s>", file, linenum, 332 patterns); 333 auth_debug_add("%.100s, line %lu: " 334 "Bad permitopen specification", file, 335 linenum); 336 free(patterns); 337 goto bad_option; 338 } 339 host = cleanhostname(host); 340 if (p == NULL || (port = permitopen_port(p)) < 0) { 341 debug("%.100s, line %lu: Bad permitopen port " 342 "<%.100s>", file, linenum, p ? p : ""); 343 auth_debug_add("%.100s, line %lu: " 344 "Bad permitopen port", file, linenum); 345 free(patterns); 346 goto bad_option; 347 } 348 if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0) 349 channel_add_permitted_opens(host, port); 350 free(patterns); 351 goto next_option; 352 } 353 cp = "tunnel=\""; 354 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 355 char *tun = NULL; 356 opts += strlen(cp); 357 tun = xmalloc(strlen(opts) + 1); 358 i = 0; 359 while (*opts) { 360 if (*opts == '"') 361 break; 362 tun[i++] = *opts++; 363 } 364 if (!*opts) { 365 debug("%.100s, line %lu: missing end quote", 366 file, linenum); 367 auth_debug_add("%.100s, line %lu: missing end quote", 368 file, linenum); 369 free(tun); 370 forced_tun_device = -1; 371 goto bad_option; 372 } 373 tun[i] = '\0'; 374 forced_tun_device = a2tun(tun, NULL); 375 free(tun); 376 if (forced_tun_device == SSH_TUNID_ERR) { 377 debug("%.100s, line %lu: invalid tun device", 378 file, linenum); 379 auth_debug_add("%.100s, line %lu: invalid tun device", 380 file, linenum); 381 forced_tun_device = -1; 382 goto bad_option; 383 } 384 auth_debug_add("Forced tun device: %d", forced_tun_device); 385 opts++; 386 goto next_option; 387 } 388 next_option: 389 /* 390 * Skip the comma, and move to the next option 391 * (or break out if there are no more). 392 */ 393 if (!*opts) 394 fatal("Bugs in auth-options.c option processing."); 395 if (*opts == ' ' || *opts == '\t') 396 break; /* End of options. */ 397 if (*opts != ',') 398 goto bad_option; 399 opts++; 400 /* Process the next option. */ 401 } 402 403 /* grant access */ 404 return 1; 405 406 bad_option: 407 logit("Bad options in %.100s file, line %lu: %.50s", 408 file, linenum, opts); 409 auth_debug_add("Bad options in %.100s file, line %lu: %.50s", 410 file, linenum, opts); 411 412 /* deny access */ 413 return 0; 414 } 415 416 #define OPTIONS_CRITICAL 1 417 #define OPTIONS_EXTENSIONS 2 418 static int 419 parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw, 420 u_int which, int crit, 421 int *cert_no_port_forwarding_flag, 422 int *cert_no_agent_forwarding_flag, 423 int *cert_no_x11_forwarding_flag, 424 int *cert_no_pty_flag, 425 int *cert_no_user_rc, 426 char **cert_forced_command, 427 int *cert_source_address_done) 428 { 429 char *command, *allowed; 430 const char *remote_ip; 431 char *name = NULL; 432 u_char *data_blob = NULL; 433 u_int nlen, dlen, clen; 434 Buffer c, data; 435 int ret = -1, result, found; 436 437 buffer_init(&data); 438 439 /* Make copy to avoid altering original */ 440 buffer_init(&c); 441 buffer_append(&c, optblob, optblob_len); 442 443 while (buffer_len(&c) > 0) { 444 if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL || 445 (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { 446 error("Certificate options corrupt"); 447 goto out; 448 } 449 buffer_append(&data, data_blob, dlen); 450 debug3("found certificate option \"%.100s\" len %u", 451 name, dlen); 452 found = 0; 453 if ((which & OPTIONS_EXTENSIONS) != 0) { 454 if (strcmp(name, "permit-X11-forwarding") == 0) { 455 *cert_no_x11_forwarding_flag = 0; 456 found = 1; 457 } else if (strcmp(name, 458 "permit-agent-forwarding") == 0) { 459 *cert_no_agent_forwarding_flag = 0; 460 found = 1; 461 } else if (strcmp(name, 462 "permit-port-forwarding") == 0) { 463 *cert_no_port_forwarding_flag = 0; 464 found = 1; 465 } else if (strcmp(name, "permit-pty") == 0) { 466 *cert_no_pty_flag = 0; 467 found = 1; 468 } else if (strcmp(name, "permit-user-rc") == 0) { 469 *cert_no_user_rc = 0; 470 found = 1; 471 } 472 } 473 if (!found && (which & OPTIONS_CRITICAL) != 0) { 474 if (strcmp(name, "force-command") == 0) { 475 if ((command = buffer_get_cstring_ret(&data, 476 &clen)) == NULL) { 477 error("Certificate constraint \"%s\" " 478 "corrupt", name); 479 goto out; 480 } 481 if (*cert_forced_command != NULL) { 482 error("Certificate has multiple " 483 "force-command options"); 484 free(command); 485 goto out; 486 } 487 *cert_forced_command = command; 488 found = 1; 489 } 490 if (strcmp(name, "source-address") == 0) { 491 if ((allowed = buffer_get_cstring_ret(&data, 492 &clen)) == NULL) { 493 error("Certificate constraint " 494 "\"%s\" corrupt", name); 495 goto out; 496 } 497 if ((*cert_source_address_done)++) { 498 error("Certificate has multiple " 499 "source-address options"); 500 free(allowed); 501 goto out; 502 } 503 remote_ip = get_remote_ipaddr(); 504 result = addr_match_cidr_list(remote_ip, 505 allowed); 506 free(allowed); 507 switch (result) { 508 case 1: 509 /* accepted */ 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 goto out; 523 case -1: 524 default: 525 error("Certificate source-address " 526 "contents invalid"); 527 goto out; 528 } 529 found = 1; 530 } 531 } 532 533 if (!found) { 534 if (crit) { 535 error("Certificate critical option \"%s\" " 536 "is not supported", name); 537 goto out; 538 } else { 539 logit("Certificate extension \"%s\" " 540 "is not supported", name); 541 } 542 } else if (buffer_len(&data) != 0) { 543 error("Certificate option \"%s\" corrupt " 544 "(extra data)", name); 545 goto out; 546 } 547 buffer_clear(&data); 548 free(name); 549 free(data_blob); 550 name = NULL; 551 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 free(*cert_forced_command); 561 *cert_forced_command = NULL; 562 } 563 if (name != NULL) 564 free(name); 565 if (data_blob != NULL) 566 free(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 free(forced_command); 629 forced_command = cert_forced_command; 630 } 631 return 0; 632 } 633 634