aboutsummaryrefslogtreecommitdiff
path: root/contrib/sucap/su.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sucap/su.c')
-rw-r--r--contrib/sucap/su.c202
1 files changed, 117 insertions, 85 deletions
diff --git a/contrib/sucap/su.c b/contrib/sucap/su.c
index 5c98e5f..e3dfe70 100644
--- a/contrib/sucap/su.c
+++ b/contrib/sucap/su.c
@@ -67,7 +67,6 @@
extern char **environ;
static pam_handle_t *pamh = NULL;
-static int state;
static int wait_for_child_caught=0;
static int need_job_control=0;
@@ -101,7 +100,7 @@ static const char *posix_env[] = {
* make_environment transcribes a selection of environment variables
* from the invoking user.
*/
-static int make_environment(pam_handle_t *pamh, int keep_env)
+static int make_environment(int keep_env)
{
const char *tmpe;
int i;
@@ -150,20 +149,26 @@ static void checkfds(void)
if (fstat(1, &st) == -1) {
fd = open("/dev/null", O_WRONLY);
- if (fd == -1) exit(1);
+ if (fd == -1) goto badfds;
if (fd != 1) {
- if (dup2(fd, 1) == -1) exit(1);
- if (close(fd) == -1) exit(1);
+ if (dup2(fd, 1) == -1) goto badfds;
+ if (close(fd) == -1) goto badfds;
}
}
if (fstat(2, &st) == -1) {
fd = open("/dev/null", O_WRONLY);
- if (fd == -1) exit(1);
+ if (fd == -1) goto badfds;
if (fd != 2) {
- if (dup2(fd, 2) == -1) exit(1);
- if (close(fd) == -1) exit(1);
+ if (dup2(fd, 2) == -1) goto badfds;
+ if (close(fd) == -1) goto badfds;
}
}
+
+ return;
+
+badfds:
+ perror("bad filedes");
+ exit(1);
}
/*
@@ -347,7 +352,8 @@ static void restore_terminal_owner(void)
* In the case of an error "err_descr" is set to the error message
* and "callname" to the name of the failed call.
*/
-int make_process_unkillable(const char **callname, const char **err_descr)
+static int make_process_unkillable(const char **callname,
+ const char **err_descr)
{
invoked_uid = getuid();
if (invoked_uid == TEMP_UID) {
@@ -367,14 +373,14 @@ int make_process_unkillable(const char **callname, const char **err_descr)
* make_process_killable restores the invoking uid to the current
* process.
*/
-void make_process_killable()
+static void make_process_killable(void)
{
(void) cap_setuid(invoked_uid);
}
/* ------ command line parser ----------------- */
-void usage(int exit_val)
+static void usage(int exit_val)
{
fprintf(stderr,"usage: su [-] [-h] [-c \"command\"] [username]\n");
exit(exit_val);
@@ -384,8 +390,8 @@ void usage(int exit_val)
* parse_command_line extracts the options from the command line
* arguments.
*/
-void parse_command_line(int argc, char *argv[],
- int *is_login, const char **user, const char **command)
+static void parse_command_line(int argc, char *argv[], int *is_login,
+ const char **user, const char **command)
{
int username_present, command_present;
@@ -464,7 +470,7 @@ static void prepare_for_job_control(int need_it)
need_job_control = need_it;
}
-int wait_for_child(pid_t child)
+static int wait_for_child(pid_t child)
{
int retval, status, exit_code;
sigset_t ourset;
@@ -585,8 +591,8 @@ int wait_for_child(pid_t child)
* Next some code that parses the spawned shell command line.
*/
-static char * const *build_shell_args(const char *pw_shell, int login,
- const char *command)
+static const char * const *build_shell_args(const char *pw_shell, int login,
+ const char *command)
{
int use_default = 1; /* flag to signal we should use the default shell */
const char **args=NULL; /* array of PATH+ARGS+NULL pointers */
@@ -714,7 +720,7 @@ static char * const *build_shell_args(const char *pw_shell, int login,
}
D(("returning arg list"));
- return (char * const *) args;
+ return (const char * const *) args;
}
@@ -738,20 +744,6 @@ static void exit_now(int exit_code, const char *format, ...)
exit(exit_code);
}
-static void exit_child_now(int exit_code, const char *format, ...)
-{
- va_list args;
-
- va_start(args,format);
- vfprintf(stderr, format, args);
- va_end(args);
-
- if (pamh != NULL)
- pam_end(pamh, (exit_code ? PAM_ABORT:PAM_SUCCESS) | PAM_DATA_SILENT);
-
- exit(exit_code);
-}
-
/* ------ PAM setup --------------------------- */
static struct pam_conv conv = {
@@ -779,8 +771,8 @@ static void do_pam_init(const char *user, int is_login)
* Fill in some blanks
*/
- retval = make_environment(pamh, !is_login);
- D(("made_environment returned: %s", pam_strerror(pamh,retval)));
+ retval = make_environment(!is_login);
+ D(("made_environment returned: %s", pam_strerror(pamh, retval)));
if (retval == PAM_SUCCESS && is_terminal) {
const char *terminal = ttyname(STDIN_FILENO);
@@ -820,8 +812,7 @@ static void do_pam_init(const char *user, int is_login)
/*
* authenticate_user arranges for the PAM authentication stack to run.
*/
-static int authenticate_user(pam_handle_t *pamh, cap_t all,
- int *retval, const char **place,
+static int authenticate_user(cap_t all, int *retval, const char **place,
const char **err_descr)
{
*place = "pre-auth cap_set_proc";
@@ -841,8 +832,7 @@ static int authenticate_user(pam_handle_t *pamh, cap_t all,
/*
* user_accounting confirms an authenticated user is permitted service.
*/
-static int user_accounting(pam_handle_t *pamh, cap_t all,
- int *retval, const char **place,
+static int user_accounting(cap_t all, int *retval, const char **place,
const char **err_descr) {
*place = "user_accounting";
if (cap_set_proc(all)) {
@@ -1092,13 +1082,10 @@ static int utmp_do_open_session(const char *user, const char *terminal,
static int utmp_do_close_session(const char *terminal,
const char **place, const char **err_descr)
{
- int retval;
struct utmp u_tmp;
const struct utmp *u_tmp_p;
char ut_line[UT_LINESIZE], ut_id[UT_IDSIZE];
- retval = 0;
-
set_terminal_name(terminal, ut_line, ut_id);
utmpname(_PATH_UTMP);
@@ -1114,7 +1101,7 @@ static int utmp_do_close_session(const char *terminal,
memcpy(&u_tmp, login_stored_utmp, sizeof(u_tmp));
u_tmp.ut_time = time(NULL); /* a new time to restart */
- retval = write_wtmp(&u_tmp, place, err_descr);
+ write_wtmp(&u_tmp, place, err_descr);
memset(login_stored_utmp, 0, sizeof(u_tmp)); /* reset entry */
free(login_stored_utmp);
@@ -1133,7 +1120,7 @@ static int utmp_do_close_session(const char *terminal,
setutent(); /* rewind file (replace old) */
pututline(&u_tmp); /* mark as dead */
- retval = write_wtmp(&u_tmp, place, err_descr);
+ write_wtmp(&u_tmp, place, err_descr);
}
}
@@ -1155,8 +1142,7 @@ static int utmp_do_close_session(const char *terminal,
* place and err_descr will be set
* Be careful: the function indirectly uses alarm().
*/
-static int utmp_open_session(pam_handle_t *pamh, pid_t pid,
- int *retval,
+static int utmp_open_session(pid_t pid, int *retval,
const char **place, const char **err_descr)
{
const char *user, *terminal, *rhost;
@@ -1177,8 +1163,7 @@ static int utmp_open_session(pam_handle_t *pamh, pid_t pid,
return utmp_do_open_session(user, terminal, rhost, pid, place, err_descr);
}
-static int utmp_close_session(pam_handle_t *pamh
- , const char **place, const char **err_descr)
+static int utmp_close_session(const char **place, const char **err_descr)
{
int retval;
const char *terminal;
@@ -1194,12 +1179,12 @@ static int utmp_close_session(pam_handle_t *pamh
}
/*
- * set_credentials raises all of the process and PAM credentials.
+ * set_credentials raises the process and PAM credentials.
*/
-static int set_credentials(pam_handle_t *pamh, cap_t all, int login,
- const char **pw_shell,
- int *retval, const char **place,
- const char **err_descr)
+static int set_credentials(cap_t all, int login,
+ const char **user_p, uid_t *uid_p,
+ const char **pw_shell, int *retval,
+ const char **place, const char **err_descr)
{
const char *user;
char *shell;
@@ -1217,6 +1202,7 @@ static int set_credentials(pam_handle_t *pamh, cap_t all, int login,
*retval = PAM_USER_UNKNOWN;
return 1;
}
+ *user_p = user;
/*
* Add the LOGNAME and HOME environment variables.
@@ -1230,6 +1216,13 @@ static int set_credentials(pam_handle_t *pamh, cap_t all, int login,
}
uid = pw->pw_uid;
+ if (uid == 0) {
+ D(("user is superuser: %s", user));
+ *retval = PAM_CRED_ERR;
+ return 1;
+ }
+ *uid_p = uid;
+
shell = x_strdup(pw->pw_shell);
if (shell == NULL) {
D(("user %s has no shell", user));
@@ -1244,11 +1237,18 @@ static int set_credentials(pam_handle_t *pamh, cap_t all, int login,
*retval = PAM_CRED_ERR;
return 1;
}
- if (pam_misc_setenv(pamh, "HOME", pw->pw_dir, 0) != PAM_SUCCESS) {
- D(("failed to set HOME"));
- *retval = PAM_CRED_ERR;
- return 1;
- }
+ }
+
+ /* bash requires these be set to the target user values */
+ if (pam_misc_setenv(pamh, "HOME", pw->pw_dir, 0) != PAM_SUCCESS) {
+ D(("failed to set HOME"));
+ *retval = PAM_CRED_ERR;
+ return 1;
+ }
+ if (pam_misc_setenv(pamh, "USER", user, 0) != PAM_SUCCESS) {
+ D(("failed to set USER"));
+ *retval = PAM_CRED_ERR;
+ return 1;
}
current = cap_get_proc();
@@ -1300,8 +1300,8 @@ static int set_credentials(pam_handle_t *pamh, cap_t all, int login,
/*
* open_session invokes the open session PAM stack.
*/
-static int open_session(pam_handle_t *pamh, cap_t all,
- int *retval, const char **place, const char **err_descr)
+static int open_session(cap_t all, int *retval, const char **place,
+ const char **err_descr)
{
/* Open the su-session */
*place = "pam_open_session";
@@ -1321,11 +1321,11 @@ static int open_session(pam_handle_t *pamh, cap_t all,
static int launch_callback_fn(void *h)
{
- pam_handle_t *pamh = h;
+ pam_handle_t *my_pamh = h;
int retval;
D(("pam_end"));
- retval = pam_end(pamh, PAM_SUCCESS | PAM_DATA_SILENT);
+ retval = pam_end(my_pamh, PAM_SUCCESS | PAM_DATA_SILENT);
pamh = NULL;
if (retval != PAM_SUCCESS) {
return -1;
@@ -1337,22 +1337,34 @@ static int launch_callback_fn(void *h)
*/
enable_terminal_signals();
+#ifdef PAM_DEBUG
+ cap_iab_t iab = cap_iab_get_proc();
+ char *text = cap_iab_to_text(iab);
+ D(("iab = %s", text));
+ cap_free(text);
+ cap_free(iab);
+ cap_t cap = cap_get_proc();
+ text = cap_to_text(cap, NULL);
+ D(("cap = %s", text));
+ cap_free(text);
+ cap_free(cap);
+#endif
+
D(("about to launch"));
return 0;
}
/* Returns PAM_<STATUS>. */
-static int perform_launch_and_cleanup(cap_t all, int is_login,
+static int perform_launch_and_cleanup(cap_t all, int is_login, const char *user,
const char *shell, const char *command)
{
- int retval, status;
- const char *user, *home;
- uid_t uid;
- char * const * shell_args;
+ int status;
+ const char *home;
+ const char * const * shell_args;
char * const * shell_env;
cap_launch_t launcher;
pid_t child;
-
+ cap_iab_t iab;
/*
* Break up the shell command into a command and arguments
@@ -1387,6 +1399,12 @@ static int perform_launch_and_cleanup(cap_t all, int is_login,
return PAM_SYSTEM_ERR;
}
+ iab = cap_iab_get_proc();
+ if (iab == NULL) {
+ D(("failed to read IAB value of process"));
+ return PAM_SYSTEM_ERR;
+ }
+
launcher = cap_new_launcher(shell_args[0],
(const char * const *) &shell_args[1],
(const char * const *) shell_env);
@@ -1394,12 +1412,16 @@ static int perform_launch_and_cleanup(cap_t all, int is_login,
D(("failed to initialize launcher"));
return PAM_SYSTEM_ERR;
}
- cap_launcher_set_iab(launcher, cap_iab_get_proc());
cap_launcher_callback(launcher, launch_callback_fn);
child = cap_launch(launcher, pamh);
cap_free(launcher);
+ if (cap_set_proc(all) != 0) {
+ D(("failed to restore process capabilities"));
+ return PAM_SYSTEM_ERR;
+ }
+
/* job control is off for login sessions */
prepare_for_job_control(!is_login && command != NULL);
@@ -1415,7 +1437,7 @@ static int perform_launch_and_cleanup(cap_t all, int is_login,
return status;
}
-static void close_session(pam_handle_t *pamh, cap_t all)
+static void close_session(cap_t all)
{
int retval;
@@ -1439,13 +1461,14 @@ int main(int argc, char *argv[])
int retcode, is_login, status;
int retval, final_retval; /* PAM_xxx return values */
const char *command, *shell;
- pid_t child;
uid_t uid;
const char *place = NULL, *err_descr = NULL;
cap_t all, t_caps;
+ const char *user;
all = cap_get_proc();
cap_fill(all, CAP_EFFECTIVE, CAP_PERMITTED);
+ cap_clear_flag(all, CAP_INHERITABLE);
checkfds();
@@ -1457,10 +1480,10 @@ int main(int argc, char *argv[])
/* ---------- parse the argument list and --------- */
/* ------ initialize the Linux-PAM interface ------ */
{
- const char *user; /* transient until PAM_USER defined */
parse_command_line(argc, argv, &is_login, &user, &command);
place = "do_pam_init";
do_pam_init(user, is_login); /* call pam_start and set PAM items */
+ user = NULL; /* transient until PAM_USER defined */
}
/*
@@ -1481,7 +1504,7 @@ int main(int argc, char *argv[])
goto su_exit;
}
- if (authenticate_user(pamh, all, &retval, &place, &err_descr) != 0) {
+ if (authenticate_user(all, &retval, &place, &err_descr) != 0) {
goto auth_exit;
}
@@ -1489,12 +1512,18 @@ int main(int argc, char *argv[])
* The user is valid, but should they have access at this
* time?
*/
- if (user_accounting(pamh, all, &retval, &place, &err_descr) != 0) {
+ if (user_accounting(all, &retval, &place, &err_descr) != 0) {
goto auth_exit;
}
D(("su attempt is confirmed as authorized"));
+ if (set_credentials(all, is_login, &user, &uid, &shell,
+ &retval, &place, &err_descr) != 0) {
+ D(("failed to set credentials"));
+ goto auth_exit;
+ }
+
/*
* ... setup terminal, ...
*/
@@ -1507,12 +1536,6 @@ int main(int argc, char *argv[])
goto auth_exit;
}
- if (set_credentials(pamh, all, is_login,
- &shell, &retval, &place, &err_descr) != 0) {
- D(("failed to set credentials"));
- goto auth_exit;
- }
-
/*
* Here the IAB value is fixed and may differ from all's
* Inheritable value. So synthesize what we need to proceed in the
@@ -1539,8 +1562,7 @@ int main(int argc, char *argv[])
* Note: we use the parent pid as a session identifier for
* the logging.
*/
- retcode = utmp_open_session(pamh, getpid(),
- &retval, &place, &err_descr);
+ retcode = utmp_open_session(getpid(), &retval, &place, &err_descr);
if (retcode > 0) {
fprintf(stderr, PAM_APP_NAME ": %s: %s\n", place, err_descr);
err_descr = NULL; /* forget about this non-critical problem */
@@ -1549,17 +1571,25 @@ int main(int argc, char *argv[])
}
}
- if (open_session(pamh, t_caps, &retval, &place, &err_descr) != 0) {
+#ifdef PAM_DEBUG
+ cap_iab_t iab = cap_iab_get_proc();
+ char *text = cap_iab_to_text(iab);
+ D(("pre-session open iab = %s", text));
+ cap_free(text);
+ cap_free(iab);
+#endif
+
+ if (open_session(t_caps, &retval, &place, &err_descr) != 0) {
goto utmp_closer;
}
- status = perform_launch_and_cleanup(t_caps, is_login, shell, command);
- close_session(pamh, all);
+ status = perform_launch_and_cleanup(all, is_login, user, shell, command);
+ close_session(all);
utmp_closer:
if (is_login) {
/* do [uw]tmp cleanup */
- retcode = utmp_close_session(pamh, &place, &err_descr);
+ retcode = utmp_close_session(&place, &err_descr);
if (retcode) {
fprintf(stderr, PAM_APP_NAME ": %s: %s\n", place, err_descr);
}
@@ -1576,7 +1606,6 @@ delete_cred:
pam_strerror(pamh, retcode));
}
-old_owner:
D(("return terminal to local control"));
restore_terminal_owner();
@@ -1602,5 +1631,8 @@ auth_exit:
}
su_exit:
+ if (status != 0) {
+ perror(PAM_APP_NAME " failed");
+ }
exit(status); /* transparent exit */
}