Annotation of src/usr.bin/ssh/auth-options.c, Revision 1.48
1.48 ! dtucker 1: /* $OpenBSD: auth-options.c,v 1.47 2010/03/04 23:27:25 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.11 markus 28: #include "auth-options.h"
1.12 markus 29: #include "servconf.h"
1.20 stevesk 30: #include "misc.h"
1.40 deraadt 31: #include "key.h"
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.12 markus 56: extern ServerOptions options;
57:
1.22 provos 58: void
1.5 markus 59: auth_clear_options(void)
60: {
61: no_agent_forwarding_flag = 0;
62: no_port_forwarding_flag = 0;
63: no_pty_flag = 0;
64: no_x11_forwarding_flag = 0;
1.41 djm 65: no_user_rc = 0;
1.45 djm 66: key_is_cert_authority = 0;
1.5 markus 67: while (custom_environment) {
68: struct envstring *ce = custom_environment;
69: custom_environment = ce->next;
70: xfree(ce->s);
71: xfree(ce);
72: }
73: if (forced_command) {
74: xfree(forced_command);
75: forced_command = NULL;
76: }
1.32 reyk 77: forced_tun_device = -1;
1.15 markus 78: channel_clear_permitted_opens();
1.5 markus 79: }
80:
1.10 markus 81: /*
82: * return 1 if access is granted, 0 if not.
83: * side effect: sets key option flags
84: */
1.1 markus 85: int
1.12 markus 86: auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum)
1.1 markus 87: {
88: const char *cp;
1.15 markus 89: int i;
1.5 markus 90:
91: /* reset options */
92: auth_clear_options();
1.13 markus 93:
94: if (!opts)
95: return 1;
1.5 markus 96:
1.12 markus 97: while (*opts && *opts != ' ' && *opts != '\t') {
1.45 djm 98: cp = "cert-authority";
99: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
100: key_is_cert_authority = 1;
101: opts += strlen(cp);
102: goto next_option;
103: }
1.1 markus 104: cp = "no-port-forwarding";
1.12 markus 105: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 106: auth_debug_add("Port forwarding disabled.");
1.1 markus 107: no_port_forwarding_flag = 1;
1.12 markus 108: opts += strlen(cp);
1.1 markus 109: goto next_option;
110: }
111: cp = "no-agent-forwarding";
1.12 markus 112: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 113: auth_debug_add("Agent forwarding disabled.");
1.1 markus 114: no_agent_forwarding_flag = 1;
1.12 markus 115: opts += strlen(cp);
1.1 markus 116: goto next_option;
117: }
118: cp = "no-X11-forwarding";
1.12 markus 119: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 120: auth_debug_add("X11 forwarding disabled.");
1.1 markus 121: no_x11_forwarding_flag = 1;
1.12 markus 122: opts += strlen(cp);
1.1 markus 123: goto next_option;
124: }
125: cp = "no-pty";
1.12 markus 126: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.24 markus 127: auth_debug_add("Pty allocation disabled.");
1.1 markus 128: no_pty_flag = 1;
1.41 djm 129: opts += strlen(cp);
130: goto next_option;
131: }
132: cp = "no-user-rc";
133: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
134: auth_debug_add("User rc file execution disabled.");
135: no_user_rc = 1;
1.12 markus 136: opts += strlen(cp);
1.1 markus 137: goto next_option;
138: }
139: cp = "command=\"";
1.12 markus 140: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
141: opts += strlen(cp);
142: forced_command = xmalloc(strlen(opts) + 1);
1.1 markus 143: i = 0;
1.12 markus 144: while (*opts) {
145: if (*opts == '"')
1.1 markus 146: break;
1.12 markus 147: if (*opts == '\\' && opts[1] == '"') {
148: opts += 2;
1.1 markus 149: forced_command[i++] = '"';
150: continue;
151: }
1.12 markus 152: forced_command[i++] = *opts++;
1.1 markus 153: }
1.12 markus 154: if (!*opts) {
1.1 markus 155: debug("%.100s, line %lu: missing end quote",
1.10 markus 156: file, linenum);
1.24 markus 157: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 158: file, linenum);
1.14 markus 159: xfree(forced_command);
160: forced_command = NULL;
161: goto bad_option;
1.1 markus 162: }
1.38 dtucker 163: forced_command[i] = '\0';
1.24 markus 164: auth_debug_add("Forced command: %.900s", forced_command);
1.12 markus 165: opts++;
1.1 markus 166: goto next_option;
167: }
168: cp = "environment=\"";
1.26 markus 169: if (options.permit_user_env &&
170: strncasecmp(opts, cp, strlen(cp)) == 0) {
1.1 markus 171: char *s;
172: struct envstring *new_envstring;
1.15 markus 173:
1.12 markus 174: opts += strlen(cp);
175: s = xmalloc(strlen(opts) + 1);
1.1 markus 176: i = 0;
1.12 markus 177: while (*opts) {
178: if (*opts == '"')
1.1 markus 179: break;
1.12 markus 180: if (*opts == '\\' && opts[1] == '"') {
181: opts += 2;
1.1 markus 182: s[i++] = '"';
183: continue;
184: }
1.12 markus 185: s[i++] = *opts++;
1.1 markus 186: }
1.12 markus 187: if (!*opts) {
1.1 markus 188: debug("%.100s, line %lu: missing end quote",
1.10 markus 189: file, linenum);
1.24 markus 190: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 191: file, linenum);
1.14 markus 192: xfree(s);
193: goto bad_option;
1.1 markus 194: }
1.38 dtucker 195: s[i] = '\0';
1.24 markus 196: auth_debug_add("Adding to environment: %.900s", s);
1.1 markus 197: debug("Adding to environment: %.900s", s);
1.12 markus 198: opts++;
1.1 markus 199: new_envstring = xmalloc(sizeof(struct envstring));
200: new_envstring->s = s;
201: new_envstring->next = custom_environment;
202: custom_environment = new_envstring;
203: goto next_option;
204: }
205: cp = "from=\"";
1.12 markus 206: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
207: const char *remote_ip = get_remote_ipaddr();
208: const char *remote_host = get_canonical_hostname(
1.28 markus 209: options.use_dns);
1.12 markus 210: char *patterns = xmalloc(strlen(opts) + 1);
1.15 markus 211:
1.12 markus 212: opts += strlen(cp);
1.1 markus 213: i = 0;
1.12 markus 214: while (*opts) {
215: if (*opts == '"')
1.1 markus 216: break;
1.12 markus 217: if (*opts == '\\' && opts[1] == '"') {
218: opts += 2;
1.1 markus 219: patterns[i++] = '"';
220: continue;
221: }
1.12 markus 222: patterns[i++] = *opts++;
1.1 markus 223: }
1.12 markus 224: if (!*opts) {
1.1 markus 225: debug("%.100s, line %lu: missing end quote",
1.10 markus 226: file, linenum);
1.24 markus 227: auth_debug_add("%.100s, line %lu: missing end quote",
1.10 markus 228: file, linenum);
1.14 markus 229: xfree(patterns);
230: goto bad_option;
1.1 markus 231: }
1.38 dtucker 232: patterns[i] = '\0';
1.12 markus 233: opts++;
1.43 djm 234: switch (match_host_and_ip(remote_host, remote_ip,
235: patterns)) {
236: case 1:
237: xfree(patterns);
238: /* Host name matches. */
239: goto next_option;
240: case -1:
241: debug("%.100s, line %lu: invalid criteria",
242: file, linenum);
243: auth_debug_add("%.100s, line %lu: "
244: "invalid criteria", file, linenum);
245: /* FALLTHROUGH */
246: case 0:
1.19 markus 247: xfree(patterns);
1.27 itojun 248: logit("Authentication tried for %.100s with "
1.12 markus 249: "correct key but not from a permitted "
250: "host (host=%.200s, ip=%.200s).",
251: pw->pw_name, remote_host, remote_ip);
1.24 markus 252: auth_debug_add("Your host '%.200s' is not "
1.12 markus 253: "permitted to use this key for login.",
254: remote_host);
1.43 djm 255: break;
1.1 markus 256: }
1.43 djm 257: /* deny access */
258: return 0;
1.15 markus 259: }
260: cp = "permitopen=\"";
261: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
1.29 djm 262: char *host, *p;
1.44 djm 263: int port;
1.15 markus 264: char *patterns = xmalloc(strlen(opts) + 1);
265:
266: opts += strlen(cp);
267: i = 0;
268: while (*opts) {
269: if (*opts == '"')
270: break;
271: if (*opts == '\\' && opts[1] == '"') {
272: opts += 2;
273: patterns[i++] = '"';
274: continue;
275: }
276: patterns[i++] = *opts++;
277: }
278: if (!*opts) {
279: debug("%.100s, line %lu: missing end quote",
280: file, linenum);
1.29 djm 281: auth_debug_add("%.100s, line %lu: missing "
282: "end quote", file, linenum);
1.15 markus 283: xfree(patterns);
284: goto bad_option;
285: }
1.38 dtucker 286: patterns[i] = '\0';
1.15 markus 287: opts++;
1.29 djm 288: p = patterns;
289: host = hpdelim(&p);
290: if (host == NULL || strlen(host) >= NI_MAXHOST) {
291: debug("%.100s, line %lu: Bad permitopen "
1.31 deraadt 292: "specification <%.100s>", file, linenum,
1.29 djm 293: patterns);
1.24 markus 294: auth_debug_add("%.100s, line %lu: "
1.29 djm 295: "Bad permitopen specification", file,
296: linenum);
1.15 markus 297: xfree(patterns);
298: goto bad_option;
299: }
1.30 deraadt 300: host = cleanhostname(host);
1.44 djm 301: if (p == NULL || (port = a2port(p)) <= 0) {
1.29 djm 302: debug("%.100s, line %lu: Bad permitopen port "
303: "<%.100s>", file, linenum, p ? p : "");
1.24 markus 304: auth_debug_add("%.100s, line %lu: "
1.20 stevesk 305: "Bad permitopen port", file, linenum);
1.15 markus 306: xfree(patterns);
307: goto bad_option;
308: }
1.16 markus 309: if (options.allow_tcp_forwarding)
1.20 stevesk 310: channel_add_permitted_opens(host, port);
1.15 markus 311: xfree(patterns);
1.32 reyk 312: goto next_option;
313: }
314: cp = "tunnel=\"";
315: if (strncasecmp(opts, cp, strlen(cp)) == 0) {
316: char *tun = NULL;
317: opts += strlen(cp);
318: tun = xmalloc(strlen(opts) + 1);
319: i = 0;
320: while (*opts) {
321: if (*opts == '"')
322: break;
323: tun[i++] = *opts++;
324: }
325: if (!*opts) {
326: debug("%.100s, line %lu: missing end quote",
327: file, linenum);
328: auth_debug_add("%.100s, line %lu: missing end quote",
329: file, linenum);
330: xfree(tun);
331: forced_tun_device = -1;
332: goto bad_option;
333: }
1.38 dtucker 334: tun[i] = '\0';
1.32 reyk 335: forced_tun_device = a2tun(tun, NULL);
336: xfree(tun);
1.33 reyk 337: if (forced_tun_device == SSH_TUNID_ERR) {
1.32 reyk 338: debug("%.100s, line %lu: invalid tun device",
339: file, linenum);
340: auth_debug_add("%.100s, line %lu: invalid tun device",
341: file, linenum);
342: forced_tun_device = -1;
343: goto bad_option;
344: }
345: auth_debug_add("Forced tun device: %d", forced_tun_device);
346: opts++;
1.1 markus 347: goto next_option;
348: }
349: next_option:
350: /*
351: * Skip the comma, and move to the next option
352: * (or break out if there are no more).
353: */
1.12 markus 354: if (!*opts)
1.1 markus 355: fatal("Bugs in auth-options.c option processing.");
1.12 markus 356: if (*opts == ' ' || *opts == '\t')
1.1 markus 357: break; /* End of options. */
1.12 markus 358: if (*opts != ',')
1.1 markus 359: goto bad_option;
1.12 markus 360: opts++;
1.1 markus 361: /* Process the next option. */
362: }
1.22 provos 363:
1.1 markus 364: /* grant access */
365: return 1;
366:
367: bad_option:
1.27 itojun 368: logit("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 369: file, linenum, opts);
1.24 markus 370: auth_debug_add("Bad options in %.100s file, line %lu: %.50s",
1.12 markus 371: file, linenum, opts);
1.22 provos 372:
1.1 markus 373: /* deny access */
374: return 0;
375: }
1.45 djm 376:
377: /*
378: * Set options from certificate constraints. These supersede user key options
379: * so this must be called after auth_parse_options().
380: */
381: int
382: auth_cert_constraints(Buffer *c_orig, struct passwd *pw)
383: {
384: u_char *name = NULL, *data_blob = NULL;
1.46 djm 385: u_int nlen, dlen, clen;
1.45 djm 386: Buffer c, data;
387: int ret = -1;
388:
389: int cert_no_port_forwarding_flag = 1;
390: int cert_no_agent_forwarding_flag = 1;
391: int cert_no_x11_forwarding_flag = 1;
392: int cert_no_pty_flag = 1;
393: int cert_no_user_rc = 1;
394: char *cert_forced_command = NULL;
395: int cert_source_address_done = 0;
396:
397: buffer_init(&data);
398:
399: /* Make copy to avoid altering original */
400: buffer_init(&c);
401: buffer_append(&c, buffer_ptr(c_orig), buffer_len(c_orig));
402:
403: while (buffer_len(&c) > 0) {
1.46 djm 404: if ((name = buffer_get_string_ret(&c, &nlen)) == NULL ||
405: (data_blob = buffer_get_string_ret(&c, &dlen)) == NULL) {
1.45 djm 406: error("Certificate constraints corrupt");
407: goto out;
408: }
1.46 djm 409: buffer_append(&data, data_blob, dlen);
1.45 djm 410: debug3("found certificate constraint \"%.100s\" len %u",
1.46 djm 411: name, dlen);
412: if (strlen(name) != nlen) {
413: error("Certificate constraint name contains \\0");
414: goto out;
415: }
1.45 djm 416: if (strcmp(name, "permit-X11-forwarding") == 0)
417: cert_no_x11_forwarding_flag = 0;
418: else if (strcmp(name, "permit-agent-forwarding") == 0)
419: cert_no_agent_forwarding_flag = 0;
420: else if (strcmp(name, "permit-port-forwarding") == 0)
421: cert_no_port_forwarding_flag = 0;
422: else if (strcmp(name, "permit-pty") == 0)
423: cert_no_pty_flag = 0;
424: else if (strcmp(name, "permit-user-rc") == 0)
425: cert_no_user_rc = 0;
426: else if (strcmp(name, "force-command") == 0) {
1.46 djm 427: char *command = buffer_get_string_ret(&data, &clen);
1.45 djm 428:
429: if (command == NULL) {
430: error("Certificate constraint \"%s\" corrupt",
431: name);
432: goto out;
433: }
1.46 djm 434: if (strlen(command) != clen) {
435: error("force-command constrain contains \\0");
436: goto out;
437: }
1.45 djm 438: if (cert_forced_command != NULL) {
439: error("Certificate has multiple "
1.47 djm 440: "force-command constraints");
1.45 djm 441: xfree(command);
442: goto out;
443: }
444: cert_forced_command = command;
445: } else if (strcmp(name, "source-address") == 0) {
1.46 djm 446: char *allowed = buffer_get_string_ret(&data, &clen);
1.45 djm 447: const char *remote_ip = get_remote_ipaddr();
448:
449: if (allowed == NULL) {
450: error("Certificate constraint \"%s\" corrupt",
451: name);
1.46 djm 452: goto out;
453: }
454: if (strlen(allowed) != clen) {
455: error("source-address constrain contains \\0");
1.45 djm 456: goto out;
457: }
458: if (cert_source_address_done++) {
459: error("Certificate has multiple "
460: "source-address constraints");
461: xfree(allowed);
462: goto out;
463: }
464: switch (addr_match_cidr_list(remote_ip, allowed)) {
465: case 1:
466: /* accepted */
467: xfree(allowed);
468: break;
469: case 0:
470: /* no match */
471: logit("Authentication tried for %.100s with "
472: "valid certificate but not from a "
473: "permitted host (ip=%.200s).",
474: pw->pw_name, remote_ip);
475: auth_debug_add("Your address '%.200s' is not "
476: "permitted to use this certificate for "
477: "login.", remote_ip);
478: xfree(allowed);
479: goto out;
480: case -1:
481: error("Certificate source-address contents "
482: "invalid");
483: xfree(allowed);
484: goto out;
485: }
486: } else {
487: error("Certificate constraint \"%s\" is not supported",
488: name);
489: goto out;
490: }
491:
492: if (buffer_len(&data) != 0) {
493: error("Certificate constraint \"%s\" corrupt "
494: "(extra data)", name);
495: goto out;
496: }
497: buffer_clear(&data);
498: xfree(name);
499: xfree(data_blob);
500: name = data_blob = NULL;
501: }
502:
503: /* successfully parsed all constraints */
504: ret = 0;
505:
506: no_port_forwarding_flag |= cert_no_port_forwarding_flag;
507: no_agent_forwarding_flag |= cert_no_agent_forwarding_flag;
508: no_x11_forwarding_flag |= cert_no_x11_forwarding_flag;
509: no_pty_flag |= cert_no_pty_flag;
510: no_user_rc |= cert_no_user_rc;
511: /* CA-specified forced command supersedes key option */
512: if (cert_forced_command != NULL) {
513: if (forced_command != NULL)
514: xfree(forced_command);
515: forced_command = cert_forced_command;
516: }
517:
518: out:
519: if (name != NULL)
520: xfree(name);
521: if (data_blob != NULL)
522: xfree(data_blob);
523: buffer_free(&data);
524: buffer_free(&c);
525: return ret;
526: }
527: