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