1 /* $OpenBSD: auth-options.c,v 1.67 2015/05/01 03:20:54 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 <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 "key.h" /* XXX for typedef */ 23 #include "buffer.h" /* XXX for typedef */ 24 #include "xmalloc.h" 25 #include "match.h" 26 #include "ssherr.h" 27 #include "log.h" 28 #include "canohost.h" 29 #include "sshbuf.h" 30 #include "misc.h" 31 #include "channels.h" 32 #include "servconf.h" 33 #include "sshkey.h" 34 #include "auth-options.h" 35 #include "hostfile.h" 36 #include "auth.h" 37 38 /* Flags set authorized_keys flags */ 39 int no_port_forwarding_flag = 0; 40 int no_agent_forwarding_flag = 0; 41 int no_x11_forwarding_flag = 0; 42 int no_pty_flag = 0; 43 int no_user_rc = 0; 44 int key_is_cert_authority = 0; 45 46 /* "command=" option. */ 47 char *forced_command = NULL; 48 49 /* "environment=" options. */ 50 struct envstring *custom_environment = NULL; 51 52 /* "tunnel=" option. */ 53 int forced_tun_device = -1; 54 55 /* "principals=" option. */ 56 char *authorized_principals = NULL; 57 58 extern ServerOptions options; 59 60 void 61 auth_clear_options(void) 62 { 63 no_agent_forwarding_flag = 0; 64 no_port_forwarding_flag = 0; 65 no_pty_flag = 0; 66 no_x11_forwarding_flag = 0; 67 no_user_rc = 0; 68 key_is_cert_authority = 0; 69 while (custom_environment) { 70 struct envstring *ce = custom_environment; 71 custom_environment = ce->next; 72 free(ce->s); 73 free(ce); 74 } 75 if (forced_command) { 76 free(forced_command); 77 forced_command = NULL; 78 } 79 if (authorized_principals) { 80 free(authorized_principals); 81 authorized_principals = NULL; 82 } 83 forced_tun_device = -1; 84 channel_clear_permitted_opens(); 85 } 86 87 /* 88 * return 1 if access is granted, 0 if not. 89 * side effect: sets key option flags 90 */ 91 int 92 auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) 93 { 94 const char *cp; 95 int i; 96 97 /* reset options */ 98 auth_clear_options(); 99 100 if (!opts) 101 return 1; 102 103 while (*opts && *opts != ' ' && *opts != '\t') { 104 cp = "cert-authority"; 105 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 106 key_is_cert_authority = 1; 107 opts += strlen(cp); 108 goto next_option; 109 } 110 cp = "no-port-forwarding"; 111 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 112 auth_debug_add("Port forwarding disabled."); 113 no_port_forwarding_flag = 1; 114 opts += strlen(cp); 115 goto next_option; 116 } 117 cp = "no-agent-forwarding"; 118 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 119 auth_debug_add("Agent forwarding disabled."); 120 no_agent_forwarding_flag = 1; 121 opts += strlen(cp); 122 goto next_option; 123 } 124 cp = "no-X11-forwarding"; 125 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 126 auth_debug_add("X11 forwarding disabled."); 127 no_x11_forwarding_flag = 1; 128 opts += strlen(cp); 129 goto next_option; 130 } 131 cp = "no-pty"; 132 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 133 auth_debug_add("Pty allocation disabled."); 134 no_pty_flag = 1; 135 opts += strlen(cp); 136 goto next_option; 137 } 138 cp = "no-user-rc"; 139 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 140 auth_debug_add("User rc file execution disabled."); 141 no_user_rc = 1; 142 opts += strlen(cp); 143 goto next_option; 144 } 145 cp = "command=\""; 146 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 147 opts += strlen(cp); 148 if (forced_command != NULL) 149 free(forced_command); 150 forced_command = xmalloc(strlen(opts) + 1); 151 i = 0; 152 while (*opts) { 153 if (*opts == '"') 154 break; 155 if (*opts == '\\' && opts[1] == '"') { 156 opts += 2; 157 forced_command[i++] = '"'; 158 continue; 159 } 160 forced_command[i++] = *opts++; 161 } 162 if (!*opts) { 163 debug("%.100s, line %lu: missing end quote", 164 file, linenum); 165 auth_debug_add("%.100s, line %lu: missing end quote", 166 file, linenum); 167 free(forced_command); 168 forced_command = NULL; 169 goto bad_option; 170 } 171 forced_command[i] = '\0'; 172 auth_debug_add("Forced command."); 173 opts++; 174 goto next_option; 175 } 176 cp = "principals=\""; 177 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 178 opts += strlen(cp); 179 if (authorized_principals != NULL) 180 free(authorized_principals); 181 authorized_principals = xmalloc(strlen(opts) + 1); 182 i = 0; 183 while (*opts) { 184 if (*opts == '"') 185 break; 186 if (*opts == '\\' && opts[1] == '"') { 187 opts += 2; 188 authorized_principals[i++] = '"'; 189 continue; 190 } 191 authorized_principals[i++] = *opts++; 192 } 193 if (!*opts) { 194 debug("%.100s, line %lu: missing end quote", 195 file, linenum); 196 auth_debug_add("%.100s, line %lu: missing end quote", 197 file, linenum); 198 free(authorized_principals); 199 authorized_principals = NULL; 200 goto bad_option; 201 } 202 authorized_principals[i] = '\0'; 203 auth_debug_add("principals: %.900s", 204 authorized_principals); 205 opts++; 206 goto next_option; 207 } 208 cp = "environment=\""; 209 if (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 opts++; 236 if (options.permit_user_env) { 237 auth_debug_add("Adding to environment: " 238 "%.900s", s); 239 debug("Adding to environment: %.900s", s); 240 new_envstring = xcalloc(1, 241 sizeof(*new_envstring)); 242 new_envstring->s = s; 243 new_envstring->next = custom_environment; 244 custom_environment = new_envstring; 245 s = NULL; 246 } 247 free(s); 248 goto next_option; 249 } 250 cp = "from=\""; 251 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 252 const char *remote_ip = get_remote_ipaddr(); 253 const char *remote_host = get_canonical_hostname( 254 options.use_dns); 255 char *patterns = xmalloc(strlen(opts) + 1); 256 257 opts += strlen(cp); 258 i = 0; 259 while (*opts) { 260 if (*opts == '"') 261 break; 262 if (*opts == '\\' && opts[1] == '"') { 263 opts += 2; 264 patterns[i++] = '"'; 265 continue; 266 } 267 patterns[i++] = *opts++; 268 } 269 if (!*opts) { 270 debug("%.100s, line %lu: missing end quote", 271 file, linenum); 272 auth_debug_add("%.100s, line %lu: missing end quote", 273 file, linenum); 274 free(patterns); 275 goto bad_option; 276 } 277 patterns[i] = '\0'; 278 opts++; 279 switch (match_host_and_ip(remote_host, remote_ip, 280 patterns)) { 281 case 1: 282 free(patterns); 283 /* Host name matches. */ 284 goto next_option; 285 case -1: 286 debug("%.100s, line %lu: invalid criteria", 287 file, linenum); 288 auth_debug_add("%.100s, line %lu: " 289 "invalid criteria", file, linenum); 290 /* FALLTHROUGH */ 291 case 0: 292 free(patterns); 293 logit("Authentication tried for %.100s with " 294 "correct key but not from a permitted " 295 "host (host=%.200s, ip=%.200s).", 296 pw->pw_name, remote_host, remote_ip); 297 auth_debug_add("Your host '%.200s' is not " 298 "permitted to use this key for login.", 299 remote_host); 300 break; 301 } 302 /* deny access */ 303 return 0; 304 } 305 cp = "permitopen=\""; 306 if (strncasecmp(opts, cp, strlen(cp)) == 0) { 307 char *host, *p; 308 int port; 309 char *patterns = xmalloc(strlen(opts) + 1); 310 311 opts += strlen(cp); 312 i = 0; 313 while (*opts) { 314 if (*opts == '"') 315 break; 316 if (*opts == '\\' && opts[1] == '"') { 317 opts += 2; 318 patterns[i++] = '"'; 319 continue; 320 } 321 patterns[i++] = *opts++; 322 } 323 if (!*opts) { 324 debug("%.100s, line %lu: missing end quote", 325 file, linenum); 326 auth_debug_add("%.100s, line %lu: missing " 327 "end quote", file, linenum); 328 free(patterns); 329 goto bad_option; 330 } 331 patterns[i] = '\0'; 332 opts++; 333 p = patterns; 334 /* XXX - add streamlocal support */ 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(struct sshbuf *oblob, 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 struct sshbuf *c = NULL, *data = NULL; 440 int r, ret = -1, result, found; 441 442 if ((c = sshbuf_fromb(oblob)) == NULL) { 443 error("%s: sshbuf_fromb failed", __func__); 444 goto out; 445 } 446 447 while (sshbuf_len(c) > 0) { 448 sshbuf_free(data); 449 data = NULL; 450 if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || 451 (r = sshbuf_froms(c, &data)) != 0) { 452 error("Unable to parse certificate options: %s", 453 ssh_err(r)); 454 goto out; 455 } 456 debug3("found certificate option \"%.100s\" len %zu", 457 name, sshbuf_len(data)); 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 ((r = sshbuf_get_cstring(data, &command, 482 NULL)) != 0) { 483 error("Unable to parse \"%s\" " 484 "section: %s", name, ssh_err(r)); 485 goto out; 486 } 487 if (*cert_forced_command != NULL) { 488 error("Certificate has multiple " 489 "force-command options"); 490 free(command); 491 goto out; 492 } 493 *cert_forced_command = command; 494 found = 1; 495 } 496 if (strcmp(name, "source-address") == 0) { 497 if ((r = sshbuf_get_cstring(data, &allowed, 498 NULL)) != 0) { 499 error("Unable to parse \"%s\" " 500 "section: %s", name, ssh_err(r)); 501 goto out; 502 } 503 if ((*cert_source_address_done)++) { 504 error("Certificate has multiple " 505 "source-address options"); 506 free(allowed); 507 goto out; 508 } 509 remote_ip = get_remote_ipaddr(); 510 result = addr_match_cidr_list(remote_ip, 511 allowed); 512 free(allowed); 513 switch (result) { 514 case 1: 515 /* accepted */ 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 goto out; 529 case -1: 530 default: 531 error("Certificate source-address " 532 "contents invalid"); 533 goto out; 534 } 535 found = 1; 536 } 537 } 538 539 if (!found) { 540 if (crit) { 541 error("Certificate critical option \"%s\" " 542 "is not supported", name); 543 goto out; 544 } else { 545 logit("Certificate extension \"%s\" " 546 "is not supported", name); 547 } 548 } else if (sshbuf_len(data) != 0) { 549 error("Certificate option \"%s\" corrupt " 550 "(extra data)", name); 551 goto out; 552 } 553 free(name); 554 name = NULL; 555 } 556 /* successfully parsed all options */ 557 ret = 0; 558 559 out: 560 if (ret != 0 && 561 cert_forced_command != NULL && 562 *cert_forced_command != NULL) { 563 free(*cert_forced_command); 564 *cert_forced_command = NULL; 565 } 566 if (name != NULL) 567 free(name); 568 sshbuf_free(data); 569 sshbuf_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(struct sshkey *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 (sshkey_cert_is_legacy(k)) { 589 /* All options are in the one field for v00 certs */ 590 if (parse_option_list(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(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(k->cert->extensions, pw, 608 OPTIONS_EXTENSIONS, 0, 609 &cert_no_port_forwarding_flag, 610 &cert_no_agent_forwarding_flag, 611 &cert_no_x11_forwarding_flag, 612 &cert_no_pty_flag, 613 &cert_no_user_rc, 614 NULL, NULL) == -1) 615 return -1; 616 } 617 618 no_port_forwarding_flag |= cert_no_port_forwarding_flag; 619 no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 620 no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 621 no_pty_flag |= cert_no_pty_flag; 622 no_user_rc |= cert_no_user_rc; 623 /* CA-specified forced command supersedes key option */ 624 if (cert_forced_command != NULL) { 625 if (forced_command != NULL) 626 free(forced_command); 627 forced_command = cert_forced_command; 628 } 629 return 0; 630 } 631 632