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