GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ssh/sshd/../auth-options.c Lines: 0 348 0.0 %
Date: 2017-11-13 Branches: 0 197 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: auth-options.c,v 1.74 2017/09/12 06:32:07 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 "key.h"	/* XXX for typedef */
23
#include "buffer.h"	/* XXX for typedef */
24
#include "xmalloc.h"
25
#include "match.h"
26
#include "ssherr.h"
27
#include "log.h"
28
#include "canohost.h"
29
#include "packet.h"
30
#include "sshbuf.h"
31
#include "misc.h"
32
#include "channels.h"
33
#include "servconf.h"
34
#include "sshkey.h"
35
#include "auth-options.h"
36
#include "hostfile.h"
37
#include "auth.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
/* XXX refactor to be stateless */
62
63
void
64
auth_clear_options(void)
65
{
66
	struct ssh *ssh = active_state;		/* XXX */
67
68
	no_agent_forwarding_flag = 0;
69
	no_port_forwarding_flag = 0;
70
	no_pty_flag = 0;
71
	no_x11_forwarding_flag = 0;
72
	no_user_rc = 0;
73
	key_is_cert_authority = 0;
74
	while (custom_environment) {
75
		struct envstring *ce = custom_environment;
76
		custom_environment = ce->next;
77
		free(ce->s);
78
		free(ce);
79
	}
80
	free(forced_command);
81
	forced_command = NULL;
82
	free(authorized_principals);
83
	authorized_principals = NULL;
84
	forced_tun_device = -1;
85
	channel_clear_permitted_opens(ssh);
86
}
87
88
/*
89
 * Match flag 'opt' in *optsp, and if allow_negate is set then also match
90
 * 'no-opt'. Returns -1 if option not matched, 1 if option matches or 0
91
 * if negated option matches.
92
 * If the option or negated option matches, then *optsp is updated to
93
 * point to the first character after the option and, if 'msg' is not NULL
94
 * then a message based on it added via auth_debug_add().
95
 */
96
static int
97
match_flag(const char *opt, int allow_negate, char **optsp, const char *msg)
98
{
99
	size_t opt_len = strlen(opt);
100
	char *opts = *optsp;
101
	int negate = 0;
102
103
	if (allow_negate && strncasecmp(opts, "no-", 3) == 0) {
104
		opts += 3;
105
		negate = 1;
106
	}
107
	if (strncasecmp(opts, opt, opt_len) == 0) {
108
		*optsp = opts + opt_len;
109
		if (msg != NULL) {
110
			auth_debug_add("%s %s.", msg,
111
			    negate ? "disabled" : "enabled");
112
		}
113
		return negate ? 0 : 1;
114
	}
115
	return -1;
116
}
117
118
/*
119
 * return 1 if access is granted, 0 if not.
120
 * side effect: sets key option flags
121
 * XXX remove side effects; fill structure instead.
122
 */
123
int
124
auth_parse_options(struct passwd *pw, char *opts, const char *file,
125
    u_long linenum)
