tDirStatus userAuthInfo(tDirReference pDirRef, tDirNodeReference pNodeRef, const char *pszUsername, tDataListPtr *ppAuthNodeListOut) { tDirStatus dsErr = eDSNoErr; tDirStatus dsCleanErr = eDSNoErr; /* Create a buffer for the resulting authentication info */ tDataBufferPtr pTmpBuf = dsDataBufferAllocate(pDirRef, s_cBufferSize); if (pTmpBuf) { /* Create the necessary lists for kDSNAttrMetaNodeLocation and kDSNAttrRecordName. */ tDataListPtr pRecordType = dsBuildListFromStrings(pDirRef, kDSStdRecordTypeUsers, NULL); tDataListPtr pRecordName = dsBuildListFromStrings(pDirRef, pszUsername, NULL); tDataListPtr pRequestedAttributes = dsBuildListFromStrings(pDirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL); if (!( pRecordType == NULL || pRecordName == NULL || pRequestedAttributes == NULL)) { /* Now search for the first matching record */ UInt32 cRecords = 1; tContextData pCtx = NULL; dsErr = dsGetRecordList(pNodeRef, pTmpBuf, pRecordName, eDSExact, pRecordType, pRequestedAttributes, false, &cRecords, &pCtx); if ( dsErr == eDSNoErr && cRecords >= 1) { /* Process the first found record. Look at any attribute one by one. */ tAttributeListRef pRecAttrListRef = NULL; tRecordEntryPtr pRecEntry = NULL; tDataListPtr pAuthNodeList = NULL; dsErr = dsGetRecordEntry(pNodeRef, pTmpBuf, 1, &pRecAttrListRef, &pRecEntry); if (dsErr == eDSNoErr) { for (size_t i = 1; i <= pRecEntry->fRecordAttributeCount; ++i) { tAttributeValueListRef pAttrValueListRef = NULL; tAttributeEntryPtr pAttrEntry = NULL; /* Get the information for this attribute. */ dsErr = dsGetAttributeEntry(pNodeRef, pTmpBuf, pRecAttrListRef, i, &pAttrValueListRef, &pAttrEntry); if (dsErr == eDSNoErr) { tAttributeValueEntryPtr pValueEntry = NULL; /* Has any value? */ if (pAttrEntry->fAttributeValueCount > 0) { dsErr = dsGetAttributeValue(pNodeRef, pTmpBuf, 1, pAttrValueListRef, &pValueEntry); if (dsErr == eDSNoErr) { /* Check for kDSNAttrMetaNodeLocation */ if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) { /* Convert the meta location attribute to a path node list */ pAuthNodeList = dsBuildFromPath(pDirRef, pValueEntry->fAttributeValueData.fBufferData, "/"); if (pAuthNodeList == NULL) dsErr = eDSAllocationFailed; } } } if (pValueEntry != NULL) dsDeallocAttributeValueEntry(pDirRef, pValueEntry); if (pAttrValueListRef) dsCloseAttributeValueList(pAttrValueListRef); if (pAttrEntry != NULL) dsDeallocAttributeEntry(pDirRef, pAttrEntry); if (dsErr != eDSNoErr) break; } } } /* Copy the results */ if (dsErr == eDSNoErr) { if (pAuthNodeList != NULL) { /* Copy out results. */ *ppAuthNodeListOut = pAuthNodeList; pAuthNodeList = NULL; } else dsErr = eDSAttributeNotFound; } if (pAuthNodeList != NULL) { dsCleanErr = dsDataListDeallocate(pDirRef, pAuthNodeList); if (dsCleanErr == eDSNoErr) free(pAuthNodeList); } if (pRecAttrListRef) dsCloseAttributeList(pRecAttrListRef); if (pRecEntry != NULL) dsDeallocRecordEntry(pDirRef, pRecEntry); } else dsErr = eDSRecordNotFound; if (pCtx) dsReleaseContinueData(pDirRef, pCtx); } else dsErr = eDSAllocationFailed; if (pRequestedAttributes != NULL) { dsCleanErr = dsDataListDeallocate(pDirRef, pRequestedAttributes); if (dsCleanErr == eDSNoErr) free(pRequestedAttributes); } if (pRecordName != NULL) { dsCleanErr = dsDataListDeallocate(pDirRef, pRecordName); if (dsCleanErr == eDSNoErr) free(pRecordName); } if (pRecordType != NULL) { dsCleanErr = dsDataListDeallocate(pDirRef, pRecordType); if (dsCleanErr == eDSNoErr) free(pRecordType); } dsDataBufferDeAllocate(pDirRef, pTmpBuf); } else dsErr = eDSAllocationFailed; return dsErr; }
//---------------------------------------------------------------------- // dsauth_set_mppe_keys // get the mppe keys from the password server // if this fails - do nothing. there is no way to // know if the keys are actually going to be needed // at this point. //---------------------------------------------------------------------- static int dsauth_set_mppe_keys(tDirReference dirRef, tDirNodeReference userNode, u_char *remmd, tAttributeValueEntryPtr authAuthorityAttr, const unsigned char *challenge) { tDataNodePtr authKeysDataNodePtr = 0; tDataBufferPtr authDataBufPtr = 0; tDataBufferPtr responseDataBufPtr = 0; tDataBufferPtr chapDataBufPtr = NULL; tDirStatus dsResult = eDSNoErr; char *ptr, *comma, *tagStart; MS_Chap2Response *resp; char *keyaccessPassword = 0; char *keyaccessName = 0; u_int32_t keyaccessNameSize = 0; u_int32_t keyaccessPasswordSize; int len, i; u_int32_t slotIDSize = 0; const char *slotID; tContextData dir_context = 0; int status = 0; mppe_keys_set = 0; // parse authAuthorityAttribute to determine if key agent needs to be used. // auth authority tag will be between 2 semicolons. tagStart = 0; ptr = authAuthorityAttr->fAttributeValueData.fBufferData; // DSAuth_hex_print("authAuth mppe", authAuthorityAttr->fAttributeValueData.fBufferData, authAuthorityAttr->fAttributeValueData.fBufferLength); for (i = 0; i < authAuthorityAttr->fAttributeValueData.fBufferLength; i++) { if (*ptr == ';') { if (tagStart == 0) tagStart = ptr + 1; else break; } ptr++; } if (*ptr != ';') { error("DSAuth plugin: Password Server not available for MPPE key retrieval.\n"); return (status); } if (strncmp(tagStart, kDSTagAuthAuthorityPasswordServer, ptr - tagStart) == 0) { dsauth_get_admin_acct(&keyaccessNameSize, &keyaccessName, &keyaccessPasswordSize, &keyaccessPassword); if (keyaccessName == 0) { error("DSAuth plugin: Could not retrieve key agent account information.\n"); return (status); } // PWS auths expect to receive the PWS ID in the MPPE phase comma = strchr(ptr, ','); if (comma == NULL) { error("DSAuth plugin: Could not retrieve slot ID.\n"); return (status); } slotID = ptr + 1; // skip the ';' as well slotIDSize = comma - slotID; } else { error("DSAuth plugin: unsupported authen authority: recved %s, want %s\n", tagStart, kDSTagAuthAuthorityPasswordServer); return (status); // unsupported authentication authority - don't set the keys } resp = (MS_Chap2Response*)remmd; if ((responseDataBufPtr = dsDataBufferAllocate(dirRef, BUF_LEN)) == 0) { error("DSAuth plugin: Could not allocate data buffer\n"); goto cleanup; } if ((authKeysDataNodePtr = dsDataNodeAllocateString(dirRef, kDSStdAuthMPPEMasterKeys)) == 0) { error("DSAuth plugin: Could not allocate data buffer\n"); goto cleanup; } if ((authDataBufPtr = dsDataBufferAllocate(dirRef, BUF_LEN)) == 0) { error("DSAuth plugin: Could not allocate data buffer\n"); goto cleanup; } /* * phase 1 uses an MSCHAP-style auth for the VPN agent (agent account, server challenge, peer challenge, agent NT response, agent account) * phase 2 retrieves the keys (PWS ID of the user, client NT response, keysize) */ // Phase 1 chapDataBufPtr = dsauth_agent_authbuffer(dirRef, keyaccessName, keyaccessNameSize, keyaccessPassword, keyaccessPasswordSize, challenge); if (chapDataBufPtr == NULL) { error("DSAuth plugin: Could not generate agent auth buffer\n"); goto cleanup; } if ((dsResult = dsDoDirNodeAuth(userNode, authKeysDataNodePtr, TRUE, chapDataBufPtr, responseDataBufPtr, &dir_context)) != eDSNoErr) { error("DSAuth plugin: Could not authenticate key agent for encryption key retrieval, err %d\n", dsResult); goto cleanup; } // Phase 2 ptr = (char*)(authDataBufPtr->fBufferData); // DSAuth_hex_print("PWS ID", slotID, slotIDSize); // DSAuth_hex_print("NTResp", resp->NTResp, NT_RESPONSE_SIZE); // 4 byte length & slotID *((u_int32_t*)ptr) = slotIDSize; ptr += sizeof(u_int32_t); memcpy(ptr, slotID, slotIDSize); ptr += slotIDSize; // 4 byte length & client digest *((u_int32_t*)ptr) = NT_RESPONSE_SIZE; ptr += sizeof(u_int32_t); memcpy(ptr, resp->NTResp, NT_RESPONSE_SIZE); ptr += NT_RESPONSE_SIZE; // 4 byte length and master key len - always 128 *((u_int32_t*)ptr) = 1; ptr += sizeof(u_int32_t); *ptr = MPPE_MAX_KEY_LEN; authDataBufPtr->fBufferLength = slotIDSize + NT_RESPONSE_SIZE + 1 + (3 * sizeof(u_int32_t)); // get the mppe keys if ((dsResult = dsDoDirNodeAuth(userNode, authKeysDataNodePtr, TRUE, authDataBufPtr, responseDataBufPtr, &dir_context)) == eDSNoErr) { if (responseDataBufPtr->fBufferLength == (2 * sizeof(u_int32_t)) + (2 * MPPE_MAX_KEY_LEN)) { ptr = (char*)(responseDataBufPtr->fBufferData); len = *((u_int32_t*)ptr); ptr += sizeof(u_int32_t); if (len == sizeof(mppe_send_key)) memcpy(mppe_send_key, ptr, sizeof(mppe_send_key)); ptr += len; len = *((u_int32_t*)ptr); ptr += sizeof(u_int32_t); if (len == sizeof(mppe_recv_key)) memcpy(mppe_recv_key, ptr, sizeof(mppe_recv_key)); mppe_keys_set = 1; status = 1; } else error("DSAuth plugin: Invalid MPPE data for encryption keys retrieval\n"); } else error("DSAuth plugin: Failed to retrieve MPPE encryption keys from the password server: errno %d, ctxt %x\n", dsResult, dir_context); cleanup: if (dir_context != 0) { dsReleaseContinueData(dirRef, dir_context); } if (keyaccessPassword) { bzero(keyaccessPassword, keyaccessPasswordSize); // clear the admin password from memory #if !TARGET_OS_EMBEDDED // This file is not built for Embedded SecKeychainItemFreeContent(NULL, keyaccessPassword); #endif /* TARGET_OS_EMBEDDED */ } if (keyaccessName) { free(keyaccessName); } if (responseDataBufPtr) dsDataBufferDeAllocate(dirRef, responseDataBufPtr); if (chapDataBufPtr) dsDataNodeDeAllocate(dirRef, chapDataBufPtr); if (authKeysDataNodePtr) dsDataNodeDeAllocate(dirRef, authKeysDataNodePtr); if (authDataBufPtr) dsDataBufferDeAllocate(dirRef, authDataBufPtr); return (status); }
tDirStatus FindUsersAuthInfo( tDirReference dirRef, tDirNodeReference nodeRef, const char * username, tDataListPtr * pathListToAuthNodePtr ) { tDirStatus err; tDirStatus junk; tDataBufferPtr buf; tDataListPtr recordType; tDataListPtr recordName; tDataListPtr requestedAttributes; unsigned long recordCount; tAttributeListRef foundRecAttrList; tContextData context; tRecordEntryPtr foundRecEntry; tDataListPtr pathListToAuthNode; if (!( (dirRef != NULL) || (nodeRef != NULL) || (username != NULL) || ( pathListToAuthNodePtr != NULL) || (*pathListToAuthNodePtr == NULL) ) ) return(eDSBadParam); recordType = NULL; recordName = NULL; requestedAttributes = NULL; foundRecAttrList = NULL; context = NULL; foundRecEntry = NULL; pathListToAuthNode = NULL; /* ** Allocate a buffer for the record results. We'll grow this ** buffer if it proves to be too small. */ err = eDSNoErr; buf = dsDataBufferAllocate(dirRef, kDefaultDSBufferSize); if (buf == NULL) err = eDSAllocationFailed; /* ** Create the information needed for the search. We're searching for ** a record of type kDSStdRecordTypeUsers whose name is "username". ** We want to get back the kDSNAttrMetaNodeLocation and kDSNAttrRecordName ** attributes. */ if (err == eDSNoErr) { recordType = dsBuildListFromStrings(dirRef, kDSStdRecordTypeUsers, NULL); recordName = dsBuildListFromStrings(dirRef, username, NULL); requestedAttributes = dsBuildListFromStrings(dirRef, kDSNAttrMetaNodeLocation, kDSNAttrRecordName, NULL); if ( (recordType == NULL) || (recordName == NULL) || (requestedAttributes == NULL) ) err = eDSAllocationFailed; } /* Search for a matching record. */ if (err == eDSNoErr) { recordCount = 1; /* we only want one match (the first) */ err = dsGetRecordListQ( dirRef, nodeRef, &buf, recordName, eDSExact, recordType, requestedAttributes, false, &recordCount, &context ); } if ( (err == eDSNoErr) && (recordCount < 1) ) err = eDSRecordNotFound; /* ** Get the first record from the search. Then enumerate the attributes for ** that record. For each attribute, extract the first value (remember that ** attributes can by multi-value). Then see if the attribute is one that ** we care about. If it is, remember the value for later processing. */ if (err == eDSNoErr) err = dsGetRecordEntry(nodeRef, buf, 1, &foundRecAttrList, &foundRecEntry); if (err == eDSNoErr) { unsigned long attrIndex; /* Iterate over the attributes. */ for (attrIndex = 1; attrIndex <= foundRecEntry->fRecordAttributeCount; attrIndex++) { tAttributeValueListRef thisValue; tAttributeEntryPtr thisAttrEntry; tAttributeValueEntryPtr thisValueEntry; const char * thisAttrName; thisValue = NULL; thisAttrEntry = NULL; thisValueEntry = NULL; /* Get the information for this attribute. */ err = dsGetAttributeEntry(nodeRef, buf, foundRecAttrList, attrIndex, &thisValue, &thisAttrEntry); if (err == eDSNoErr) { thisAttrName = thisAttrEntry->fAttributeSignature.fBufferData; /* We only care about attributes that have values. */ if (thisAttrEntry->fAttributeValueCount > 0) { /* ** Get the first value for this attribute. This is common ** code for the two potential attribute values listed below, ** so we do it first. */ err = dsGetAttributeValue(nodeRef, buf, 1, thisValue, &thisValueEntry); if (err == eDSNoErr) { const char * thisValueDataPtr; unsigned long thisValueDataLen; thisValueDataPtr = thisValueEntry->fAttributeValueData.fBufferData; thisValueDataLen = thisValueEntry->fAttributeValueData.fBufferLength; /* ** Handle each of the two attributes we care about; ** ignore any others. */ if ( STcompare(thisAttrName, kDSNAttrMetaNodeLocation) == 0 ) { /* ** This is the kDSNAttrMetaNodeLocation attribute, ** which contains a path to the node used for ** authenticating this record; convert its value ** into a path list. */ pathListToAuthNode = dsBuildFromPath( dirRef, thisValueDataPtr, "/" ); if (pathListToAuthNode == NULL) err = eDSAllocationFailed; } } else { fprintf(stderr, "FindUsersAuthInfo: Unexpected no-value attribute '%s'.", thisAttrName); } } /* Clean up. */ if (thisValueEntry != NULL) dsDeallocAttributeValueEntry(dirRef, thisValueEntry); if (thisValue != NULL) dsCloseAttributeValueList(thisValue); if (thisAttrEntry != NULL) dsDeallocAttributeEntry(dirRef, thisAttrEntry); if (err != eDSNoErr) break; } } /* Check attr loop */ } /* Copy results out to caller. */ if (err == eDSNoErr) { if (pathListToAuthNode != NULL) { /* Copy out results. */ *pathListToAuthNodePtr = pathListToAuthNode; /* NULL out locals so that we don't dispose them. */ pathListToAuthNode = NULL; } else err = eDSAttributeNotFound; } /* Clean up. */ if (pathListToAuthNode != NULL) dsDataListAndHeaderDeallocate(dirRef, pathListToAuthNode); if (foundRecAttrList != NULL) dsCloseAttributeList(foundRecAttrList); if (context != NULL) dsReleaseContinueData(dirRef, context); if (foundRecAttrList != NULL) dsDeallocRecordEntry(dirRef, foundRecEntry); if (requestedAttributes != NULL) dsDataListAndHeaderDeallocate(dirRef, requestedAttributes); if (recordName != NULL) dsDataListAndHeaderDeallocate(dirRef, recordName); if (recordType != NULL) dsDataListAndHeaderDeallocate(dirRef, recordType); if (buf != NULL) dsDataBufferDeAllocate(dirRef, buf); return err; }
CFArrayRef find_user_records_by_name(KADirectoryNode *node, const char *username) { tDirStatus status = 0; long unsigned record_count = 0; // This is an input variable too tContextData continuation_data_ptr = NULL; CFMutableArrayRef results = NULL; tDataListPtr recordTypeDataList = dsBuildListFromStrings(node->_directoryRef, kDSStdRecordTypeUsers, NULL); tDataListPtr returnAttributesDataList = dsBuildListFromStrings(node->_directoryRef, kDSAttributesAll, NULL); tDataListPtr record_name_ptr = dsBuildListFromStrings(node->_directoryRef, username, NULL); do { for (;;) { status = dsGetRecordList(node->_nodeRef, node->_dataBufferPtr, record_name_ptr, eDSExact, recordTypeDataList, returnAttributesDataList, false, /* attr info and data */ &record_count, &continuation_data_ptr); // Of all errors we only try to recover from eDSBufferTooSmall here if ((status != eDSBufferTooSmall) || !(node->_dataBufferPtr = double_databuffer_or_bail(node->_directoryRef, node->_dataBufferPtr))) break; } if (status) break; if (record_count > 0) { long unsigned i; if (!results) results = CFArrayCreateMutable(kCFAllocatorDefault, record_count, &kCFTypeArrayCallBacks); for (i=1; i <= record_count; i++) { CFMutableDictionaryRef record = get_record_at_index(node, i); if (record) { CFArrayAppendValue(results, record); CFRelease(record); } } } } while (continuation_data_ptr != NULL); if (record_name_ptr) dsDataListDeallocate(node->_directoryRef, record_name_ptr); if (recordTypeDataList) dsDataListDeallocate(node->_directoryRef, recordTypeDataList); if (returnAttributesDataList) dsDataListDeallocate(node->_directoryRef, returnAttributesDataList); if (continuation_data_ptr) dsReleaseContinueData(node->_nodeRef, continuation_data_ptr); return results; }
tDirStatus getdsnodepath(tDirReference dirRef, tDataListPtr *dsnodepath) { tDirStatus err; tDirStatus junk; tDataBufferPtr buf; tDirPatternMatch patternToFind; unsigned long nodeCount; tContextData context; if (!( (dirRef != NULL) || ( dsnodepath != NULL) || (*dsnodepath == NULL) ) ) return( eDSBadParam ); if (authlocalonly) patternToFind = eDSLocalNodeNames; else patternToFind = eDSAuthenticationSearchNodeName; context = NULL; /* ** Allocate a buffer for the node find results. We'll grow ** this buffer if it proves to be to small. */ buf = dsDataBufferAllocate(dirRef, kDefaultDSBufferSize); err = eDSNoErr; if (buf == NULL) err = eDSAllocationFailed; /* ** Find the node. Note that this is a degenerate case because ** we're only looking for a single node, the search node, so ** we don't need to loop calling dsFindDirNodes, which is the ** standard way of using dsFindDirNodes. */ if (err == eDSNoErr) { err = dsFindDirNodesQ( dirRef, &buf, /* place results here */ NULL, /* no pattern, rather... */ patternToFind, /* ... hardwired search type */ &nodeCount, &context ); } /* If we didn't find any nodes, that's bad. */ if ( (err == eDSNoErr) && (nodeCount < 1) ) err = eDSNodeNotFound; /* ** Grab the first node from the buffer. Note that the inDirNodeIndex ** parameter to dsGetDirNodeName is one-based, so we pass in the constant ** 1. ** ** Also, if we found more than one, that's unusual, but not enough to ** cause us to error. */ if (err == eDSNoErr) err = dsGetDirNodeName(dirRef, buf, 1, dsnodepath); /* Clean up. */ if (context != NULL) dsReleaseContinueData(dirRef, context); if (buf != NULL) dsDataBufferDeAllocate(dirRef, buf); return err; }