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