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