126
{
127
	struct ssh *ssh = active_state;		/* XXX */
128
	const char *cp;
129
	int i, r;
130
131
	/* reset options */
132
	auth_clear_options();
133
134
	if (!opts)
135
		return 1;
136
137
	while (*opts && *opts != ' ' && *opts != '\t') {
138
		if ((r = match_flag("cert-authority", 0, &opts, NULL)) != -1) {
139
			key_is_cert_authority = r;
140
			goto next_option;
141
		}
142
		if ((r = match_flag("restrict", 0, &opts, NULL)) != -1) {
143
			auth_debug_add("Key is restricted.");
144
			no_port_forwarding_flag = 1;
145
			no_agent_forwarding_flag = 1;
146
			no_x11_forwarding_flag = 1;
147
			no_pty_flag = 1;
148
			no_user_rc = 1;
149
			goto next_option;
150
		}
151
		if ((r = match_flag("port-forwarding", 1, &opts,
152
		    "Port forwarding")) != -1) {
153
			no_port_forwarding_flag = r != 1;
154
			goto next_option;
155
		}
156
		if ((r = match_flag("agent-forwarding", 1, &opts,
157
		    "Agent forwarding")) != -1) {
158
			no_agent_forwarding_flag = r != 1;
159
			goto next_option;
160
		}
161
		if ((r = match_flag("x11-forwarding", 1, &opts,
162
		    "X11 forwarding")) != -1) {
163
			no_x11_forwarding_flag = r != 1;
164
			goto next_option;
165
		}
166
		if ((r = match_flag("pty", 1, &opts,
167
		    "PTY allocation")) != -1) {
168
			no_pty_flag = r != 1;
169
			goto next_option;
170
		}
171
		if ((r = match_flag("user-rc", 1, &opts,
172
		    "User rc execution")) != -1) {
173
			no_user_rc = r != 1;
174
			goto next_option;
175
		}
176
		cp = "command=\"";
177
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
178
			opts += strlen(cp);
179
			free(forced_command);
180
			forced_command = xmalloc(strlen(opts) + 1);
181
			i = 0;
182
			while (*opts) {
183
				if (*opts == '"')
184
					break;
185
				if (*opts == '\\' && opts[1] == '"') {
186
					opts += 2;
187
					forced_command[i++] = '"';
188
					continue;
189
				}
190
				forced_command[i++] = *opts++;
191
			}
192
			if (!*opts) {
193
				debug("%.100s, line %lu: missing end quote",
194
				    file, linenum);
195
				auth_debug_add("%.100s, line %lu: missing end quote",
196
				    file, linenum);
197
				free(forced_command);
198
				forced_command = NULL;
199
				goto bad_option;
200
			}
201
			forced_command[i] = '\0';
202
			auth_debug_add("Forced command.");
203
			opts++;
204
			goto next_option;
205
		}
206
		cp = "principals=\"";
207
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
208
			opts += strlen(cp);
209
			free(authorized_principals);
210
			authorized_principals = xmalloc(strlen(opts) + 1);
211
			i = 0;
212
			while (*opts) {
213
				if (*opts == '"')
214
					break;
215
				if (*opts == '\\' && opts[1] == '"') {
216
					opts += 2;
217
					authorized_principals[i++] = '"';
218
					continue;
219
				}
220
				authorized_principals[i++] = *opts++;
221
			}
222
			if (!*opts) {
223
				debug("%.100s, line %lu: missing end quote",
224
				    file, linenum);
225
				auth_debug_add("%.100s, line %lu: missing end quote",
226
				    file, linenum);
227
				free(authorized_principals);
228
				authorized_principals = NULL;
229
				goto bad_option;
230
			}
231
			authorized_principals[i] = '\0';
232
			auth_debug_add("principals: %.900s",
233
			    authorized_principals);
234
			opts++;
235
			goto next_option;
236
		}
237
		cp = "environment=\"";
238
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
239
			char *s;
240
			struct envstring *new_envstring;
241
242
			opts += strlen(cp);
243
			s = xmalloc(strlen(opts) + 1);
244
			i = 0;
245
			while (*opts) {
246
				if (*opts == '"')
247
					break;
248
				if (*opts == '\\' && opts[1] == '"') {
249
					opts += 2;
250
					s[i++] = '"';
251
					continue;
252
				}
253
				s[i++] = *opts++;
254
			}
255
			if (!*opts) {
256
				debug("%.100s, line %lu: missing end quote",
257
				    file, linenum);
258
				auth_debug_add("%.100s, line %lu: missing end quote",
259
				    file, linenum);
260
				free(s);
261
				goto bad_option;
262
			}
263
			s[i] = '\0';
264
			opts++;
265
			if (options.permit_user_env) {
266
				auth_debug_add("Adding to environment: "
267
				    "%.900s", s);
268
				debug("Adding to environment: %.900s", s);
269
				new_envstring = xcalloc(1,
270
				    sizeof(*new_envstring));
271
				new_envstring->s = s;
272
				new_envstring->next = custom_environment;
273
				custom_environment = new_envstring;
274
				s = NULL;
275
			}
276
			free(s);
277
			goto next_option;
278
		}
279
		cp = "from=\"";
280
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
281
			const char *remote_ip = ssh_remote_ipaddr(ssh);
282
			const char *remote_host = auth_get_canonical_hostname(
283
			    ssh, options.use_dns);
284
			char *patterns = xmalloc(strlen(opts) + 1);
285
286
			opts += strlen(cp);
287
			i = 0;
