auth-options.c revision 157016
1219820Sjeff/*
2219820Sjeff * Author: Tatu Ylonen <ylo@cs.hut.fi>
3219820Sjeff * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4219820Sjeff *                    All rights reserved
5219820Sjeff * As far as I am concerned, the code I have written for this software
6219820Sjeff * can be used freely for any purpose.  Any derived versions of this
7219820Sjeff * software must be clearly marked as such, and if the derived work is
8219820Sjeff * incompatible with the protocol description in the RFC file, it must be
9219820Sjeff * called by a name other than "ssh" or "Secure Shell".
10219820Sjeff */
11219820Sjeff
12219820Sjeff#include "includes.h"
13219820SjeffRCSID("$OpenBSD: auth-options.c,v 1.33 2005/12/08 18:34:11 reyk Exp $");
14219820Sjeff
15219820Sjeff#include "xmalloc.h"
16219820Sjeff#include "match.h"
17219820Sjeff#include "log.h"
18219820Sjeff#include "canohost.h"
19219820Sjeff#include "channels.h"
20219820Sjeff#include "auth-options.h"
21219820Sjeff#include "servconf.h"
22219820Sjeff#include "misc.h"
23219820Sjeff#include "monitor_wrap.h"
24219820Sjeff#include "auth.h"
25219820Sjeff
26219820Sjeff/* Flags set authorized_keys flags */
27219820Sjeffint no_port_forwarding_flag = 0;
28219820Sjeffint no_agent_forwarding_flag = 0;
29219820Sjeffint no_x11_forwarding_flag = 0;
30219820Sjeffint no_pty_flag = 0;
31219820Sjeff
32219820Sjeff/* "command=" option. */
33219820Sjeffchar *forced_command = NULL;
34219820Sjeff
35219820Sjeff/* "environment=" options. */
36219820Sjeffstruct envstring *custom_environment = NULL;
37219820Sjeff
38219820Sjeff/* "tunnel=" option. */
39219820Sjeffint forced_tun_device = -1;
40219820Sjeff
41219820Sjeffextern ServerOptions options;
42219820Sjeff
43219820Sjeffvoid
44219820Sjeffauth_clear_options(void)
45219820Sjeff{
46219820Sjeff	no_agent_forwarding_flag = 0;
47219820Sjeff	no_port_forwarding_flag = 0;
48219820Sjeff	no_pty_flag = 0;
49219820Sjeff	no_x11_forwarding_flag = 0;
50219820Sjeff	while (custom_environment) {
51219820Sjeff		struct envstring *ce = custom_environment;
52219820Sjeff		custom_environment = ce->next;
53219820Sjeff		xfree(ce->s);
54219820Sjeff		xfree(ce);
55219820Sjeff	}
56219820Sjeff	if (forced_command) {
57219820Sjeff		xfree(forced_command);
58219820Sjeff		forced_command = NULL;
59219820Sjeff	}
60219820Sjeff	forced_tun_device = -1;
61219820Sjeff	channel_clear_permitted_opens();
62219820Sjeff	auth_debug_reset();
63219820Sjeff}
64219820Sjeff
65219820Sjeff/*
66219820Sjeff * return 1 if access is granted, 0 if not.
67219820Sjeff * side effect: sets key option flags
68219820Sjeff */
69219820Sjeffint
70219820Sjeffauth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
71219820Sjeff{
72219820Sjeff	const char *cp;
73219820Sjeff	int i;
74219820Sjeff
75219820Sjeff	/* reset options */
76219820Sjeff	auth_clear_options();
77219820Sjeff
78219820Sjeff	if (!opts)
79219820Sjeff		return 1;
80219820Sjeff
81219820Sjeff	while (*opts && *opts != ' ' && *opts != '\t') {
82219820Sjeff		cp = "no-port-forwarding";
83219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
84219820Sjeff			auth_debug_add("Port forwarding disabled.");
85219820Sjeff			no_port_forwarding_flag = 1;
86219820Sjeff			opts += strlen(cp);
87219820Sjeff			goto next_option;
88219820Sjeff		}
89219820Sjeff		cp = "no-agent-forwarding";
90219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
91219820Sjeff			auth_debug_add("Agent forwarding disabled.");
92219820Sjeff			no_agent_forwarding_flag = 1;
93219820Sjeff			opts += strlen(cp);
94219820Sjeff			goto next_option;
95219820Sjeff		}
96219820Sjeff		cp = "no-X11-forwarding";
97219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
98219820Sjeff			auth_debug_add("X11 forwarding disabled.");
99219820Sjeff			no_x11_forwarding_flag = 1;
100219820Sjeff			opts += strlen(cp);
101219820Sjeff			goto next_option;
102219820Sjeff		}
103219820Sjeff		cp = "no-pty";
104219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
105219820Sjeff			auth_debug_add("Pty allocation disabled.");
106219820Sjeff			no_pty_flag = 1;
107219820Sjeff			opts += strlen(cp);
108219820Sjeff			goto next_option;
109219820Sjeff		}
110219820Sjeff		cp = "command=\"";
111219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
112219820Sjeff			opts += strlen(cp);
113219820Sjeff			forced_command = xmalloc(strlen(opts) + 1);
114219820Sjeff			i = 0;
115219820Sjeff			while (*opts) {
116219820Sjeff				if (*opts == '"')
117219820Sjeff					break;
118219820Sjeff				if (*opts == '\\' && opts[1] == '"') {
119219820Sjeff					opts += 2;
120219820Sjeff					forced_command[i++] = '"';
121219820Sjeff					continue;
122219820Sjeff				}
123219820Sjeff				forced_command[i++] = *opts++;
124219820Sjeff			}
125219820Sjeff			if (!*opts) {
126219820Sjeff				debug("%.100s, line %lu: missing end quote",
127219820Sjeff				    file, linenum);
128219820Sjeff				auth_debug_add("%.100s, line %lu: missing end quote",
129219820Sjeff				    file, linenum);
130219820Sjeff				xfree(forced_command);
131219820Sjeff				forced_command = NULL;
132219820Sjeff				goto bad_option;
133219820Sjeff			}
134219820Sjeff			forced_command[i] = 0;
135219820Sjeff			auth_debug_add("Forced command: %.900s", forced_command);
136219820Sjeff			opts++;
137219820Sjeff			goto next_option;
138219820Sjeff		}
139219820Sjeff		cp = "environment=\"";
140219820Sjeff		if (options.permit_user_env &&
141219820Sjeff		    strncasecmp(opts, cp, strlen(cp)) == 0) {
142219820Sjeff			char *s;
143219820Sjeff			struct envstring *new_envstring;
144219820Sjeff
145219820Sjeff			opts += strlen(cp);
146219820Sjeff			s = xmalloc(strlen(opts) + 1);
147219820Sjeff			i = 0;
148219820Sjeff			while (*opts) {
149219820Sjeff				if (*opts == '"')
150219820Sjeff					break;
151219820Sjeff				if (*opts == '\\' && opts[1] == '"') {
152219820Sjeff					opts += 2;
153219820Sjeff					s[i++] = '"';
154219820Sjeff					continue;
155219820Sjeff				}
156219820Sjeff				s[i++] = *opts++;
157219820Sjeff			}
158219820Sjeff			if (!*opts) {
159219820Sjeff				debug("%.100s, line %lu: missing end quote",
160219820Sjeff				    file, linenum);
161219820Sjeff				auth_debug_add("%.100s, line %lu: missing end quote",
162219820Sjeff				    file, linenum);
163219820Sjeff				xfree(s);
164219820Sjeff				goto bad_option;
165219820Sjeff			}
166219820Sjeff			s[i] = 0;
167219820Sjeff			auth_debug_add("Adding to environment: %.900s", s);
168219820Sjeff			debug("Adding to environment: %.900s", s);
169219820Sjeff			opts++;
170219820Sjeff			new_envstring = xmalloc(sizeof(struct envstring));
171219820Sjeff			new_envstring->s = s;
172219820Sjeff			new_envstring->next = custom_environment;
173219820Sjeff			custom_environment = new_envstring;
174219820Sjeff			goto next_option;
175219820Sjeff		}
176219820Sjeff		cp = "from=\"";
177219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
178219820Sjeff			const char *remote_ip = get_remote_ipaddr();
179219820Sjeff			const char *remote_host = get_canonical_hostname(
180219820Sjeff			    options.use_dns);
181219820Sjeff			char *patterns = xmalloc(strlen(opts) + 1);
182219820Sjeff
183219820Sjeff			opts += strlen(cp);
184219820Sjeff			i = 0;
185219820Sjeff			while (*opts) {
186219820Sjeff				if (*opts == '"')
187219820Sjeff					break;
188219820Sjeff				if (*opts == '\\' && opts[1] == '"') {
189219820Sjeff					opts += 2;
190219820Sjeff					patterns[i++] = '"';
191219820Sjeff					continue;
192219820Sjeff				}
193219820Sjeff				patterns[i++] = *opts++;
194219820Sjeff			}
195219820Sjeff			if (!*opts) {
196219820Sjeff				debug("%.100s, line %lu: missing end quote",
197219820Sjeff				    file, linenum);
198219820Sjeff				auth_debug_add("%.100s, line %lu: missing end quote",
199219820Sjeff				    file, linenum);
200219820Sjeff				xfree(patterns);
201219820Sjeff				goto bad_option;
202219820Sjeff			}
203219820Sjeff			patterns[i] = 0;
204219820Sjeff			opts++;
205219820Sjeff			if (match_host_and_ip(remote_host, remote_ip,
206219820Sjeff			    patterns) != 1) {
207219820Sjeff				xfree(patterns);
208219820Sjeff				logit("Authentication tried for %.100s with "
209219820Sjeff				    "correct key but not from a permitted "
210219820Sjeff				    "host (host=%.200s, ip=%.200s).",
211219820Sjeff				    pw->pw_name, remote_host, remote_ip);
212219820Sjeff				auth_debug_add("Your host '%.200s' is not "
213219820Sjeff				    "permitted to use this key for login.",
214219820Sjeff				    remote_host);
215219820Sjeff				/* deny access */
216219820Sjeff				return 0;
217219820Sjeff			}
218219820Sjeff			xfree(patterns);
219219820Sjeff			/* Host name matches. */
220219820Sjeff			goto next_option;
221219820Sjeff		}
222219820Sjeff		cp = "permitopen=\"";
223219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
224219820Sjeff			char *host, *p;
225219820Sjeff			u_short port;
226219820Sjeff			char *patterns = xmalloc(strlen(opts) + 1);
227219820Sjeff
228219820Sjeff			opts += strlen(cp);
229219820Sjeff			i = 0;
230219820Sjeff			while (*opts) {
231219820Sjeff				if (*opts == '"')
232219820Sjeff					break;
233219820Sjeff				if (*opts == '\\' && opts[1] == '"') {
234219820Sjeff					opts += 2;
235219820Sjeff					patterns[i++] = '"';
236219820Sjeff					continue;
237219820Sjeff				}
238219820Sjeff				patterns[i++] = *opts++;
239219820Sjeff			}
240219820Sjeff			if (!*opts) {
241219820Sjeff				debug("%.100s, line %lu: missing end quote",
242219820Sjeff				    file, linenum);
243219820Sjeff				auth_debug_add("%.100s, line %lu: missing "
244219820Sjeff				    "end quote", file, linenum);
245219820Sjeff				xfree(patterns);
246219820Sjeff				goto bad_option;
247219820Sjeff			}
248219820Sjeff			patterns[i] = 0;
249219820Sjeff			opts++;
250219820Sjeff			p = patterns;
251219820Sjeff			host = hpdelim(&p);
252219820Sjeff			if (host == NULL || strlen(host) >= NI_MAXHOST) {
253219820Sjeff				debug("%.100s, line %lu: Bad permitopen "
254219820Sjeff				    "specification <%.100s>", file, linenum,
255219820Sjeff				    patterns);
256219820Sjeff				auth_debug_add("%.100s, line %lu: "
257219820Sjeff				    "Bad permitopen specification", file,
258219820Sjeff				    linenum);
259219820Sjeff				xfree(patterns);
260219820Sjeff				goto bad_option;
261219820Sjeff			}
262219820Sjeff			host = cleanhostname(host);
263219820Sjeff			if (p == NULL || (port = a2port(p)) == 0) {
264219820Sjeff				debug("%.100s, line %lu: Bad permitopen port "
265219820Sjeff				    "<%.100s>", file, linenum, p ? p : "");
266219820Sjeff				auth_debug_add("%.100s, line %lu: "
267219820Sjeff				    "Bad permitopen port", file, linenum);
268219820Sjeff				xfree(patterns);
269219820Sjeff				goto bad_option;
270219820Sjeff			}
271219820Sjeff			if (options.allow_tcp_forwarding)
272219820Sjeff				channel_add_permitted_opens(host, port);
273219820Sjeff			xfree(patterns);
274219820Sjeff			goto next_option;
275219820Sjeff		}
276219820Sjeff		cp = "tunnel=\"";
277219820Sjeff		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
278219820Sjeff			char *tun = NULL;
279219820Sjeff			opts += strlen(cp);
280219820Sjeff			tun = xmalloc(strlen(opts) + 1);
281219820Sjeff			i = 0;
282219820Sjeff			while (*opts) {
283219820Sjeff				if (*opts == '"')
284219820Sjeff					break;
285219820Sjeff				tun[i++] = *opts++;
286219820Sjeff			}
287219820Sjeff			if (!*opts) {
288219820Sjeff				debug("%.100s, line %lu: missing end quote",
289219820Sjeff				    file, linenum);
290219820Sjeff				auth_debug_add("%.100s, line %lu: missing end quote",
291219820Sjeff				    file, linenum);
292219820Sjeff				xfree(tun);
293219820Sjeff				forced_tun_device = -1;
294219820Sjeff				goto bad_option;
295219820Sjeff			}
296219820Sjeff			tun[i] = 0;
297219820Sjeff			forced_tun_device = a2tun(tun, NULL);
298219820Sjeff			xfree(tun);
299219820Sjeff			if (forced_tun_device == SSH_TUNID_ERR) {
300219820Sjeff				debug("%.100s, line %lu: invalid tun device",
301219820Sjeff				    file, linenum);
302219820Sjeff				auth_debug_add("%.100s, line %lu: invalid tun device",
303219820Sjeff				    file, linenum);
304219820Sjeff				forced_tun_device = -1;
305219820Sjeff				goto bad_option;
306219820Sjeff			}
307219820Sjeff			auth_debug_add("Forced tun device: %d", forced_tun_device);
308219820Sjeff			opts++;
309219820Sjeff			goto next_option;
310219820Sjeff		}
311219820Sjeffnext_option:
312219820Sjeff		/*
313219820Sjeff		 * Skip the comma, and move to the next option
314219820Sjeff		 * (or break out if there are no more).
315219820Sjeff		 */
316219820Sjeff		if (!*opts)
317219820Sjeff			fatal("Bugs in auth-options.c option processing.");
318219820Sjeff		if (*opts == ' ' || *opts == '\t')
319219820Sjeff			break;		/* End of options. */
320219820Sjeff		if (*opts != ',')
321219820Sjeff			goto bad_option;
322219820Sjeff		opts++;
323219820Sjeff		/* Process the next option. */
324219820Sjeff	}
325219820Sjeff
326219820Sjeff	if (!use_privsep)
327219820Sjeff		auth_debug_send();
328219820Sjeff
329219820Sjeff	/* grant access */
330219820Sjeff	return 1;
331219820Sjeff
332219820Sjeffbad_option:
333219820Sjeff	logit("Bad options in %.100s file, line %lu: %.50s",
334219820Sjeff	    file, linenum, opts);
335219820Sjeff	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
336219820Sjeff	    file, linenum, opts);
337219820Sjeff
338219820Sjeff	if (!use_privsep)
339219820Sjeff		auth_debug_send();
340219820Sjeff
341219820Sjeff	/* deny access */
342219820Sjeff	return 0;
343219820Sjeff}
344219820Sjeff