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