diff options
Diffstat (limited to 'contrib/sucap/su.c')
-rw-r--r-- | contrib/sucap/su.c | 202 |
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 */ } |