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