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