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