diff options
Diffstat (limited to 'auth-options.c')
-rw-r--r-- | auth-options.c | 650 |
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 */ | ||
61 | int no_port_forwarding_flag = 0; | ||
62 | int no_agent_forwarding_flag = 0; | ||
63 | int no_x11_forwarding_flag = 0; | ||
64 | int no_pty_flag = 0; | ||
65 | int no_user_rc = 0; | ||
66 | int key_is_cert_authority = 0; | ||
67 | |||
68 | /* "command=" option. */ | ||
69 | char *forced_command = NULL; | ||
70 | |||
71 | /* "environment=" options. */ | ||
72 | struct envstring *custom_environment = NULL; | ||
73 | |||
74 | /* "tunnel=" option. */ | ||
75 | int forced_tun_device = -1; | ||
76 | |||
77 | /* "principals=" option. */ | ||
78 | char *authorized_principals = NULL; | ||
79 | |||
80 | extern ServerOptions options; | ||
81 | |||
82 | /* XXX refactor to be stateless */ | ||
83 | |||
84 | void | ||
85 | auth_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 | */ | ||
117 | static int | ||
118 | match_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 | */ | ||
144 | int | ||
145 | auth_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 | } | ||
445 | next_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 | |||
463 | bad_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 | ||
475 | static int | ||
476 | parse_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 | */ | ||
627 | int | ||
628 | auth_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 |