288
			while (*opts) {
289
				if (*opts == '"')
290
					break;
291
				if (*opts == '\\' && opts[1] == '"') {
292
					opts += 2;
293
					patterns[i++] = '"';
294
					continue;
295
				}
296
				patterns[i++] = *opts++;
297
			}
298
			if (!*opts) {
299
				debug("%.100s, line %lu: missing end quote",
300
				    file, linenum);
301
				auth_debug_add("%.100s, line %lu: missing end quote",
302
				    file, linenum);
303
				free(patterns);
304
				goto bad_option;
305
			}
306
			patterns[i] = '\0';
307
			opts++;
308
			switch (match_host_and_ip(remote_host, remote_ip,
309
			    patterns)) {
310
			case 1:
311
				free(patterns);
312
				/* Host name matches. */
313
				goto next_option;
314
			case -1:
315
				debug("%.100s, line %lu: invalid criteria",
316
				    file, linenum);
317
				auth_debug_add("%.100s, line %lu: "
318
				    "invalid criteria", file, linenum);
319
				/* FALLTHROUGH */
320
			case 0:
321
				free(patterns);
322
				logit("Authentication tried for %.100s with "
323
				    "correct key but not from a permitted "
324
				    "host (host=%.200s, ip=%.200s).",
325
				    pw->pw_name, remote_host, remote_ip);
326
				auth_debug_add("Your host '%.200s' is not "
327
				    "permitted to use this key for login.",
328
				    remote_host);
329
				break;
330
			}
331
			/* deny access */
332
			return 0;
333
		}
334
		cp = "permitopen=\"";
335
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
336
			char *host, *p;
337
			int port;
338
			char *patterns = xmalloc(strlen(opts) + 1);
339
340
			opts += strlen(cp);
341
			i = 0;
342
			while (*opts) {
343
				if (*opts == '"')
344
					break;
345
				if (*opts == '\\' && opts[1] == '"') {
346
					opts += 2;
347
					patterns[i++] = '"';
348
					continue;
349
				}
350
				patterns[i++] = *opts++;
351
			}
352
			if (!*opts) {
353
				debug("%.100s, line %lu: missing end quote",
354
				    file, linenum);
355
				auth_debug_add("%.100s, line %lu: missing "
356
				    "end quote", file, linenum);
357
				free(patterns);
358
				goto bad_option;
359
			}
360
			patterns[i] = '\0';
361
			opts++;
362
			p = patterns;
363
			/* XXX - add streamlocal support */
364
			host = hpdelim(&p);
365
			if (host == NULL || strlen(host) >= NI_MAXHOST) {
366
				debug("%.100s, line %lu: Bad permitopen "
367
				    "specification <%.100s>", file, linenum,
368
				    patterns);
369
				auth_debug_add("%.100s, line %lu: "
370
				    "Bad permitopen specification", file,
371
				    linenum);
372
				free(patterns);
373
				goto bad_option;
374
			}
375
			host = cleanhostname(host);
376
			if (p == NULL || (port = permitopen_port(p)) < 0) {
377
				debug("%.100s, line %lu: Bad permitopen port "
378
				    "<%.100s>", file, linenum, p ? p : "");
379
				auth_debug_add("%.100s, line %lu: "
380
				    "Bad permitopen port", file, linenum);
381
				free(patterns);
382
				goto bad_option;
383
			}
384
			if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
385
				channel_add_permitted_opens(ssh, host, port);
386
			free(patterns);
387
			goto next_option;
388
		}
389
		cp = "tunnel=\"";
390
		if (strncasecmp(opts, cp, strlen(cp)) == 0) {
391
			char *tun = NULL;
392
			opts += strlen(cp);
393
			tun = xmalloc(strlen(opts) + 1);
394
			i = 0;
395
			while (*opts) {
396
				if (*opts == '"')
397
					break;
398
				tun[i++] = *opts++;
399
			}
400
			if (!*opts) {
401
				debug("%.100s, line %lu: missing end quote",
402
				    file, linenum);
403
				auth_debug_add("%.100s, line %lu: missing end quote",
404
				    file, linenum);
405
				free(tun);
406
				forced_tun_device = -1;
407
				goto bad_option;
408
			}
409
			tun[i] = '\0';
410
			forced_tun_device = a2tun(tun, NULL);
411
			free(tun);
412
			if (forced_tun_device == SSH_TUNID_ERR) {
413
				debug("%.100s, line %lu: invalid tun device",
414
				    file, linenum);
415
				auth_debug_add("%.100s, line %lu: invalid tun device",
416
				    file, linenum);
417
				forced_tun_device = -1;
418
				goto bad_option;
419
			}
420
			auth_debug_add("Forced tun device: %d", forced_tun_device);
421
			opts++;
422
			goto next_option;
423
		}
