auth-options.c revision 204917
1204917Sdes/* $OpenBSD: auth-options.c,v 1.48 2010/03/07 11:57:13 dtucker Exp $ */ 265668Skris/* 365668Skris * Author: Tatu Ylonen <ylo@cs.hut.fi> 465668Skris * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 565668Skris * All rights reserved 665668Skris * As far as I am concerned, the code I have written for this software 765668Skris * can be used freely for any purpose. Any derived versions of this 865668Skris * software must be clearly marked as such, and if the derived work is 965668Skris * incompatible with the protocol description in the RFC file, it must be 1065668Skris * called by a name other than "ssh" or "Secure Shell". 1165668Skris */ 1265668Skris 1365668Skris#include "includes.h" 1465668Skris 15162852Sdes#include <sys/types.h> 16162852Sdes 17162852Sdes#include <netdb.h> 18162852Sdes#include <pwd.h> 19162852Sdes#include <string.h> 20162852Sdes#include <stdio.h> 21162852Sdes#include <stdarg.h> 22162852Sdes 23181111Sdes#include "openbsd-compat/sys-queue.h" 2465668Skris#include "xmalloc.h" 2565668Skris#include "match.h" 2676259Sgreen#include "log.h" 2776259Sgreen#include "canohost.h" 28162852Sdes#include "buffer.h" 2976259Sgreen#include "channels.h" 3076259Sgreen#include "auth-options.h" 3176259Sgreen#include "servconf.h" 3292555Sdes#include "misc.h" 33162852Sdes#include "key.h" 34162852Sdes#include "hostfile.h" 35162852Sdes#include "auth.h" 36162852Sdes#ifdef GSSAPI 37162852Sdes#include "ssh-gss.h" 38162852Sdes#endif 3998675Sdes#include "monitor_wrap.h" 4065668Skris 4165668Skris/* Flags set authorized_keys flags */ 4265668Skrisint no_port_forwarding_flag = 0; 4365668Skrisint no_agent_forwarding_flag = 0; 4465668Skrisint no_x11_forwarding_flag = 0; 4565668Skrisint no_pty_flag = 0; 46181111Sdesint no_user_rc = 0; 47204917Sdesint key_is_cert_authority = 0; 4865668Skris 4965668Skris/* "command=" option. */ 5065668Skrischar *forced_command = NULL; 5165668Skris 5265668Skris/* "environment=" options. */ 5365668Skrisstruct envstring *custom_environment = NULL; 5465668Skris 55157016Sdes/* "tunnel=" option. */ 56157016Sdesint forced_tun_device = -1; 57157016Sdes 5876259Sgreenextern ServerOptions options; 5976259Sgreen 6069587Sgreenvoid 6169587Sgreenauth_clear_options(void) 6269587Sgreen{ 6369587Sgreen no_agent_forwarding_flag = 0; 6469587Sgreen no_port_forwarding_flag = 0; 6569587Sgreen no_pty_flag = 0; 6669587Sgreen no_x11_forwarding_flag = 0; 67181111Sdes no_user_rc = 0; 68204917Sdes key_is_cert_authority = 0; 6969587Sgreen while (custom_environment) { 7069587Sgreen struct envstring *ce = custom_environment; 7169587Sgreen custom_environment = ce->next; 7269587Sgreen xfree(ce->s); 7369587Sgreen xfree(ce); 7469587Sgreen } 7569587Sgreen if (forced_command) { 7669587Sgreen xfree(forced_command); 7769587Sgreen forced_command = NULL; 7869587Sgreen } 79157016Sdes forced_tun_device = -1; 8076259Sgreen channel_clear_permitted_opens(); 8169587Sgreen} 8269587Sgreen 8376259Sgreen/* 8476259Sgreen * return 1 if access is granted, 0 if not. 8576259Sgreen * side effect: sets key option flags 8676259Sgreen */ 8765668Skrisint 8876259Sgreenauth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) 8965668Skris{ 9065668Skris const char *cp; 9176259Sgreen int i; 9269587Sgreen 9369587Sgreen /* reset options */ 9469587Sgreen auth_clear_options(); 9569587Sgreen 9676259Sgreen if (!opts) 9776259Sgreen return 1; 9876259Sgreen 9976259Sgreen while (*opts && *opts != ' ' && *opts != '\t') { 100204917Sdes cp = "cert-authority"; 101204917Sdes if (strncasecmp(opts, cp, strlen(cp)) == 0) { 102204917Sdes key_is_cert_authority = 1; 103204917Sdes opts += strlen(cp); 104204917Sdes goto next_option; 105204917Sdes } 10665668Skris cp = "no-port-forwarding"; 10776259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 10898675Sdes auth_debug_add("Port forwarding disabled."); 10965668Skris no_port_forwarding_flag = 1; 11076259Sgreen opts += strlen(cp); 11165668Skris goto next_option; 11265668Skris } 11365668Skris cp = "no-agent-forwarding"; 11476259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 11598675Sdes auth_debug_add("Agent forwarding disabled."); 11665668Skris no_agent_forwarding_flag = 1; 11776259Sgreen opts += strlen(cp); 11865668Skris goto next_option; 11965668Skris } 12065668Skris cp = "no-X11-forwarding"; 12176259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 12298675Sdes auth_debug_add("X11 forwarding disabled."); 12365668Skris no_x11_forwarding_flag = 1; 12476259Sgreen opts += strlen(cp); 12565668Skris goto next_option; 12665668Skris } 12765668Skris cp = "no-pty"; 12876259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 12998675Sdes auth_debug_add("Pty allocation disabled."); 13065668Skris no_pty_flag = 1; 13176259Sgreen opts += strlen(cp); 13265668Skris goto next_option; 13365668Skris } 134181111Sdes cp = "no-user-rc"; 135181111Sdes if (strncasecmp(opts, cp, strlen(cp)) == 0) { 136181111Sdes auth_debug_add("User rc file execution disabled."); 137181111Sdes no_user_rc = 1; 138181111Sdes opts += strlen(cp); 139181111Sdes goto next_option; 140181111Sdes } 14165668Skris cp = "command=\""; 14276259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 14376259Sgreen opts += strlen(cp); 14476259Sgreen forced_command = xmalloc(strlen(opts) + 1); 14565668Skris i = 0; 14676259Sgreen while (*opts) { 14776259Sgreen if (*opts == '"') 14865668Skris break; 14976259Sgreen if (*opts == '\\' && opts[1] == '"') { 15076259Sgreen opts += 2; 15165668Skris forced_command[i++] = '"'; 15265668Skris continue; 15365668Skris } 15476259Sgreen forced_command[i++] = *opts++; 15565668Skris } 15676259Sgreen if (!*opts) { 15765668Skris debug("%.100s, line %lu: missing end quote", 15876259Sgreen file, linenum); 15998675Sdes auth_debug_add("%.100s, line %lu: missing end quote", 16076259Sgreen file, linenum); 16176259Sgreen xfree(forced_command); 16276259Sgreen forced_command = NULL; 16376259Sgreen goto bad_option; 16465668Skris } 165162852Sdes forced_command[i] = '\0'; 16698675Sdes auth_debug_add("Forced command: %.900s", forced_command); 16776259Sgreen opts++; 16865668Skris goto next_option; 16965668Skris } 17065668Skris cp = "environment=\""; 171106121Sdes if (options.permit_user_env && 172106121Sdes strncasecmp(opts, cp, strlen(cp)) == 0) { 17365668Skris char *s; 17465668Skris struct envstring *new_envstring; 17576259Sgreen 17676259Sgreen opts += strlen(cp); 17776259Sgreen s = xmalloc(strlen(opts) + 1); 17865668Skris i = 0; 17976259Sgreen while (*opts) { 18076259Sgreen if (*opts == '"') 18165668Skris break; 18276259Sgreen if (*opts == '\\' && opts[1] == '"') { 18376259Sgreen opts += 2; 18465668Skris s[i++] = '"'; 18565668Skris continue; 18665668Skris } 18776259Sgreen s[i++] = *opts++; 18865668Skris } 18976259Sgreen if (!*opts) { 19065668Skris debug("%.100s, line %lu: missing end quote", 19176259Sgreen file, linenum); 19298675Sdes auth_debug_add("%.100s, line %lu: missing end quote", 19376259Sgreen file, linenum); 19476259Sgreen xfree(s); 19576259Sgreen goto bad_option; 19665668Skris } 197162852Sdes s[i] = '\0'; 19898675Sdes auth_debug_add("Adding to environment: %.900s", s); 19965668Skris debug("Adding to environment: %.900s", s); 20076259Sgreen opts++; 20165668Skris new_envstring = xmalloc(sizeof(struct envstring)); 20265668Skris new_envstring->s = s; 20365668Skris new_envstring->next = custom_environment; 20465668Skris custom_environment = new_envstring; 20565668Skris goto next_option; 20665668Skris } 20765668Skris cp = "from=\""; 20876259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 20976259Sgreen const char *remote_ip = get_remote_ipaddr(); 21076259Sgreen const char *remote_host = get_canonical_hostname( 211124208Sdes options.use_dns); 21276259Sgreen char *patterns = xmalloc(strlen(opts) + 1); 21376259Sgreen 21476259Sgreen opts += strlen(cp); 21565668Skris i = 0; 21676259Sgreen while (*opts) { 21776259Sgreen if (*opts == '"') 21865668Skris break; 21976259Sgreen if (*opts == '\\' && opts[1] == '"') { 22076259Sgreen opts += 2; 22165668Skris patterns[i++] = '"'; 22265668Skris continue; 22365668Skris } 22476259Sgreen patterns[i++] = *opts++; 22565668Skris } 22676259Sgreen if (!*opts) { 22765668Skris debug("%.100s, line %lu: missing end quote", 22876259Sgreen file, linenum); 22998675Sdes auth_debug_add("%.100s, line %lu: missing end quote", 23076259Sgreen file, linenum); 23176259Sgreen xfree(patterns); 23276259Sgreen goto bad_option; 23365668Skris } 234162852Sdes patterns[i] = '\0'; 23576259Sgreen opts++; 236181111Sdes switch (match_host_and_ip(remote_host, remote_ip, 237181111Sdes patterns)) { 238181111Sdes case 1: 23992555Sdes xfree(patterns); 240181111Sdes /* Host name matches. */ 241181111Sdes goto next_option; 242181111Sdes case -1: 243181111Sdes debug("%.100s, line %lu: invalid criteria", 244181111Sdes file, linenum); 245181111Sdes auth_debug_add("%.100s, line %lu: " 246181111Sdes "invalid criteria", file, linenum); 247181111Sdes /* FALLTHROUGH */ 248181111Sdes case 0: 249181111Sdes xfree(patterns); 250124208Sdes logit("Authentication tried for %.100s with " 25176259Sgreen "correct key but not from a permitted " 25276259Sgreen "host (host=%.200s, ip=%.200s).", 25376259Sgreen pw->pw_name, remote_host, remote_ip); 25498675Sdes auth_debug_add("Your host '%.200s' is not " 25576259Sgreen "permitted to use this key for login.", 25676259Sgreen remote_host); 257181111Sdes break; 25865668Skris } 259181111Sdes /* deny access */ 260181111Sdes return 0; 26165668Skris } 26276259Sgreen cp = "permitopen=\""; 26376259Sgreen if (strncasecmp(opts, cp, strlen(cp)) == 0) { 264146998Sdes char *host, *p; 265192595Sdes int port; 26676259Sgreen char *patterns = xmalloc(strlen(opts) + 1); 26776259Sgreen 26876259Sgreen opts += strlen(cp); 26976259Sgreen i = 0; 27076259Sgreen while (*opts) { 27176259Sgreen if (*opts == '"') 27276259Sgreen break; 27376259Sgreen if (*opts == '\\' && opts[1] == '"') { 27476259Sgreen opts += 2; 27576259Sgreen patterns[i++] = '"'; 27676259Sgreen continue; 27776259Sgreen } 27876259Sgreen patterns[i++] = *opts++; 27976259Sgreen } 28076259Sgreen if (!*opts) { 28176259Sgreen debug("%.100s, line %lu: missing end quote", 28276259Sgreen file, linenum); 283146998Sdes auth_debug_add("%.100s, line %lu: missing " 284146998Sdes "end quote", file, linenum); 28576259Sgreen xfree(patterns); 28676259Sgreen goto bad_option; 28776259Sgreen } 288162852Sdes patterns[i] = '\0'; 28976259Sgreen opts++; 290146998Sdes p = patterns; 291146998Sdes host = hpdelim(&p); 292146998Sdes if (host == NULL || strlen(host) >= NI_MAXHOST) { 293146998Sdes debug("%.100s, line %lu: Bad permitopen " 294147001Sdes "specification <%.100s>", file, linenum, 295146998Sdes patterns); 29698675Sdes auth_debug_add("%.100s, line %lu: " 297146998Sdes "Bad permitopen specification", file, 298146998Sdes linenum); 29976259Sgreen xfree(patterns); 30076259Sgreen goto bad_option; 30176259Sgreen } 302147001Sdes host = cleanhostname(host); 303192595Sdes if (p == NULL || (port = a2port(p)) <= 0) { 304146998Sdes debug("%.100s, line %lu: Bad permitopen port " 305146998Sdes "<%.100s>", file, linenum, p ? p : ""); 30698675Sdes auth_debug_add("%.100s, line %lu: " 30792555Sdes "Bad permitopen port", file, linenum); 30876259Sgreen xfree(patterns); 30976259Sgreen goto bad_option; 31076259Sgreen } 31176259Sgreen if (options.allow_tcp_forwarding) 31292555Sdes channel_add_permitted_opens(host, port); 31376259Sgreen xfree(patterns); 31476259Sgreen goto next_option; 31576259Sgreen } 316157016Sdes cp = "tunnel=\""; 317157016Sdes if (strncasecmp(opts, cp, strlen(cp)) == 0) { 318157016Sdes char *tun = NULL; 319157016Sdes opts += strlen(cp); 320157016Sdes tun = xmalloc(strlen(opts) + 1); 321157016Sdes i = 0; 322157016Sdes while (*opts) { 323157016Sdes if (*opts == '"') 324157016Sdes break; 325157016Sdes tun[i++] = *opts++; 326157016Sdes } 327157016Sdes if (!*opts) { 328157016Sdes debug("%.100s, line %lu: missing end quote", 329157016Sdes file, linenum); 330157016Sdes auth_debug_add("%.100s, line %lu: missing end quote", 331157016Sdes file, linenum); 332157016Sdes xfree(tun); 333157016Sdes forced_tun_device = -1; 334157016Sdes goto bad_option; 335157016Sdes } 336162852Sdes tun[i] = '\0'; 337157016Sdes forced_tun_device = a2tun(tun, NULL); 338157016Sdes xfree(tun); 339157016Sdes if (forced_tun_device == SSH_TUNID_ERR) { 340157016Sdes debug("%.100s, line %lu: invalid tun device", 341157016Sdes file, linenum); 342157016Sdes auth_debug_add("%.100s, line %lu: invalid tun device", 343157016Sdes file, linenum); 344157016Sdes forced_tun_device = -1; 345157016Sdes goto bad_option; 346157016Sdes } 347157016Sdes auth_debug_add("Forced tun device: %d", forced_tun_device); 348157016Sdes opts++; 349157016Sdes goto next_option; 350157016Sdes } 35165668Skrisnext_option: 35265668Skris /* 35365668Skris * Skip the comma, and move to the next option 35465668Skris * (or break out if there are no more). 35565668Skris */ 35676259Sgreen if (!*opts) 35765668Skris fatal("Bugs in auth-options.c option processing."); 35876259Sgreen if (*opts == ' ' || *opts == '\t') 35965668Skris break; /* End of options. */ 36076259Sgreen if (*opts != ',') 36165668Skris goto bad_option; 36276259Sgreen opts++; 36365668Skris /* Process the next option. */ 36465668Skris } 36598675Sdes 36665668Skris /* grant access */ 36765668Skris return 1; 36865668Skris 36965668Skrisbad_option: 370124208Sdes logit("Bad options in %.100s file, line %lu: %.50s", 37176259Sgreen file, linenum, opts); 37298675Sdes auth_debug_add("Bad options in %.100s file, line %lu: %.50s", 37376259Sgreen file, linenum, opts); 37498675Sdes 37565668Skris /* deny access */ 37665668Skris return 0; 37765668Skris} 378204917Sdes 379204917Sdes/* 380204917Sdes * Set options from certificate constraints. These supersede user key options 381204917Sdes * so this must be called after auth_parse_options(). 382204917Sdes */ 383204917Sdesint 384204917Sdesauth_cert_constraints(Buffer *c_orig, struct passwd *pw) 385204917Sdes{ 386204917Sdes u_char *name = NULL, *data_blob = NULL; 387204917Sdes u_int nlen, dlen, clen; 388204917Sdes Buffer c, data; 389204917Sdes int ret = -1; 390204917Sdes 391204917Sdes int cert_no_port_forwarding_flag = 1; 392204917Sdes int cert_no_agent_forwarding_flag = 1; 393204917Sdes int cert_no_x11_forwarding_flag = 1; 394204917Sdes int cert_no_pty_flag = 1; 395204917Sdes int cert_no_user_rc = 1; 396204917Sdes char *cert_forced_command = NULL; 397204917Sdes int cert_source_address_done = 0; 398204917Sdes 399204917Sdes buffer_init(&data); 400204917Sdes 401204917Sdes /* Make copy to avoid altering original */ 402204917Sdes buffer_init(&c); 403204917Sdes buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig)); 404204917Sdes 405204917Sdes while (buffer_len(&c) > 0) { 406204917Sdes if ((name = buffer_get_string_ret(&c, &nlen)) == NULL || 407204917Sdes (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) { 408204917Sdes error("Certificate constraints corrupt"); 409204917Sdes goto out; 410204917Sdes } 411204917Sdes buffer_append(&data, data_blob, dlen); 412204917Sdes debug3("found certificate constraint \"%.100s\" len %u", 413204917Sdes name, dlen); 414204917Sdes if (strlen(name) != nlen) { 415204917Sdes error("Certificate constraint name contains \\0"); 416204917Sdes goto out; 417204917Sdes } 418204917Sdes if (strcmp(name, "permit-X11-forwarding") == 0) 419204917Sdes cert_no_x11_forwarding_flag = 0; 420204917Sdes else if (strcmp(name, "permit-agent-forwarding") == 0) 421204917Sdes cert_no_agent_forwarding_flag = 0; 422204917Sdes else if (strcmp(name, "permit-port-forwarding") == 0) 423204917Sdes cert_no_port_forwarding_flag = 0; 424204917Sdes else if (strcmp(name, "permit-pty") == 0) 425204917Sdes cert_no_pty_flag = 0; 426204917Sdes else if (strcmp(name, "permit-user-rc") == 0) 427204917Sdes cert_no_user_rc = 0; 428204917Sdes else if (strcmp(name, "force-command") == 0) { 429204917Sdes char *command = buffer_get_string_ret(&data, &clen); 430204917Sdes 431204917Sdes if (command == NULL) { 432204917Sdes error("Certificate constraint \"%s\" corrupt", 433204917Sdes name); 434204917Sdes goto out; 435204917Sdes } 436204917Sdes if (strlen(command) != clen) { 437204917Sdes error("force-command constrain contains \\0"); 438204917Sdes goto out; 439204917Sdes } 440204917Sdes if (cert_forced_command != NULL) { 441204917Sdes error("Certificate has multiple " 442204917Sdes "force-command constraints"); 443204917Sdes xfree(command); 444204917Sdes goto out; 445204917Sdes } 446204917Sdes cert_forced_command = command; 447204917Sdes } else if (strcmp(name, "source-address") == 0) { 448204917Sdes char *allowed = buffer_get_string_ret(&data, &clen); 449204917Sdes const char *remote_ip = get_remote_ipaddr(); 450204917Sdes 451204917Sdes if (allowed == NULL) { 452204917Sdes error("Certificate constraint \"%s\" corrupt", 453204917Sdes name); 454204917Sdes goto out; 455204917Sdes } 456204917Sdes if (strlen(allowed) != clen) { 457204917Sdes error("source-address constrain contains \\0"); 458204917Sdes goto out; 459204917Sdes } 460204917Sdes if (cert_source_address_done++) { 461204917Sdes error("Certificate has multiple " 462204917Sdes "source-address constraints"); 463204917Sdes xfree(allowed); 464204917Sdes goto out; 465204917Sdes } 466204917Sdes switch (addr_match_cidr_list(remote_ip, allowed)) { 467204917Sdes case 1: 468204917Sdes /* accepted */ 469204917Sdes xfree(allowed); 470204917Sdes break; 471204917Sdes case 0: 472204917Sdes /* no match */ 473204917Sdes logit("Authentication tried for %.100s with " 474204917Sdes "valid certificate but not from a " 475204917Sdes "permitted host (ip=%.200s).", 476204917Sdes pw->pw_name, remote_ip); 477204917Sdes auth_debug_add("Your address '%.200s' is not " 478204917Sdes "permitted to use this certificate for " 479204917Sdes "login.", remote_ip); 480204917Sdes xfree(allowed); 481204917Sdes goto out; 482204917Sdes case -1: 483204917Sdes error("Certificate source-address contents " 484204917Sdes "invalid"); 485204917Sdes xfree(allowed); 486204917Sdes goto out; 487204917Sdes } 488204917Sdes } else { 489204917Sdes error("Certificate constraint \"%s\" is not supported", 490204917Sdes name); 491204917Sdes goto out; 492204917Sdes } 493204917Sdes 494204917Sdes if (buffer_len(&data) != 0) { 495204917Sdes error("Certificate constraint \"%s\" corrupt " 496204917Sdes "(extra data)", name); 497204917Sdes goto out; 498204917Sdes } 499204917Sdes buffer_clear(&data); 500204917Sdes xfree(name); 501204917Sdes xfree(data_blob); 502204917Sdes name = data_blob = NULL; 503204917Sdes } 504204917Sdes 505204917Sdes /* successfully parsed all constraints */ 506204917Sdes ret = 0; 507204917Sdes 508204917Sdes no_port_forwarding_flag |= cert_no_port_forwarding_flag; 509204917Sdes no_agent_forwarding_flag |= cert_no_agent_forwarding_flag; 510204917Sdes no_x11_forwarding_flag |= cert_no_x11_forwarding_flag; 511204917Sdes no_pty_flag |= cert_no_pty_flag; 512204917Sdes no_user_rc |= cert_no_user_rc; 513204917Sdes /* CA-specified forced command supersedes key option */ 514204917Sdes if (cert_forced_command != NULL) { 515204917Sdes if (forced_command != NULL) 516204917Sdes xfree(forced_command); 517204917Sdes forced_command = cert_forced_command; 518204917Sdes } 519204917Sdes 520204917Sdes out: 521204917Sdes if (name != NULL) 522204917Sdes xfree(name); 523204917Sdes if (data_blob != NULL) 524204917Sdes xfree(data_blob); 525204917Sdes buffer_free(&data); 526204917Sdes buffer_free(&c); 527204917Sdes return ret; 528204917Sdes} 529204917Sdes 530