OSDN Git Service

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