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