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