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