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