xref: /original-bsd/usr.bin/su/su.c (revision 183db9ee)
1 /*
2  * Copyright (c) 1988 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 char copyright[] =
20 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
21  All rights reserved.\n";
22 #endif /* not lint */
23 
24 #ifndef lint
25 static char sccsid[] = "@(#)su.c	5.10 (Berkeley) 10/18/88";
26 #endif /* not lint */
27 
28 #include <sys/param.h>
29 #include <sys/time.h>
30 #include <sys/resource.h>
31 #include <syslog.h>
32 #include <stdio.h>
33 #include <pwd.h>
34 #include <grp.h>
35 
36 main(argc, argv)
37 	int argc;
38 	char **argv;
39 {
40 	extern char **environ;
41 	extern int errno, optind;
42 	register struct passwd *pwd;
43 	register char *p, **g;
44 	struct group *gr;
45 	uid_t ruid, getuid();
46 	int asme, ch, fulllogin, fastlogin, prio;
47 	enum { UNSET, YES, NO } iscsh = UNSET;
48 	char *user, *shell, *username, *cleanenv[2], *nargv[4], **np;
49 	char namebuf[50], shellbuf[MAXPATHLEN];
50 	char *crypt(), *getpass(), *getenv(), *getlogin(), *rindex(), *strcpy();
51 
52 	np = &nargv[3];
53 	*np-- = NULL;
54 	asme = fulllogin = fastlogin = 0;
55 	while ((ch = getopt(argc, argv, "-flm")) != EOF)
56 		switch((char)ch) {
57 		case 'f':
58 			fastlogin = 1;
59 			break;
60 		case '-':
61 		case 'l':
62 			fulllogin = 1;
63 			break;
64 		case 'm':
65 			asme = 1;
66 			break;
67 		case '?':
68 		default:
69 			fprintf(stderr, "usage: su [-flm] [login]\n");
70 			exit(1);
71 		}
72 	argv += optind;
73 
74 	errno = 0;
75 	prio = getpriority(PRIO_PROCESS, 0);
76 	if (errno)
77 		prio = 0;
78 	(void)setpriority(PRIO_PROCESS, 0, -2);
79 
80 	/* get current login name and shell */
81 	if ((pwd = getpwuid(ruid = getuid())) == NULL) {
82 		fprintf(stderr, "su: who are you?\n");
83 		exit(1);
84 	}
85 	username = strcpy(namebuf, pwd->pw_name);
86 	if (asme)
87 		if (pwd->pw_shell && *pwd->pw_shell)
88 			shell = strcpy(shellbuf,  pwd->pw_shell);
89 		else {
90 			shell = "/bin/sh";
91 			iscsh = NO;
92 		}
93 
94 	/* get target login information */
95 	user = *argv ? *argv : "root";
96 	if ((pwd = getpwnam(user)) == NULL) {
97 		fprintf(stderr, "su: unknown login %s\n", user);
98 		exit(1);
99 	}
100 
101 	/* only allow those in group zero to su to root. */
102 	if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
103 		for (g = gr->gr_mem;; ++g) {
104 			if (!*g) {
105 				fprintf(stderr, "su: you are not in the correct group to su %s.\n", user);
106 				exit(1);
107 			}
108 			if (!strcmp(username, *g))
109 				break;
110 		}
111 
112 	/* if target requires a password, verify it */
113 	if (ruid && *pwd->pw_passwd) {
114 		p = getpass("Password:");
115 		if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
116 			fprintf(stderr, "Sorry\n");
117 			if (pwd->pw_uid == 0)
118 				syslog(LOG_CRIT|LOG_AUTH, "su: BAD SU %s on %s", username, ttyname(2));
119 			exit(1);
120 		}
121 	}
122 
123 	/* if not asme or target's shell isn't standard, use it */
124 	if (!asme || !chshell(pwd->pw_shell))
125 		if (pwd->pw_shell && *pwd->pw_shell) {
126 			shell = pwd->pw_shell;
127 			iscsh = UNSET;
128 		} else {
129 			shell = "/bin/sh";
130 			iscsh = NO;
131 		}
132 
133 	/* if we're forking a csh, we want to slightly muck the args */
134 	if (iscsh == UNSET) {
135 		if (p = rindex(shell, '/'))
136 			++p;
137 		else
138 			p = shell;
139 		iscsh = strcmp(p, "csh") ? NO : YES;
140 	}
141 
142 	/* set permissions */
143 	if (setgid(pwd->pw_gid) < 0) {
144 		perror("su: setgid");
145 		exit(1);
146 	}
147 	if (initgroups(user, pwd->pw_gid)) {
148 		fprintf(stderr, "su: initgroups failed\n");
149 		exit(1);
150 	}
151 	if (setuid(pwd->pw_uid) < 0) {
152 		perror("su: setuid");
153 		exit(1);
154 	}
155 
156 	if (!asme) {
157 		if (fulllogin) {
158 			p = getenv("TERM");
159 			cleanenv[0] = "PATH=:/usr/ucb:/bin:/usr/bin";
160 			cleanenv[1] = NULL;
161 			environ = cleanenv;
162 			(void)setenv("TERM", p, 1);
163 			if (chdir(pwd->pw_dir) < 0) {
164 				fprintf(stderr, "su: no directory\n");
165 				exit(1);
166 			}
167 		}
168 		if (fulllogin || pwd->pw_uid)
169 			(void)setenv("USER", pwd->pw_name, 1);
170 		(void)setenv("HOME", pwd->pw_dir, 1);
171 		(void)setenv("SHELL", shell, 1);
172 	}
173 
174 	if (iscsh == YES) {
175 		if (fastlogin)
176 			*np-- = "-f";
177 		if (asme)
178 			*np-- = "-m";
179 	}
180 
181 	/* csh strips the first character... */
182 	*np = fulllogin ? "-su" : iscsh == YES ? "_su" : "su";
183 
184 	if (pwd->pw_uid == 0)
185 		syslog(LOG_NOTICE|LOG_AUTH, "su: %s on %s",
186 		    username, ttyname(2));
187 
188 	(void)setpriority(PRIO_PROCESS, 0, prio);
189 
190 	execv(shell, np);
191 	fprintf(stderr, "su: no shell.\n");
192 	exit(1);
193 }
194 
195 chshell(sh)
196 	char *sh;
197 {
198 	char *cp, *getusershell();
199 
200 	while ((cp = getusershell()) != NULL)
201 		if (!strcmp(cp, sh))
202 			return(1);
203 	return(0);
204 }
205