1/* 2 * Copyright (c) 2007-2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#define _PAM_EXTERN_FUNCTIONS 25#define PAM_SM_SESSION 26#include <security/pam_modules.h> 27#include <security/pam_appl.h> 28 29#include <stdio.h> 30#include <string.h> 31#include <unistd.h> 32#include <vproc.h> 33#include <vproc_priv.h> 34#include <servers/bootstrap.h> 35#include <bootstrap_priv.h> 36#include <pwd.h> 37#include <sys/syslimits.h> 38 39#define SESSION_TYPE_OPT "launchd_session_type" 40#define DEFAULT_SESSION_TYPE VPROCMGR_SESSION_BACKGROUND 41#define NULL_SESSION_TYPE "NullSession" 42 43/* An application may modify the default behavior as follows: 44 * (1) Choose a specific session type: 45 * pam_putenv(pamh, "launchd_session_type=Aqua"); 46 * (2) Choose to not start a new session: 47 * pam_putenv(pamh, "launchd_session_type=NullSession"); 48 * Otherwise, if launchd_session_type is not set, a new session of the 49 * default type will be created. 50 */ 51 52extern vproc_err_t _vproc_post_fork_ping(void); 53 54static mach_port_t 55get_root_bootstrap_port(void) 56{ 57 mach_port_t parent_port = MACH_PORT_NULL; 58 mach_port_t previous_port = MACH_PORT_NULL; 59 do { 60 if (previous_port) { 61 if (previous_port != bootstrap_port) { 62 mach_port_deallocate(mach_task_self(), previous_port); 63 } 64 previous_port = parent_port; 65 } else { 66 previous_port = bootstrap_port; 67 } 68 if (bootstrap_parent(previous_port, &parent_port) != 0) { 69 return MACH_PORT_NULL; 70 } 71 } while (parent_port != previous_port); 72 73 return parent_port; 74} 75 76PAM_EXTERN int 77pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 78{ 79 char buffer[2*PATH_MAX]; 80 const char* default_session_type = DEFAULT_SESSION_TYPE; 81 const char* session_type = pam_getenv(pamh, SESSION_TYPE_OPT); 82 const char* username; 83 struct passwd *pwd; 84 struct passwd pwdbuf; 85 uid_t uid, suid; 86 87 /* Deterine the launchd session type. */ 88 if (NULL == (default_session_type = openpam_get_option(pamh, SESSION_TYPE_OPT))) { 89 openpam_log(PAM_LOG_DEBUG, "No session type specified."); 90 default_session_type = DEFAULT_SESSION_TYPE; 91 } 92 if (NULL == session_type) { 93 session_type = default_session_type; 94 } else if (0 == strcmp(session_type, NULL_SESSION_TYPE)) { 95 openpam_log(PAM_LOG_DEBUG, "Skipping due to NULL session type."); 96 return PAM_IGNORE; 97 } 98 99 /* Get the username (and UID). */ 100 if (PAM_SUCCESS != pam_get_item(pamh, PAM_USER, (void *)&username) || NULL == username) { 101 openpam_log(PAM_LOG_DEBUG, "The username could not be obtained."); 102 return PAM_IGNORE; 103 } 104 if (0 != getpwnam_r(username, &pwdbuf, buffer, sizeof(buffer), &pwd) || NULL == pwd) { 105 openpam_log(PAM_LOG_DEBUG, "The pwd for %s could not be obtained.", username); 106 return PAM_IGNORE; 107 } 108 uid = pwd->pw_uid; 109 openpam_log(PAM_LOG_DEBUG, "Going to switch to (%s) %u's %s session", username, uid, session_type); 110 111 /* If we're running as root, set the root Mach bootstrap as our bootstrap port. If not, we fail. */ 112 if (geteuid() == 0) { 113 mach_port_t rbs = get_root_bootstrap_port(); 114 if (rbs) { 115 mach_port_mod_refs(mach_task_self(), bootstrap_port, MACH_PORT_RIGHT_SEND, -1); 116 task_set_bootstrap_port(mach_task_self(), rbs); 117 bootstrap_port = rbs; 118 } 119 } else { 120 openpam_log(PAM_LOG_DEBUG, "We are not running as root."); 121 return PAM_IGNORE; 122 } 123 124 /* We need to set the UID to appease launchd, then lookup the per-user bootstrap. */ 125 suid = getuid(); 126 setreuid(0, 0); 127 mach_port_t puc = MACH_PORT_NULL; 128 kern_return_t kr = bootstrap_look_up_per_user(bootstrap_port, NULL, uid, &puc); 129 setreuid(suid, 0); 130 if (BOOTSTRAP_SUCCESS != kr) { 131 openpam_log(PAM_LOG_ERROR, "Could not look up per-user bootstrap for UID %u.", uid); 132 return PAM_IGNORE; 133 } else if (BOOTSTRAP_NOT_PRIVILEGED == kr) { 134 openpam_log(PAM_LOG_ERROR, "Permission denied to look up per-user bootstrap for UID %u.", uid); 135 /* If this happens, bootstrap_port is probably already set appropriately anyway. */ 136 return PAM_IGNORE; 137 } 138 139 /* Set our bootstrap port to be that of the Background session of the per-user launchd. */ 140 mach_port_mod_refs(mach_task_self(), bootstrap_port, MACH_PORT_RIGHT_SEND, -1); 141 task_set_bootstrap_port(mach_task_self(), puc); 142 bootstrap_port = puc; 143 144 /* Now move ourselves into the appropriate session. */ 145 if (strncmp(session_type, VPROCMGR_SESSION_BACKGROUND, sizeof(VPROCMGR_SESSION_BACKGROUND)) != 0) { 146 vproc_err_t verr = NULL; 147 if (NULL != (verr = _vprocmgr_switch_to_session(session_type, 0))) { 148 openpam_log(PAM_LOG_ERROR, "Unable to switch to %u's %s session (0x%p).", uid, session_type, verr); 149 return PAM_SESSION_ERR; 150 } 151 } 152 if (NULL != _vproc_post_fork_ping()) { 153 openpam_log(PAM_LOG_ERROR, "Calling _vproc_post_fork_ping failed."); 154 return PAM_SESSION_ERR; 155 } 156 157 return PAM_SUCCESS; 158} 159 160PAM_EXTERN int 161pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 162{ 163 return PAM_SUCCESS; 164} 165