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