NET_API_STATUS NetUserGetLocalGroups( PCWSTR pwszHostname, PCWSTR pwszUsername, DWORD dwLevel, DWORD dwFlags, PVOID *ppBuffer, DWORD dwMaxBufferSize, PDWORD pdwNumEntries, PDWORD pdwTotalEntries ) { const DWORD dwBuiltinDomainAccess = DOMAIN_ACCESS_OPEN_ACCOUNT | DOMAIN_ACCESS_ENUM_ACCOUNTS; const DWORD dwUserAccess = USER_ACCESS_GET_GROUP_MEMBERSHIP; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; PNET_CONN pConn = NULL; SAMR_BINDING hSamrBinding = NULL; DOMAIN_HANDLE hDomain = NULL; DOMAIN_HANDLE hBtinDomain = NULL; ACCOUNT_HANDLE hUser = NULL; PSID pDomainSid = NULL; PSID pUserSid = NULL; DWORD dwUserRid = 0; DWORD dwSidLen = 0; DWORD i = 0; PDWORD pdwUserRids = NULL; PDWORD pdwBuiltinUserRids = NULL; DWORD dwRidsCount = 0; DWORD dwBuiltinRidsCount = 0; DWORD dwInfoLevelSize = 0; DWORD dwTotalNumEntries = 0; PWSTR *ppwszAliasNames = NULL; PWSTR *ppwszBuiltinAliasNames = NULL; PDWORD pdwAliasTypes = NULL; PDWORD pdwBuiltinAliasTypes = NULL; PWSTR *ppwszLocalGroupNames = NULL; PVOID pSourceBuffer = NULL; PVOID pBuffer = NULL; PVOID pBufferCursor = NULL; DWORD dwSize = 0; DWORD dwTotalSize = 0; DWORD dwNumEntries = 0; DWORD dwSpaceAvailable = 0; PIO_CREDS pCreds = NULL; NET_VALIDATION_LEVEL eValidation = NET_VALIDATION_NONE; BAIL_ON_INVALID_PTR(pwszUsername, err); BAIL_ON_INVALID_PTR(ppBuffer, err); BAIL_ON_INVALID_PTR(pdwNumEntries, err); BAIL_ON_INVALID_PTR(pdwTotalEntries, err); switch (dwLevel) { case 0: dwInfoLevelSize = sizeof(LOCALGROUP_USERS_INFO_0); break; default: err = ERROR_INVALID_LEVEL; BAIL_ON_WIN_ERROR(err); } status = LwIoGetActiveCreds(NULL, &pCreds); BAIL_ON_NT_STATUS(status); status = NetConnectSamr(&pConn, pwszHostname, 0, dwBuiltinDomainAccess, pCreds); BAIL_ON_NT_STATUS(status); hSamrBinding = pConn->Rpc.Samr.hBinding; hDomain = pConn->Rpc.Samr.hDomain; hBtinDomain = pConn->Rpc.Samr.hBuiltin; pDomainSid = pConn->Rpc.Samr.pDomainSid; status = NetOpenUser(pConn, pwszUsername, dwUserAccess, &hUser, &dwUserRid); BAIL_ON_NT_STATUS(status); dwSidLen = RtlLengthRequiredSid(pDomainSid->SubAuthorityCount + 1); err = LwAllocateMemory(dwSidLen, OUT_PPVOID(&pUserSid)); BAIL_ON_WIN_ERROR(err); status = RtlCopySid(dwSidLen, pUserSid, pDomainSid); BAIL_ON_NT_STATUS(status); status = RtlAppendRidSid(dwSidLen, pUserSid, dwUserRid); BAIL_ON_NT_STATUS(status); status = SamrGetAliasMembership(hSamrBinding, hDomain, &pUserSid, 1, &pdwUserRids, &dwRidsCount); BAIL_ON_NT_STATUS(status); status = SamrGetAliasMembership(hSamrBinding, hBtinDomain, &pUserSid, 1, &pdwBuiltinUserRids, &dwBuiltinRidsCount); BAIL_ON_NT_STATUS(status); dwTotalNumEntries = dwRidsCount + dwBuiltinRidsCount; err = LwAllocateMemory( sizeof(ppwszLocalGroupNames[0]) * dwTotalNumEntries, OUT_PPVOID(&ppwszLocalGroupNames)); BAIL_ON_WIN_ERROR(err); if (dwRidsCount > 0) { status = SamrLookupRids(hSamrBinding, hDomain, dwRidsCount, pdwUserRids, &ppwszAliasNames, &pdwAliasTypes); BAIL_ON_NT_STATUS(status); for (i = 0; i < dwRidsCount; i++) { ppwszLocalGroupNames[i] = ppwszAliasNames[i]; } } if (dwBuiltinRidsCount > 0) { status = SamrLookupRids(hSamrBinding, hBtinDomain, dwBuiltinRidsCount, pdwBuiltinUserRids, &ppwszBuiltinAliasNames, &pdwBuiltinAliasTypes); BAIL_ON_NT_STATUS(status); for (i = 0; i < dwBuiltinRidsCount; i++) { ppwszLocalGroupNames[i + dwRidsCount] = ppwszBuiltinAliasNames[i]; } } for (i = 0; i < dwTotalNumEntries; i++) { pSourceBuffer = ppwszLocalGroupNames[i]; dwSize = 0; err = NetAllocateLocalGroupUsersInfo(NULL, NULL, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); dwTotalSize += dwSize; dwNumEntries++; if (dwTotalSize > dwMaxBufferSize) { dwTotalSize -= dwSize; dwNumEntries--; break; } } if (dwTotalNumEntries > 0 && dwNumEntries == 0) { err = ERROR_INSUFFICIENT_BUFFER; BAIL_ON_WIN_ERROR(err); } if (dwTotalSize) { status = NetAllocateMemory(OUT_PPVOID(&pBuffer), dwTotalSize); BAIL_ON_NT_STATUS(status); } dwSize = 0; pBufferCursor = pBuffer; dwSpaceAvailable = dwTotalSize; for (i = 0; i < dwNumEntries; i++) { pSourceBuffer = ppwszLocalGroupNames[i]; pBufferCursor = pBuffer + (i * dwInfoLevelSize); err = NetAllocateLocalGroupUsersInfo(pBufferCursor, &dwSpaceAvailable, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); } if (dwNumEntries < dwTotalNumEntries) { err = ERROR_MORE_DATA; } status = SamrClose(hSamrBinding, hUser); BAIL_ON_NT_STATUS(status); *ppBuffer = pBuffer; *pdwNumEntries = dwNumEntries; *pdwTotalEntries = dwTotalNumEntries; cleanup: LW_SAFE_FREE_MEMORY(pUserSid); LW_SAFE_FREE_MEMORY(ppwszLocalGroupNames); if (pdwUserRids) { SamrFreeMemory(pdwUserRids); } if (pdwBuiltinUserRids) { SamrFreeMemory(pdwBuiltinUserRids); } if (ppwszAliasNames) { SamrFreeMemory(ppwszAliasNames); } if (pdwAliasTypes) { SamrFreeMemory(pdwAliasTypes); } if (ppwszBuiltinAliasNames) { SamrFreeMemory(ppwszBuiltinAliasNames); } if (pdwBuiltinAliasTypes) { SamrFreeMemory(pdwBuiltinAliasTypes); } if (pCreds) { LwIoDeleteCreds(pCreds); } return err; error: if (pBuffer) { NetFreeMemory(pBuffer); } *ppBuffer = NULL; *pdwNumEntries = 0; *pdwTotalEntries = 0; goto cleanup; }
NTSTATUS SamrLookupNames( IN SAMR_BINDING hBinding, IN DOMAIN_HANDLE hDomain, IN DWORD dwNumNames, IN PWSTR *ppwszNames, OUT UINT32 **ppRids, OUT UINT32 **ppTypes, OUT UINT32 *pRidsCount ) { NTSTATUS ntStatus = STATUS_SUCCESS; NTSTATUS ntLookupStatus = STATUS_SUCCESS; DWORD dwError = ERROR_SUCCESS; PUNICODE_STRING pNames = NULL; DWORD iName = 0; IDS Rids = {0}; IDS Types = {0}; UINT32 *pRids = NULL; UINT32 *pTypes = NULL; DWORD dwOffset = 0; DWORD dwSpaceLeft = 0; DWORD dwSize = 0; BAIL_ON_INVALID_PTR(hBinding, ntStatus); BAIL_ON_INVALID_PTR(hDomain, ntStatus); BAIL_ON_INVALID_PTR(ppwszNames, ntStatus); BAIL_ON_INVALID_PTR(ppRids, ntStatus); BAIL_ON_INVALID_PTR(ppTypes, ntStatus); /* pRidsCount can be NULL, in which case the number of returned rids must match num_names. */ dwError = LwAllocateMemory(sizeof(pNames[0]) * dwNumNames, OUT_PPVOID(&pNames)); BAIL_ON_WIN_ERROR(dwError); for (iName = 0; iName < dwNumNames; iName++) { dwError = LwAllocateUnicodeStringFromWc16String( &pNames[iName], ppwszNames[iName]); BAIL_ON_WIN_ERROR(dwError); } DCERPC_CALL(ntStatus, cli_SamrLookupNames((handle_t)hBinding, hDomain, dwNumNames, pNames, &Rids, &Types)); if (ntStatus != STATUS_SUCCESS && ntStatus != STATUS_SOME_NOT_MAPPED) { BAIL_ON_NT_STATUS(ntStatus); } ntLookupStatus = ntStatus; if (Rids.dwCount != Types.dwCount) { ntStatus = STATUS_REPLY_MESSAGE_MISMATCH; BAIL_ON_NT_STATUS(ntStatus); } ntStatus = SamrAllocateIds(NULL, &dwOffset, NULL, &Rids, &dwSize); BAIL_ON_NT_STATUS(ntStatus); dwSpaceLeft = sizeof(pRids[0]) * Rids.dwCount; dwSize = 0; dwOffset = 0; ntStatus = SamrAllocateMemory(OUT_PPVOID(&pRids), dwSpaceLeft); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrAllocateIds(pRids, &dwOffset, &dwSpaceLeft, &Rids, &dwSize); BAIL_ON_NT_STATUS(ntStatus); dwSpaceLeft = sizeof(pTypes[0]) * Types.dwCount; dwSize = 0; dwOffset = 0; ntStatus = SamrAllocateMemory(OUT_PPVOID(&pTypes), dwSpaceLeft); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrAllocateIds(pTypes, &dwOffset, &dwSpaceLeft, &Types, &dwSize); BAIL_ON_NT_STATUS(ntStatus); if (pRidsCount) { *pRidsCount = Rids.dwCount; } else if (Rids.dwCount != dwNumNames) { ntStatus = STATUS_REPLY_MESSAGE_MISMATCH; BAIL_ON_NT_STATUS(ntStatus); } *ppRids = pRids; *ppTypes = pTypes; cleanup: SamrCleanStubIds(&Rids); SamrCleanStubIds(&Types); if (pNames) { for (iName = 0; iName < dwNumNames; iName++) { LwFreeUnicodeString(&(pNames[iName])); } LW_SAFE_FREE_MEMORY(pNames); } if (ntStatus == STATUS_SUCCESS && dwError != ERROR_SUCCESS) { ntStatus = LwWin32ErrorToNtStatus(dwError); } if (ntStatus == STATUS_SUCCESS && ntLookupStatus != STATUS_SUCCESS) { ntStatus = ntLookupStatus; } return ntStatus; error: if (pRids) { SamrFreeMemory(pRids); } if (pTypes) { SamrFreeMemory(pTypes); } if (pRidsCount) { *pRidsCount = 0; } if (ppRids) { *ppRids = NULL; } if (ppTypes) { *ppTypes = NULL; } goto cleanup; }
NET_API_STATUS NetLocalGroupEnum( PCWSTR pwszHostname, DWORD dwLevel, PVOID *ppBuffer, DWORD dwMaxBufferSize, PDWORD pdwNumEntries, PDWORD pdwTotalNumEntries, PDWORD pdwResume ) { const DWORD dwAccountFlags = 0; const DWORD dwAliasAccessFlags = ALIAS_ACCESS_LOOKUP_INFO; const WORD wInfoLevel = ALIAS_INFO_ALL; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; DWORD dwResume = 0; PNET_CONN pConn = NULL; SAMR_BINDING hSamrBinding = NULL; DOMAIN_HANDLE hDomain = NULL; DOMAIN_HANDLE hBtinDomain = NULL; DWORD dwSamrResume = 0; PWSTR *ppwszDomainAliases = NULL; PDWORD pdwDomainRids = NULL; DWORD dwNumDomainEntries = 0; DWORD dwTotalNumDomainEntries = 0; PWSTR *ppwszBtinDomainAliases = NULL; PDWORD pdwBtinDomainRids = NULL; DWORD dwNumBtinDomainEntries = 0; DWORD dwTotalNumBtinDomainEntries = 0; DWORD dwTotalNumEntries = 0; DWORD dwNumEntries = 0; DWORD i = 0; PDWORD pdwRids = NULL; PWSTR *ppwszAliases = NULL; ACCOUNT_HANDLE hAlias = NULL; AliasInfo *pSamrAliasInfo = NULL; AliasInfoAll **ppAliasInfo = NULL; DWORD dwInfoLevelSize = 0; PVOID pSourceBuffer = NULL; DWORD dwSize = 0; DWORD dwTotalSize = 0; DWORD dwSpaceAvailable = 0; PVOID pBuffer = NULL; PVOID pBufferCursor = NULL; PIO_CREDS pCreds = NULL; NET_VALIDATION_LEVEL eValidation = NET_VALIDATION_NONE; BAIL_ON_INVALID_PTR(ppBuffer, err); BAIL_ON_INVALID_PTR(pdwNumEntries, err); BAIL_ON_INVALID_PTR(pdwTotalNumEntries, err); BAIL_ON_INVALID_PTR(pdwResume, err); switch (dwLevel) { case 0: dwInfoLevelSize = sizeof(LOCALGROUP_INFO_0); break; case 1: dwInfoLevelSize = sizeof(LOCALGROUP_INFO_1); break; default: err = ERROR_INVALID_LEVEL; BAIL_ON_WIN_ERROR(err); } dwResume = *pdwResume; status = LwIoGetActiveCreds(NULL, &pCreds); BAIL_ON_NT_STATUS(status); status = NetConnectSamr(&pConn, pwszHostname, 0, 0, pCreds); BAIL_ON_NT_STATUS(status); hSamrBinding = pConn->Rpc.Samr.hBinding; hDomain = pConn->Rpc.Samr.hDomain; hBtinDomain = pConn->Rpc.Samr.hBuiltin; do { status = SamrEnumDomainAliases(hSamrBinding, hDomain, &dwSamrResume, dwAccountFlags, &ppwszDomainAliases, &pdwDomainRids, &dwNumDomainEntries); if (status != STATUS_SUCCESS && status != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(status); } if (ppwszDomainAliases) { SamrFreeMemory(ppwszDomainAliases); ppwszDomainAliases = NULL; } if (pdwDomainRids) { SamrFreeMemory(pdwDomainRids); pdwDomainRids = NULL; } dwTotalNumDomainEntries += dwNumDomainEntries; dwNumDomainEntries = 0; } while (status == STATUS_MORE_ENTRIES); dwSamrResume = 0; do { status = SamrEnumDomainAliases(hSamrBinding, hBtinDomain, &dwSamrResume, dwAccountFlags, &ppwszBtinDomainAliases, &pdwBtinDomainRids, &dwNumBtinDomainEntries); if (status != STATUS_SUCCESS && status != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(status); } if (ppwszBtinDomainAliases) { SamrFreeMemory(ppwszBtinDomainAliases); ppwszBtinDomainAliases = NULL; } if (pdwBtinDomainRids) { SamrFreeMemory(pdwBtinDomainRids); pdwBtinDomainRids = NULL; } dwTotalNumBtinDomainEntries += dwNumBtinDomainEntries; dwNumBtinDomainEntries = 0; } while (status == STATUS_MORE_ENTRIES); dwTotalNumEntries = dwTotalNumDomainEntries + dwTotalNumBtinDomainEntries; status = NetAllocateMemory(OUT_PPVOID(&pdwRids), sizeof(pdwRids[0]) * dwTotalNumEntries); BAIL_ON_NT_STATUS(status); status = NetAllocateMemory(OUT_PPVOID(&ppwszAliases), sizeof(ppwszAliases[0]) * dwTotalNumEntries); BAIL_ON_NT_STATUS(status); status = NetAllocateMemory(OUT_PPVOID(&ppAliasInfo), sizeof(ppAliasInfo[0]) * dwTotalNumEntries); BAIL_ON_NT_STATUS(status); dwTotalNumDomainEntries = 0; dwTotalNumBtinDomainEntries = 0; dwSamrResume = 0; do { status = SamrEnumDomainAliases(hSamrBinding, hDomain, &dwSamrResume, dwAccountFlags, &ppwszDomainAliases, &pdwDomainRids, &dwNumDomainEntries); if (status != STATUS_SUCCESS && status != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(status); } for (i = 0; i < dwNumDomainEntries; i++) { err = LwAllocateWc16String(&ppwszAliases[dwTotalNumDomainEntries + i], ppwszDomainAliases[i]); BAIL_ON_WIN_ERROR(err); pdwRids[dwTotalNumDomainEntries + i] = pdwDomainRids[i]; } dwTotalNumDomainEntries += dwNumDomainEntries; dwNumDomainEntries = 0; if (ppwszDomainAliases) { SamrFreeMemory(ppwszDomainAliases); ppwszDomainAliases = NULL; } if (pdwDomainRids) { SamrFreeMemory(pdwDomainRids); pdwDomainRids = NULL; } } while (status == STATUS_MORE_ENTRIES); dwSamrResume = 0; do { status = SamrEnumDomainAliases(hSamrBinding, hBtinDomain, &dwSamrResume, dwAccountFlags, &ppwszBtinDomainAliases, &pdwBtinDomainRids, &dwNumBtinDomainEntries); if (status != STATUS_SUCCESS && status != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(status); } for (i = 0; i < dwNumBtinDomainEntries; i++) { err = LwAllocateWc16String(&ppwszAliases[dwTotalNumDomainEntries + dwTotalNumBtinDomainEntries + i], ppwszBtinDomainAliases[i]); BAIL_ON_WIN_ERROR(err); pdwRids[dwTotalNumDomainEntries + dwTotalNumBtinDomainEntries + i] = pdwBtinDomainRids[i]; } dwTotalNumBtinDomainEntries += dwNumBtinDomainEntries; dwNumBtinDomainEntries = 0; if (ppwszBtinDomainAliases) { SamrFreeMemory(ppwszBtinDomainAliases); ppwszBtinDomainAliases = NULL; } if (pdwBtinDomainRids) { SamrFreeMemory(pdwBtinDomainRids); pdwBtinDomainRids = NULL; } } while (status == STATUS_MORE_ENTRIES); for (i = dwResume; i < dwTotalNumEntries; i++) { if (dwLevel == 0) { pSourceBuffer = ppwszAliases[i]; } else { DOMAIN_HANDLE hDom = NULL; DWORD dwRid = 0; hDom = (i < dwTotalNumDomainEntries) ? hDomain : hBtinDomain; dwRid = pdwRids[i]; status = SamrOpenAlias(hSamrBinding, hDom, dwAliasAccessFlags, dwRid, &hAlias); BAIL_ON_NT_STATUS(status); status = SamrQueryAliasInfo(hSamrBinding, hAlias, wInfoLevel, &pSamrAliasInfo); BAIL_ON_NT_STATUS(status); ppAliasInfo[i - dwResume] = &pSamrAliasInfo->all; pSourceBuffer = &pSamrAliasInfo->all; status = SamrClose(hSamrBinding, hAlias); BAIL_ON_NT_STATUS(status); } dwSize = 0; err = NetAllocateLocalGroupInfo(NULL, NULL, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); dwTotalSize += dwSize; dwNumEntries++; if (dwTotalSize > dwMaxBufferSize) { dwTotalSize -= dwSize; dwNumEntries--; break; } } if (dwTotalNumEntries > 0 && dwNumEntries == 0) { err = ERROR_INSUFFICIENT_BUFFER; BAIL_ON_WIN_ERROR(err); } if (dwTotalSize) { status = NetAllocateMemory(OUT_PPVOID(&pBuffer), dwTotalSize); BAIL_ON_NT_STATUS(status); } dwSize = 0; pBufferCursor = pBuffer; dwSpaceAvailable = dwTotalSize; for (i = 0; i < dwNumEntries; i++) { if (dwLevel == 0) { pSourceBuffer = ppwszAliases[dwResume + i]; } else { pSourceBuffer = ppAliasInfo[i]; } pBufferCursor = pBuffer + (i * dwInfoLevelSize); err = NetAllocateLocalGroupInfo(pBufferCursor, &dwSpaceAvailable, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); } if (dwResume + dwNumEntries < dwTotalNumEntries) { err = ERROR_MORE_DATA; } *ppBuffer = pBuffer; *pdwNumEntries = dwNumEntries; *pdwTotalNumEntries = dwTotalNumEntries; *pdwResume = dwResume + dwNumEntries; cleanup: NetDisconnectSamr(&pConn); if (pdwRids) { NetFreeMemory(pdwRids); } if (ppwszAliases) { for (i = 0; i < dwTotalNumEntries; i++) { LW_SAFE_FREE_MEMORY(ppwszAliases[i]); } NetFreeMemory(ppwszAliases); } if (ppAliasInfo) { for (i = 0; i < dwNumEntries; i++) { if (ppAliasInfo[i]) { SamrFreeMemory(ppAliasInfo[i]); } } NetFreeMemory(ppAliasInfo); } if (ppwszDomainAliases) { SamrFreeMemory(ppwszDomainAliases); } if (pdwDomainRids) { SamrFreeMemory(pdwDomainRids); } if (ppwszBtinDomainAliases) { SamrFreeMemory(ppwszBtinDomainAliases); } if (pdwBtinDomainRids) { SamrFreeMemory(pdwBtinDomainRids); } if (pCreds) { LwIoDeleteCreds(pCreds); } if (err == ERROR_SUCCESS && status != STATUS_SUCCESS) { err = NtStatusToWin32Error(status); } return err; error: if (pBuffer) { NetFreeMemory(pBuffer); } *ppBuffer = NULL; *pdwNumEntries = 0; *pdwTotalNumEntries = 0; *pdwResume = 0; goto cleanup; }
NTSTATUS NetOpenUser( PNET_CONN pConn, PCWSTR pwszUsername, DWORD dwAccessMask, ACCOUNT_HANDLE *phUser, PDWORD pdwRid ) { const DWORD dwNumUsers = 1; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; SAMR_BINDING hSamrBinding = NULL; DOMAIN_HANDLE hDomain = NULL; ACCOUNT_HANDLE hUser = NULL; PWSTR ppwszUsernames[1] = {0}; PDWORD pdwRids = NULL; PDWORD pdwTypes = NULL; BAIL_ON_INVALID_PTR(pConn, err); BAIL_ON_INVALID_PTR(pwszUsername, err); BAIL_ON_INVALID_PTR(phUser, err); BAIL_ON_INVALID_PTR(pdwRid, err); hSamrBinding = pConn->Rpc.Samr.hBinding; hDomain = pConn->Rpc.Samr.hDomain; err = LwAllocateWc16String(&ppwszUsernames[0], pwszUsername); BAIL_ON_WIN_ERROR(err); status = SamrLookupNames(hSamrBinding, hDomain, dwNumUsers, ppwszUsernames, &pdwRids, &pdwTypes, NULL); BAIL_ON_NT_STATUS(status); status = SamrOpenUser(hSamrBinding, hDomain, dwAccessMask, pdwRids[0], &hUser); BAIL_ON_NT_STATUS(status); *pdwRid = pdwRids[0]; *phUser = hUser; cleanup: if (pdwRids) { SamrFreeMemory(pdwRids); } if (pdwTypes) { SamrFreeMemory(pdwTypes); } LW_SAFE_FREE_MEMORY(ppwszUsernames[0]); return status; error: *pdwRid = 0; *phUser = NULL; goto cleanup; }
static NTSTATUS LsaDisableMachineAccount( IN PCWSTR pwszDCName, IN LW_PIO_CREDS pCreds, IN PCWSTR pwszMachineAccountName ) { const DWORD dwConnAccess = SAMR_ACCESS_OPEN_DOMAIN | SAMR_ACCESS_ENUM_DOMAINS; const DWORD dwDomainAccess = DOMAIN_ACCESS_ENUM_ACCOUNTS | DOMAIN_ACCESS_OPEN_ACCOUNT | DOMAIN_ACCESS_LOOKUP_INFO_2; const DWORD dwUserAccess = USER_ACCESS_GET_ATTRIBUTES | USER_ACCESS_SET_ATTRIBUTES | USER_ACCESS_SET_PASSWORD; NTSTATUS ntStatus = STATUS_SUCCESS; SAMR_BINDING hSamrBinding = NULL; CONNECT_HANDLE hConnect = NULL; PSID pBuiltinSid = NULL; DWORD dwResume = 0; DWORD dwSize = 256; PWSTR *ppwszDomainNames = NULL; DWORD i = 0; DWORD dwNumEntries = 0; PSID pSid = NULL; PSID pDomainSid = NULL; DOMAIN_HANDLE hDomain = NULL; PDWORD pdwRids = NULL; PDWORD pdwTypes = NULL; ACCOUNT_HANDLE hUser = NULL; DWORD dwLevel = 0; UserInfo *pInfo = NULL; DWORD dwFlagsDisable = 0; UserInfo Info; memset(&Info, 0, sizeof(Info)); ntStatus = SamrInitBindingDefault(&hSamrBinding, pwszDCName, pCreds); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrConnect2(hSamrBinding, pwszDCName, dwConnAccess, &hConnect); BAIL_ON_NT_STATUS(ntStatus); ntStatus = RtlAllocateWellKnownSid( WinBuiltinDomainSid, NULL, &pBuiltinSid); BAIL_ON_NT_STATUS(ntStatus); do { ntStatus = SamrEnumDomains(hSamrBinding, hConnect, &dwResume, dwSize, &ppwszDomainNames, &dwNumEntries); BAIL_ON_NT_STATUS(ntStatus); if (ntStatus != STATUS_SUCCESS && ntStatus != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(ntStatus); } for (i = 0; pDomainSid == NULL && i < dwNumEntries; i++) { ntStatus = SamrLookupDomain(hSamrBinding, hConnect, ppwszDomainNames[i], &pSid); BAIL_ON_NT_STATUS(ntStatus); if (!RtlEqualSid(pSid, pBuiltinSid)) { ntStatus = RtlDuplicateSid(&pDomainSid, pSid); BAIL_ON_NT_STATUS(ntStatus); } SamrFreeMemory(pSid); pSid = NULL; } if (ppwszDomainNames) { SamrFreeMemory(ppwszDomainNames); ppwszDomainNames = NULL; } } while (ntStatus == STATUS_MORE_ENTRIES); ntStatus = SamrOpenDomain(hSamrBinding, hConnect, dwDomainAccess, pDomainSid, &hDomain); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrLookupNames(hSamrBinding, hDomain, 1, (PWSTR*)&pwszMachineAccountName, &pdwRids, &pdwTypes, NULL); if (ntStatus == STATUS_NONE_MAPPED) { BAIL_ON_LSA_ERROR(NERR_SetupAlreadyJoined); } ntStatus = SamrOpenUser(hSamrBinding, hDomain, dwUserAccess, pdwRids[0], &hUser); BAIL_ON_NT_STATUS(ntStatus); dwLevel = 16; ntStatus = SamrQueryUserInfo(hSamrBinding, hUser, dwLevel, &pInfo); BAIL_ON_NT_STATUS(ntStatus); dwFlagsDisable = pInfo->info16.account_flags | ACB_DISABLED; Info.info16.account_flags = dwFlagsDisable; ntStatus = SamrSetUserInfo2(hSamrBinding, hUser, dwLevel, &Info); BAIL_ON_NT_STATUS(ntStatus); cleanup: if (hSamrBinding && hUser) { SamrClose(hSamrBinding, hUser); } if (hSamrBinding && hDomain) { SamrClose(hSamrBinding, hDomain); } if (hSamrBinding && hConnect) { SamrClose(hSamrBinding, hConnect); } if (hSamrBinding) { SamrFreeBinding(&hSamrBinding); } if (pInfo) { SamrFreeMemory(pInfo); } if (pdwRids) { SamrFreeMemory(pdwRids); } if (pdwTypes) { SamrFreeMemory(pdwTypes); } if (ppwszDomainNames) { SamrFreeMemory(ppwszDomainNames); } RTL_FREE(&pBuiltinSid); RTL_FREE(&pDomainSid); return ntStatus; error: goto cleanup; }
NTSTATUS NetOpenAlias( PNET_CONN pConn, PCWSTR pwszAliasname, DWORD dwAccessMask, ACCOUNT_HANDLE *phAlias, PDWORD pdwRid ) { const DWORD dwNumAliases = 1; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; SAMR_BINDING hSamrBinding = NULL; DOMAIN_HANDLE hDomains[2] = {0}; DOMAIN_HANDLE hDomain = NULL; ACCOUNT_HANDLE hAlias = NULL; PWSTR ppwszAliasnames[1] = {0}; PDWORD pdwRids = NULL; PDWORD pdwTypes = NULL; DWORD dwAliasRid = 0; DWORD i = 0; BAIL_ON_INVALID_PTR(pConn, err); BAIL_ON_INVALID_PTR(pwszAliasname, err); BAIL_ON_INVALID_PTR(phAlias, err); BAIL_ON_INVALID_PTR(pdwRid, err); hSamrBinding = pConn->Rpc.Samr.hBinding; hDomains[0] = pConn->Rpc.Samr.hDomain; hDomains[1] = pConn->Rpc.Samr.hBuiltin; err = LwAllocateWc16String(&ppwszAliasnames[0], pwszAliasname); BAIL_ON_WIN_ERROR(err); /* * Try to look for alias in host domain first, then in builtin */ for (i = 0; i < sizeof(hDomains)/sizeof(hDomains[0]); i++) { status = SamrLookupNames(hSamrBinding, hDomains[i], dwNumAliases, ppwszAliasnames, (PUINT32*)&pdwRids, (PUINT32*)&pdwTypes, NULL); if (status == STATUS_SUCCESS) { /* * Alias has been found in one of domains so pass * that domain handle further down */ hDomain = hDomains[i]; dwAliasRid = pdwRids[0]; break; } else if (status == STATUS_NONE_MAPPED) { if (pdwRids) { SamrFreeMemory((void*)pdwRids); pdwRids = NULL; } if (pdwTypes) { SamrFreeMemory((void*)pdwTypes); pdwTypes = NULL; } continue; } /* Catch other possible errors */ BAIL_ON_NT_STATUS(status); } /* Allow to open alias only if a valid one has been found */ BAIL_ON_NT_STATUS(status); status = SamrOpenAlias(hSamrBinding, hDomain, dwAccessMask, dwAliasRid, &hAlias); BAIL_ON_NT_STATUS(status); *pdwRid = dwAliasRid; *phAlias = hAlias; cleanup: LW_SAFE_FREE_MEMORY(ppwszAliasnames[0]); if (pdwRids) { SamrFreeMemory((void*)pdwRids); } if (pdwTypes) { SamrFreeMemory((void*)pdwTypes); } return status; error: *pdwRid = 0; *phAlias = NULL; goto cleanup; }
NTSTATUS SamrQueryDisplayInfo( IN SAMR_BINDING hBinding, IN DOMAIN_HANDLE hDomain, IN WORD swLevel, IN UINT32 StartIdx, IN UINT32 MaxEntries, IN UINT32 BufferSize, OUT UINT32 *pTotalSize, OUT UINT32 *pReturnedSize, OUT SamrDisplayInfo **ppInfo ) { NTSTATUS ntStatus = STATUS_SUCCESS; NTSTATUS ntRetStatus = STATUS_SUCCESS; UINT32 TotalSize = 0; UINT32 ReturnedSize = 0; SamrDisplayInfo Info; SamrDisplayInfo *pDispInfo = NULL; DWORD dwOffset = 0; DWORD dwSpaceLeft = 0; DWORD dwSize = 0; BAIL_ON_INVALID_PTR(hBinding, ntStatus); BAIL_ON_INVALID_PTR(hDomain, ntStatus); BAIL_ON_INVALID_PTR(pTotalSize, ntStatus); BAIL_ON_INVALID_PTR(pReturnedSize, ntStatus); BAIL_ON_INVALID_PTR(ppInfo, ntStatus); memset(&Info, 0, sizeof(Info)); DCERPC_CALL(ntStatus, cli_SamrQueryDisplayInfo((handle_t)hBinding, hDomain, swLevel, StartIdx, MaxEntries, BufferSize, &TotalSize, &ReturnedSize, &Info)); /* Preserve returned status code */ ntRetStatus = ntStatus; /* Status other than success doesn't have to mean failure here */ if (ntStatus != STATUS_SUCCESS && ntStatus != STATUS_MORE_ENTRIES) { BAIL_ON_NT_STATUS(ntStatus); } ntStatus = SamrAllocateDisplayInfo(NULL, &dwOffset, NULL, swLevel, &Info, &dwSize); BAIL_ON_NT_STATUS(ntStatus); dwSpaceLeft = dwSize; dwSize = 0; dwOffset = 0; ntStatus = SamrAllocateMemory(OUT_PPVOID(&pDispInfo), dwSpaceLeft); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrAllocateDisplayInfo(pDispInfo, &dwOffset, &dwSpaceLeft, swLevel, &Info, &dwSize); BAIL_ON_NT_STATUS(ntStatus); *pTotalSize = TotalSize; *pReturnedSize = ReturnedSize; *ppInfo = pDispInfo; cleanup: SamrCleanStubDisplayInfo(&Info, swLevel); if (ntStatus == STATUS_SUCCESS && (ntRetStatus == STATUS_SUCCESS || ntRetStatus == STATUS_MORE_ENTRIES)) { ntStatus = ntRetStatus; } return ntStatus; error: if (pDispInfo) { SamrFreeMemory(pDispInfo); } if (pTotalSize) { *pTotalSize = 0; } if (pReturnedSize) { *pReturnedSize = 0; } if (ppInfo) { *ppInfo = NULL; } goto cleanup; }
static NTSTATUS LsaSrvLookupBuiltinSids( PPOLICY_CONTEXT pPolCtx, PACCOUNT_SIDS pAccountSids, RefDomainList *pDomains, TranslatedNameArray2 *pNamesArray, PDWORD pdwBuiltinDomainIndex ) { NTSTATUS ntStatus = STATUS_SUCCESS; DWORD dwError = ERROR_SUCCESS; DWORD dwBuiltinDomIndex = 0; LsaDomainInfo *pBuiltinDomainInfo = NULL; WCHAR wszBuiltinDomainName[] = LSA_BUILTIN_DOMAIN_NAME; PSID pBuiltinDomainSid = NULL; PDWORD pdwBuiltinRids = NULL; PWSTR *ppwszBuiltinNames = NULL; PDWORD pdwBuiltinTypes = NULL; DWORD i = 0; dwBuiltinDomIndex = pDomains->count; pBuiltinDomainInfo = &(pDomains->domains[dwBuiltinDomIndex]); dwError = LwAllocateWellKnownSid(WinBuiltinDomainSid, NULL, &pBuiltinDomainSid, NULL); BAIL_ON_LSA_ERROR(dwError); ntStatus = LsaSrvInitUnicodeStringEx(&pBuiltinDomainInfo->name, wszBuiltinDomainName); BAIL_ON_NTSTATUS_ERROR(ntStatus); ntStatus = LsaSrvDuplicateSid(&pBuiltinDomainInfo->sid, pBuiltinDomainSid); BAIL_ON_NTSTATUS_ERROR(ntStatus); dwError = LwAllocateMemory( sizeof(pdwBuiltinRids[0]) * pAccountSids->dwCount, OUT_PPVOID(&pdwBuiltinRids)); BAIL_ON_LSA_ERROR(dwError); for (i = 0; i < pAccountSids->dwCount; i++) { PSID pSid = pAccountSids->ppSids[i]; DWORD iSubAuthority = pSid->SubAuthorityCount - 1; if (pSid->SubAuthorityCount == 2) { pdwBuiltinRids[i] = pSid->SubAuthority[iSubAuthority]; } else { /* This could be builtin domain SID to be resolved so just avoid accidental match with an existing RID */ pdwBuiltinRids[i] = 0; } } ntStatus = SamrLookupRids(pPolCtx->hSamrBinding, pPolCtx->hBuiltinDomain, pAccountSids->dwCount, pdwBuiltinRids, &ppwszBuiltinNames, &pdwBuiltinTypes); if (ntStatus != STATUS_SUCCESS && ntStatus != LW_STATUS_SOME_NOT_MAPPED && ntStatus != STATUS_NONE_MAPPED) { BAIL_ON_NTSTATUS_ERROR(ntStatus); } for (i = 0; i < pAccountSids->dwCount; i++) { DWORD iTransName = pAccountSids->pdwIndices[i]; TranslatedName2 *pDstBuiltinName = &(pNamesArray->names[iTransName]); if ((ntStatus == STATUS_SUCCESS || ntStatus == LW_STATUS_SOME_NOT_MAPPED) && ppwszBuiltinNames[i] != NULL) { /* RID (and thus SID) has been resolved to a name */ ntStatus = LsaSrvInitUnicodeString(&pDstBuiltinName->name, ppwszBuiltinNames[i]); BAIL_ON_NTSTATUS_ERROR(ntStatus); pDstBuiltinName->type = pdwBuiltinTypes[i]; pDstBuiltinName->sid_index = dwBuiltinDomIndex; } else if (pAccountSids->ppSids[i]->SubAuthorityCount == 1 && RtlIsPrefixSid(pAccountSids->ppSids[i], pBuiltinDomainInfo->sid)) { /* RID is unknown because SID turns out to be the builtin domain SID, not an account SID */ pDstBuiltinName->type = SID_TYPE_DOMAIN; pDstBuiltinName->sid_index = dwBuiltinDomIndex; } else { /* RID is unknown */ pDstBuiltinName->type = SID_TYPE_UNKNOWN; pDstBuiltinName->sid_index = 0; } pDstBuiltinName->unknown1 = 0; } pDomains->count = dwBuiltinDomIndex + 1; pNamesArray->count += pAccountSids->dwCount; *pdwBuiltinDomainIndex = dwBuiltinDomIndex; /* * Lookup status is checked later by the caller * so avoid bailing accidentally because other lookups * may be successful */ if (ntStatus == STATUS_SOME_NOT_MAPPED || ntStatus == STATUS_NONE_MAPPED) { ntStatus = STATUS_SUCCESS; } cleanup: LW_SAFE_FREE_MEMORY(pdwBuiltinRids); if (ppwszBuiltinNames) { SamrFreeMemory(ppwszBuiltinNames); } if (pdwBuiltinTypes) { SamrFreeMemory(pdwBuiltinTypes); } if (ntStatus == STATUS_SUCCESS && dwError != ERROR_SUCCESS) { ntStatus = LwWin32ErrorToNtStatus(dwError); } return ntStatus; error: goto cleanup; }
NET_API_STATUS NetLocalGroupGetMembers( IN PCWSTR pwszHostname, IN PCWSTR pwszAliasname, IN DWORD dwLevel, OUT PVOID *ppBuffer, IN DWORD dwMaxBufferSize, OUT PDWORD pdwNumEntries, OUT PDWORD pdwTotalEntries, OUT PDWORD pdwResume ) { const DWORD dwLsaAccessFlags = LSA_ACCESS_LOOKUP_NAMES_SIDS; const DWORD dwAliasAccessFlags = ALIAS_ACCESS_GET_MEMBERS; const WORD wLookupLevel = 1; NTSTATUS status = STATUS_SUCCESS; WINERROR err = ERROR_SUCCESS; PNET_CONN pConn = NULL; SAMR_BINDING hSamrBinding = NULL; LSA_BINDING hLsaBinding = NULL; ACCOUNT_HANDLE hAlias = NULL; PSID *ppSids = NULL; DWORD dwInfoLevelSize = 0; DWORD dwTotalNumEntries = 0; DWORD dwResume = 0; DWORD dwAliasRid = 0; DWORD i = 0; DWORD dwNumSids = 0; DWORD dwCount = 0; POLICY_HANDLE hLsaPolicy = NULL; SID_ARRAY Sids = {0}; RefDomainList *pDomains = NULL; TranslatedName *pNames = NULL; PNET_RESOLVED_NAME pResolvedNames = NULL; PVOID pSourceBuffer = NULL; PVOID pBuffer = NULL; PVOID pBufferCursor = NULL; DWORD dwSize = 0; DWORD dwTotalSize = 0; DWORD dwNumEntries = 0; DWORD dwSpaceAvailable = 0; PIO_CREDS pCreds = NULL; NET_VALIDATION_LEVEL eValidation = NET_VALIDATION_NONE; BAIL_ON_INVALID_PTR(pwszAliasname, err); BAIL_ON_INVALID_PTR(ppBuffer, err); BAIL_ON_INVALID_PTR(pdwNumEntries, err); BAIL_ON_INVALID_PTR(pdwTotalEntries, err); BAIL_ON_INVALID_PTR(pdwResume, err); switch (dwLevel) { case 0: dwInfoLevelSize = sizeof(LOCALGROUP_MEMBERS_INFO_0); break; case 3: dwInfoLevelSize = sizeof(LOCALGROUP_MEMBERS_INFO_3); break; case 1: case 2: default: err = ERROR_INVALID_LEVEL; BAIL_ON_WIN_ERROR(err); } dwResume = *pdwResume; status = LwIoGetActiveCreds(NULL, &pCreds); BAIL_ON_NT_STATUS(status); status = NetConnectSamr(&pConn, pwszHostname, 0, 0, pCreds); BAIL_ON_NT_STATUS(status); hSamrBinding = pConn->Rpc.Samr.hBinding; status = NetOpenAlias(pConn, pwszAliasname, dwAliasAccessFlags, &hAlias, &dwAliasRid); if (status == STATUS_NONE_MAPPED) { /* No such alias in host's domain. Try to look in builtin domain. */ status = NetOpenAlias(pConn, pwszAliasname, dwAliasAccessFlags, &hAlias, &dwAliasRid); BAIL_ON_NT_STATUS(status); } else if (status != STATUS_SUCCESS) { BAIL_ON_NT_STATUS(status); } status = SamrGetMembersInAlias(hSamrBinding, hAlias, &ppSids, &dwNumSids); BAIL_ON_NT_STATUS(status); status = SamrClose(hSamrBinding, hAlias); BAIL_ON_NT_STATUS(status); dwTotalNumEntries = dwNumSids; if (dwLevel == 0) { for (i = 0; i + dwResume < dwNumSids; i++) { pSourceBuffer = ppSids[i + dwResume]; err = NetAllocateLocalGroupMembersInfo(NULL, NULL, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); dwTotalSize += dwSize; dwNumEntries++; if (dwTotalSize > dwMaxBufferSize) { dwTotalSize -= dwSize; dwNumEntries--; break; } } } else { status = NetConnectLsa(&pConn, pwszHostname, dwLsaAccessFlags, pCreds); BAIL_ON_NT_STATUS(status); hLsaBinding = pConn->Rpc.Lsa.hBinding; hLsaPolicy = pConn->Rpc.Lsa.hPolicy; Sids.dwNumSids = dwNumSids; status = NetAllocateMemory(OUT_PPVOID(&Sids.pSids), sizeof(Sids.pSids[0]) * Sids.dwNumSids); BAIL_ON_NT_STATUS(status); for (i = 0; i < Sids.dwNumSids; i++) { Sids.pSids[i].pSid = ppSids[i]; } status = LsaLookupSids(hLsaBinding, hLsaPolicy, &Sids, &pDomains, &pNames, wLookupLevel, &dwCount); if (status != STATUS_SUCCESS && status != LW_STATUS_SOME_NOT_MAPPED) { BAIL_ON_NT_STATUS(status); } status = NetAllocateMemory(OUT_PPVOID(&pResolvedNames), sizeof(*pResolvedNames) * dwCount); BAIL_ON_NT_STATUS(status); for (i = 0; i + dwResume < dwCount; i++) { DWORD iDomain = pNames[i + dwResume].sid_index; pResolvedNames[i].AccountName = pNames[i + dwResume].name; pResolvedNames[i].usType = pNames[i + dwResume].type; pResolvedNames[i].DomainName = pDomains->domains[iDomain].name; pSourceBuffer = pResolvedNames; err = NetAllocateLocalGroupMembersInfo(NULL, NULL, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); dwTotalSize += dwSize; dwNumEntries++; if (dwTotalSize > dwMaxBufferSize) { dwTotalSize -= dwSize; dwNumEntries--; break; } } } if (dwTotalNumEntries > 0 && dwNumEntries == 0) { err = ERROR_INSUFFICIENT_BUFFER; BAIL_ON_WIN_ERROR(err); } if (dwTotalSize) { status = NetAllocateMemory(OUT_PPVOID(&pBuffer), dwTotalSize); BAIL_ON_NT_STATUS(status); } dwSize = 0; pBufferCursor = pBuffer; dwSpaceAvailable = dwTotalSize; for (i = 0; i < dwNumEntries; i++) { if (dwLevel == 0) { pSourceBuffer = ppSids[i + dwResume]; } else { pSourceBuffer = &(pResolvedNames[i]); } pBufferCursor = pBuffer + (i * dwInfoLevelSize); err = NetAllocateLocalGroupMembersInfo(pBufferCursor, &dwSpaceAvailable, dwLevel, pSourceBuffer, &dwSize, eValidation); BAIL_ON_WIN_ERROR(err); } if (dwResume + dwNumEntries < dwTotalNumEntries) { err = ERROR_MORE_DATA; } *ppBuffer = pBuffer; *pdwResume = dwResume + dwNumEntries; *pdwNumEntries = dwNumEntries; *pdwTotalEntries = dwTotalNumEntries; cleanup: NetDisconnectSamr(&pConn); if (Sids.pSids) { NetFreeMemory(Sids.pSids); } if (ppSids) { SamrFreeMemory(ppSids); } if (pNames) { SamrFreeMemory(pNames); } if (pDomains) { SamrFreeMemory(pDomains); } if (pCreds) { LwIoDeleteCreds(pCreds); } if (err == ERROR_SUCCESS && status != STATUS_SUCCESS) { err = LwNtStatusToWin32Error(status); } return err; error: if (pBuffer) { NetFreeMemory(pBuffer); } *ppBuffer = NULL; goto cleanup; }
NTSTATUS SamrGetUserGroups( IN SAMR_BINDING hBinding, IN ACCOUNT_HANDLE hUser, OUT UINT32 **ppRids, OUT UINT32 **ppAttributes, OUT UINT32 *pCount ) { NTSTATUS ntStatus = STATUS_SUCCESS; UINT32 *pRids = NULL; UINT32 *pAttributes = NULL; PRID_WITH_ATTRIBUTE_ARRAY pRidWithAttr = NULL; DWORD dwRidCount = 0; DWORD dwOffset = 0; DWORD dwSpaceLeft = 0; DWORD dwSize = 0; BAIL_ON_INVALID_PTR(hBinding, ntStatus); BAIL_ON_INVALID_PTR(hUser, ntStatus); BAIL_ON_INVALID_PTR(ppRids, ntStatus); BAIL_ON_INVALID_PTR(ppAttributes, ntStatus); BAIL_ON_INVALID_PTR(pCount, ntStatus); DCERPC_CALL(ntStatus, cli_SamrGetUserGroups((handle_t)hBinding, hUser, &pRidWithAttr)); BAIL_ON_NT_STATUS(ntStatus); if (pRidWithAttr) { dwRidCount = pRidWithAttr->dwCount; dwSpaceLeft = sizeof(pRids[0]) * dwRidCount; ntStatus = SamrAllocateMemory(OUT_PPVOID(&pRids), dwSpaceLeft); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrAllocateRidsFromRidWithAttributeArray( pRids, &dwOffset, &dwSpaceLeft, pRidWithAttr, &dwSize); BAIL_ON_NT_STATUS(ntStatus); dwSpaceLeft = sizeof(pAttributes[0]) * dwRidCount; dwSize = 0; dwOffset = 0; ntStatus = SamrAllocateMemory(OUT_PPVOID(&pAttributes), dwSpaceLeft); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SamrAllocateAttributesFromRidWithAttributeArray( pAttributes, &dwOffset, &dwSpaceLeft, pRidWithAttr, &dwSize); BAIL_ON_NT_STATUS(ntStatus); } *ppRids = pRids; *ppAttributes = pAttributes; *pCount = dwRidCount; cleanup: if (pRidWithAttr) { SamrFreeStubRidWithAttributeArray(pRidWithAttr); } return ntStatus; error: if (pRids) { SamrFreeMemory(pRids); } if (pAttributes) { SamrFreeMemory(pAttributes); } if (ppRids) { *ppRids = NULL; } if (ppAttributes) { *ppAttributes = NULL; } if (pCount) { *pCount = 0; } goto cleanup; }