No empty .Rs/.Re
[netbsd-mini2440.git] / crypto / dist / heimdal / appl / su / su.c
blob884b9a56f27400c2be0e7911d75da2fb304d251b
1 /*
2 * Copyright (c) 1999 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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. */
33 #include <config.h>
35 __RCSID("$Heimdal: su.c 21988 2007-10-19 05:36:54Z lha $"
36 "$NetBSD$");
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
42 #include <syslog.h>
44 #ifdef HAVE_PATHS_H
45 #include <paths.h>
46 #endif
48 #ifdef HAVE_SHADOW_H
49 #include <shadow.h>
50 #endif
52 #include <pwd.h>
53 #ifdef HAVE_CRYPT_H
54 #include <crypt.h>
55 #endif
57 #include "crypto-headers.h"
58 #ifdef KRB5
59 #include <krb5.h>
60 #endif
61 #ifdef KRB4
62 #include <krb.h>
63 #endif
64 #include <kafs.h>
65 #include <err.h>
66 #include <roken.h>
67 #include <getarg.h>
69 #include "supaths.h"
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];
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 },
99 static void
100 usage (int ret)
102 arg_printusage (args,
103 sizeof(args)/sizeof(*args),
104 NULL,
105 "[login [shell arguments]]");
106 exit (ret);
109 static void
110 free_info(struct passwd *p)
112 free (p->pw_name);
113 free (p->pw_passwd);
114 free (p->pw_dir);
115 free (p->pw_shell);
116 free (p);
119 static struct passwd*
120 dup_info(const struct passwd *pwd)
122 struct passwd *info;
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;
138 return info;
141 #if defined(KRB4) || defined(KRB5)
142 static void
143 set_tkfile()
145 #ifndef TKT_ROOT
146 #define TKT_ROOT "/tmp/tkt"
147 #endif
148 int fd;
149 if(*tkfile != '\0')
150 return;
151 snprintf(tkfile, sizeof(tkfile), "%s_XXXXXX", TKT_ROOT);
152 fd = mkstemp(tkfile);
153 if(fd >= 0)
154 close(fd);
155 #ifdef KRB4
156 krb_set_tkt_string(tkfile);
157 #endif
159 #endif
161 #ifdef KRB5
162 static krb5_context context;
163 static krb5_ccache ccache;
165 static int
166 krb5_verify(const struct passwd *login_info,
167 const struct passwd *su_info,
168 const char *kerberos_instance)
170 krb5_error_code ret;
171 krb5_principal p;
172 krb5_realm *realms, *r;
173 char *login_name = NULL;
174 int user_ok = 0;
176 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN)
177 login_name = getlogin();
178 #endif
179 ret = krb5_init_context (&context);
180 if (ret) {
181 #if 0
182 warnx("krb5_init_context failed: %d", ret);
183 #endif
184 return 1;
187 ret = krb5_get_default_realms(context, &realms);
188 if (ret)
189 return 1;
191 /* Check all local realms */
192 for (r = realms; *r != NULL && !user_ok; r++) {
194 if (login_name == NULL || strcmp (login_name, "root") == 0)
195 login_name = login_info->pw_name;
196 if (strcmp (su_info->pw_name, "root") == 0)
197 ret = krb5_make_principal(context, &p, *r,
198 login_name,
199 kerberos_instance,
200 NULL);
201 else
202 ret = krb5_make_principal(context, &p, *r,
203 su_info->pw_name,
204 NULL);
205 if (ret) {
206 krb5_free_host_realm(context, realms);
207 return 1;
210 /* if we are su-ing too root, check with krb5_kuserok */
211 if (su_info->pw_uid == 0 && !krb5_kuserok(context, p, su_info->pw_name))
212 continue;
214 ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
215 if(ret) {
216 krb5_free_host_realm(context, realms);
217 krb5_free_principal (context, p);
218 return 1;
220 ret = krb5_verify_user(context, p, ccache, NULL, TRUE, NULL);
221 krb5_free_principal (context, p);
222 switch (ret) {
223 case 0:
224 user_ok = 1;
225 break;
226 case KRB5_LIBOS_PWDINTR :
227 krb5_cc_destroy(context, ccache);
228 break;
229 case KRB5KRB_AP_ERR_BAD_INTEGRITY:
230 case KRB5KRB_AP_ERR_MODIFIED:
231 krb5_cc_destroy(context, ccache);
232 krb5_warnx(context, "Password incorrect");
233 break;
234 default :
235 krb5_cc_destroy(context, ccache);
236 krb5_warn(context, ret, "krb5_verify_user");
237 break;
240 krb5_free_host_realm(context, realms);
241 if (!user_ok)
242 return 1;
243 return 0;
246 static int
247 krb5_start_session(void)
249 krb5_ccache ccache2;
250 char *cc_name;
251 int ret;
253 ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2);
254 if (ret) {
255 krb5_cc_destroy(context, ccache);
256 return 1;
259 ret = krb5_cc_copy_cache(context, ccache, ccache2);
261 ret = asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2),
262 krb5_cc_get_name(context, ccache2));
263 if (ret == -1)
264 errx(1, "malloc - out of memory");
265 esetenv("KRB5CCNAME", cc_name, 1);
267 /* we want to export this even if we don't directly support KRB4 */
268 set_tkfile();
269 esetenv("KRBTKFILE", tkfile, 1);
271 /* convert creds? */
272 if(k_hasafs()) {
273 if (k_setpag() == 0)
274 krb5_afslog(context, ccache2, NULL, NULL);
277 krb5_cc_close(context, ccache2);
278 krb5_cc_destroy(context, ccache);
279 return 0;
281 #endif
283 #ifdef KRB4
285 static int
286 krb_verify(const struct passwd *login_info,
287 const struct passwd *su_info,
288 const char *kerberos_instance)
290 int ret;
291 char *login_name = NULL;
292 char *name, *instance, realm[REALM_SZ];
294 #if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN)
295 login_name = getlogin();
296 #endif
298 ret = krb_get_lrealm(realm, 1);
300 if (login_name == NULL || strcmp (login_name, "root") == 0)
301 login_name = login_info->pw_name;
302 if (strcmp (su_info->pw_name, "root") == 0) {
303 name = login_name;
304 instance = (char*)kerberos_instance;
305 } else {
306 name = su_info->pw_name;
307 instance = "";
310 if(su_info->pw_uid != 0 ||
311 krb_kuserok(name, instance, realm, su_info->pw_name) == 0) {
312 char password[128];
313 char *prompt;
314 ret = asprintf (&prompt,
315 "%s's Password: ",
316 krb_unparse_name_long (name, instance, realm));
317 if (ret == -1)
318 return (1);
319 if (UI_UTIL_read_pw_string (password, sizeof (password), prompt, 0)) {
320 memset (password, 0, sizeof (password));
321 free(prompt);
322 return (1);
324 free(prompt);
325 if (strlen(password) == 0)
326 return (1); /* Empty passwords are not allowed */
327 set_tkfile();
328 setuid(geteuid()); /* need to run as root here */
329 ret = krb_verify_user(name, instance, realm, password,
330 KRB_VERIFY_SECURE, NULL);
331 memset(password, 0, sizeof(password));
333 if(ret) {
334 warnx("%s", krb_get_err_text(ret));
335 return 1;
337 chown (tkt_string(), su_info->pw_uid, su_info->pw_gid);
338 return 0;
340 return 1;
344 static int
345 krb_start_session(void)
347 esetenv("KRBTKFILE", tkfile, 1);
349 /* convert creds? */
350 if(k_hasafs() && k_setpag() == 0)
351 krb_afslog(NULL, NULL);
353 return 0;
355 #endif
357 #define GROUP_MEMBER 0
358 #define GROUP_MISSING 1
359 #define GROUP_EMPTY 2
360 #define GROUP_NOT_MEMBER 3
362 static int
363 group_member_p(const char *group, const char *user)
365 struct group *g;
366 int i;
367 g = getgrnam(group);
368 if(g == NULL)
369 return GROUP_MISSING;
370 if(g->gr_mem[0] == NULL)
371 return GROUP_EMPTY;
372 for(i = 0; g->gr_mem[i] != NULL; i++)
373 if(strcmp(user, g->gr_mem[i]) == 0)
374 return GROUP_MEMBER;
375 return GROUP_NOT_MEMBER;
378 static int
379 verify_unix(struct passwd *login, struct passwd *su)
381 char prompt[128];
382 char pw_buf[1024];
383 char *pw;
384 int r;
385 if(su->pw_passwd != NULL && *su->pw_passwd != '\0') {
386 snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name);
387 r = UI_UTIL_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0);
388 if(r != 0)
389 exit(0);
390 pw = crypt(pw_buf, su->pw_passwd);
391 memset(pw_buf, 0, sizeof(pw_buf));
392 if(strcmp(pw, su->pw_passwd) != 0) {
393 syslog (LOG_ERR | LOG_AUTH, "%s to %s: incorrect password",
394 login->pw_name, su->pw_name);
395 return 1;
398 /* if su:ing to root, check membership of group wheel or root; if
399 that group doesn't exist, or is empty, allow anyone to su
400 root */
401 if(su->pw_uid == 0) {
402 #ifndef ROOT_GROUP
403 #define ROOT_GROUP "wheel"
404 #endif
405 int gs = group_member_p(ROOT_GROUP, login->pw_name);
406 if(gs == GROUP_NOT_MEMBER) {
407 syslog (LOG_ERR | LOG_AUTH, "%s to %s: not in group %s",
408 login->pw_name, su->pw_name, ROOT_GROUP);
409 return 1;
411 return 0;
413 return 0;
417 main(int argc, char **argv)
419 int i, optind = 0;
420 char *su_user;
421 struct passwd *su_info;
422 struct passwd *login_info;
424 struct passwd *pwd;
426 char *shell;
428 int ok = 0;
429 int kerberos_error=1;
431 setprogname (argv[0]);
433 if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
434 usage(1);
436 for (i=0; i < optind; i++)
437 if (strcmp(argv[i], "-") == 0) {
438 full_login = 1;
439 break;
442 if(help_flag)
443 usage(0);
444 if(version_flag) {
445 print_version(NULL);
446 exit(0);
448 if(optind >= argc)
449 su_user = "root";
450 else
451 su_user = argv[optind++];
453 if (!issuid() && getuid() != 0)
454 warnx("Not setuid and you are root, expect this to fail");
456 pwd = k_getpwnam(su_user);
457 if(pwd == NULL)
458 errx (1, "unknown login %s", su_user);
459 if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) {
460 syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user);
461 errx (1, "unknown login %s", su_user);
463 su_info = dup_info(pwd);
464 if (su_info == NULL)
465 errx (1, "malloc: out of memory");
467 pwd = getpwuid(getuid());
468 if(pwd == NULL)
469 errx(1, "who are you?");
470 login_info = dup_info(pwd);
471 if (login_info == NULL)
472 errx (1, "malloc: out of memory");
473 if(env_flag)
474 shell = login_info->pw_shell;
475 else
476 shell = su_info->pw_shell;
477 if(shell == NULL || *shell == '\0')
478 shell = _PATH_BSHELL;
481 #ifdef KRB5
482 if(kerberos_flag && ok == 0 &&
483 (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0)
484 ok = 5;
485 #endif
486 #ifdef KRB4
487 if(kerberos_flag && ok == 0 &&
488 (kerberos_error = krb_verify(login_info, su_info, kerberos_instance)) == 0)
489 ok = 4;
490 #endif
492 if(ok == 0 && login_info->pw_uid && verify_unix(login_info, su_info) != 0) {
493 printf("Sorry!\n");
494 exit(1);
497 #ifdef HAVE_GETSPNAM
498 { struct spwd *sp;
499 long today;
501 sp = getspnam(su_info->pw_name);
502 if (sp != NULL) {
503 today = time(0)/(24L * 60 * 60);
504 if (sp->sp_expire > 0) {
505 if (today >= sp->sp_expire) {
506 if (login_info->pw_uid)
507 errx(1,"Your account has expired.");
508 else
509 printf("Your account has expired.");
511 else if (sp->sp_expire - today < 14)
512 printf("Your account will expire in %d days.\n",
513 (int)(sp->sp_expire - today));
515 if (sp->sp_max > 0) {
516 if (today >= sp->sp_lstchg + sp->sp_max) {
517 if (login_info->pw_uid)
518 errx(1,"Your password has expired. Choose a new one.");
519 else
520 printf("Your password has expired. Choose a new one.");
522 else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn)
523 printf("Your account will expire in %d days.\n",
524 (int)(sp->sp_lstchg + sp->sp_max -today));
528 #endif
530 char *tty = ttyname (STDERR_FILENO);
531 syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s on %s" : "%s to %s",
532 login_info->pw_name, su_info->pw_name, tty);
536 if(!env_flag) {
537 if(full_login) {
538 char *t = getenv ("TERM");
539 char **newenv = NULL;
540 int i, j;
542 i = read_environment(_PATH_ETC_ENVIRONMENT, &newenv);
544 environ = malloc ((10 + i) * sizeof (char *));
545 if (environ == NULL)
546 err (1, "malloc");
547 environ[0] = NULL;
549 for (j = 0; j < i; j++) {
550 char *p = strchr(newenv[j], '=');
551 *p++ = 0;
552 esetenv (newenv[j], p, 1);
554 free(newenv);
556 esetenv ("PATH", _PATH_DEFPATH, 1);
557 if (t)
558 esetenv ("TERM", t, 1);
559 if (chdir (su_info->pw_dir) < 0)
560 errx (1, "no directory");
562 if (full_login || su_info->pw_uid)
563 esetenv ("USER", su_info->pw_name, 1);
564 esetenv("HOME", su_info->pw_dir, 1);
565 esetenv("SHELL", shell, 1);
569 int i;
570 char **args;
571 char *p;
573 p = strrchr(shell, '/');
574 if(p)
575 p++;
576 else
577 p = shell;
579 if (strcmp(p, "csh") != 0)
580 csh_f_flag = 0;
582 args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args));
583 if (args == NULL)
584 err (1, "malloc");
585 i = 0;
586 if(full_login) {
587 if (asprintf(&args[i++], "-%s", p) == -1)
588 errx (1, "malloc");
589 } else
590 args[i++] = p;
591 if (cmd) {
592 args[i++] = "-c";
593 args[i++] = cmd;
596 if (csh_f_flag)
597 args[i++] = "-f";
599 for (argv += optind; *argv; ++argv)
600 args[i++] = *argv;
601 args[i] = NULL;
603 if(setgid(su_info->pw_gid) < 0)
604 err(1, "setgid");
605 if (initgroups (su_info->pw_name, su_info->pw_gid) < 0)
606 err (1, "initgroups");
607 if(setuid(su_info->pw_uid) < 0
608 || (su_info->pw_uid != 0 && setuid(0) == 0))
609 err(1, "setuid");
611 #ifdef KRB5
612 if (ok == 5)
613 krb5_start_session();
614 #endif
615 #ifdef KRB4
616 if (ok == 4)
617 krb_start_session();
618 #endif
619 execv(shell, args);
622 exit(1);