1 /*- 2 * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 3 * Copyright (c) 2004-2007 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 * $Id: su.c 408 2007-12-21 11:36:24Z des $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/wait.h> 40 41 #include <err.h> 42 #include <grp.h> 43 #include <pwd.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <syslog.h> 48 #include <unistd.h> 49 50 #include <security/pam_appl.h> 51 #include <security/openpam.h> /* for openpam_ttyconv() */ 52 53 extern char **environ; 54 55 static pam_handle_t *pamh; 56 static struct pam_conv pamc; 57 58 static void 59 usage(void) 60 { 61 62 fprintf(stderr, "Usage: su [login [args]]\n"); 63 exit(1); 64 } 65 66 int 67 main(int argc, char *argv[]) 68 { 69 char hostname[MAXHOSTNAMELEN]; 70 const char *user, *tty; 71 const void *item; 72 char **args, **pam_envlist, **pam_env; 73 struct passwd *pwd; 74 int o, pam_err, status; 75 pid_t pid; 76 77 while ((o = getopt(argc, argv, "h")) != -1) 78 switch (o) { 79 case 'h': 80 default: 81 usage(); 82 } 83 84 argc -= optind; 85 argv += optind; 86 87 if (argc > 0) { 88 user = *argv; 89 --argc; 90 ++argv; 91 } else { 92 user = "root"; 93 } 94 95 /* initialize PAM */ 96 pamc.conv = &openpam_ttyconv; 97 pam_start("su", user, &pamc, &pamh); 98 99 /* set some items */ 100 gethostname(hostname, sizeof(hostname)); 101 if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS) 102 goto pamerr; 103 user = getlogin(); 104 if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS) 105 goto pamerr; 106 tty = ttyname(STDERR_FILENO); 107 if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS) 108 goto pamerr; 109 110 /* authenticate the applicant */ 111 if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS) 112 goto pamerr; 113 if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD) 114 pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); 115 if (pam_err != PAM_SUCCESS) 116 goto pamerr; 117 118 /* establish the requested credentials */ 119 if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) 120 goto pamerr; 121 122 /* authentication succeeded; open a session */ 123 if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) 124 goto pamerr; 125 126 /* get mapped user name; PAM may have changed it */ 127 pam_err = pam_get_item(pamh, PAM_USER, &item); 128 if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user = item)) == NULL) 129 goto pamerr; 130 131 /* export PAM environment */ 132 if ((pam_envlist = pam_getenvlist(pamh)) != NULL) { 133 for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) { 134 putenv(*pam_env); 135 free(*pam_env); 136 } 137 free(pam_envlist); 138 } 139 140 /* build argument list */ 141 if ((args = calloc(argc + 2, sizeof *args)) == NULL) { 142 warn("calloc()"); 143 goto err; 144 } 145 *args = pwd->pw_shell; 146 memcpy(args + 1, argv, argc * sizeof *args); 147 148 /* fork and exec */ 149 switch ((pid = fork())) { 150 case -1: 151 warn("fork()"); 152 goto err; 153 case 0: 154 /* child: give up privs and start a shell */ 155 156 /* set uid and groups */ 157 if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { 158 warn("initgroups()"); 159 _exit(1); 160 } 161 if (setgid(pwd->pw_gid) == -1) { 162 warn("setgid()"); 163 _exit(1); 164 } 165 if (setuid(pwd->pw_uid) == -1) { 166 warn("setuid()"); 167 _exit(1); 168 } 169 execve(*args, args, environ); 170 warn("execve()"); 171 _exit(1); 172 default: 173 /* parent: wait for child to exit */ 174 waitpid(pid, &status, 0); 175 176 /* close the session and release PAM resources */ 177 pam_err = pam_close_session(pamh, 0); 178 pam_end(pamh, pam_err); 179 180 exit(WEXITSTATUS(status)); 181 } 182 183 pamerr: 184 fprintf(stderr, "Sorry\n"); 185 err: 186 pam_end(pamh, pam_err); 187 exit(1); 188 } 189