Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.57
1.57 ! djm 1: /* $OpenBSD: auth-options.c,v 1.56 2011/10/18 04:58:26 djm Exp $ */
1.3 deraadt 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:
1.36 stevesk 13: #include <sys/types.h>
1.42 djm 14: #include <sys/queue.h>
1.36 stevesk 15:
1.37 stevesk 16: #include <netdb.h>
1.36 stevesk 17: #include <pwd.h>
1.39 stevesk 18: #include <string.h>
1.40 deraadt 19: #include <stdio.h>
20: #include <stdarg.h>
1.1 markus 21:
22: #include "xmalloc.h"
23: #include "match.h"
1.11 markus 24: #include "log.h"
25: #include "canohost.h"
1.40 deraadt 26: #include "buffer.h"
1.18 markus 27: #include "channels.h"
1.12 markus 28: #include "servconf.h"
1.20 stevesk 29: #include "misc.h"
1.40 deraadt 30: #include "key.h"
1.50 djm 31: #include "auth-options.h"
1.40 deraadt 32: #include "hostfile.h"
33: #include "auth.h"
34: #ifdef GSSAPI
35: #include "ssh-gss.h"
36: #endif
1.22 provos 37: #include "monitor_wrap.h"
1.1 markus 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;
1.41 djm 44: int no_user_rc = 0;
1.45 djm 45: int key_is_cert_authority = 0;
1.1 markus 46:
47: /* "command=" option. */
48: char *forced_command = NULL;
49:
50: /* "environment=" options. */
51: struct envstring *custom_environment = NULL;
52:
1.32 reyk 53: /* "tunnel=" option. */
54: int forced_tun_device = -1;
55:
1.51 djm 56: /* "principals=" option. */
57: char *authorized_principals = NULL;
58:
1.12 markus 59: extern ServerOptions options;
60:
1.22 provos 61: void
1.5 markus 62: auth_clear_options(void)
63: {
64: no_agent_forwarding_flag = 0;
65: no_port_forwarding_flag = 0;
66: no_pty_flag = 0;
67: no_x11_forwarding_flag = 0;
1.41 djm 68: no_user_rc = 0;
1.45 djm 69: key_is_cert_authority = 0;
1.5 markus 70: while (custom_environment) {
71: struct envstring *ce = custom_environment;
72: custom_environment = ce->next;
73: xfree(ce->s);
74: xfree(ce);
75: }
76: if (forced_command) {
77: xfree(forced_command);
78: forced_command = NULL;
79: }
1.51 djm 80: if (authorized_principals) {
81: xfree(authorized_principals);
82: authorized_principals = NULL;
83: }
1.32 reyk 84: forced_tun_device = -1;
1.15 markus 85: channel_clear_permitted_opens();
1.5 markus 86: }
87:
1.10 markus 88: /*
89: * return 1 if access is granted, 0 if not.
90: * side effect: sets key option flags
91: */
1.1 markus 92: int
1.12 markus 93: auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
1.1 markus 94: {
95: const char *cp;
1.15 markus 96: int i;
1.5 markus 97:
98: /* reset options */
99: auth_clear_options();
1.13 markus 100:
101: if (!opts)
102: return 1;
1.5 markus 103:
1.12 markus 104: while (*opts && *opts != ' ' && *opts != '\t') {
1.45 djm 105: cp = "cert-authority";
106: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
107: key_is_cert_authority = 1;
108: opts += strlen(cp);
109: goto next_option;
110: }
1.1 markus 111: cp = "no-port-forwarding";
1.12 markus 112: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 113: auth_debug_add("Port forwarding disabled.");
1.1 markus 114: no_port_forwarding_flag = 1;
1.12 markus 115: opts += strlen(cp);
1.1 markus 116: goto next_option;
117: }
118: cp = "no-agent-forwarding";
1.12 markus 119: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 120: auth_debug_add("Agent forwarding disabled.");
1.1 markus 121: no_agent_forwarding_flag = 1;
1.12 markus 122: opts += strlen(cp);
1.1 markus 123: goto next_option;
124: }
125: cp = "no-X11-forwarding";
1.12 markus 126: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 127: auth_debug_add("X11 forwarding disabled.");
1.1 markus 128: no_x11_forwarding_flag = 1;
1.12 markus 129: opts += strlen(cp);
1.1 markus 130: goto next_option;
131: }
132: cp = "no-pty";
1.12 markus 133: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 134: auth_debug_add("Pty allocation disabled.");
1.1 markus 135: no_pty_flag = 1;
1.41 djm 136: opts += strlen(cp);
137: goto next_option;
138: }
139: cp = "no-user-rc";
140: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
141: auth_debug_add("User rc file execution disabled.");
142: no_user_rc = 1;
1.12 markus 143: opts += strlen(cp);
1.1 markus 144: goto next_option;
145: }
146: cp = "command=\"";
1.12 markus 147: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
148: opts += strlen(cp);
1.51 djm 149: if (forced_command != NULL)
150: xfree(forced_command);
1.12 markus 151: forced_command = xmalloc(strlen(opts) + 1);
1.1 markus 152: i = 0;
1.12 markus 153: while (*opts) {
154: if (*opts == '"')
1.1 markus 155: break;
1.12 markus 156: if (*opts == '\\' && opts[1] == '"') {
157: opts += 2;
1.1 markus 158: forced_command[i++] = '"';
159: continue;
160: }
1.12 markus 161: forced_command[i++] = *opts++;
1.1 markus 162: }
1.12 markus 163: if (!*opts) {
1.1 markus 164: debug("%.100s, line %lu: missing end quote",
1.10 markus 165: file, linenum);
1.24 markus 166: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 167: file, linenum);
1.14 markus 168: xfree(forced_command);
169: forced_command = NULL;
170: goto bad_option;
1.1 markus 171: }
1.38 dtucker 172: forced_command[i] = '\0';
1.54 djm 173: auth_debug_add("Forced command.");
1.51 djm 174: opts++;
175: goto next_option;
176: }
177: cp = "principals=\"";
178: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
179: opts += strlen(cp);
180: if (authorized_principals != NULL)
181: xfree(authorized_principals);
182: authorized_principals = xmalloc(strlen(opts) + 1);
183: i = 0;
184: while (*opts) {
185: if (*opts == '"')
186: break;
187: if (*opts == '\\' && opts[1] == '"') {
188: opts += 2;
189: authorized_principals[i++] = '"';
190: continue;
191: }
192: authorized_principals[i++] = *opts++;
193: }
194: if (!*opts) {
195: debug("%.100s, line %lu: missing end quote",
196: file, linenum);
197: auth_debug_add("%.100s, line %lu: missing end quote",
198: file, linenum);
199: xfree(authorized_principals);
200: authorized_principals = NULL;
201: goto bad_option;
202: }
203: authorized_principals[i] = '\0';
204: auth_debug_add("principals: %.900s",
205: authorized_principals);
1.12 markus 206: opts++;
1.1 markus 207: goto next_option;
208: }
209: cp = "environment=\"";
1.26 markus 210: if (options.permit_user_env &&
211: strncasecmp(opts, cp, strlen(cp)) == 0) {
1.1 markus 212: char *s;
213: struct envstring *new_envstring;
1.15 markus 214:
1.12 markus 215: opts += strlen(cp);
216: s = xmalloc(strlen(opts) + 1);
1.1 markus 217: i = 0;
1.12 markus 218: while (*opts) {
219: if (*opts == '"')
1.1 markus 220: break;
1.12 markus 221: if (*opts == '\\' && opts[1] == '"') {
222: opts += 2;
1.1 markus 223: s[i++] = '"';
224: continue;
225: }
1.12 markus 226: s[i++] = *opts++;
1.1 markus 227: }
1.12 markus 228: if (!*opts) {
1.1 markus 229: debug("%.100s, line %lu: missing end quote",
1.10 markus 230: file, linenum);
1.24 markus 231: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 232: file, linenum);
1.14 markus 233: xfree(s);
234: goto bad_option;
1.1 markus 235: }
1.38 dtucker 236: s[i] = '\0';
1.24 markus 237: auth_debug_add("Adding to environment: %.900s", s);
1.1 markus 238: debug("Adding to environment: %.900s", s);
1.12 markus 239: opts++;
1.1 markus 240: new_envstring = xmalloc(sizeof(struct envstring));
241: new_envstring->s = s;
242: new_envstring->next = custom_environment;
243: custom_environment = new_envstring;
244: goto next_option;
245: }
246: cp = "from=\"";
1.12 markus 247: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
248: const char *remote_ip = get_remote_ipaddr();
249: const char *remote_host = get_canonical_hostname(
1.28 markus 250: options.use_dns);
1.12 markus 251: char *patterns = xmalloc(strlen(opts) + 1);
1.15 markus 252:
1.12 markus 253: opts += strlen(cp);
1.1 markus 254: i = 0;
1.12 markus 255: while (*opts) {
256: if (*opts == '"')
1.1 markus 257: break;
1.12 markus 258: if (*opts == '\\' && opts[1] == '"') {
259: opts += 2;
1.1 markus 260: patterns[i++] = '"';
261: continue;
262: }
1.12 markus 263: patterns[i++] = *opts++;
1.1 markus 264: }
1.12 markus 265: if (!*opts) {
1.1 markus 266: debug("%.100s, line %lu: missing end quote",
1.10 markus 267: file, linenum);
1.24 markus 268: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 269: file, linenum);
1.14 markus 270: xfree(patterns);
271: goto bad_option;
1.1 markus 272: }
1.38 dtucker 273: patterns[i] = '\0';
1.12 markus 274: opts++;
1.43 djm 275: switch (match_host_and_ip(remote_host, remote_ip,
276: patterns)) {
277: case 1:
278: xfree(patterns);
279: /* Host name matches. */
280: goto next_option;
281: case -1:
282: debug("%.100s, line %lu: invalid criteria",
283: file, linenum);
284: auth_debug_add("%.100s, line %lu: "
285: "invalid criteria", file, linenum);
286: /* FALLTHROUGH */
287: case 0:
1.19 markus 288: xfree(patterns);
1.27 itojun 289: logit("Authentication tried for %.100s with "
1.12 markus 290: "correct key but not from a permitted "
291: "host (host=%.200s, ip=%.200s).",
292: pw->pw_name, remote_host, remote_ip);
1.24 markus 293: auth_debug_add("Your host '%.200s' is not "
1.12 markus 294: "permitted to use this key for login.",
295: remote_host);
1.43 djm 296: break;
1.1 markus 297: }
1.43 djm 298: /* deny access */
299: return 0;
1.15 markus 300: }
301: cp = "permitopen=\"";
302: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.29 djm 303: char *host, *p;
1.44 djm 304: int port;
1.15 markus 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);
1.29 djm 322: auth_debug_add("%.100s, line %lu: missing "
323: "end quote", file, linenum);
1.15 markus 324: xfree(patterns);
325: goto bad_option;
326: }
1.38 dtucker 327: patterns[i] = '\0';
1.15 markus 328: opts++;
1.29 djm 329: p = patterns;
330: host = hpdelim(&p);
331: if (host == NULL || strlen(host) >= NI_MAXHOST) {
332: debug("%.100s, line %lu: Bad permitopen "
1.31 deraadt 333: "specification <%.100s>", file, linenum,
1.29 djm 334: patterns);
1.24 markus 335: auth_debug_add("%.100s, line %lu: "
1.29 djm 336: "Bad permitopen specification", file,
337: linenum);
1.15 markus 338: xfree(patterns);
339: goto bad_option;
340: }
1.30 deraadt 341: host = cleanhostname(host);
1.55 dtucker 342: if (p == NULL || (port = permitopen_port(p)) < 0) {
1.29 djm 343: debug("%.100s, line %lu: Bad permitopen port "
344: "<%.100s>", file, linenum, p ? p : "");
1.24 markus 345: auth_debug_add("%.100s, line %lu: "
1.20 stevesk 346: "Bad permitopen port", file, linenum);
1.15 markus 347: xfree(patterns);
348: goto bad_option;
349: }
1.57 ! djm 350: if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0)
1.20 stevesk 351: channel_add_permitted_opens(host, port);
1.15 markus 352: xfree(patterns);
1.32 reyk 353: goto next_option;
354: }
355: cp = "tunnel=\"";
356: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
357: char *tun = NULL;
358: opts += strlen(cp);
359: tun = xmalloc(strlen(opts) + 1);
360: i = 0;
361: while (*opts) {
362: if (*opts == '"')
363: break;
364: tun[i++] = *opts++;
365: }
366: if (!*opts) {
367: debug("%.100s, line %lu: missing end quote",
368: file, linenum);
369: auth_debug_add("%.100s, line %lu: missing end quote",
370: file, linenum);
371: xfree(tun);
372: forced_tun_device = -1;
373: goto bad_option;
374: }
1.38 dtucker 375: tun[i] = '\0';
1.32 reyk 376: forced_tun_device = a2tun(tun, NULL);
377: xfree(tun);
1.33 reyk 378: if (forced_tun_device == SSH_TUNID_ERR) {
1.32 reyk 379: debug("%.100s, line %lu: invalid tun device",
380: file, linenum);
381: auth_debug_add("%.100s, line %lu: invalid tun device",
382: file, linenum);
383: forced_tun_device = -1;
384: goto bad_option;
385: }
386: auth_debug_add("Forced tun device: %d", forced_tun_device);
387: opts++;
1.1 markus 388: goto next_option;
389: }
390: next_option:
391: /*
392: * Skip the comma, and move to the next option
393: * (or break out if there are no more).
394: */
1.12 markus 395: if (!*opts)
1.1 markus 396: fatal("Bugs in auth-options.c option processing.");
1.12 markus 397: if (*opts == ' ' || *opts == '\t')
1.1 markus 398: break; /* End of options. */
1.12 markus 399: if (*opts != ',')
1.1 markus 400: goto bad_option;
1.12 markus 401: opts++;
1.1 markus 402: /* Process the next option. */
403: }
1.22 provos 404:
1.1 markus 405: /* grant access */
406: return 1;
407:
408: bad_option:
1.27 itojun 409: logit("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 410: file, linenum, opts);
1.24 markus 411: auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 412: file, linenum, opts);
1.22 provos 413:
1.1 markus 414: /* deny access */
415: return 0;
416: }
1.45 djm 417:
1.52 djm 418: #define OPTIONS_CRITICAL 1
419: #define OPTIONS_EXTENSIONS 2
420: static int
421: parse_option_list(u_char *optblob, size_t optblob_len, struct passwd *pw,
422: u_int which, int crit,
423: int *cert_no_port_forwarding_flag,
424: int *cert_no_agent_forwarding_flag,
425: int *cert_no_x11_forwarding_flag,
426: int *cert_no_pty_flag,
427: int *cert_no_user_rc,
428: char **cert_forced_command,
429: int *cert_source_address_done)
1.45 djm 430: {
1.52 djm 431: char *command, *allowed;
432: const char *remote_ip;
1.45 djm 433: u_char *name = NULL, *data_blob = NULL;
1.46 djm 434: u_int nlen, dlen, clen;
1.45 djm 435: Buffer c, data;
1.52 djm 436: int ret = -1, found;
1.45 djm 437:
438: buffer_init(&data);
439:
440: /* Make copy to avoid altering original */
441: buffer_init(&c);
1.52 djm 442: buffer_append(&c, optblob, optblob_len);
1.45 djm 443:
444: while (buffer_len(&c) > 0) {
1.53 djm 445: if ((name = buffer_get_cstring_ret(&c, &nlen)) == NULL ||
1.46 djm 446: (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
1.50 djm 447: error("Certificate options corrupt");
1.45 djm 448: goto out;
449: }
1.46 djm 450: buffer_append(&data, data_blob, dlen);
1.52 djm 451: debug3("found certificate option \"%.100s\" len %u",
1.46 djm 452: name, dlen);
1.52 djm 453: found = 0;
454: if ((which & OPTIONS_EXTENSIONS) != 0) {
455: if (strcmp(name, "permit-X11-forwarding") == 0) {
456: *cert_no_x11_forwarding_flag = 0;
457: found = 1;
458: } else if (strcmp(name,
459: "permit-agent-forwarding") == 0) {
460: *cert_no_agent_forwarding_flag = 0;
461: found = 1;
462: } else if (strcmp(name,
463: "permit-port-forwarding") == 0) {
464: *cert_no_port_forwarding_flag = 0;
465: found = 1;
466: } else if (strcmp(name, "permit-pty") == 0) {
467: *cert_no_pty_flag = 0;
468: found = 1;
469: } else if (strcmp(name, "permit-user-rc") == 0) {
470: *cert_no_user_rc = 0;
471: found = 1;
472: }
473: }
474: if (!found && (which & OPTIONS_CRITICAL) != 0) {
475: if (strcmp(name, "force-command") == 0) {
1.53 djm 476: if ((command = buffer_get_cstring_ret(&data,
1.52 djm 477: &clen)) == NULL) {
478: error("Certificate constraint \"%s\" "
479: "corrupt", name);
480: goto out;
481: }
482: if (*cert_forced_command != NULL) {
483: error("Certificate has multiple "
484: "force-command options");
485: xfree(command);
486: goto out;
487: }
488: *cert_forced_command = command;
489: found = 1;
1.45 djm 490: }
1.52 djm 491: if (strcmp(name, "source-address") == 0) {
1.53 djm 492: if ((allowed = buffer_get_cstring_ret(&data,
1.52 djm 493: &clen)) == NULL) {
494: error("Certificate constraint "
495: "\"%s\" corrupt", name);
496: goto out;
497: }
498: if ((*cert_source_address_done)++) {
499: error("Certificate has multiple "
500: "source-address options");
501: xfree(allowed);
502: goto out;
503: }
504: remote_ip = get_remote_ipaddr();
505: switch (addr_match_cidr_list(remote_ip,
506: allowed)) {
507: case 1:
508: /* accepted */
509: xfree(allowed);
510: break;
511: case 0:
512: /* no match */
513: logit("Authentication tried for %.100s "
514: "with valid certificate but not "
515: "from a permitted host "
516: "(ip=%.200s).", pw->pw_name,
517: remote_ip);
518: auth_debug_add("Your address '%.200s' "
519: "is not permitted to use this "
520: "certificate for login.",
521: remote_ip);
522: xfree(allowed);
523: goto out;
524: case -1:
525: error("Certificate source-address "
526: "contents invalid");
527: xfree(allowed);
528: goto out;
529: }
530: found = 1;
1.46 djm 531: }
1.52 djm 532: }
533:
534: if (!found) {
535: if (crit) {
536: error("Certificate critical option \"%s\" "
537: "is not supported", name);
1.45 djm 538: goto out;
1.52 djm 539: } else {
540: logit("Certificate extension \"%s\" "
541: "is not supported", name);
1.45 djm 542: }
1.52 djm 543: } else if (buffer_len(&data) != 0) {
544: error("Certificate option \"%s\" corrupt "
1.45 djm 545: "(extra data)", name);
546: goto out;
547: }
548: buffer_clear(&data);
549: xfree(name);
550: xfree(data_blob);
551: name = data_blob = NULL;
552: }
1.50 djm 553: /* successfully parsed all options */
1.45 djm 554: ret = 0;
555:
1.52 djm 556: out:
557: if (ret != 0 &&
558: cert_forced_command != NULL &&
559: *cert_forced_command != NULL) {
560: xfree(*cert_forced_command);
561: *cert_forced_command = NULL;
562: }
563: if (name != NULL)
564: xfree(name);
565: if (data_blob != NULL)
566: xfree(data_blob);
567: buffer_free(&data);
568: buffer_free(&c);
569: return ret;
570: }
571:
572: /*
573: * Set options from critical certificate options. These supersede user key
574: * options so this must be called after auth_parse_options().
575: */
576: int
577: auth_cert_options(Key *k, struct passwd *pw)
578: {
579: int cert_no_port_forwarding_flag = 1;
580: int cert_no_agent_forwarding_flag = 1;
581: int cert_no_x11_forwarding_flag = 1;
582: int cert_no_pty_flag = 1;
583: int cert_no_user_rc = 1;
584: char *cert_forced_command = NULL;
585: int cert_source_address_done = 0;
586:
587: if (key_cert_is_legacy(k)) {
588: /* All options are in the one field for v00 certs */
589: if (parse_option_list(buffer_ptr(&k->cert->critical),
590: buffer_len(&k->cert->critical), pw,
591: OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
592: &cert_no_port_forwarding_flag,
593: &cert_no_agent_forwarding_flag,
594: &cert_no_x11_forwarding_flag,
595: &cert_no_pty_flag,
596: &cert_no_user_rc,
597: &cert_forced_command,
598: &cert_source_address_done) == -1)
599: return -1;
600: } else {
601: /* Separate options and extensions for v01 certs */
602: if (parse_option_list(buffer_ptr(&k->cert->critical),
603: buffer_len(&k->cert->critical), pw,
604: OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
605: &cert_forced_command,
606: &cert_source_address_done) == -1)
607: return -1;
608: if (parse_option_list(buffer_ptr(&k->cert->extensions),
609: buffer_len(&k->cert->extensions), pw,
610: OPTIONS_EXTENSIONS, 1,
611: &cert_no_port_forwarding_flag,
612: &cert_no_agent_forwarding_flag,
613: &cert_no_x11_forwarding_flag,
614: &cert_no_pty_flag,
615: &cert_no_user_rc,
616: NULL, NULL) == -1)
617: return -1;
618: }
619:
1.45 djm 620: no_port_forwarding_flag |= cert_no_port_forwarding_flag;
621: no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
622: no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
623: no_pty_flag |= cert_no_pty_flag;
624: no_user_rc |= cert_no_user_rc;
625: /* CA-specified forced command supersedes key option */
626: if (cert_forced_command != NULL) {
627: if (forced_command != NULL)
628: xfree(forced_command);
629: forced_command = cert_forced_command;
630: }
1.52 djm 631: return 0;
1.45 djm 632: }
633: