#define _PAM_EXTERN_FUNCTIONS
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include <security/pam_appl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <vproc.h>
#include <vproc_priv.h>
#include <servers/bootstrap.h>
#include <bootstrap_priv.h>
#include <pwd.h>
#include <sys/syslimits.h>
#define SESSION_TYPE_OPT "launchd_session_type"
#define DEFAULT_SESSION_TYPE VPROCMGR_SESSION_BACKGROUND
#define NULL_SESSION_TYPE "NullSession"
extern vproc_err_t _vproc_post_fork_ping(void);
static mach_port_t
get_root_bootstrap_port(void)
{
mach_port_t parent_port = MACH_PORT_NULL;
mach_port_t previous_port = MACH_PORT_NULL;
do {
if (previous_port) {
if (previous_port != bootstrap_port) {
mach_port_deallocate(mach_task_self(), previous_port);
}
previous_port = parent_port;
} else {
previous_port = bootstrap_port;
}
if (bootstrap_parent(previous_port, &parent_port) != 0) {
return MACH_PORT_NULL;
}
} while (parent_port != previous_port);
return parent_port;
}
PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
char buffer[2*PATH_MAX];
const char* default_session_type = DEFAULT_SESSION_TYPE;
const char* session_type = pam_getenv(pamh, SESSION_TYPE_OPT);
const char* username;
struct passwd *pwd;
struct passwd pwdbuf;
uid_t uid, suid;
if (NULL == (default_session_type = openpam_get_option(pamh, SESSION_TYPE_OPT))) {
openpam_log(PAM_LOG_DEBUG, "No session type specified.");
default_session_type = DEFAULT_SESSION_TYPE;
}
if (NULL == session_type) {
session_type = default_session_type;
} else if (0 == strcmp(session_type, NULL_SESSION_TYPE)) {
openpam_log(PAM_LOG_DEBUG, "Skipping due to NULL session type.");
return PAM_IGNORE;
}
if (PAM_SUCCESS != pam_get_item(pamh, PAM_USER, (void *)&username) || NULL == username) {
openpam_log(PAM_LOG_DEBUG, "The username could not be obtained.");
return PAM_IGNORE;
}
if (0 != getpwnam_r(username, &pwdbuf, buffer, sizeof(buffer), &pwd) || NULL == pwd) {
openpam_log(PAM_LOG_DEBUG, "The pwd for %s could not be obtained.", username);
return PAM_IGNORE;
}
uid = pwd->pw_uid;
openpam_log(PAM_LOG_DEBUG, "Going to switch to (%s) %u's %s session", username, uid, session_type);
if (geteuid() == 0) {
mach_port_t rbs = get_root_bootstrap_port();
if (rbs) {
mach_port_mod_refs(mach_task_self(), bootstrap_port, MACH_PORT_RIGHT_SEND, -1);
task_set_bootstrap_port(mach_task_self(), rbs);
bootstrap_port = rbs;
}
} else {
openpam_log(PAM_LOG_DEBUG, "We are not running as root.");
return PAM_IGNORE;
}
suid = getuid();
setreuid(0, 0);
mach_port_t puc = MACH_PORT_NULL;
kern_return_t kr = bootstrap_look_up_per_user(bootstrap_port, NULL, uid, &puc);
setreuid(suid, 0);
if (BOOTSTRAP_SUCCESS != kr) {
openpam_log(PAM_LOG_ERROR, "Could not look up per-user bootstrap for UID %u.", uid);
return PAM_IGNORE;
} else if (BOOTSTRAP_NOT_PRIVILEGED == kr) {
openpam_log(PAM_LOG_ERROR, "Permission denied to look up per-user bootstrap for UID %u.", uid);
return PAM_IGNORE;
}
mach_port_mod_refs(mach_task_self(), bootstrap_port, MACH_PORT_RIGHT_SEND, -1);
task_set_bootstrap_port(mach_task_self(), puc);
bootstrap_port = puc;
if (strncmp(session_type, VPROCMGR_SESSION_BACKGROUND, sizeof(VPROCMGR_SESSION_BACKGROUND)) != 0) {
vproc_err_t verr = NULL;
if (NULL != (verr = _vprocmgr_switch_to_session(session_type, 0))) {
openpam_log(PAM_LOG_ERROR, "Unable to switch to %u's %s session (0x%p).", uid, session_type, verr);
return PAM_SESSION_ERR;
}
}
if (NULL != _vproc_post_fork_ping()) {
openpam_log(PAM_LOG_ERROR, "Calling _vproc_post_fork_ping failed.");
return PAM_SESSION_ERR;
}
return PAM_SUCCESS;
}
PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return PAM_SUCCESS;
}