]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/appl/su/su.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / appl / su / su.c
1 /*
2  * Copyright (c) 1999 - 2008 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of KTH nor the names of its contributors may be
18  *    used to endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33 #include <config.h>
34
35 RCSID("$Id$");
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #include <syslog.h>
42
43 #ifdef HAVE_PATHS_H
44 #include <paths.h>
45 #endif
46
47 #ifdef HAVE_SHADOW_H
48 #include <shadow.h>
49 #endif
50
51 #include <pwd.h>
52 #ifdef HAVE_CRYPT_H
53 #include <crypt.h>
54 #endif
55
56 #include "crypto-headers.h"
57 #ifdef KRB5
58 #include <krb5.h>
59 #endif
60 #include <kafs.h>
61 #include <err.h>
62 #include <roken.h>
63 #include <getarg.h>
64
65 #include "supaths.h"
66
67 #if !HAVE_DECL_ENVIRON
68 extern char **environ;
69 #endif
70
71 int kerberos_flag = 1;
72 int csh_f_flag;
73 int full_login;
74 int env_flag;
75 char *kerberos_instance = "root";
76 int help_flag;
77 int version_flag;
78 char *cmd;
79 char tkfile[256];
80
81 struct getargs args[] = {
82     { "kerberos", 'K', arg_negative_flag, &kerberos_flag,
83       "don't use kerberos" },
84     { NULL,       'f', arg_flag,          &csh_f_flag,
85       "don't read .cshrc" },
86     { "full",     'l', arg_flag,          &full_login,
87       "simulate full login" },
88     { NULL,       'm', arg_flag,          &env_flag,
89       "leave environment unmodified" },
90     { "instance", 'i', arg_string,        &kerberos_instance,
91       "root instance to use" },
92     { "command",  'c', arg_string,        &cmd,
93       "command to execute" },
94     { "help",     'h', arg_flag,          &help_flag },
95     { "version",  0,   arg_flag,          &version_flag },
96 };
97
98
99 static void
100 usage (int ret)
101 {
102     arg_printusage (args,
103                     sizeof(args)/sizeof(*args),
104                     NULL,
105                     "[login [shell arguments]]");
106     exit (ret);
107 }
108
109 static void
110 free_info(struct passwd *p)
111 {
112     free (p->pw_name);
113     free (p->pw_passwd);
114     free (p->pw_dir);
115     free (p->pw_shell);
116     free (p);
117 }
118
119 static struct passwd*
120 dup_info(const struct passwd *pwd)
121 {
122     struct passwd *info;
123
124     info = malloc(sizeof(*info));
125     if(info == NULL)
126         return NULL;
127     info->pw_name = strdup(pwd->pw_name);
128     info->pw_passwd = strdup(pwd->pw_passwd);
129     info->pw_uid = pwd->pw_uid;
130     info->pw_gid = pwd->pw_gid;
131     info->pw_dir = strdup(pwd->pw_dir);
132     info->pw_shell = strdup(pwd->pw_shell);
133     if(info->pw_name == NULL || info->pw_passwd == NULL ||
134        info->pw_dir == NULL || info->pw_shell == NULL) {
135         free_info (info);
136         return NULL;
137     }
138     return info;
139 }
140
141 #ifdef KRB5
142 static krb5_context context;
143 static krb5_ccache ccache;
144
145 static int
146 krb5_verify(const struct passwd *login_info,
147             const struct passwd *su_info,
148             const char *kerberos_instance)
149 {
150     krb5_error_code ret;
151     krb5_principal p;
152     krb5_realm *realms, *r;
153     char *login_name = NULL;
154     int user_ok = 0;
155
156 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN)
157     login_name = getlogin();
158 #endif
159     ret = krb5_init_context (&context);
160     if (ret) {
161 #if 0
162         warnx("krb5_init_context failed: %d", ret);
163 #endif
164         return 1;
165     }
166
167     ret = krb5_get_default_realms(context, &realms);
168     if (ret)
169         return 1;
170
171     /* Check all local realms */
172     for (r = realms; *r != NULL && !user_ok; r++) {
173
174         if (login_name == NULL || strcmp (login_name, "root") == 0)
175             login_name = login_info->pw_name;
176         if (strcmp (su_info->pw_name, "root") == 0)
177             ret = krb5_make_principal(context, &p, *r,
178                                       login_name,
179                                       kerberos_instance,
180                                       NULL);
181         else
182             ret = krb5_make_principal(context, &p, *r,
183                                       su_info->pw_name,
184                                       NULL);
185         if (ret) {
186             krb5_free_host_realm(context, realms);
187             return 1;
188         }
189
190         /* if we are su-ing too root, check with krb5_kuserok */
191         if (su_info->pw_uid == 0 && !krb5_kuserok(context, p, su_info->pw_name))
192             continue;
193
194         ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
195         if(ret) {
196             krb5_free_host_realm(context, realms);
197             krb5_free_principal (context, p);
198             return 1;
199         }
200         ret = krb5_verify_user(context, p, ccache, NULL, TRUE, NULL);
201         krb5_free_principal (context, p);
202         switch (ret) {
203         case 0:
204             user_ok = 1;
205             break;
206         case KRB5_LIBOS_PWDINTR :
207             krb5_cc_destroy(context, ccache);
208             break;
209         case KRB5KRB_AP_ERR_BAD_INTEGRITY:
210         case KRB5KRB_AP_ERR_MODIFIED:
211             krb5_cc_destroy(context, ccache);
212             krb5_warnx(context, "Password incorrect");
213             break;
214         default :
215             krb5_cc_destroy(context, ccache);
216             krb5_warn(context, ret, "krb5_verify_user");
217             break;
218         }
219     }
220     krb5_free_host_realm(context, realms);
221     if (!user_ok)
222         return 1;
223     return 0;
224 }
225
226 static int
227 krb5_start_session(void)
228 {
229     krb5_ccache ccache2;
230     char *cc_name;
231     int ret;
232
233     ret = krb5_cc_new_unique(context, krb5_cc_type_file, NULL, &ccache2);
234     if (ret) {
235         krb5_cc_destroy(context, ccache);
236         return 1;
237     }
238
239     ret = krb5_cc_copy_cache(context, ccache, ccache2);
240     if (ret) {
241         krb5_cc_destroy(context, ccache);
242         krb5_cc_destroy(context, ccache2);
243         return 1;
244     }
245
246     ret = asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2),
247                    krb5_cc_get_name(context, ccache2));
248     if (ret == -1) {
249         krb5_cc_destroy(context, ccache);
250         krb5_cc_destroy(context, ccache2);
251         errx(1, "malloc - out of memory");
252     }
253     esetenv("KRB5CCNAME", cc_name, 1);
254
255     /* convert creds? */
256     if(k_hasafs()) {
257         if (k_setpag() == 0)
258             krb5_afslog(context, ccache2, NULL, NULL);
259     }
260
261     krb5_cc_close(context, ccache2);
262     krb5_cc_destroy(context, ccache);
263     return 0;
264 }
265 #endif
266
267
268 #define GROUP_MEMBER            0
269 #define GROUP_MISSING           1
270 #define GROUP_EMPTY             2
271 #define GROUP_NOT_MEMBER        3
272
273 static int
274 group_member_p(const char *group, const char *user)
275 {
276     struct group *g;
277     int i;
278     g = getgrnam(group);
279     if(g == NULL)
280         return GROUP_MISSING;
281     if(g->gr_mem[0] == NULL)
282         return GROUP_EMPTY;
283     for(i = 0; g->gr_mem[i] != NULL; i++)
284         if(strcmp(user, g->gr_mem[i]) == 0)
285             return GROUP_MEMBER;
286     return GROUP_NOT_MEMBER;
287 }
288
289 static int
290 verify_unix(struct passwd *login, struct passwd *su)
291 {
292     char prompt[128];
293     char pw_buf[1024];
294     char *pw;
295     int r;
296     if(su->pw_passwd != NULL && *su->pw_passwd != '\0') {
297         snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name);
298         r = UI_UTIL_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0);
299         if(r != 0)
300             exit(0);
301         pw = crypt(pw_buf, su->pw_passwd);
302         memset(pw_buf, 0, sizeof(pw_buf));
303         if(strcmp(pw, su->pw_passwd) != 0) {
304             syslog (LOG_ERR | LOG_AUTH, "%s to %s: incorrect password",
305                     login->pw_name, su->pw_name);
306             return 1;
307         }
308     }
309     /* if su:ing to root, check membership of group wheel or root; if
310        that group doesn't exist, or is empty, allow anyone to su
311        root */
312     if(su->pw_uid == 0) {
313 #ifndef ROOT_GROUP
314 #define ROOT_GROUP "wheel"
315 #endif
316         int gs = group_member_p(ROOT_GROUP, login->pw_name);
317         if(gs == GROUP_NOT_MEMBER) {
318             syslog (LOG_ERR | LOG_AUTH, "%s to %s: not in group %s",
319                     login->pw_name, su->pw_name, ROOT_GROUP);
320             return 1;
321         }
322         return 0;
323     }
324     return 0;
325 }
326
327 int
328 main(int argc, char **argv)
329 {
330     int i, optind = 0;
331     char *su_user;
332     struct passwd *su_info;
333     struct passwd *login_info;
334
335     struct passwd *pwd;
336
337     char *shell;
338
339     int ok = 0;
340
341     setprogname (argv[0]);
342
343     if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
344         usage(1);
345
346     for (i=0; i < optind; i++)
347       if (strcmp(argv[i], "-") == 0) {
348          full_login = 1;
349          break;
350       }
351
352     if(help_flag)
353         usage(0);
354     if(version_flag) {
355         print_version(NULL);
356         exit(0);
357     }
358     if(optind >= argc)
359         su_user = "root";
360     else
361         su_user = argv[optind++];
362
363     if (!issuid() && getuid() != 0)
364         warnx("Not setuid and you are not root, expect this to fail");
365
366     pwd = k_getpwnam(su_user);
367     if(pwd == NULL)
368         errx (1, "unknown login %s", su_user);
369     if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) {
370         syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user);
371         errx (1, "unknown login %s", su_user);
372     }
373     su_info = dup_info(pwd);
374     if (su_info == NULL)
375         errx (1, "malloc: out of memory");
376
377         pwd = getpwuid(getuid());
378     if(pwd == NULL)
379         errx(1, "who are you?");
380     login_info = dup_info(pwd);
381     if (login_info == NULL)
382         errx (1, "malloc: out of memory");
383     if(env_flag)
384         shell = login_info->pw_shell;
385     else
386         shell = su_info->pw_shell;
387     if(shell == NULL || *shell == '\0')
388         shell = _PATH_BSHELL;
389
390
391 #ifdef KRB5
392     if(kerberos_flag && ok == 0 &&
393        krb5_verify(login_info, su_info, kerberos_instance) == 0)
394         ok = 5;
395 #endif
396
397     if(ok == 0 && login_info->pw_uid && verify_unix(login_info, su_info) != 0) {
398         printf("Sorry!\n");
399         exit(1);
400     }
401
402 #ifdef HAVE_GETSPNAM
403    {  struct spwd *sp;
404       long    today;
405
406     sp = getspnam(su_info->pw_name);
407     if (sp != NULL) {
408         today = time(0)/(24L * 60 * 60);
409         if (sp->sp_expire > 0) {
410             if (today >= sp->sp_expire) {
411                 if (login_info->pw_uid)
412                     errx(1,"Your account has expired.");
413                 else
414                     printf("Your account has expired.");
415             }
416             else if (sp->sp_expire - today < 14)
417                 printf("Your account will expire in %d days.\n",
418                        (int)(sp->sp_expire - today));
419         }
420         if (sp->sp_max > 0) {
421             if (today >= sp->sp_lstchg + sp->sp_max) {
422                 if (login_info->pw_uid)
423                     errx(1,"Your password has expired. Choose a new one.");
424                 else
425                     printf("Your password has expired. Choose a new one.");
426             }
427             else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn)
428                 printf("Your account will expire in %d days.\n",
429                        (int)(sp->sp_lstchg + sp->sp_max -today));
430         }
431     }
432     }
433 #endif
434     {
435         char *tty = ttyname (STDERR_FILENO);
436         syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s on %s" : "%s to %s",
437                 login_info->pw_name, su_info->pw_name, tty);
438     }
439
440
441     if(!env_flag) {
442         if(full_login) {
443             char *t = getenv ("TERM");
444             char **newenv = NULL;
445             int i, j;
446
447             i = read_environment(_PATH_ETC_ENVIRONMENT, &newenv);
448
449             environ = malloc ((10 + i) * sizeof (char *));
450             if (environ == NULL)
451                 err (1, "malloc");
452             environ[0] = NULL;
453
454             for (j = 0; j < i; j++) {
455                 char *p = strchr(newenv[j], '=');
456                 if (p == NULL)
457                     errx(1, "enviroment '%s' missing '='", newenv[j]);
458                 *p++ = 0;
459                 esetenv (newenv[j], p, 1);
460             }
461             free(newenv);
462
463             esetenv ("PATH", _PATH_DEFPATH, 1);
464             if (t)
465                 esetenv ("TERM", t, 1);
466             if (chdir (su_info->pw_dir) < 0)
467                 errx (1, "no directory");
468         }
469         if (full_login || su_info->pw_uid)
470             esetenv ("USER", su_info->pw_name, 1);
471         esetenv("HOME", su_info->pw_dir, 1);
472         esetenv("SHELL", shell, 1);
473     }
474
475     {
476         int i;
477         char **args;
478         char *p;
479
480         p = strrchr(shell, '/');
481         if(p)
482             p++;
483         else
484             p = shell;
485
486         if (strcmp(p, "csh") != 0)
487             csh_f_flag = 0;
488
489         args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args));
490         if (args == NULL)
491             err (1, "malloc");
492         i = 0;
493         if(full_login) {
494             if (asprintf(&args[i++], "-%s", p) == -1)
495                 errx (1, "malloc");
496         } else
497             args[i++] = p;
498         if (cmd) {
499            args[i++] = "-c";
500            args[i++] = cmd;
501         }
502
503         if (csh_f_flag)
504             args[i++] = "-f";
505
506         for (argv += optind; *argv; ++argv)
507            args[i++] = *argv;
508         args[i] = NULL;
509
510         if(setgid(su_info->pw_gid) < 0)
511             err(1, "setgid");
512         if (initgroups (su_info->pw_name, su_info->pw_gid) < 0)
513             err (1, "initgroups");
514         if(setuid(su_info->pw_uid) < 0
515            || (su_info->pw_uid != 0 && setuid(0) == 0))
516             err(1, "setuid");
517
518 #ifdef KRB5
519         if (ok == 5)
520            krb5_start_session();
521 #endif
522         execve(shell, args, environ);
523     }
524
525     exit(1);
526 }