/* Unix SMB/CIFS implementation. Password and authentication handling Copyright (C) Andrew Tridgell 1992-2000 Copyright (C) Luke Kenneth Casson Leighton 1996-2000 Copyright (C) Andrew Bartlett 2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "includes.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_AUTH #include #include tDirNodeReference getusernode(tDirReference dirRef, const char *userName) { tDirStatus status = eDSNoErr; long bufferSize = 1024 * 10; unsigned long returnCount = 0; tDataBufferPtr dataBuffer = NULL; tDirNodeReference searchNodeRef = 0; tDataListPtr searchNodeName = NULL; tDirNodeReference userNodeRef = 0; tDataListPtr userNodePath = NULL; char userNodePathStr[256] = {0}; char recUserName[128] = {0}; tDataListPtr recName = NULL; tDataListPtr recType = NULL; tDataListPtr attrType = NULL; tAttributeListRef attributeListRef = 0; tRecordEntryPtr outRecordEntryPtr = NULL; tAttributeEntryPtr attributeInfo = NULL; tAttributeValueListRef attributeValueListRef = 0; tAttributeValueEntryPtr attrValue = NULL; long i = 0; dataBuffer = dsDataBufferAllocate(dirRef, bufferSize); if (dataBuffer == NULL) goto cleanup; status = dsFindDirNodes(dirRef, dataBuffer, NULL, eDSSearchNodeName, &returnCount, NULL); if ((status != eDSNoErr) || (returnCount <= 0)) goto cleanup; status = dsGetDirNodeName(dirRef, dataBuffer, 1, &searchNodeName); if (status != eDSNoErr) goto cleanup; status = dsOpenDirNode(dirRef, searchNodeName, &searchNodeRef); if (status != eDSNoErr) goto cleanup; recName = dsBuildListFromStrings(dirRef, userName, NULL); recType = dsBuildListFromStrings(dirRef, kDSStdRecordTypeUsers, NULL); attrType = dsBuildListFromStrings(dirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL); status = dsGetRecordList(searchNodeRef, dataBuffer, recName, eDSiExact, recType, attrType, 0, &returnCount, NULL); if (status != eDSNoErr) goto cleanup; status = dsGetRecordEntry(searchNodeRef, dataBuffer, 1, &attributeListRef, &outRecordEntryPtr); if (status == eDSNoErr) { for (i = 1 ; i <= outRecordEntryPtr->fRecordAttributeCount; i++) { status = dsGetAttributeEntry(searchNodeRef, dataBuffer, attributeListRef, i, &attributeValueListRef, &attributeInfo); status = dsGetAttributeValue(searchNodeRef, dataBuffer, 1, attributeValueListRef, &attrValue); if (status == eDSNoErr) { if (strncmp(attributeInfo->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation, strlen(kDSNAttrMetaNodeLocation)) == 0) { strncpy(userNodePathStr, attrValue->fAttributeValueData.fBufferData, attrValue->fAttributeValueData.fBufferSize); ///DEBUG(0,("getusernode: [%d]userNodePathStr (%s)\n", i, userNodePathStr )); } else if (strncmp(attributeInfo->fAttributeSignature.fBufferData, kDSNAttrRecordName, strlen(kDSNAttrRecordName)) == 0) { strncpy(recUserName, attrValue->fAttributeValueData.fBufferData, attrValue->fAttributeValueData.fBufferSize); ///DEBUG(0,("getusernode: [%d]recUserName (%s)\n", i, recUserName )); } } if (attrValue != NULL) { ///DEBUG(0,("getusernode: [%d]dsDeallocAttributeValueEntry dirRef(%d) attrValue(%d)\n", i, dirRef, attrValue )); dsDeallocAttributeValueEntry(dirRef, attrValue); attrValue = NULL; } if (attributeValueListRef != 0) { dsCloseAttributeValueList(attributeValueListRef); attributeValueListRef = 0; } if (attributeInfo != NULL) { ///DEBUG(0,("getusernode: [%d]dsDeallocAttributeEntry dirRef(%d) attributeInfo(%d)\n", i, dirRef, attributeInfo )); dsDeallocAttributeEntry(dirRef, attributeInfo); attributeInfo = NULL; } } if (outRecordEntryPtr != NULL) { dsDeallocRecordEntry(dirRef, outRecordEntryPtr); outRecordEntryPtr = NULL; } if (strlen(userNodePathStr) != 0 && strlen(recUserName) != 0) { userNodePath = dsBuildFromPath(dirRef, userNodePathStr, "/"); status = dsOpenDirNode(dirRef, userNodePath, &userNodeRef); dsDataListDeallocate( dirRef, userNodePath); free(userNodePath); } } cleanup: if (dataBuffer != NULL) dsDataBufferDeAllocate(dirRef, dataBuffer); if (searchNodeName != NULL) { dsDataListDeallocate(dirRef, searchNodeName); free(searchNodeName); } if (searchNodeRef != 0) dsCloseDirNode(searchNodeRef); if (recName != NULL) { dsDataListDeallocate(dirRef, recName); free(recName); } if (recType != NULL) { dsDataListDeallocate(dirRef, recType); free(recType); } if (attrType != NULL) { dsDataListDeallocate(dirRef, attrType); free(attrType); } return userNodeRef; } static NTSTATUS map_dserr_to_nterr(tDirStatus dirStatus) { switch (dirStatus) { case (eDSAuthFailed): case (eDSAuthBadPassword): return NT_STATUS_WRONG_PASSWORD; case (eDSAuthAccountInactive): return NT_STATUS_ACCOUNT_DISABLED; case (eDSAuthNewPasswordRequired): case (eDSAuthPasswordExpired): return NT_STATUS_PASSWORD_MUST_CHANGE; default: return NT_STATUS_WRONG_PASSWORD; }; } tDirStatus opendirectory_auth_user(tDirReference dirRef, tDirNodeReference userNode, const char* user, u_int8_t *challenge, u_int8_t *password, char *inAuthMethod) { tDirStatus status = eDSNoErr; tDirStatus bufferStatus = eDSNoErr; unsigned long curr = 0; unsigned long len = 0; tDataBufferPtr authBuff = NULL; tDataBufferPtr stepBuff = NULL; tDataNodePtr authType = NULL; authBuff = dsDataBufferAllocate( dirRef, 2048 ); if ( authBuff != NULL ) { stepBuff = dsDataBufferAllocate( dirRef, 2048 ); if ( stepBuff != NULL ) { authType = dsDataNodeAllocateString( dirRef, inAuthMethod); if ( authType != NULL ) { // User Name len = strlen( user ); memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof( long ); memcpy( &(authBuff->fBufferData[ curr ]), user, len ); curr += len; // C8 len = 8; memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof (long ); memcpy( &(authBuff->fBufferData[ curr ]), challenge, len ); curr += len; // P24 len = 24; memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof (long ); memcpy( &(authBuff->fBufferData[ curr ]), password, len ); curr += len; authBuff->fBufferLength = curr; status = dsDoDirNodeAuth( userNode, authType, True, authBuff, stepBuff, NULL ); if ( status == eDSNoErr ) { DEBUG(1,("User \"%s\" authenticated successfully with \"%s\" :)\n", user, inAuthMethod )); } else { DEBUG(1,("User \"%s\" failed to authenticate with \"%s\" (%d) :(\n", user, inAuthMethod,status) ); } } bufferStatus = dsDataBufferDeAllocate( dirRef, stepBuff ); if ( bufferStatus != eDSNoErr ) { DEBUG(1,("*** dsDataBufferDeAllocate(2) faild with error = %d: \n", bufferStatus) ); } } else { DEBUG(1,("*** dsDataBufferAllocate(2) faild with \n" )); } bufferStatus = dsDataBufferDeAllocate( dirRef, authBuff ); if ( bufferStatus != eDSNoErr ) { DEBUG(1,( "*** dsDataBufferDeAllocate(2) faild with error = %d: \n", bufferStatus )); } } else { DEBUG(1,("*** dsDataBufferAllocate(1) faild with \n" )); } if (authType != NULL) dsDataNodeDeAllocate(dirRef, authType); return status; } tDirStatus opendirectory_ntlmv2_auth_user(tDirReference dirRef, tDirNodeReference userNode, const char* user, const char* domain, const DATA_BLOB *sec_blob, const DATA_BLOB *ntv2_response, DATA_BLOB *user_sess_key) { tDirStatus status = eDSNoErr; tDirStatus bufferStatus = eDSNoErr; unsigned long curr = 0; unsigned long len = 0; tDataBufferPtr authBuff = NULL; tDataBufferPtr stepBuff = NULL; tDataNodePtr authType = NULL; /* The auth method constant is: dsAuthMethodStandard:dsAuthNodeNTLMv2 The format for data in the step buffer is: 4 byte len + directory-services name 4 byte len + server challenge 4 byte len + client "blob" - 16 bytes of client digest + the blob data 4 byte len + user name used in the digest (usually the same as item #1 in the buffer) 4 byte len + domain */ authBuff = dsDataBufferAllocate( dirRef, 2048 ); if ( authBuff != NULL ) { stepBuff = dsDataBufferAllocate( dirRef, 2048 ); if ( stepBuff != NULL ) { authType = dsDataNodeAllocateString( dirRef, "dsAuthMethodStandard:dsAuthNodeNTLMv2"); if ( authType != NULL ) { // directory-services name len = strlen( user ); memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof( long ); memcpy( &(authBuff->fBufferData[ curr ]), user, len ); curr += len; // server challenge len = 8; memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof (long ); memcpy( &(authBuff->fBufferData[ curr ]), sec_blob->data, len ); curr += len; // client "blob" - 16 bytes of client digest + the blob_data len = ntv2_response->length; memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof (long ); memcpy( &(authBuff->fBufferData[ curr ]), ntv2_response->data, len ); curr += len; // user name used in the digest (usually the same as item #1 in the buffer) len = strlen( user ); memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof( long ); memcpy( &(authBuff->fBufferData[ curr ]), user, len ); curr += len; // domain len = strlen( domain ); memcpy( &(authBuff->fBufferData[ curr ]), &len, 4 ); curr += sizeof( long ); memcpy( &(authBuff->fBufferData[ curr ]), domain, len ); curr += len; authBuff->fBufferLength = curr; status = dsDoDirNodeAuth( userNode, authType, True, authBuff, stepBuff, NULL ); if ( status == eDSNoErr ) { DEBUG(1,("User \"%s\" authenticated successfully with \"%s\" :)\n", user, "dsAuthMethodStandard:dsAuthNodeNTLMv2" )); } else { DEBUG(1,("User \"%s\" failed to authenticate with \"%s\" (%d) :(\n", user, "dsAuthMethodStandard:dsAuthNodeNTLMv2",status) ); } } bufferStatus = dsDataBufferDeAllocate( dirRef, stepBuff ); if ( bufferStatus != eDSNoErr ) { DEBUG(1,("*** dsDataBufferDeAllocate(2) faild with error = %d: \n", bufferStatus) ); } } else { DEBUG(1,("*** dsDataBufferAllocate(2) faild with \n" )); } bufferStatus = dsDataBufferDeAllocate( dirRef, authBuff ); if ( bufferStatus != eDSNoErr ) { DEBUG(1,( "*** dsDataBufferDeAllocate(2) faild with error = %d: \n", bufferStatus )); } } else { DEBUG(1,("*** dsDataBufferAllocate(1) faild with \n" )); } if (authType != NULL) dsDataNodeDeAllocate(dirRef, authType); return status; } /**************************************************************************** core of smb password checking routine. ****************************************************************************/ static tDirStatus opendirectory_smb_pwd_check_ntlmv1(tDirReference dirRef, tDirNodeReference userNode, const char *user, char *inAuthMethod, const DATA_BLOB *nt_response, const DATA_BLOB *sec_blob, DATA_BLOB *user_sess_key) { tDirStatus status = eDSAuthFailed; tDirStatus keyStatus = eDSAuthFailed; u_int32_t key_length = 0; if (sec_blob->length != 8) { DEBUG(0, ("opendirectory_smb_pwd_check_ntlmv1: incorrect challenge size (%ld)\n", sec_blob->length)); return eDSAuthFailed; } if (nt_response->length != 24) { DEBUG(0, ("opendirectory_smb_pwd_check_ntlmv1: incorrect password length (%ld)\n", nt_response->length)); return eDSAuthFailed; } if ((user_sess_key != NULL) && (strcmp(inAuthMethod,kDSStdAuthSMB_NT_Key) == 0) ) { *user_sess_key = data_blob(NULL, 16); become_root(); status = opendirectory_user_auth_and_session_key(dirRef, userNode, user, sec_blob->data, nt_response->data, user_sess_key->data, &key_length, NULL); unbecome_root(); } if (eDSAuthMethodNotSupported == status || eNotHandledByThisNode == status || (strcmp(inAuthMethod,kDSStdAuthSMB_LM_Key) == 0)) { status = opendirectory_auth_user(dirRef, userNode,(const char*) user, sec_blob->data, nt_response->data, inAuthMethod); DEBUG(1, ("opendirectory_smb_pwd_check_ntlmv1: [%d]opendirectory_auth_user\n", status)); if (user_sess_key != NULL) { *user_sess_key = data_blob(NULL, 16); become_root(); keyStatus = opendirectory_user_session_key(dirRef, userNode, user, user_sess_key->data, NULL); unbecome_root(); DEBUG(2, ("opendirectory_smb_pwd_check_ntlmv1: [%d]opendirectory_user_session_key\n", keyStatus)); } } else { DEBUG(1, ("opendirectory_smb_pwd_check_ntlmv1: [%d]opendirectory_user_auth_and_session_key key_length(%d)\n", status, key_length)); } return (status); } /**************************************************************************** Core of smb password checking routine. (NTLMv2, LMv2) Note: The same code works with both NTLMv2 and LMv2. ****************************************************************************/ static tDirStatus opendirectory_smb_pwd_check_ntlmv2(tDirReference dirRef, tDirNodeReference userNode, const DATA_BLOB *ntv2_response, const DATA_BLOB *sec_blob, const char *user, const char *domain, BOOL upper_case_domain, /* should the domain be transformed into upper case? */ DATA_BLOB *user_sess_key) { tDirStatus status = eDSAuthFailed; tDirStatus keyStatus = eDSNoErr; u_int32_t session_key_len = 0; if (sec_blob->length != 8) { DEBUG(0, ("opendirectory_smb_pwd_check_ntlmv2: incorrect challenge size (%lu)\n", (unsigned long)sec_blob->length)); return False; } if (ntv2_response->length < 24) { /* We MUST have more than 16 bytes, or the stuff below will go crazy. No known implementation sends less than the 24 bytes for LMv2, let alone NTLMv2. */ DEBUG(0, ("opendirectory_smb_pwd_check_ntlmv2: incorrect password length (%lu)\n", (unsigned long)ntv2_response->length)); return False; } status = opendirectory_ntlmv2_auth_user(dirRef, userNode, user, domain, sec_blob, ntv2_response, user_sess_key ); DEBUG(4, ("opendirectory_smb_pwd_check_ntlmv2: [%d]opendirectory_[%s]_auth_user\n", status, ntv2_response->length == 24 ? "LMv2" : "NTLMv2")); if (eDSNoErr == status) { if (user_sess_key != NULL) { *user_sess_key = data_blob(NULL, 16); become_root(); keyStatus = opendirectory_ntlmv2user_session_key(user, ntv2_response->length, ntv2_response->data, domain, &session_key_len, user_sess_key->data, NULL); unbecome_root(); DEBUG(2, ("opendirectory_smb_pwd_check_ntlmv2: [%d]opendirectory_[%s]user_session_key len(%d)\n", keyStatus, ntv2_response->length == 24 ? "LMv2" : "NTLMv2", session_key_len)); } } return (status); } /** * Check a challenge-response password against the value of the NT or * LM password hash. * * @param mem_ctx talloc context * @param challenge 8-byte challenge. If all zero, forces plaintext comparison * @param nt_response 'unicode' NT response to the challenge, or unicode password * @param lm_response ASCII or LANMAN response to the challenge, or password in DOS code page * @param username internal Samba username, for log messages * @param client_username username the client used * @param client_domain domain name the client used (may be mapped) * @param nt_pw MD4 unicode password from our passdb or similar * @param lm_pw LANMAN ASCII password from our passdb or similar * @param user_sess_key User session key * @param lm_sess_key LM session key (first 8 bytes of the LM hash) */ NTSTATUS opendirectory_opendirectory_ntlm_password_check(tDirReference dirRef, tDirNodeReference userNode, TALLOC_CTX *mem_ctx, const DATA_BLOB *challenge, const DATA_BLOB *lm_response, const DATA_BLOB *nt_response, const DATA_BLOB *lm_interactive_pwd, const DATA_BLOB *nt_interactive_pwd, const char *username, const char *client_username, const char *client_domain, DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key) { tDirStatus dirStatus = eDSAuthFailed; if (nt_response->length != 0 && nt_response->length < 24) { DEBUG(2,("opendirectory_ntlm_password_check: invalid NT password length (%lu) for user %s\n", (unsigned long)nt_response->length, username)); } if (nt_response->length >= 24) { if (nt_response->length > 24) { /* We have the NT MD4 hash challenge available - see if we can use it */ DEBUG(4,("opendirectory_ntlm_password_check: Checking NTLMv2 password with domain [%s]\n", client_domain)); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, nt_response, challenge, client_username, client_domain, False, user_sess_key)) == eDSNoErr) { #if DEBUG_LMv2 DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password with domain [%s]\n", client_domain)); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, lm_response, challenge, client_username, client_domain, False, user_sess_key)) == eDSNoErr) { DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password with domain [%s]\n", client_domain)); } else { DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password with domain [%s]\n", client_domain)); } #endif return NT_STATUS_OK; } DEBUG(4,("opendirectory_ntlm_password_check: Checking NTLMv2 password with uppercased version of domain [%s]\n", client_domain)); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, nt_response, challenge, client_username, client_domain, True, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } DEBUG(4,("opendirectory_ntlm_password_check: Checking NTLMv2 password without a domain\n")); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, nt_response, challenge, client_username, "", False, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } else { DEBUG(3,("opendirectory_ntlm_password_check: NTLMv2 password check failed\n")); return map_dserr_to_nterr(dirStatus); } } if (lp_ntlm_auth() || (nt_interactive_pwd && nt_interactive_pwd->length)) { /* We have the NT MD4 hash challenge available - see if we can use it (ie. does it exist in the smbpasswd file). */ DEBUG(4,("opendirectory_ntlm_password_check: Checking NT MD4 password\n")); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv1(dirRef, userNode, username, kDSStdAuthSMB_NT_Key, nt_response, challenge, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } else { DEBUG(3,("opendirectory_ntlm_password_check: NT MD4 password check failed for user %s\n", username)); return map_dserr_to_nterr(dirStatus); } } else { DEBUG(2,("opendirectory_ntlm_password_check: NTLMv1 passwords NOT PERMITTED for user %s\n", username)); /* no return, because we might pick up LMv2 in the LM field */ } } if (lm_response->length == 0) { DEBUG(3,("opendirectory_ntlm_password_check: NEITHER LanMan nor NT password supplied for user %s\n", username)); return NT_STATUS_WRONG_PASSWORD; } if (lm_response->length < 24) { DEBUG(2,("opendirectory_ntlm_password_check: invalid LanMan password length (%lu) for user %s\n", (unsigned long)nt_response->length, username)); return NT_STATUS_WRONG_PASSWORD; } if (!lp_lanman_auth()) { DEBUG(3,("opendirectory_ntlm_password_check: Lanman passwords NOT PERMITTED for user %s\n", username)); } else { DEBUG(4,("opendirectory_ntlm_password_check: Checking LM password\n")); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv1(dirRef, userNode, username, kDSStdAuthSMB_LM_Key, lm_response, challenge, NULL)) == eDSNoErr) { return NT_STATUS_OK; } else { DEBUG(3,("opendirectory_ntlm_password_check: LM password check failed for user %s\n",username)); // return map_dserr_to_nterr(dirStatus); } } /* if (!nt_pw) { DEBUG(4,("opendirectory_ntlm_password_check: LM password check failed for user, no NT password %s\n",username)); return NT_STATUS_WRONG_PASSWORD; } */ /* This is for 'LMv2' authentication. almost NTLMv2 but limited to 24 bytes. - related to Win9X, legacy NAS pass-though authentication */ DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password with domain %s\n", client_domain)); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, lm_response, challenge, client_username, client_domain, False, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password with upper-cased version of domain %s\n", client_domain)); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, lm_response, challenge, client_username, client_domain, True, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } DEBUG(4,("opendirectory_ntlm_password_check: Checking LMv2 password without a domain\n")); if ((dirStatus = opendirectory_smb_pwd_check_ntlmv2( dirRef, userNode, lm_response, challenge, client_username, "", False, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } /* Apparently NT accepts NT responses in the LM field - I think this is related to Win9X pass-though authentication */ DEBUG(4,("opendirectory_ntlm_password_check: Checking NT MD4 password in LM field\n")); if (lp_ntlm_auth()) { if ((dirStatus = opendirectory_smb_pwd_check_ntlmv1(dirRef, userNode, username, kDSStdAuthSMB_NT_Key, lm_response, challenge, user_sess_key)) == eDSNoErr) { return NT_STATUS_OK; } DEBUG(3,("opendirectory_ntlm_password_check: LM password, NT MD4 password in LM field and LMv2 failed for user %s\n",username)); } else { DEBUG(3,("opendirectory_ntlm_password_check: LM password and LMv2 failed for user %s, and NT MD4 password in LM field not permitted\n",username)); } return NT_STATUS_WRONG_PASSWORD; } /**************************************************************************** Do a specific test for an smb password being correct, given a smb_password and the lanman and NT responses. ****************************************************************************/ static NTSTATUS opendirectory_password_ok(tDirReference dirRef, tDirNodeReference userNode, const struct auth_context *auth_context, TALLOC_CTX *mem_ctx, SAM_ACCOUNT *sampass, const auth_usersupplied_info *user_info, DATA_BLOB *user_sess_key, DATA_BLOB *lm_sess_key) { uint16 acct_ctrl; const char *username = pdb_get_username(sampass); acct_ctrl = pdb_get_acct_ctrl(sampass); if (acct_ctrl & ACB_PWNOTREQ) { if (lp_null_passwords()) { DEBUG(3,("Account for user '%s' has no password and null passwords are allowed.\n", username)); return NT_STATUS_OK; } else { DEBUG(3,("Account for user '%s' has no password and null passwords are NOT allowed.\n", username)); return NT_STATUS_LOGON_FAILURE; } } return opendirectory_opendirectory_ntlm_password_check(dirRef, userNode, mem_ctx, &auth_context->challenge, &user_info->lm_resp, &user_info->nt_resp, &user_info->lm_interactive_pwd, &user_info->nt_interactive_pwd, username, user_info->smb_name.str, user_info->client_domain.str, user_sess_key, lm_sess_key); } /**************************************************************************** Do a specific test for a SAM_ACCOUNT being vaild for this connection (ie not disabled, expired and the like). ****************************************************************************/ static NTSTATUS opendirectory_account_ok(TALLOC_CTX *mem_ctx, SAM_ACCOUNT *sampass, const auth_usersupplied_info *user_info) { uint16 acct_ctrl = pdb_get_acct_ctrl(sampass); char *workstation_list; time_t kickoff_time; DEBUG(4,("opendirectory_account_ok: Checking SMB password for user %s\n",pdb_get_username(sampass))); /* Quit if the account was disabled. */ if (acct_ctrl & ACB_DISABLED) { DEBUG(1,("Account for user '%s' was disabled.\n", pdb_get_username(sampass))); return NT_STATUS_ACCOUNT_DISABLED; } /* Test account expire time */ kickoff_time = pdb_get_kickoff_time(sampass); if (kickoff_time != 0 && time(NULL) > kickoff_time) { DEBUG(1,("Account for user '%s' has expired.\n", pdb_get_username(sampass))); DEBUG(3,("Account expired at '%ld' unix time.\n", (long)kickoff_time)); return NT_STATUS_ACCOUNT_EXPIRED; } if (!(pdb_get_acct_ctrl(sampass) & ACB_PWNOEXP)) { time_t must_change_time = pdb_get_pass_must_change_time(sampass); time_t last_set_time = pdb_get_pass_last_set_time(sampass); /* check for immediate expiry "must change at next logon" */ if (must_change_time == 0 && last_set_time != 0) { DEBUG(1,("Account for user '%s' password must change!.\n", pdb_get_username(sampass))); return NT_STATUS_PASSWORD_MUST_CHANGE; } /* check for expired password */ if (must_change_time < time(NULL) && must_change_time != 0) { DEBUG(1,("Account for user '%s' password expired!.\n", pdb_get_username(sampass))); DEBUG(1,("Password expired at '%s' (%ld) unix time.\n", http_timestring(must_change_time), (long)must_change_time)); return NT_STATUS_PASSWORD_EXPIRED; } } /* Test workstation. Workstation list is comma separated. */ workstation_list = talloc_strdup(mem_ctx, pdb_get_workstations(sampass)); if (!workstation_list) return NT_STATUS_NO_MEMORY; if (*workstation_list) { BOOL invalid_ws = True; const char *s = workstation_list; fstring tok; while (next_token(&s, tok, ",", sizeof(tok))) { DEBUG(10,("checking for workstation match %s and %s (len=%d)\n", tok, user_info->wksta_name.str, user_info->wksta_name.len)); if(strequal(tok, user_info->wksta_name.str)) { invalid_ws = False; break; } } if (invalid_ws) return NT_STATUS_INVALID_WORKSTATION; } if (acct_ctrl & ACB_DOMTRUST) { DEBUG(2,("opendirectory_account_ok: Domain trust account %s denied by server\n", pdb_get_username(sampass))); return NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT; } if (acct_ctrl & ACB_SVRTRUST) { DEBUG(2,("opendirectory_account_ok: Server trust account %s denied by server\n", pdb_get_username(sampass))); return NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT; } if (acct_ctrl & ACB_WSTRUST) { DEBUG(4,("opendirectory_account_ok: Wksta trust account %s denied by server\n", pdb_get_username(sampass))); return NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT; } return NT_STATUS_OK; } /**************************************************************************** check if a username/password is OK assuming the password is a 24 byte SMB hash supplied in the user_info structure return an NT_STATUS constant. ****************************************************************************/ static NTSTATUS check_opendirectory_security(const struct auth_context *auth_context, void *my_private_data, TALLOC_CTX *mem_ctx, const auth_usersupplied_info *user_info, auth_serversupplied_info **server_info) { SAM_ACCOUNT *sampass=NULL; BOOL ret = False; NTSTATUS nt_status = NT_STATUS_OK; DATA_BLOB user_sess_key = data_blob(NULL, 0); DATA_BLOB lm_sess_key = data_blob(NULL, 0); tDirStatus dirStatus = eDSNoErr; tDirReference dirRef = 0; tDirNodeReference userNodeRef = 0; if (!user_info || !auth_context) { return NT_STATUS_UNSUCCESSFUL; } /* Can't use the talloc version here, becouse the returned struct gets kept on the server_info */ if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam(&sampass))) { return nt_status; } /* get the account information */ if (user_info->internal_username.str && strlen(user_info->internal_username.str)) { become_root(); ret = pdb_getsampwnam(sampass, user_info->internal_username.str); unbecome_root(); } if (ret == False) { DEBUG(3,("Couldn't find user '%s' in passdb file.\n", user_info->internal_username.str)); pdb_free_sam(&sampass); return NT_STATUS_NO_SUCH_USER; } nt_status = opendirectory_account_ok(mem_ctx, sampass, user_info); if (!NT_STATUS_IS_OK(nt_status)) { pdb_free_sam(&sampass); return nt_status; } dirStatus = dsOpenDirService(&dirRef); if (dirStatus != eDSNoErr) { DEBUG(0,("check_opendirectory_security: [%d]dsOpenDirService error\n", dirStatus)); pdb_free_sam(&sampass); return NT_STATUS_UNSUCCESSFUL; } userNodeRef = getusernode(dirRef, pdb_get_username(sampass)); if (userNodeRef != 0) { nt_status = opendirectory_password_ok(dirRef, userNodeRef, auth_context, mem_ctx, sampass, user_info, &user_sess_key, &lm_sess_key); dsCloseDirNode( userNodeRef ); dsCloseDirService(dirRef); } else { dsCloseDirService(dirRef); pdb_free_sam(&sampass); return NT_STATUS_NO_SUCH_USER; } if (!NT_STATUS_IS_OK(nt_status)) { pdb_free_sam(&sampass); return nt_status; } if (!NT_STATUS_IS_OK(nt_status = make_server_info_sam(server_info, sampass))) { DEBUG(0,("check_opendirectory_security: make_server_info_sam() failed with '%s'\n", nt_errstr(nt_status))); return nt_status; } (*server_info)->user_session_key = user_sess_key; (*server_info)->lm_session_key = lm_sess_key; return nt_status; } /* module initialisation */ NTSTATUS auth_init_opendirectory(struct auth_context *auth_context, const char *param, auth_methods **auth_method) { if (!make_auth_methods(auth_context, auth_method)) { return NT_STATUS_NO_MEMORY; } (*auth_method)->auth = check_opendirectory_security; (*auth_method)->name = "opendirectory"; return NT_STATUS_OK; } NTSTATUS init_module(void) { return smb_register_auth(AUTH_INTERFACE_VERSION, "opendirectory", auth_init_opendirectory); }