su.c revision 72445
1/*
2 * Copyright (c) 1999 - 2001 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
35RCSID("$Id: su.c,v 1.18 2001/01/26 16:02:49 joda Exp $");
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
53#include <des.h>
54#include <krb5.h>
55#include <kafs.h>
56#include <err.h>
57#include <roken.h>
58#include <getarg.h>
59#include <kafs.h>
60
61#ifndef _PATH_DEFPATH
62#define _PATH_DEFPATH "/usr/bin:/bin"
63#endif
64
65#ifndef _PATH_BSHELL
66#define _PATH_BSHELL "/bin/sh"
67#endif
68
69int kerberos_flag = 1;
70int csh_f_flag;
71int full_login;
72int env_flag;
73char *kerberos_instance = "root";
74int help_flag;
75int version_flag;
76char *cmd;
77
78struct getargs args[] = {
79    { "kerberos", 'K', arg_negative_flag, &kerberos_flag,
80      "don't use kerberos" },
81    { NULL,	  'f', arg_flag,	  &csh_f_flag,
82      "don't read .cshrc" },
83    { "full",	  'l', arg_flag,          &full_login,
84      "simulate full login" },
85    { NULL,	  'm', arg_flag,          &env_flag,
86      "leave environment unmodified" },
87    { "instance", 'i', arg_string,        &kerberos_instance,
88      "root instance to use" },
89    { "command",  'c', arg_string,        &cmd,
90      "command to execute" },
91    { "help", 	  'h', arg_flag,          &help_flag },
92    { "version",  0,   arg_flag,          &version_flag },
93};
94
95
96static void
97usage (int ret)
98{
99    arg_printusage (args,
100		    sizeof(args)/sizeof(*args),
101		    NULL,
102		    "[login [shell arguments]]");
103    exit (ret);
104}
105
106static struct passwd*
107make_info(struct passwd *pwd)
108{
109    struct passwd *info;
110    info = malloc(sizeof(*info));
111    if(info == NULL)
112	return NULL;
113    info->pw_name = strdup(pwd->pw_name);
114    info->pw_passwd = strdup(pwd->pw_passwd);
115    info->pw_uid = pwd->pw_uid;
116    info->pw_gid = pwd->pw_gid;
117    info->pw_dir = strdup(pwd->pw_dir);
118    info->pw_shell = strdup(pwd->pw_shell);
119    if(info->pw_name == NULL || info->pw_passwd == NULL ||
120       info->pw_dir == NULL || info->pw_shell == NULL)
121	return NULL;
122    return info;
123}
124
125#ifdef KRB5
126static krb5_context context;
127static krb5_ccache ccache;
128#endif
129
130static int
131krb5_verify(struct passwd *login_info, struct passwd *su_info,
132	    const char *kerberos_instance)
133{
134#ifdef KRB5
135    krb5_error_code ret;
136    krb5_principal p;
137
138    ret = krb5_init_context (&context);
139    if (ret) {
140#if 0
141	warnx("krb5_init_context failed: %d", ret);
142#endif
143	return 1;
144    }
145
146    if (strcmp (su_info->pw_name, "root") == 0)
147	ret = krb5_make_principal(context, &p, NULL,
148				  login_info->pw_name,
149				  kerberos_instance,
150				  NULL);
151    else
152	ret = krb5_make_principal(context, &p, NULL,
153				  su_info->pw_name,
154				  NULL);
155    if(ret)
156	return 1;
157
158    if(su_info->pw_uid != 0 || krb5_kuserok(context, p, su_info->pw_name)) {
159	ret = krb5_cc_gen_new(context, &krb5_mcc_ops, &ccache);
160	if(ret) {
161#if 1
162	    krb5_warn(context, ret, "krb5_cc_gen_new");
163#endif
164	    krb5_free_principal (context, p);
165	    return 1;
166	}
167	ret = krb5_verify_user_lrealm(context, p, ccache, NULL, TRUE, NULL);
168	krb5_free_principal (context, p);
169	if(ret) {
170	    krb5_cc_destroy(context, ccache);
171	    switch (ret) {
172	    case KRB5_LIBOS_PWDINTR :
173		break;
174	    case KRB5KRB_AP_ERR_BAD_INTEGRITY:
175	    case KRB5KRB_AP_ERR_MODIFIED:
176		krb5_warnx(context, "Password incorrect");
177		break;
178	    default :
179		krb5_warn(context, ret, "krb5_verify_user");
180		break;
181	    }
182	    return 1;
183	}
184	return 0;
185    }
186    krb5_free_principal (context, p);
187#endif
188    return 1;
189}
190
191#ifdef KRB5
192static int
193krb5_start_session(void)
194{
195    krb5_ccache ccache2;
196    char *cc_name;
197    int ret;
198
199    ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache2);
200    if (ret) {
201	krb5_cc_destroy(context, ccache);
202	return 1;
203    }
204
205    ret = krb5_cc_copy_cache(context, ccache, ccache2);
206
207    asprintf(&cc_name, "%s:%s", krb5_cc_get_type(context, ccache2),
208	     krb5_cc_get_name(context, ccache2));
209    esetenv("KRB5CCNAME", cc_name, 1);
210
211    /* we want to export this even if we don't directly support KRB4 */
212    {
213#ifndef TKT_ROOT
214#define TKT_ROOT "/tmp/tkt"
215#endif
216	int fd;
217	char tkfile[256];
218	strlcpy(tkfile, TKT_ROOT, sizeof(tkfile));
219	strlcat(tkfile, "_XXXXXX", sizeof(tkfile));
220	fd = mkstemp(tkfile);
221	if(fd >= 0) {
222	    close(fd);
223	    esetenv("KRBTKFILE", tkfile, 1);
224	}
225    }
226
227#ifdef KRB4
228    /* convert creds? */
229    if(k_hasafs()) {
230	if (k_setpag() == 0)
231	    krb5_afslog(context, ccache2, NULL, NULL);
232    }
233#endif
234
235    krb5_cc_close(context, ccache2);
236    krb5_cc_destroy(context, ccache);
237    return 0;
238}
239#endif
240
241static int
242verify_unix(struct passwd *su)
243{
244    char prompt[128];
245    char pw_buf[1024];
246    char *pw;
247    int r;
248    if(su->pw_passwd != NULL && *su->pw_passwd != '\0') {
249	snprintf(prompt, sizeof(prompt), "%s's password: ", su->pw_name);
250	r = des_read_pw_string(pw_buf, sizeof(pw_buf), prompt, 0);
251	if(r != 0)
252	    exit(0);
253	pw = crypt(pw_buf, su->pw_passwd);
254	memset(pw_buf, 0, sizeof(pw_buf));
255	if(strcmp(pw, su->pw_passwd) != 0)
256	    return 1;
257    }
258    return 0;
259}
260
261int
262main(int argc, char **argv)
263{
264    int i, optind = 0;
265    char *su_user;
266    struct passwd *su_info;
267    char *login_user = NULL;
268    struct passwd *login_info;
269
270    struct passwd *pwd;
271
272    char *shell;
273
274    int ok = 0;
275    int kerberos_error=1;
276
277    set_progname (argv[0]);
278
279    if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
280	usage(1);
281
282    for (i=0; i < optind; i++)
283      if (strcmp(argv[i], "-") == 0) {
284	 full_login = 1;
285	 break;
286      }
287
288    if(help_flag)
289	usage(0);
290    if(version_flag) {
291	print_version(NULL);
292	exit(0);
293    }
294    if(optind >= argc)
295	su_user = "root";
296    else
297	su_user = argv[optind++];
298
299    pwd = k_getpwnam(su_user);
300    if(pwd == NULL)
301	errx (1, "unknown login %s", su_user);
302    if (pwd->pw_uid == 0 && strcmp ("root", su_user) != 0) {
303	syslog (LOG_ALERT, "NIS attack, user %s has uid 0", su_user);
304	errx (1, "unknown login %s", su_user);
305    }
306    su_info = make_info(pwd);
307
308#if defined(HAVE_GETLOGIN) && !defined(POSIX_GETLOGIN)
309    login_user = getlogin();
310#endif
311    if(login_user == NULL || (pwd = getpwnam(login_user)) == NULL)
312	pwd = getpwuid(getuid());
313    if(pwd == NULL)
314	errx(1, "who are you?");
315    login_info = make_info(pwd);
316    if(env_flag)
317	shell = login_info->pw_shell;
318    else
319	shell = su_info->pw_shell;
320    if(shell == NULL || *shell == '\0')
321	shell = _PATH_BSHELL;
322
323    if(kerberos_flag && ok == 0 &&
324      (kerberos_error=krb5_verify(login_info, su_info, kerberos_instance)) == 0)
325	ok++;
326
327    if(ok == 0 && login_info->pw_uid && verify_unix(su_info) != 0) {
328	printf("Sorry!\n");
329	exit(1);
330    }
331
332#ifdef HAVE_GETSPNAM
333   {  struct spwd *sp;
334      long    today;
335
336    sp = getspnam(su_info->pw_name);
337    if (sp != NULL) {
338	today = time(0)/(24L * 60 * 60);
339	if (sp->sp_expire > 0) {
340	    if (today >= sp->sp_expire) {
341		if (login_info->pw_uid)
342		    errx(1,"Your account has expired.");
343		else
344		    printf("Your account has expired.");
345            }
346            else if (sp->sp_expire - today < 14)
347                printf("Your account will expire in %d days.\n",
348		       (int)(sp->sp_expire - today));
349	}
350	if (sp->sp_max > 0) {
351	    if (today >= sp->sp_lstchg + sp->sp_max) {
352		if (login_info->pw_uid)
353		    errx(1,"Your password has expired. Choose a new one.");
354		else
355		    printf("Your password has expired. Choose a new one.");
356	    }
357	    else if (today >= sp->sp_lstchg + sp->sp_max - sp->sp_warn)
358		printf("Your account will expire in %d days.\n",
359		       (int)(sp->sp_lstchg + sp->sp_max -today));
360	}
361    }
362    }
363#endif
364    {
365	char *tty = ttyname (STDERR_FILENO);
366	syslog (LOG_NOTICE | LOG_AUTH, tty ? "%s to %s" : "%s to %s on %s",
367		login_info->pw_name, su_info->pw_name, tty);
368    }
369
370
371    if(!env_flag) {
372	if(full_login) {
373	    char *t = getenv ("TERM");
374
375	    environ = malloc (10 * sizeof (char *));
376	    if (environ == NULL)
377		err (1, "malloc");
378	    environ[0] = NULL;
379	    esetenv ("PATH", _PATH_DEFPATH, 1);
380	    if (t)
381		esetenv ("TERM", t, 1);
382	    if (chdir (su_info->pw_dir) < 0)
383		errx (1, "no directory");
384	}
385	if (full_login || su_info->pw_uid)
386	    esetenv ("USER", su_info->pw_name, 1);
387	esetenv("HOME", su_info->pw_dir, 1);
388	esetenv("SHELL", shell, 1);
389    }
390
391    {
392	int i;
393	char **args;
394	char *p;
395
396	p = strrchr(shell, '/');
397	if(p)
398	    p++;
399	else
400	    p = shell;
401
402	if (strcmp(p, "csh") != 0)
403	    csh_f_flag = 0;
404
405        args = malloc(((cmd ? 2 : 0) + 1 + argc - optind + 1 + csh_f_flag) * sizeof(*args));
406	if (args == NULL)
407	    err (1, "malloc");
408	i = 0;
409	if(full_login)
410	    asprintf(&args[i++], "-%s", p);
411	else
412	    args[i++] = p;
413	if (cmd) {
414	   args[i++] = "-c";
415	   args[i++] = cmd;
416	}
417
418	if (csh_f_flag)
419	    args[i++] = "-f";
420
421	for (argv += optind; *argv; ++argv)
422	   args[i++] = *argv;
423	args[i] = NULL;
424
425	if(setgid(su_info->pw_gid) < 0)
426	    err(1, "setgid");
427	if (initgroups (su_info->pw_name, su_info->pw_gid) < 0)
428	    err (1, "initgroups");
429	if(setuid(su_info->pw_uid) < 0
430	   || (su_info->pw_uid != 0 && setuid(0) == 0))
431	    err(1, "setuid");
432
433#ifdef KRB5
434        if (!kerberos_error)
435           krb5_start_session();
436#endif
437	execv(shell, args);
438    }
439
440    exit(1);
441}
442