/* * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * Portions Copyright (c) 2001 PADL Software Pty Ltd. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Portions Copyright (c) 2000 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.1 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /****************************************************************** * The purpose of this module is to provide a basic password * authentication module for Mac OS X. ******************************************************************/ #include #include #include #include #include #include #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #include #include static int get_boolean_value(const void *p) { int value; if (CFBooleanGetTypeID() == CFGetTypeID(p)) return CFBooleanGetValue(p); else if (CFNumberGetTypeID() == CFGetTypeID(p) && CFNumberGetValue(p, kCFNumberIntType, &value)) return 0 != value; else return 0; } static int check_pwpolicy(ODRecordRef record) { CFDictionaryRef policy = NULL; const void *isDisabled; const void *newPasswordRequired; int retval; if (NULL == (policy = ODRecordCopyPasswordPolicy(kCFAllocatorDefault, record, NULL)) || NULL == (isDisabled = CFDictionaryGetValue(policy, CFSTR("isDisabled"))) || !get_boolean_value(isDisabled)) retval = PAM_SUCCESS; else retval = PAM_PERM_DENIED; if (NULL != policy && NULL != (newPasswordRequired = CFDictionaryGetValue(policy, CFSTR("newPasswordRequired"))) && get_boolean_value(newPasswordRequired)) retval = PAM_NEW_AUTHTOK_REQD; if (NULL != policy) CFRelease(policy); return retval; } static int check_authauthority(ODRecordRef record) { int retval = PAM_SUCCESS; CFArrayRef vals = ODRecordCopyValues(record, CFSTR(kDSNAttrAuthenticationAuthority), NULL); if (vals != NULL) { CFIndex count = CFArrayGetCount(vals); CFIndex i; for (i = 0; i < count; ++i) { const void *val = CFArrayGetValueAtIndex(vals, i); if (val == NULL || CFGetTypeID(val) != CFStringGetTypeID() || CFStringHasPrefix(val, CFSTR(kDSValueAuthAuthorityDisabledUser))) { retval = PAM_PERM_DENIED; break; } } CFRelease(vals); } return retval; } static int check_shell(ODRecordRef record) { int retval = PAM_SUCCESS; CFArrayRef vals = ODRecordCopyValues(record, CFSTR(kDS1AttrUserShell), NULL); if (vals != NULL) { CFIndex count = CFArrayGetCount(vals); CFIndex i; for (i = 0; i < count; ++i) { const void *val = CFArrayGetValueAtIndex(vals, i); if (val == NULL || CFGetTypeID(val) != CFStringGetTypeID() || CFStringCompare(val, CFSTR("/usr/bin/false"), 0) == kCFCompareEqualTo) { retval = PAM_PERM_DENIED; break; } } CFRelease(vals); } return retval; } PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval; const char *user = NULL; /* get the username */ retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) { return retval; } if (user == NULL || *user == '\0') { return PAM_PERM_DENIED; } /* check if the user's account is disabled */ ODNodeRef cfNodeRef = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, eDSAuthenticationSearchNodeName, NULL); if (cfNodeRef != NULL) { CFStringRef cfUser = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8); if (cfUser != NULL) { ODRecordRef cfRecord = ODNodeCopyRecord(cfNodeRef, CFSTR(kDSStdRecordTypeUsers), cfUser, NULL, NULL); if (cfRecord != NULL) { if (retval == PAM_SUCCESS) { retval = check_pwpolicy(cfRecord); } if (retval == PAM_SUCCESS) { retval = check_authauthority(cfRecord); } if (retval == PAM_SUCCESS && !openpam_get_option(pamh, "no_check_shell")) { retval = check_shell(cfRecord); } CFRelease(cfRecord); } CFRelease(cfUser); } CFRelease(cfNodeRef); } return retval; } PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { static const char password_prompt[] = "Password:"; int retval = PAM_SUCCESS; const char *user; const char *password = NULL; CFErrorRef odErr; if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) return retval; if (PAM_SUCCESS != (retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&password))) return retval; if (NULL == password) { if (PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_AUTHTOK, &password, password_prompt))) return PAM_AUTH_ERR; } if ((password[0] == '\0') && ((NULL == openpam_get_option(pamh, "nullok")) || (flags & PAM_DISALLOW_NULL_AUTHTOK))) return PAM_AUTH_ERR; /* verify the user's password */ retval = PAM_USER_UNKNOWN; ODNodeRef cfNodeRef = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, eDSAuthenticationSearchNodeName, NULL); if (cfNodeRef != NULL) { CFStringRef cfUser = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8); CFStringRef cfPassword = CFStringCreateWithCString(NULL, password, kCFStringEncodingUTF8); if ((cfUser != NULL) && (cfPassword != NULL)) { ODRecordRef cfRecord = ODNodeCopyRecord(cfNodeRef, CFSTR(kDSStdRecordTypeUsers), cfUser, NULL, NULL); if (cfRecord != NULL) { if (!ODRecordVerifyPassword(cfRecord, cfPassword, &odErr)) { switch (CFErrorGetCode(odErr)) { case kODErrorCredentialsAccountNotFound: retval = PAM_USER_UNKNOWN; break; case kODErrorCredentialsAccountDisabled: case kODErrorCredentialsAccountInactive: retval = PAM_PERM_DENIED; break; case kODErrorCredentialsPasswordExpired: case kODErrorCredentialsPasswordChangeRequired: retval = PAM_SUCCESS; break; default: retval = PAM_AUTH_ERR; break; } } else { retval = PAM_SUCCESS; } CFRelease(cfRecord); if (odErr) { CFRelease(odErr); } } else { retval = PAM_AUTH_ERR; } CFRelease(cfUser); CFRelease(cfPassword); } CFRelease(cfNodeRef); } return retval; } PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc, const char **argv) { return PAM_SUCCESS; } PAM_EXTERN int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc, const char **argv) { static const char old_password_prompt[] = "Old Password:"; static const char new_password_prompt[] = "New Password:"; int retval = PAM_SUCCESS; const char *user; const char *new_password = NULL; const char *old_password = NULL; CFErrorRef odErr; if (flags & PAM_PRELIM_CHECK) return PAM_SUCCESS; if ((retval = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) return retval; if (PAM_SUCCESS != (retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (void *)&old_password))) return retval; if (NULL == old_password && PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &old_password, old_password_prompt))) return retval; if (PAM_SUCCESS != (retval = pam_get_item(pamh, PAM_AUTHTOK, (void *)&new_password))) return retval; if (NULL == new_password && PAM_SUCCESS != (retval = pam_get_authtok(pamh, PAM_AUTHTOK, &new_password, new_password_prompt))) return retval; /* reset the user's password */ retval = PAM_SYSTEM_ERR; ODNodeRef cfNodeRef = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, eDSAuthenticationSearchNodeName, NULL); if (cfNodeRef != NULL) { CFStringRef cfUser = CFStringCreateWithCString(NULL, user, kCFStringEncodingUTF8); CFStringRef cfOldPassword = CFStringCreateWithCString(NULL, old_password, kCFStringEncodingUTF8); CFStringRef cfNewPassword = CFStringCreateWithCString(NULL, new_password, kCFStringEncodingUTF8); if ((cfUser != NULL) && (cfOldPassword != NULL) && (cfNewPassword != NULL)) { ODRecordRef cfRecord = ODNodeCopyRecord(cfNodeRef, CFSTR(kDSStdRecordTypeUsers), cfUser, NULL, NULL); if (cfRecord != NULL) { if (!ODRecordChangePassword(cfRecord, cfOldPassword, cfNewPassword, &odErr)) { switch (CFErrorGetCode(odErr)) { case kODErrorCredentialsInvalid: case kODErrorCredentialsPasswordQualityFailed: retval = PAM_AUTHTOK_ERR; break; case kODErrorCredentialsNotAuthorized: case kODErrorCredentialsAccountDisabled: case kODErrorCredentialsAccountInactive: retval = PAM_PERM_DENIED; break; case kODErrorCredentialsPasswordUnrecoverable: retval = PAM_AUTHTOK_RECOVERY_ERR; break; default: retval = PAM_ABORT; break; } } else { retval = PAM_SUCCESS; } CFRelease(cfRecord); } else { retval = PAM_SERVICE_ERR; } CFRelease(cfUser); CFRelease(cfOldPassword); CFRelease(cfNewPassword); } CFRelease(cfNodeRef); } return retval; }