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