1/* 2 * Copyright (c) 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#include <security/pam_appl.h> 25#include <security/pam_modules.h> 26#include <security/openpam.h> 27 28#include <sys/mount.h> 29#include <sys/param.h> 30#include <sys/stat.h> 31#include <stdio.h> 32#include <unistd.h> 33#include <limits.h> 34#include <string.h> 35#include <pwd.h> 36#include <utmpx.h> 37 38#include <CoreFoundation/CoreFoundation.h> 39#include <OpenDirectory/OpenDirectory.h> 40#include <NetFS/URLMount.h> 41#include <DiskImages/DIHLFileVaultInterface.h> 42#include <DiskImages/DIFrameworkUtilities.h> 43 44#include "Common.h" 45 46#define PM_DISPLAY_NAME "mount" 47 48PAM_EXTERN int 49pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) 50{ 51 static const char password_prompt[] = "Password:"; 52 char *authenticator = NULL; 53 54 if (PAM_SUCCESS != pam_get_authtok(pamh, PAM_AUTHTOK, (void *)&authenticator, password_prompt)) { 55 openpam_log(PAM_LOG_DEBUG, "Unable to obtain the authtok."); 56 return PAM_IGNORE; 57 } 58 if (PAM_SUCCESS != pam_setenv(pamh, "mount_authenticator", authenticator, 1)) { 59 openpam_log(PAM_LOG_DEBUG, "Unable to set the authtok in the environment."); 60 return PAM_IGNORE; 61 } 62 63 return PAM_SUCCESS; 64} 65 66 67PAM_EXTERN int 68pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) 69{ 70 return PAM_SUCCESS; 71} 72 73 74PAM_EXTERN int 75pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 76{ 77 int retval = PAM_SUCCESS; 78 char *server_URL = NULL; 79 char *path = NULL; 80 char *homedir = NULL; 81 int mountdirlen = PATH_MAX; 82 char *mountdir = NULL; 83 const char *username = NULL; 84 const char *authenticator = NULL; 85 struct passwd *pwd = NULL; 86 struct passwd pwdbuf; 87 char pwbuffer[2 * PATH_MAX]; 88 unsigned int was_remounted = 0; 89 90 /* get the username */ 91 if (PAM_SUCCESS != (retval = pam_get_user(pamh, &username, NULL))) { 92 openpam_log(PAM_LOG_ERROR, "Unable to get the username: %s", pam_strerror(pamh, retval)); 93 goto fin; 94 } 95 if (username == NULL || *username == '\0') { 96 openpam_log(PAM_LOG_ERROR, "Username is invalid."); 97 retval = PAM_PERM_DENIED; 98 goto fin; 99 } 100 101 /* get the uid */ 102 if (0 != getpwnam_r(username, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) || NULL == pwd) { 103 openpam_log(PAM_LOG_ERROR, "Unknown user \"%s\".", username); 104 retval = PAM_SYSTEM_ERR; 105 goto fin; 106 } 107 108 /* get the authenticator */ 109 if (NULL == (authenticator = pam_getenv(pamh, "mount_authenticator"))) { 110 openpam_log(PAM_LOG_DEBUG, "Unable to retrieve the authenticator."); 111 retval = PAM_IGNORE; 112 goto fin; 113 } 114 115 /* get the server_URL, path and homedir from OD */ 116 if (PAM_SUCCESS != (retval = od_extract_home(pamh, username, &server_URL, &path, &homedir))) { 117 openpam_log(PAM_LOG_ERROR, "Error retrieve data from OpenDirectory: %s", pam_strerror(pamh, retval)); 118 goto fin; 119 } 120 121 openpam_log(PAM_LOG_DEBUG, " UID: %d", pwd->pw_uid); 122 openpam_log(PAM_LOG_DEBUG, " server_URL: %s", server_URL); 123 openpam_log(PAM_LOG_DEBUG, " path: %s", path); 124 openpam_log(PAM_LOG_DEBUG, " homedir: %s", homedir); 125 openpam_log(PAM_LOG_DEBUG, " username: %s", username); 126 //openpam_log(PAM_LOG_DEBUG, " authenticator: %s", authenticator); // We don't want to log user's passwords. 127 128 /* determine if we need to mount the home folder */ 129 // this triggers the automounting for nfs 130 if (0 == access(homedir, F_OK) || EACCES == errno) { 131 openpam_log(PAM_LOG_DEBUG, "The home folder share is already mounted."); 132 } 133 if (NULL == (mountdir = malloc(mountdirlen+1))) { 134 openpam_log(PAM_LOG_DEBUG, "Failed to malloc the mountdir."); 135 retval = PAM_IGNORE; 136 goto fin; 137 } 138 139 /* mount the home folder */ 140 if (NULL != server_URL && retval == PAM_SUCCESS) { 141 // for an afp or smb home folder 142 if (NULL != path) { 143 if (0 != NetFSMountHomeDirectoryWithAuthentication(server_URL, 144 homedir, 145 path, 146 pwd->pw_uid, 147 mountdirlen, 148 mountdir, 149 username, 150 authenticator, 151 kNetFSAllowKerberos, 152 &was_remounted)) { 153 openpam_log(PAM_LOG_DEBUG, "Unable to mount the home folder."); 154 retval = PAM_SESSION_ERR; 155 goto fin; 156 } 157 else { 158 if (0 != was_remounted) { 159 openpam_log(PAM_LOG_DEBUG, "Remounted home folder."); 160 } 161 else { 162 openpam_log(PAM_LOG_DEBUG, "Mounted home folder."); 163 } 164 /* cache the homedir and path for close_session */ 165 pam_set_data(pamh, "homedir", homedir, openpam_free_data); 166 pam_set_data(pamh, "path", path, openpam_free_data); 167 homedir = NULL; 168 path = NULL; 169 } 170 } 171 // for a FileVault home folder 172 if (NULL == path && NULL != homedir) { 173 CFStringRef password = CFStringCreateWithCString(NULL, authenticator, kCFStringEncodingUTF8); 174 CFStringRef url = CFStringCreateWithCString(NULL, server_URL, kCFStringEncodingUTF8); 175 CFURLRef dmgin = CFURLCreateWithString(NULL, url, NULL); 176 CFStringRef mountpoint = CFStringCreateWithCString(NULL, homedir, kCFStringEncodingUTF8); 177 CFStringRef devicepath = NULL; 178 CFStringRef mountpath = NULL; 179 180 if (0 != DIHLFVMount(dmgin, kDIHLFVCredUserPasswordType, password, mountpoint, NULL, NULL, NULL, &mountpath, &devicepath)) { 181 openpam_log(PAM_LOG_ERROR, "Unable to mount the FileVault home folder."); 182 retval = PAM_SESSION_ERR; 183 } 184 else { 185 openpam_log(PAM_LOG_DEBUG, "Mounted FileVault home folder."); 186 pam_set_data(pamh, "devicepath", (void *)devicepath, pam_cf_cleanup); 187 } 188 CFRelease(url); 189 CFRelease(password); 190 CFRelease(dmgin); 191 CFRelease(mountpoint); 192 } 193 } 194 else { 195 // skip unmount for local homes 196 pam_set_data(pamh, "path", strdup(""), openpam_free_data); 197 } 198 199 200fin: 201 pam_unsetenv(pamh, "mount_authenticator"); 202 free(homedir); 203 free(mountdir); 204 free(server_URL); 205 free(path); 206 207 return retval; 208} 209 210 211PAM_EXTERN int 212pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) 213{ 214 int retval = PAM_SUCCESS; 215 const char *username; 216 char *server_URL = NULL; 217 char *path = NULL; 218 char *homedir = NULL; 219 CFStringRef devicepath = NULL; 220 struct passwd *pwd = NULL; 221 struct passwd pwdbuf; 222 char pwbuffer[2 * PATH_MAX]; 223 CFStringRef cfhomedir = NULL; 224 CFURLRef home_url = NULL; 225 DASessionRef dasession = NULL; 226 DADiskRef da = NULL; 227 const char *devnode = NULL; 228 229 /* get the username */ 230 retval = pam_get_user(pamh, &username, NULL); 231 if (retval != PAM_SUCCESS) { 232 openpam_log(PAM_LOG_ERROR, "Unable to get the username: %s", pam_strerror(pamh, retval)); 233 return retval; 234 } 235 if (username == NULL || *username == '\0') { 236 openpam_log(PAM_LOG_ERROR, "Username is invalid."); 237 return PAM_PERM_DENIED; 238 } 239 240 /* determine if we need to unmount the home folder */ 241 struct utmpx *x; 242 setutxent(); 243 while (NULL != (x = getutxent())) { 244 if (USER_PROCESS == x->ut_type && 0 == strcmp(x->ut_user, username) && x->ut_pid != getpid()) { 245 openpam_log(PAM_LOG_DEBUG, "User is still logged in elsewhere (%s), skipping home folder unmount.", x->ut_line); 246 return PAM_IGNORE; 247 } 248 } 249 endutxent(); 250 251 /* try to retrieve the cached devicepath */ 252 if (PAM_SUCCESS != pam_get_data(pamh, "devicepath", (void *)&devicepath)) { 253 openpam_log(PAM_LOG_DEBUG, "No cached devicepath in the PAM context."); 254 } 255 if (NULL != devicepath) { 256 if (NULL == (devicepath = CFStringCreateCopy(kCFAllocatorDefault, devicepath))) { 257 openpam_log(PAM_LOG_ERROR, "Failed to duplicate the devicepath."); 258 retval = PAM_BUF_ERR; 259 goto fin; 260 } 261 } 262 263 /* try to retrieve the cached homedir */ 264 if (PAM_SUCCESS != pam_get_data(pamh, "homedir", (void *)&homedir)) { 265 openpam_log(PAM_LOG_DEBUG, "No cached homedir in the PAM context."); 266 } 267 if (NULL != homedir) { 268 if (NULL == (homedir = strdup(homedir))) { 269 openpam_log(PAM_LOG_ERROR, "Failed to duplicate the homedir."); 270 retval = PAM_BUF_ERR; 271 goto fin; 272 } 273 } 274 275 /* try to retrieve the cached path */ 276 if (PAM_SUCCESS != pam_get_data(pamh, "path", (void *)&path)) { 277 openpam_log(PAM_LOG_DEBUG, "No cached path in the PAM context."); 278 } 279 if (NULL != path) { 280 if (NULL == (path = strdup(path))) { 281 openpam_log(PAM_LOG_ERROR, "Failed to duplicate the path."); 282 retval = PAM_BUF_ERR; 283 goto fin; 284 } 285 } 286 287 /* skip unmount for local homes */ 288 if (NULL != path && 0 == strcmp("", path)) { 289 openpam_log(PAM_LOG_DEBUG, "Skipping unmount."); 290 goto fin; 291 } 292 293 /* get the homedir and path or devicepath if needed */ 294 if ((NULL == homedir || NULL == path) && NULL == devicepath) { 295 if (PAM_SUCCESS != (retval = od_extract_home(pamh, username, &server_URL, &path, &homedir))) { 296 openpam_log(PAM_LOG_ERROR, "Error retrieve data from OpenDirectory: %s", pam_strerror(pamh, retval)); 297 goto fin; 298 } 299 if (NULL != server_URL && NULL == path && NULL != homedir) { 300 openpam_log(PAM_LOG_DEBUG, "Constructing the FileVault home path."); 301 if ((NULL == (cfhomedir = CFStringCreateWithCString(kCFAllocatorDefault, homedir, kCFStringEncodingUTF8))) || 302 (NULL == (home_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, cfhomedir, kCFURLPOSIXPathStyle, true))) || 303 (NULL == (dasession = DASessionCreate(kCFAllocatorDefault))) || 304 (NULL == (da = DADiskCreateFromVolumePath(kCFAllocatorDefault, dasession, home_url))) || 305 (NULL == (devnode = DADiskGetBSDName(da))) || 306 (NULL == (devicepath = CFStringCreateWithCString(kCFAllocatorDefault, devnode, kCFStringEncodingUTF8)))) { 307 openpam_log(PAM_LOG_ERROR, "Unable to to construct the FileVault home path."); 308 retval = PAM_SYSTEM_ERR; 309 goto fin; 310 } 311 } 312 } 313 314 /* attempt to unmount the home folder */ 315 if (NULL == devicepath && NULL != homedir && NULL != path) { 316 // not FileVault 317 if (0 != getpwnam_r(username, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) || NULL == pwd) { 318 openpam_log(PAM_LOG_ERROR, "Unknown user \"%s\".", username); 319 retval = PAM_SYSTEM_ERR; 320 goto fin; 321 } 322 if (0 != NetFSUnmountHomeDirectory(homedir, path, pwd->pw_uid, 0)) { 323 openpam_log(PAM_LOG_DEBUG, "Unable to unmount the home folder: %s.", strerror(errno)); 324 retval = PAM_IGNORE; 325 } 326 else { 327 openpam_log(PAM_LOG_DEBUG, "Unmounted home folder."); 328 } 329 } 330 else if (NULL != devicepath && NULL != homedir && NULL == path) { 331 // FileVault 332 if (0 != (retval = DIHLFVUnmount(devicepath, kDIHLFVUnmountNormally, kDIHLFVUnmountNoTimeout))) { 333 openpam_log(PAM_LOG_DEBUG, "Unable to unmount the FileVault home folder: %s.", DIStrError(retval)); 334 retval = PAM_IGNORE; 335 } 336 else { 337 openpam_log(PAM_LOG_DEBUG, "Unmounted FileVault home folder."); 338 } 339 } 340 else { 341 // nothing 342 openpam_log(PAM_LOG_DEBUG, "There is nothing to unmount."); 343 retval = PAM_IGNORE; 344 } 345 346fin: 347 free(server_URL); 348 free(path); 349 free(homedir); 350 351 if (home_url) 352 CFRelease(home_url); 353 if (cfhomedir) 354 CFRelease(cfhomedir); 355 if (dasession) 356 CFRelease(dasession); 357 if (da) 358 CFRelease(da); 359 if (devicepath) 360 CFRelease(devicepath); 361 362 return retval; 363} 364