xref: /original-bsd/usr.bin/su/su.c (revision 7e95f7c4)
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.12 (Berkeley) 03/05/89";
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 #include "pathnames.h"
36 
37 main(argc, argv)
38 	int argc;
39 	char **argv;
40 {
41 	extern char **environ;
42 	extern int errno, optind;
43 	register struct passwd *pwd;
44 	register char *p, **g;
45 	struct group *gr;
46 	uid_t ruid, getuid();
47 	int asme, ch, fulllogin, fastlogin, prio;
48 	enum { UNSET, YES, NO } iscsh = UNSET;
49 	char *user, *shell, *username, *cleanenv[2], *nargv[4], **np;
50 	char namebuf[50], shellbuf[MAXPATHLEN];
51 	char *crypt(), *getpass(), *getenv(), *getlogin(), *rindex(), *strcpy();
52 
53 	np = &nargv[3];
54 	*np-- = NULL;
55 	asme = fulllogin = fastlogin = 0;
56 	while ((ch = getopt(argc, argv, "-flm")) != EOF)
57 		switch((char)ch) {
58 		case 'f':
59 			fastlogin = 1;
60 			break;
61 		case '-':
62 		case 'l':
63 			fulllogin = 1;
64 			break;
65 		case 'm':
66 			asme = 1;
67 			break;
68 		case '?':
69 		default:
70 			fprintf(stderr, "usage: su [-flm] [login]\n");
71 			exit(1);
72 		}
73 	argv += optind;
74 
75 	errno = 0;
76 	prio = getpriority(PRIO_PROCESS, 0);
77 	if (errno)
78 		prio = 0;
79 	(void)setpriority(PRIO_PROCESS, 0, -2);
80 
81 	/* get current login name and shell */
82 	if ((pwd = getpwuid(ruid = getuid())) == NULL) {
83 		fprintf(stderr, "su: who are you?\n");
84 		exit(1);
85 	}
86 	username = strcpy(namebuf, pwd->pw_name);
87 	if (asme)
88 		if (pwd->pw_shell && *pwd->pw_shell)
89 			shell = strcpy(shellbuf,  pwd->pw_shell);
90 		else {
91 			shell = _PATH_BSHELL;
92 			iscsh = NO;
93 		}
94 
95 	/* get target login information */
96 	user = *argv ? *argv : "root";
97 	if ((pwd = getpwnam(user)) == NULL) {
98 		fprintf(stderr, "su: unknown login %s\n", user);
99 		exit(1);
100 	}
101 
102 	/* only allow those in group zero to su to root. */
103 	if (pwd->pw_uid == 0 && (gr = getgrgid((gid_t)0)))
104 		for (g = gr->gr_mem;; ++g) {
105 			if (!*g) {
106 				fprintf(stderr, "su: you are not in the correct group to su %s.\n", user);
107 				exit(1);
108 			}
109 			if (!strcmp(username, *g))
110 				break;
111 		}
112 
113 	/* if target requires a password, verify it */
114 	if (ruid && *pwd->pw_passwd) {
115 		p = getpass("Password:");
116 		if (strcmp(pwd->pw_passwd, crypt(p, pwd->pw_passwd))) {
117 			fprintf(stderr, "Sorry\n");
118 			if (pwd->pw_uid == 0)
119 				syslog(LOG_CRIT|LOG_AUTH, "su: BAD SU %s on %s", username, ttyname(2));
120 			exit(1);
121 		}
122 	}
123 
124 	if (asme) {
125 		/* if asme and non-standard target shell, must be root */
126 		if (!chshell(pwd->pw_shell) && ruid) {
127 			fprintf(stderr, "su: Permission denied.\n");
128 			exit(1);
129 		}
130 	}
131 	else if (pwd->pw_shell && *pwd->pw_shell) {
132 		shell = pwd->pw_shell;
133 		iscsh = UNSET;
134 	} else {
135 		shell = _PATH_BSHELL;
136 		iscsh = NO;
137 	}
138 
139 	/* if we're forking a csh, we want to slightly muck the args */
140 	if (iscsh == UNSET) {
141 		if (p = rindex(shell, '/'))
142 			++p;
143 		else
144 			p = shell;
145 		iscsh = strcmp(p, "csh") ? NO : YES;
146 	}
147 
148 	/* set permissions */
149 	if (setgid(pwd->pw_gid) < 0) {
150 		perror("su: setgid");
151 		exit(1);
152 	}
153 	if (initgroups(user, pwd->pw_gid)) {
154 		fprintf(stderr, "su: initgroups failed\n");
155 		exit(1);
156 	}
157 	if (setuid(pwd->pw_uid) < 0) {
158 		perror("su: setuid");
159 		exit(1);
160 	}
161 
162 	if (!asme) {
163 		if (fulllogin) {
164 			p = getenv("TERM");
165 			cleanenv[0] = _PATH_SEARCHPATH;
166 			cleanenv[1] = NULL;
167 			environ = cleanenv;
168 			(void)setenv("TERM", p, 1);
169 			if (chdir(pwd->pw_dir) < 0) {
170 				fprintf(stderr, "su: no directory\n");
171 				exit(1);
172 			}
173 		}
174 		if (fulllogin || pwd->pw_uid)
175 			(void)setenv("USER", pwd->pw_name, 1);
176 		(void)setenv("HOME", pwd->pw_dir, 1);
177 		(void)setenv("SHELL", shell, 1);
178 	}
179 
180 	if (iscsh == YES) {
181 		if (fastlogin)
182 			*np-- = "-f";
183 		if (asme)
184 			*np-- = "-m";
185 	}
186 
187 	/* csh strips the first character... */
188 	*np = fulllogin ? "-su" : iscsh == YES ? "_su" : "su";
189 
190 	if (pwd->pw_uid == 0)
191 		syslog(LOG_NOTICE|LOG_AUTH, "su: %s on %s",
192 		    username, ttyname(2));
193 
194 	(void)setpriority(PRIO_PROCESS, 0, prio);
195 
196 	execv(shell, np);
197 	fprintf(stderr, "su: no shell.\n");
198 	exit(1);
199 }
200 
201 chshell(sh)
202 	char *sh;
203 {
204 	char *cp, *getusershell();
205 
206 	while ((cp = getusershell()) != NULL)
207 		if (!strcmp(cp, sh))
208 			return(1);
209 	return(0);
210 }
211