1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * Portions Copyright (c) 2001 PADL Software Pty Ltd. All rights reserved. 4 * 5 * @APPLE_LICENSE_HEADER_START@ 6 * 7 * Portions Copyright (c) 2000 Apple Computer, Inc. All Rights 8 * Reserved. This file contains Original Code and/or Modifications of 9 * Original Code as defined in and that are subject to the Apple Public 10 * Source License Version 1.1 (the "License"). You may not use this file 11 * except in compliance with the License. Please obtain a copy of the 12 * License at http://www.apple.com/publicsource and read it before using 13 * this file. 14 * 15 * The Original Code and all software distributed under the License are 16 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the 20 * License for the specific language governing rights and limitations 21 * under the License. 22 * 23 * @APPLE_LICENSE_HEADER_END@ 24 */ 25 26/****************************************************************** 27 * The purpose of this module is to provide a basic password 28 * authentication module for Mac OS X. 29 ******************************************************************/ 30 31#include <pwd.h> 32#include <stdio.h> 33#include <unistd.h> 34#include <membership.h> 35#include <membershipPriv.h> 36#include <CoreFoundation/CoreFoundation.h> 37#include <OpenDirectory/OpenDirectory.h> 38#include <DirectoryService/DirectoryService.h> 39 40#define PAM_SM_AUTH 41#define PAM_SM_ACCOUNT 42 43#include <security/pam_modules.h> 44#include <security/pam_appl.h> 45 46/* legacy definition */ 47#ifndef kDSValueAuthAuthorityDisabledUser 48#define kDSValueAuthAuthorityDisabledUser ";DisabledUser;" 49#endif 50 51#include "Common.h" 52 53#define PM_DISPLAY_NAME "OpenDirectory" 54#define PAM_OD_PW_EXP "ODPasswordExpire" 55 56PAM_EXTERN int 57pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) 58{ 59 int retval = PAM_PERM_DENIED; 60 const char *user = NULL; 61 char *homedir = NULL; 62 ODRecordRef cfRecord = NULL; 63 struct passwd *pwd = NULL; 64 struct passwd pwdbuf; 65 char pwbuffer[2 * PATH_MAX]; 66 const char *ttl_str = NULL; 67 int ttl = 30 * 60; 68 69 /* get the username */ 70 retval = pam_get_user(pamh, &user, NULL); 71 if (PAM_SUCCESS != retval) { 72 goto cleanup; 73 } 74 if (user == NULL || *user == '\0') { 75 retval = PAM_PERM_DENIED; 76 goto cleanup; 77 } 78 79 /* refresh the membership */ 80 if (0 != getpwnam_r(user, &pwdbuf, pwbuffer, sizeof(pwbuffer), &pwd) || NULL == pwd) { 81 openpam_log(PAM_LOG_ERROR, "%s - Unable to get pwd record.", PM_DISPLAY_NAME); 82 retval = PAM_USER_UNKNOWN; 83 goto cleanup; 84 } 85 if (NULL != (ttl_str = openpam_get_option(pamh, "refresh"))) { 86 ttl = strtol(ttl_str, NULL, 10) * 60; 87 } 88 mbr_set_identifier_ttl(ID_TYPE_UID, &pwd->pw_uid, sizeof(pwd->pw_uid), ttl); 89 openpam_log(PAM_LOG_DEBUG, "%s - Membership cache TTL set to %d.", PM_DISPLAY_NAME, ttl); 90 91 /* Get user record from OD */ 92 retval = od_record_create_cstring(pamh, &cfRecord, (const char*)user); 93 if (PAM_SUCCESS != retval) { 94 openpam_log(PAM_LOG_ERROR, "%s - Unable to get user record: %d.", PM_DISPLAY_NAME, retval); 95 goto cleanup; 96 } 97 98 /* check if authentication returned password expired */ 99 if (pam_getenv(pamh, PAM_OD_PW_EXP) != NULL) { 100 openpam_log(PAM_LOG_DEBUG, "%s - Password expired.", PM_DISPLAY_NAME); 101 retval = PAM_NEW_AUTHTOK_REQD; 102 goto cleanup; 103 } 104 105 /* check user password policy */ 106 retval = od_record_check_pwpolicy(cfRecord); 107 if (PAM_SUCCESS != retval) { 108 goto cleanup; 109 } 110 111 /* check user authentication authority */ 112 retval = od_record_check_authauthority(cfRecord); 113 if (PAM_SUCCESS != retval) { 114 goto cleanup; 115 } 116 117 /* check user home directory */ 118 if (!openpam_get_option(pamh, "no_check_home")) { 119 retval = od_record_check_homedir(cfRecord); 120 if (PAM_SUCCESS != retval) { 121 goto cleanup; 122 } 123 } 124 125 /* check user shell */ 126 if (!openpam_get_option(pamh, "no_check_shell")) { 127 retval = od_record_check_shell(cfRecord); 128 if (PAM_SUCCESS != retval) { 129 goto cleanup; 130 } 131 } 132 133cleanup: 134 if (NULL != cfRecord) { 135 CFRelease(cfRecord); 136 } 137 free(homedir); 138 pam_unsetenv(pamh, PAM_OD_PW_EXP); 139 140 141 return retval; 142} 143 144 145PAM_EXTERN int 146pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) 147{ 148 static const char password_prompt[] = "Password:"; 149 int retval = PAM_SUCCESS; 150 const char *user = NULL; 151 const char *password = NULL; 152 CFStringRef cfPassword = NULL; 153 CFErrorRef odErr = NULL; 154 ODRecordRef cfRecord = NULL; 155 156 if (PAM_SUCCESS != (retval = pam_get_user(pamh, &user, NULL))) { 157 openpam_log(PAM_LOG_DEBUG, "%s - Unable to obtain the username.", PM_DISPLAY_NAME); 158 goto cleanup; 159 } 160 if (PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_AUTHTOK, &password, password_prompt))) { 161 openpam_log(PAM_LOG_DEBUG, "%s - Error obtaining the authtok.", PM_DISPLAY_NAME); 162 retval = PAM_AUTH_ERR; 163 goto cleanup; 164 } 165 if ((password[0] == '\0') && ((NULL == openpam_get_option(pamh, "nullok")) || (flags & PAM_DISALLOW_NULL_AUTHTOK))) { 166 openpam_log(PAM_LOG_DEBUG, "%s - NULL passwords are not allowed.", PM_DISPLAY_NAME); 167 retval = PAM_AUTH_ERR; 168 goto cleanup; 169 } 170 171 /* Get user record from OD */ 172 retval = od_record_create_cstring(pamh, &cfRecord, (const char*)user); 173 if (PAM_SUCCESS != retval) { 174 openpam_log(PAM_LOG_ERROR, "%s - Unable to get user record.", PM_DISPLAY_NAME); 175 goto cleanup; 176 } 177 178 if (NULL == cfRecord) { 179 openpam_log(PAM_LOG_ERROR, "%s - User record NULL.", PM_DISPLAY_NAME); 180 return PAM_USER_UNKNOWN; 181 } 182 183 /* verify the user's password */ 184 cfPassword = CFStringCreateWithCString(kCFAllocatorDefault, password, kCFStringEncodingUTF8); 185 retval = PAM_USER_UNKNOWN; 186 if (!ODRecordVerifyPassword(cfRecord, cfPassword, &odErr)) { 187 switch (CFErrorGetCode(odErr)) { 188 case kODErrorCredentialsAccountNotFound: 189 retval = PAM_USER_UNKNOWN; 190 openpam_log(PAM_LOG_DEBUG, "%s - Account not found or invalid.", PM_DISPLAY_NAME); 191 break; 192 case kODErrorCredentialsAccountDisabled: 193 case kODErrorCredentialsAccountInactive: 194 openpam_log(PAM_LOG_DEBUG, "%s - The account is disabled or inactive.", PM_DISPLAY_NAME); 195 retval = PAM_PERM_DENIED; 196 break; 197 case kODErrorCredentialsPasswordExpired: 198 case kODErrorCredentialsPasswordChangeRequired: 199 openpam_log(PAM_LOG_DEBUG, "%s - The authtok is expired or requires updating.", PM_DISPLAY_NAME); 200 pam_setenv(pamh, PAM_OD_PW_EXP, "yes", 1); 201 retval = PAM_SUCCESS; 202 break; 203 case kODErrorCredentialsInvalid: 204 openpam_log(PAM_LOG_DEBUG, "%s - The authtok is incorrect.", PM_DISPLAY_NAME); 205 retval = PAM_AUTH_ERR; 206 break; 207 default: 208 openpam_log(PAM_LOG_DEBUG, "%s Unexpected error code from ODRecordVerifyPassword(): %ld.", 209 PM_DISPLAY_NAME, CFErrorGetCode(odErr)); 210 retval = PAM_AUTH_ERR; 211 break; 212 } 213 } else { 214 retval = PAM_SUCCESS; 215 } 216 217cleanup: 218 if (NULL != cfRecord) { 219 CFRelease(cfRecord); 220 } 221 222 if (NULL != cfPassword) { 223 CFRelease(cfPassword); 224 } 225 226 if (NULL != odErr) { 227 CFRelease(odErr); 228 } 229 230 return retval; 231} 232 233 234PAM_EXTERN int 235pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) 236{ 237 return PAM_SUCCESS; 238} 239 240 241PAM_EXTERN int 242pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) 243{ 244 static const char old_password_prompt[] = "Old Password:"; 245 static const char new_password_prompt[] = "New Password:"; 246 int retval = PAM_SUCCESS; 247 const char *user = NULL; 248 const char *new_password = NULL; 249 const char *old_password = NULL; 250 CFErrorRef odErr = NULL; 251 ODRecordRef cfRecord = NULL; 252 CFStringRef cfOldPassword = NULL; 253 CFStringRef cfNewPassword = NULL; 254 255 if (flags & PAM_PRELIM_CHECK) { 256 retval = PAM_SUCCESS; 257 goto cleanup; 258 } 259 260 if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) { 261 openpam_log(PAM_LOG_DEBUG, "%s - Error obtaining the username.", PM_DISPLAY_NAME); 262 goto cleanup; 263 } 264 265 if (PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &old_password, old_password_prompt))) { 266 openpam_log(PAM_LOG_DEBUG, "%s - Error obtaining the old password.", PM_DISPLAY_NAME); 267 goto cleanup; 268 } 269 if (PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_AUTHTOK, &new_password, new_password_prompt))) { 270 openpam_log(PAM_LOG_DEBUG, "%s - Error obtaining the new password.", PM_DISPLAY_NAME); 271 goto cleanup; 272 } 273 274 /* Get user record from OD */ 275 retval = od_record_create_cstring(pamh, &cfRecord, (const char*)user); 276 if (PAM_SUCCESS != retval) { 277 openpam_log(PAM_LOG_ERROR, "%s - Unable to get user record.", PM_DISPLAY_NAME); 278 goto cleanup; 279 } 280 281 /* reset the user's password */ 282 cfOldPassword = CFStringCreateWithCString(kCFAllocatorDefault, old_password, kCFStringEncodingUTF8); 283 cfNewPassword = CFStringCreateWithCString(kCFAllocatorDefault, new_password, kCFStringEncodingUTF8); 284 285 retval = PAM_SYSTEM_ERR; 286 if (!ODRecordChangePassword(cfRecord, cfOldPassword, cfNewPassword, &odErr)) { 287 switch (CFErrorGetCode(odErr)) { 288 case kODErrorCredentialsInvalid: 289 case kODErrorCredentialsPasswordQualityFailed: 290 openpam_log(PAM_LOG_DEBUG, "%s - The authtok is invaild or of low quality.", PM_DISPLAY_NAME); 291 retval = PAM_AUTHTOK_ERR; 292 break; 293 case kODErrorCredentialsNotAuthorized: 294 case kODErrorCredentialsAccountDisabled: 295 case kODErrorCredentialsAccountInactive: 296 openpam_log(PAM_LOG_DEBUG, "%s - The account not authorized, disabled or inactive.", PM_DISPLAY_NAME); 297 retval = PAM_PERM_DENIED; 298 break; 299 case kODErrorCredentialsPasswordUnrecoverable: 300 openpam_log(PAM_LOG_DEBUG, "%s - The authtok us unrecoverable.", PM_DISPLAY_NAME); 301 retval = PAM_AUTHTOK_RECOVERY_ERR; 302 break; 303 default: 304 openpam_log(PAM_LOG_DEBUG, "%s - There was an unexpected error while changing the password.", PM_DISPLAY_NAME); 305 retval = PAM_ABORT; 306 break; 307 } 308 } else { 309 retval = PAM_SUCCESS; 310 } 311 312cleanup: 313 if (NULL != odErr) { 314 CFRelease(odErr); 315 } 316 317 if (NULL != cfRecord) { 318 CFRelease(cfRecord); 319 } 320 321 if (NULL != cfOldPassword) { 322 CFRelease(cfOldPassword); 323 } 324 325 if (NULL != cfNewPassword) { 326 CFRelease(cfNewPassword); 327 } 328 329 return retval; 330} 331