424
next_option:
425
		/*
426
		 * Skip the comma, and move to the next option
427
		 * (or break out if there are no more).
428
		 */
429
		if (!*opts)
430
			fatal("Bugs in auth-options.c option processing.");
431
		if (*opts == ' ' || *opts == '\t')
432
			break;		/* End of options. */
433
		if (*opts != ',')
434
			goto bad_option;
435
		opts++;
436
		/* Process the next option. */
437
	}
438
439
	/* grant access */
440
	return 1;
441
442
bad_option:
443
	logit("Bad options in %.100s file, line %lu: %.50s",
444
	    file, linenum, opts);
445
	auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
446
	    file, linenum, opts);
447
448
	/* deny access */
449
	return 0;
450
}
451
452
#define OPTIONS_CRITICAL	1
453
#define OPTIONS_EXTENSIONS	2
454
static int
455
parse_option_list(struct sshbuf *oblob, struct passwd *pw,
456
    u_int which, int crit,
457
    int *cert_no_port_forwarding_flag,
458
    int *cert_no_agent_forwarding_flag,
459
    int *cert_no_x11_forwarding_flag,
460
    int *cert_no_pty_flag,
461
    int *cert_no_user_rc,
462
    char **cert_forced_command,
463
    int *cert_source_address_done)
464
{
465
	struct ssh *ssh = active_state;		/* XXX */
466
	char *command, *allowed;
467
	const char *remote_ip;
468
	char *name = NULL;
469
	struct sshbuf *c = NULL, *data = NULL;
470
	int r, ret = -1, result, found;
471
472
	if ((c = sshbuf_fromb(oblob)) == NULL) {
473
		error("%s: sshbuf_fromb failed", __func__);
474
		goto out;
475
	}
476
477
	while (sshbuf_len(c) > 0) {
478
		sshbuf_free(data);
479
		data = NULL;
480
		if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 ||
481
		    (r = sshbuf_froms(c, &data)) != 0) {
482
			error("Unable to parse certificate options: %s",
483
			    ssh_err(r));
484
			goto out;
485
		}
486
		debug3("found certificate option \"%.100s\" len %zu",
487
		    name, sshbuf_len(data));
488
		found = 0;
489
		if ((which & OPTIONS_EXTENSIONS) != 0) {
490
			if (strcmp(name, "permit-X11-forwarding") == 0) {
491
				*cert_no_x11_forwarding_flag = 0;
492
				found = 1;
493
			} else if (strcmp(name,
494
			    "permit-agent-forwarding") == 0) {
495
				*cert_no_agent_forwarding_flag = 0;
496
				found = 1;
497
			} else if (strcmp(name,
498
			    "permit-port-forwarding") == 0) {
499
				*cert_no_port_forwarding_flag = 0;
500
				found = 1;
501
			} else if (strcmp(name, "permit-pty") == 0) {
502
				*cert_no_pty_flag = 0;
503
				found = 1;
504
			} else if (strcmp(name, "permit-user-rc") == 0) {
505
				*cert_no_user_rc = 0;
506
				found = 1;
507
			}
508
		}
509
		if (!found && (which & OPTIONS_CRITICAL) != 0) {
510
			if (strcmp(name, "force-command") == 0) {
511
				if ((r = sshbuf_get_cstring(data, &command,
512
				    NULL)) != 0) {
513
					error("Unable to parse \"%s\" "
514
					    "section: %s", name, ssh_err(r));
515
					goto out;
516
				}
517
				if (*cert_forced_command != NULL) {
518
					error("Certificate has multiple "
519
					    "force-command options");
520
					free(command);
521
					goto out;
522
				}
523
				*cert_forced_command = command;
524
				found = 1;
525
			}
526
			if (strcmp(name, "source-address") == 0) {
527
				if ((r = sshbuf_get_cstring(data, &allowed,
528
				    NULL)) != 0) {
529
					error("Unable to parse \"%s\" "
530
					    "section: %s", name, ssh_err(r));
531
					goto out;
532
				}
533
				if ((*cert_source_address_done)++) {
534
					error("Certificate has multiple "
535
					    "source-address options");
536
					free(allowed);
537
					goto out;
538
				}
539
				remote_ip = ssh_remote_ipaddr(ssh);
540
				result = addr_match_cidr_list(remote_ip,
541
				    allowed);
542
				free(allowed);
543
				switch (result) {
544
				case 1:
545
					/* accepted */
546
					break;
547
				case 0:
548
					/* no match */
549
					logit("Authentication tried for %.100s "
550
					    "with valid certificate but not "
551
					    "from a permitted host "
552
					    "(ip=%.200s).", pw->pw_name,
553
					    remote_ip);
554
					auth_debug_add("Your address '%.200s' "
555
					    "is not permitted to use this "
556
					    "certificate for login.",
557
					    remote_ip);
558
					goto out;
559
				case -1:
560
				default:
561
					error("Certificate source-address "
562
					    "contents invalid");
563
					goto out;
564
				}
565
				found = 1;
566
			}
567
		}
568
569
		if (!found) {
570
			if (crit) {
571
				error("Certificate critical option \"%s\" "
572
				    "is not supported", name);
573
				goto out;
574
			} else {
575
				logit("Certificate extension \"%s\" "
576
				    "is not supported", name);
577
			}
578
		} else if (sshbuf_len(data) != 0) {
579
			error("Certificate option \"%s\" corrupt "
580
			    "(extra data)", name);
581
			goto out;
582
		}
583
		free(name);
584
		name = NULL;
585
	}
