xref: /netbsd-src/external/bsd/openpam/dist/bin/su/su.c (revision 63aea4bd5b445e491ff0389fe27ec78b3099dba3)
1 /*	$NetBSD: su.c,v 1.6 2014/10/24 18:27:12 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
5  * Copyright (c) 2004-2011 Dag-Erling Smørgrav
6  * All rights reserved.
7  *
8  * This software was developed for the FreeBSD Project by ThinkSec AS and
9  * Network Associates Laboratories, the Security Research Division of
10  * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
11  * ("CBOSS"), as part of the DARPA CHATS research program.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  * 3. The name of the author may not be used to endorse or promote
22  *    products derived from this software without specific prior written
23  *    permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  * Id: su.c 648 2013-03-05 17:54:27Z des
38  */
39 
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43 
44 #include <sys/param.h>
45 #include <sys/wait.h>
46 
47 #include <err.h>
48 #include <grp.h>
49 #include <pwd.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53 #include <syslog.h>
54 #include <unistd.h>
55 
56 #include <security/pam_appl.h>
57 #include <security/openpam.h>	/* for openpam_ttyconv() */
58 
59 extern char **environ;
60 
61 static pam_handle_t *pamh;
62 static struct pam_conv pamc;
63 
64 static void
65 usage(void)
66 {
67 
68 	fprintf(stderr, "usage: su [login [args]]\n");
69 	exit(1);
70 }
71 
72 int
73 main(int argc, char *argv[])
74 {
75 	char hostname[MAXHOSTNAMELEN];
76 	const char *user, *tty;
77 	const void *item;
78 	char **args, **pam_envlist, **pam_env;
79 	struct passwd *pwd;
80 	int o, pam_err, status;
81 	pid_t pid;
82 
83 	while ((o = getopt(argc, argv, "")) != -1)
84 		switch (o) {
85 		default:
86 			usage();
87 		}
88 
89 	argc -= optind;
90 	argv += optind;
91 
92 	if (argc > 0) {
93 		user = *argv;
94 		--argc;
95 		++argv;
96 	} else {
97 		user = "root";
98 	}
99 
100 	/* initialize PAM */
101 	pamc.conv = &openpam_ttyconv;
102 	pam_start("su", user, &pamc, &pamh);
103 
104 	/* set some items */
105 	gethostname(hostname, sizeof(hostname));
106 	if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
107 		goto pamerr;
108 	user = getlogin();
109 	if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
110 		goto pamerr;
111 	tty = ttyname(STDERR_FILENO);
112 	if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
113 		goto pamerr;
114 
115 	/* authenticate the applicant */
116 	if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
117 		goto pamerr;
118 	if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
119 		pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
120 	if (pam_err != PAM_SUCCESS)
121 		goto pamerr;
122 
123 	/* establish the requested credentials */
124 	if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
125 		goto pamerr;
126 
127 	/* authentication succeeded; open a session */
128 	if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
129 		goto pamerr;
130 
131 	/* get mapped user name; PAM may have changed it */
132 	pam_err = pam_get_item(pamh, PAM_USER, &item);
133 	if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user = item)) == NULL)
134 		goto pamerr;
135 
136 	/* export PAM environment */
137 	if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
138 		for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
139 			putenv(*pam_env);
140 			free(*pam_env);
141 		}
142 		free(pam_envlist);
143 	}
144 
145 	/* build argument list */
146 	if ((args = calloc(argc + 2, sizeof *args)) == NULL) {
147 		warn("calloc()");
148 		goto err;
149 	}
150 	*args = pwd->pw_shell;
151 	memcpy(args + 1, argv, argc * sizeof *args);
152 
153 	/* fork and exec */
154 	switch ((pid = fork())) {
155 	case -1:
156 		warn("fork()");
157 		goto err;
158 	case 0:
159 		/* child: give up privs and start a shell */
160 
161 		/* set uid and groups */
162 		if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
163 			warn("initgroups()");
164 			_exit(1);
165 		}
166 		if (setgid(pwd->pw_gid) == -1) {
167 			warn("setgid()");
168 			_exit(1);
169 		}
170 		if (setuid(pwd->pw_uid) == -1) {
171 			warn("setuid()");
172 			_exit(1);
173 		}
174 		execve(*args, args, environ);
175 		warn("execve()");
176 		_exit(1);
177 	default:
178 		/* parent: wait for child to exit */
179 		waitpid(pid, &status, 0);
180 
181 		/* close the session and release PAM resources */
182 		pam_err = pam_close_session(pamh, 0);
183 		pam_end(pamh, pam_err);
184 
185 		exit(WEXITSTATUS(status));
186 	}
187 
188 pamerr:
189 	fprintf(stderr, "Sorry\n");
190 err:
191 	pam_end(pamh, pam_err);
192 	exit(1);
193 }
194