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