586
	/* successfully parsed all options */
587
	ret = 0;
588
589
 out:
590
	if (ret != 0 &&
591
	    cert_forced_command != NULL &&
592
	    *cert_forced_command != NULL) {
593
		free(*cert_forced_command);
594
		*cert_forced_command = NULL;
595
	}
596
	free(name);
597
	sshbuf_free(data);
598
	sshbuf_free(c);
599
	return ret;
600
}
601
602
/*
603
 * Set options from critical certificate options. These supersede user key
604
 * options so this must be called after auth_parse_options().
605
 */
606
int
607
auth_cert_options(struct sshkey *k, struct passwd *pw, const char **reason)
608
{
609
	int cert_no_port_forwarding_flag = 1;
610
	int cert_no_agent_forwarding_flag = 1;
611
	int cert_no_x11_forwarding_flag = 1;
612
	int cert_no_pty_flag = 1;
613
	int cert_no_user_rc = 1;
614
	char *cert_forced_command = NULL;
615
	int cert_source_address_done = 0;
616
617
	*reason = "invalid certificate options";
618
619
	/* Separate options and extensions for v01 certs */
620
	if (parse_option_list(k->cert->critical, pw,
621
	    OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
622
	    &cert_forced_command,
623
	    &cert_source_address_done) == -1)
624
		return -1;
625
	if (parse_option_list(k->cert->extensions, pw,
626
	    OPTIONS_EXTENSIONS, 0,
627
	    &cert_no_port_forwarding_flag,
628
	    &cert_no_agent_forwarding_flag,
629
	    &cert_no_x11_forwarding_flag,
630
	    &cert_no_pty_flag,
631
	    &cert_no_user_rc,
632
	    NULL, NULL) == -1)
633
		return -1;
634
635
	no_port_forwarding_flag |= cert_no_port_forwarding_flag;
636
	no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
637
	no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
638
	no_pty_flag |= cert_no_pty_flag;
639
	no_user_rc |= cert_no_user_rc;
640
	/*
641
	 * Only permit both CA and key option forced-command if they match.
642
	 * Otherwise refuse the certificate.
643
	 */
644
	if (cert_forced_command != NULL && forced_command != NULL) {
645
		if (strcmp(forced_command, cert_forced_command) == 0) {
646
			free(forced_command);
647
			forced_command = cert_forced_command;
648
		} else {
649
			*reason = "certificate and key options forced command "
650
			    "do not match";
651
			free(cert_forced_command);
652
			return -1;
653
		}
654
	} else if (cert_forced_command != NULL)
655
		forced_command = cert_forced_command;
656
	/* success */
657
	*reason = NULL;
658
	return 0;
659
}
660