DWORD ADCacheDuplicateMembershipContents( PLSA_GROUP_MEMBERSHIP pDest, PLSA_GROUP_MEMBERSHIP pSrc ) { DWORD dwError = 0; dwError = LwStrDupOrNull( pSrc->pszParentSid, &pDest->pszParentSid); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->pszChildSid, &pDest->pszChildSid); BAIL_ON_LSA_ERROR(dwError); pDest->version = pSrc->version; pDest->bIsInPac = pSrc->bIsInPac; pDest->bIsInPacOnly = pSrc->bIsInPacOnly; pDest->bIsInLdap = pSrc->bIsInLdap; pDest->bIsDomainPrimaryGroup = pSrc->bIsDomainPrimaryGroup; cleanup: return dwError; error: goto cleanup; }
/** * Get name component from UPN, Domain\User string, or NAME=VALUE pair. * * @param logonName Name to parse. * @return Name component (Dynamically allocated.) */ PSTR GetNameComp(IN PSTR logonName) { DWORD dwError = 0; PSTR name = NULL; int i; if(!logonName) { goto cleanup; } for(i = 0; i < strlen(logonName); ++i) { if(logonName[i] == '\\') { dwError = LwStrDupOrNull(logonName + i + 1, &name); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); goto cleanup; } } for(i = 0; i < strlen(logonName); ++i) { if(logonName[i] == '@') { dwError = LwStrndup(logonName, i, &name); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); goto cleanup; } } for(i = 0; i < strlen(logonName); ++i) { if(logonName[i] == '=') { dwError = LwStrndup(logonName + i + 1, strlen(logonName + i + 1), &name); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); goto cleanup; } } for(i = 0; i < strlen(logonName); ++i) { if(logonName[i] == '/') { dwError = LwAllocateMemory(sizeof(CHAR) * (i + 1), OUT_PPVOID(&name)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); strncpy(name, (PCSTR) logonName, i); goto cleanup; } } dwError = LwStrDupOrNull(logonName, &name); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); cleanup: return name; error: goto cleanup; }
DWORD LsaMarshalGroupInfo0ToGroupAddInfo( HANDLE hLsa, PLSA_GROUP_INFO_0 pGroupInfo, PLSA_GROUP_ADD_INFO* ppAddInfo ) { DWORD dwError = 0; PLSA_GROUP_ADD_INFO pAddInfo = NULL; dwError = LwAllocateMemory(sizeof(*pAddInfo), OUT_PPVOID(&pAddInfo)); BAIL_ON_LSA_ERROR(dwError); pAddInfo->gid = pGroupInfo->gid; dwError = LwStrDupOrNull(pGroupInfo->pszName, &pAddInfo->pszName); BAIL_ON_LSA_ERROR(dwError); *ppAddInfo = pAddInfo; cleanup: return dwError; error: *ppAddInfo = NULL; if (pAddInfo) { LsaFreeGroupAddInfo(pAddInfo); } goto cleanup; }
/** * Get parent DN. This method dynamically allocates memory, which must be freed. * * @param str DN to parse. * @param out parent DN. * @return 0 on success; error code on failure. */ DWORD GetParentDN(IN PSTR str, OUT PSTR *out) { DWORD dwError = 0; INT len = 0; INT ind = 0; if(!str || !str[0]) { *out = NULL; goto cleanup; } if(!IsCommaPresent(str)) { dwError = LwStrDupOrNull("", out); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); goto cleanup; } ind = GetFirstIndexOfChar(str, ',', 1); len = strlen(str) - ind; dwError = LwAllocateMemory(sizeof(CHAR) * (len + 1), OUT_PPVOID(out)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); strncpy(*out, str + ind + 1, len - 1); cleanup: return dwError; error: goto cleanup; }
/** * Normalize user name by adding domain prefix if the name is not UPN. * * @param userNameC User name. * @param domainC Domain name. * @param userNameN Normalized user name to fill out. * @return 0 on success; error code on failure. */ static DWORD NormalizeUserName( IN PSTR userNameC, IN PSTR domainC, OUT PSTR *userNameN ) { DWORD dwError = ERROR_SUCCESS; PSTR userName = NULL; PSTR domain = NULL; if(!IsFullOrUPN(userNameC)) { dwError = GetDomainComp(domainC, &domain); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); dwError = LwAllocateStringPrintf(&userName, "%s\\%s", domain, userNameC); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); } else { dwError = LwStrDupOrNull((PCSTR) userNameC, &userName); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); } *userNameN = userName; cleanup: LW_SAFE_FREE_MEMORY(domain); return dwError; error: LW_SAFE_FREE_MEMORY(userName); goto cleanup; }
/** * Get Net BIOS domain name from a fully qualified name. * * @param str Name to parse. * @param out Domain component. * @return 0 on success; error code on failure. */ DWORD GetDomainComp(IN PSTR str, OUT PSTR *out) { DWORD dwError = 0; INT len = 0; if(!str || !str[0]) { *out = NULL; goto cleanup; } if(!IsDotPresent(str)) { dwError = LwStrDupOrNull(str, out); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); goto cleanup; } len = GetFirstIndexOfChar(str, '.', 1); dwError = LwAllocateMemory(sizeof(CHAR) * (len + 1), OUT_PPVOID(out)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); strncpy(*out, str, len); cleanup: return dwError; error: goto cleanup; }
DWORD LsaImplFillMachinePasswordInfoA( IN PLSA_MACHINE_PASSWORD_INFO_A pSourcePasswordInfo, OUT PLSA_MACHINE_PASSWORD_INFO_A pTargetPasswordInfo ) { DWORD dwError = 0; dwError = LsaImplFillMachineAccountInfoA( &pSourcePasswordInfo->Account, &pTargetPasswordInfo->Account); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSourcePasswordInfo->Password, &pTargetPasswordInfo->Password); BAIL_ON_LSA_ERROR(dwError); error: if (dwError) { LsaImplFreeMachinePasswordInfoContentsA(pTargetPasswordInfo); } return dwError; }
static DWORD LsaImplFillMachineAccountInfoA( IN PLSA_MACHINE_ACCOUNT_INFO_A pSourceAccountInfo, OUT PLSA_MACHINE_ACCOUNT_INFO_A pTargetAccountInfo ) { DWORD dwError = 0; dwError = LwStrDupOrNull( pSourceAccountInfo->DnsDomainName, &pTargetAccountInfo->DnsDomainName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSourceAccountInfo->NetbiosDomainName, &pTargetAccountInfo->NetbiosDomainName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSourceAccountInfo->DomainSid, &pTargetAccountInfo->DomainSid); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSourceAccountInfo->SamAccountName, &pTargetAccountInfo->SamAccountName); BAIL_ON_LSA_ERROR(dwError); pTargetAccountInfo->AccountFlags = pSourceAccountInfo->AccountFlags; pTargetAccountInfo->KeyVersionNumber = pSourceAccountInfo->KeyVersionNumber; dwError = LwStrDupOrNull( pSourceAccountInfo->Fqdn, &pTargetAccountInfo->Fqdn); BAIL_ON_LSA_ERROR(dwError); pTargetAccountInfo->LastChangeTime = pSourceAccountInfo->LastChangeTime; error: if (dwError) { LsaImplFreeMachineAccountInfoContentsA(pTargetAccountInfo); } return dwError; }
DWORD LsaSrvCopyDCInfo( PLSA_DC_INFO pSrcInfo, PLSA_DC_INFO* ppDCInfo ) { DWORD dwError = 0; PLSA_DC_INFO pDCInfo = NULL; dwError = LwAllocateMemory( sizeof(LSA_DC_INFO), (PVOID*)&pDCInfo); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcInfo->pszName, &pDCInfo->pszName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcInfo->pszAddress, &pDCInfo->pszAddress); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcInfo->pszSiteName, &pDCInfo->pszSiteName); BAIL_ON_LSA_ERROR(dwError); pDCInfo->dwFlags = pSrcInfo->dwFlags; *ppDCInfo = pDCInfo; cleanup: return dwError; error: *ppDCInfo = NULL; if (pDCInfo) { LsaFreeDCInfo(pDCInfo); } goto cleanup; }
static DWORD AD_SetConfig_RequireMembershipOf( PLSA_AD_CONFIG pConfig, PCSTR pszName, PCSTR pszValue ) { DWORD dwError = 0; PCSTR pszIter = pszValue; PSTR pszMember = NULL; pszIter = pszValue; while (pszIter != NULL && *pszIter != '\0') { PSTR pszEnd; while (*pszIter == ' ') { ++pszIter; } dwError = LwStrDupOrNull( pszIter, &pszMember); BAIL_ON_LSA_ERROR(dwError); pszEnd = pszMember + strlen(pszMember); while (pszEnd > pszMember && pszEnd[-1] == ' ') { --pszEnd; } *pszEnd = '\0'; dwError = LwDLinkedListAppend( &pConfig->pUnresolvedMemberList, pszMember); BAIL_ON_LSA_ERROR(dwError); pszMember = NULL; pszIter += strlen(pszIter) + 1; } cleanup: LW_SAFE_FREE_STRING(pszMember); return dwError; error: goto cleanup; }
/** * Get domain from DN. E.g. if passed OU=Users,DC=corpqa,DC=centeris,DC=com, * it will return corpqa.centeris.com. * * @param dn Distinguished name. * @param domain Domain * @return 0 on success; error code on failure. */ DWORD GetDomainFromDN(IN PSTR dn, OUT PSTR *domain) { DWORD dwError = 0; PSTR buf = NULL; PSTR bufp = 0; PSTR dcp = NULL; PSTR commap = NULL; PSTR ndn = NULL; int len = 0; dwError = LwStrDupOrNull(dn, &ndn); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); LwStrToLower(ndn); dwError = LwAllocateMemory(sizeof(CHAR) * (strlen(ndn) + 1), OUT_PPVOID(&buf)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); bufp = buf; commap = ndn; dcp = ndn; while(dcp && (dcp = strstr((PCSTR) dcp, "dc="))) { if(!dcp) { break; } dcp += 3; if(*dcp == '\0') { break; } commap = strstr((PCSTR) dcp, ","); if(commap == NULL) { len = strlen(dcp); } else { len = commap - dcp; } if(bufp != buf) { strcpy(bufp, "."); ++bufp; } strncpy(bufp, (PCSTR) dcp, len); bufp += len; dcp += len; } if(bufp == buf) { dwError = ADT_ERR_INVALID_ARG; ADT_BAIL_ON_ERROR_NP(dwError); } dwError = LwStrDupOrNull(buf, domain); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); cleanup: LW_SAFE_FREE_MEMORY(buf); LW_SAFE_FREE_MEMORY(ndn); return dwError; error: goto cleanup; }
DWORD LsaSrvCopyTrustedDomainInfoArray( DWORD dwNumDomains, PLSA_TRUSTED_DOMAIN_INFO pSrcDomainInfoArray, PLSA_TRUSTED_DOMAIN_INFO* ppDomainInfoArray ) { DWORD dwError = 0; PLSA_TRUSTED_DOMAIN_INFO pDomainInfoArray = NULL; DWORD iDomain = 0; dwError = LwAllocateMemory( dwNumDomains * sizeof(LSA_TRUSTED_DOMAIN_INFO), (PVOID*)&pDomainInfoArray); BAIL_ON_LSA_ERROR(dwError); for (; iDomain < dwNumDomains; iDomain++) { PLSA_TRUSTED_DOMAIN_INFO pSrcDomainInfo = &pSrcDomainInfoArray[iDomain]; PLSA_TRUSTED_DOMAIN_INFO pDestDomainInfo = &pDomainInfoArray[iDomain]; dwError = LwStrDupOrNull( pSrcDomainInfo->pszDnsDomain, &pDestDomainInfo->pszDnsDomain); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcDomainInfo->pszNetbiosDomain, &pDestDomainInfo->pszNetbiosDomain); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcDomainInfo->pszDomainSID, &pDestDomainInfo->pszDomainSID); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcDomainInfo->pszDomainGUID, &pDestDomainInfo->pszDomainGUID); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcDomainInfo->pszTrusteeDnsDomain, &pDestDomainInfo->pszTrusteeDnsDomain); BAIL_ON_LSA_ERROR(dwError); pDestDomainInfo->dwTrustFlags = pSrcDomainInfo->dwTrustFlags; pDestDomainInfo->dwTrustType = pSrcDomainInfo->dwTrustType; pDestDomainInfo->dwTrustAttributes = pSrcDomainInfo->dwTrustAttributes; pDestDomainInfo->dwTrustDirection = pSrcDomainInfo->dwTrustDirection; pDestDomainInfo->dwTrustMode = pSrcDomainInfo->dwTrustMode; dwError = LwStrDupOrNull( pSrcDomainInfo->pszForestName, &pDestDomainInfo->pszForestName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrcDomainInfo->pszClientSiteName, &pDestDomainInfo->pszClientSiteName); BAIL_ON_LSA_ERROR(dwError); pDestDomainInfo->dwDomainFlags = pSrcDomainInfo->dwDomainFlags; if (pSrcDomainInfo->pDCInfo) { dwError = LsaSrvCopyDCInfo( pSrcDomainInfo->pDCInfo, &pDestDomainInfo->pDCInfo); BAIL_ON_LSA_ERROR(dwError); } if (pSrcDomainInfo->pGCInfo) { dwError = LsaSrvCopyDCInfo( pSrcDomainInfo->pGCInfo, &pDestDomainInfo->pGCInfo); BAIL_ON_LSA_ERROR(dwError); } } *ppDomainInfoArray = pDomainInfoArray; cleanup: return dwError; error: *ppDomainInfoArray = NULL; if (pDomainInfoArray) { LsaFreeDomainInfoArray(dwNumDomains, pDomainInfoArray); } goto cleanup; }
DWORD UmnSrvUpdateADAccounts( HANDLE hLsass, PLW_EVENTLOG_CONNECTION pEventlog, HANDLE hReg, HKEY hParameters, long long PreviousRun, long long Now ) { DWORD dwError = 0; PSTR pMemberList = NULL; PCSTR pIter = NULL; PSTR pMember = NULL; PLW_HASH_TABLE pUsers = NULL; LWREG_CONFIG_ITEM ADConfigDescription[] = { { "RequireMembershipOf", TRUE, LwRegTypeMultiString, 0, MAXDWORD, NULL, &pMemberList, NULL }, }; PLSASTATUS pLsaStatus = NULL; // Do not free PSTR pDomain = NULL; // Do not free PSTR pCell = NULL; PLSA_SECURITY_OBJECT pAllUsers = NULL; DWORD i = 0; dwError = LwHashCreate( 100, LwHashStringCompare, LwHashStringHash, UmnSrvHashFreeObjectValue, NULL, &pUsers); BAIL_ON_UMN_ERROR(dwError); dwError = RegProcessConfig( AD_PROVIDER_REGKEY, AD_PROVIDER_POLICY_REGKEY, ADConfigDescription, sizeof(ADConfigDescription)/sizeof(ADConfigDescription[0])); BAIL_ON_UMN_ERROR(dwError); if (pMemberList && pMemberList[0]) { pIter = pMemberList; while (*pIter != 0) { dwError = LwStrDupOrNull( pIter, &pMember); BAIL_ON_UMN_ERROR(dwError); LwStripWhitespace( pMember, TRUE, TRUE); dwError = UmnSrvAddUsersFromMembership( hLsass, pUsers, pMember); BAIL_ON_UMN_ERROR(dwError); pIter += strlen(pIter) + 1; } } else { dwError = LsaGetStatus2( hLsass, NULL, &pLsaStatus); BAIL_ON_UMN_ERROR(dwError); for (i = 0; i < pLsaStatus->dwCount; i++) { if (pLsaStatus->pAuthProviderStatusList[i].pszDomain) { pDomain = pLsaStatus->pAuthProviderStatusList[i].pszDomain; } if (pLsaStatus->pAuthProviderStatusList[i].pszCell) { pCell = pLsaStatus->pAuthProviderStatusList[i].pszCell; } } if (pDomain || pCell) { dwError = LwAllocateMemory( sizeof(*pAllUsers), (PVOID*)&pAllUsers); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "S-INVALID", &pAllUsers->pszObjectSid); BAIL_ON_UMN_ERROR(dwError); pAllUsers->enabled = TRUE; pAllUsers->bIsLocal = FALSE; dwError = LwAllocateString( "AllDomains", &pAllUsers->pszNetbiosDomainName); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "AllUsers", &pAllUsers->pszSamAccountName); BAIL_ON_UMN_ERROR(dwError); pAllUsers->type = LSA_OBJECT_TYPE_USER; dwError = LwAllocateString( "S-INVALID", &pAllUsers->userInfo.pszPrimaryGroupSid); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "All Users", &pAllUsers->userInfo.pszUnixName); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "All Users", &pAllUsers->userInfo.pszGecos); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "", &pAllUsers->userInfo.pszShell); BAIL_ON_UMN_ERROR(dwError); dwError = LwAllocateString( "", &pAllUsers->userInfo.pszHomedir); BAIL_ON_UMN_ERROR(dwError); if (pCell) { dwError = LwAllocateStringPrintf( &pAllUsers->userInfo.pszDisplayName, "All Users in cell %s", pCell); BAIL_ON_UMN_ERROR(dwError); } else { dwError = LwAllocateStringPrintf( &pAllUsers->userInfo.pszDisplayName, "All Users accessible from domain %s", pDomain); BAIL_ON_UMN_ERROR(dwError); } dwError = LwHashSetValue( pUsers, pAllUsers->pszObjectSid, pAllUsers); BAIL_ON_UMN_ERROR(dwError); pAllUsers = NULL; } } dwError = UmnSrvUpdateADAccountsByHash( hLsass, pEventlog, hReg, hParameters, pUsers, PreviousRun, Now); BAIL_ON_UMN_ERROR(dwError); cleanup: if (pLsaStatus) { LsaFreeStatus(pLsaStatus); } LW_SAFE_FREE_STRING(pMemberList); LW_SAFE_FREE_STRING(pMember); LwHashSafeFree(&pUsers); if (pAllUsers) { LsaFreeSecurityObject(pAllUsers); } return dwError; error: goto cleanup; }
DWORD LsaPamGetLoginId( pam_handle_t* pamh, PPAMCONTEXT pPamContext, PSTR* ppszLoginId, BOOLEAN bAllowPrompt ) { DWORD dwError = 0; PSTR pszLoginId = NULL; PSTR pszPamId = NULL; int iPamError = 0; LSA_LOG_PAM_DEBUG("LsaPamGetLoginId::begin"); iPamError = pam_get_item( pamh, PAM_USER, (PAM_GET_ITEM_TYPE)&pszPamId); dwError = LsaPamUnmapErrorCode(iPamError); #if HAVE_DECL_PAM_BAD_ITEM if (dwError == LsaPamUnmapErrorCode(PAM_BAD_ITEM)) { pszPamId = NULL; dwError = 0; } #endif BAIL_ON_LSA_ERROR(dwError); if (LW_IS_NULL_OR_EMPTY_STR(pszPamId) && bAllowPrompt) { iPamError = pam_get_user( pamh, (PPCHAR_ARG_CAST)&pszPamId, NULL); dwError = LsaPamUnmapErrorCode(iPamError); if (dwError) { dwError = (dwError == LsaPamUnmapErrorCode(PAM_CONV_AGAIN)) ? LsaPamUnmapErrorCode(PAM_INCOMPLETE) : LsaPamUnmapErrorCode(PAM_SERVICE_ERR); BAIL_ON_LSA_ERROR(dwError); } if (LW_IS_NULL_OR_EMPTY_STR(pszPamId)) { dwError = LsaPamUnmapErrorCode(PAM_SERVICE_ERR); BAIL_ON_LSA_ERROR(dwError); } } dwError = LwStrDupOrNull( pszPamId, &pszLoginId); BAIL_ON_LSA_ERROR(dwError); LW_SAFE_FREE_STRING(pPamContext->pszLoginName); dwError = LwStrDupOrNull(pszPamId, &pPamContext->pszLoginName); BAIL_ON_LSA_ERROR(dwError); if (ppszLoginId) { *ppszLoginId = pszLoginId; } else { LW_SAFE_FREE_STRING(pszLoginId); } cleanup: LSA_LOG_PAM_DEBUG("LsaPamGetLoginId::end"); return dwError; error: LW_SAFE_FREE_STRING(pszLoginId); if (ppszLoginId) { *ppszLoginId = NULL; } LSA_LOG_PAM_ERROR("LsaPamGetLoginId failed [error code: %u]", dwError); goto cleanup; }
DWORD LsaMarshalGroupModInfoToGroupModInfo2( HANDLE hLsa, PLSA_GROUP_MOD_INFO pModInfo1, PLSA_GROUP_MOD_INFO_2* ppModInfo2 ) { DWORD dwError = 0; PLSA_GROUP_MOD_INFO_2 pModInfo2 = NULL; DWORD dwIndex = 0; LSA_QUERY_LIST QueryList; PLSA_SECURITY_OBJECT* ppObjects = NULL; DWORD dwGid = (DWORD) pModInfo1->gid; dwError = LwAllocateMemory(sizeof(*pModInfo2), OUT_PPVOID(&pModInfo2)); BAIL_ON_LSA_ERROR(dwError); memcpy(&pModInfo2->actions, &pModInfo1->actions, sizeof(pModInfo2->actions)); pModInfo2->dwAddMembersNum = pModInfo1->dwAddMembersNum; pModInfo2->dwRemoveMembersNum = pModInfo1->dwRemoveMembersNum; QueryList.pdwIds = &dwGid; dwError = LsaFindObjects( hLsa, NULL, 0, LSA_OBJECT_TYPE_GROUP, LSA_QUERY_TYPE_BY_UNIX_ID, 1, QueryList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (ppObjects[0] == NULL) { dwError = LW_ERROR_NO_SUCH_GROUP; BAIL_ON_LSA_ERROR(dwError); } dwError = LwStrDupOrNull(ppObjects[0]->pszObjectSid, &pModInfo2->pszSid); BAIL_ON_LSA_ERROR(dwError); if (pModInfo1->pAddMembers) { dwError = LwAllocateMemory( sizeof(*pModInfo2->ppszAddMembers) * pModInfo2->dwAddMembersNum, OUT_PPVOID(&pModInfo2->ppszAddMembers)); BAIL_ON_LSA_ERROR(dwError); for (dwIndex = 0; dwIndex < pModInfo2->dwAddMembersNum; dwIndex++) { dwError = LwAllocateString( pModInfo1->pAddMembers[dwIndex].pszSid, &pModInfo2->ppszAddMembers[dwIndex]); BAIL_ON_LSA_ERROR(dwError); } } if (pModInfo1->pRemoveMembers) { dwError = LwAllocateMemory( sizeof(*pModInfo2->ppszRemoveMembers) * pModInfo2->dwRemoveMembersNum, OUT_PPVOID(&pModInfo2->ppszRemoveMembers)); BAIL_ON_LSA_ERROR(dwError); for (dwIndex = 0; dwIndex < pModInfo2->dwRemoveMembersNum; dwIndex++) { dwError = LwAllocateString( pModInfo1->pRemoveMembers[dwIndex].pszSid, &pModInfo2->ppszRemoveMembers[dwIndex]); BAIL_ON_LSA_ERROR(dwError); } } *ppModInfo2 = pModInfo2; cleanup: LsaUtilFreeSecurityObjectList(1, ppObjects); return dwError; error: *ppModInfo2 = NULL; if (pModInfo2) { LsaFreeGroupModInfo2(pModInfo2); } goto cleanup; }
DWORD LsaMarshalUserInfo( PLSA_SECURITY_OBJECT pUser, DWORD dwUserInfoLevel, PVOID* ppUserInfo ) { DWORD dwError = 0; PVOID pUserInfo = NULL; /* These variables represent pUserInfo casted to different types. Do not * free these values directly, free pUserInfo instead. */ PLSA_USER_INFO_0 pUserInfo0 = NULL; PLSA_USER_INFO_1 pUserInfo1 = NULL; PLSA_USER_INFO_2 pUserInfo2 = NULL; *ppUserInfo = NULL; BAIL_ON_INVALID_POINTER(pUser); if (pUser->type != LSA_OBJECT_TYPE_USER) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (!pUser->enabled) { dwError = LW_ERROR_NO_SUCH_USER; BAIL_ON_LSA_ERROR(dwError); } switch(dwUserInfoLevel) { case 0: dwError = LwAllocateMemory( sizeof(LSA_USER_INFO_0), (PVOID*)&pUserInfo); BAIL_ON_LSA_ERROR(dwError); pUserInfo0 = (PLSA_USER_INFO_0) pUserInfo; break; case 1: dwError = LwAllocateMemory( sizeof(LSA_USER_INFO_1), (PVOID*)&pUserInfo); BAIL_ON_LSA_ERROR(dwError); pUserInfo0 = (PLSA_USER_INFO_0) pUserInfo; pUserInfo1 = (PLSA_USER_INFO_1) pUserInfo; break; case 2: dwError = LwAllocateMemory( sizeof(LSA_USER_INFO_2), (PVOID*)&pUserInfo); BAIL_ON_LSA_ERROR(dwError); pUserInfo0 = (PLSA_USER_INFO_0) pUserInfo; pUserInfo1 = (PLSA_USER_INFO_1) pUserInfo; pUserInfo2 = (PLSA_USER_INFO_2) pUserInfo; break; default: dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); break; } if (pUserInfo0 != NULL) { pUserInfo0->uid = pUser->userInfo.uid; pUserInfo0->gid = pUser->userInfo.gid; dwError = LwAllocateString( pUser->userInfo.pszUnixName, &pUserInfo0->pszName); BAIL_ON_LSA_ERROR(dwError); // Optional values use LwStrDupOrNull. Required values use // LwAllocateString. dwError = LwStrDupOrNull( pUser->userInfo.pszPasswd, &pUserInfo0->pszPasswd); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pUser->userInfo.pszGecos, &pUserInfo0->pszGecos); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pUser->userInfo.pszShell, &pUserInfo0->pszShell); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pUser->userInfo.pszHomedir, &pUserInfo0->pszHomedir); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pUser->pszObjectSid, &pUserInfo0->pszSid); BAIL_ON_LSA_ERROR(dwError); } if (pUserInfo1 != NULL) { dwError = LwStrDupOrNull( pUser->pszDN, &pUserInfo1->pszDN); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pUser->userInfo.pszUPN, &pUserInfo1->pszUPN); BAIL_ON_LSA_ERROR(dwError); pUserInfo1->bIsGeneratedUPN = pUser->userInfo.bIsGeneratedUPN; pUserInfo1->bIsLocalUser = pUser->bIsLocal; pUserInfo1->pLMHash = NULL; pUserInfo1->dwLMHashLen = 0; pUserInfo1->pNTHash = NULL; pUserInfo1->dwNTHashLen = 0; } if (pUserInfo2 != NULL) { struct timeval current_tv; UINT64 u64current_NTtime = 0; if (pUser->userInfo.bIsAccountInfoKnown) { if (gettimeofday(¤t_tv, NULL) < 0) { dwError = LwMapErrnoToLwError(errno); BAIL_ON_LSA_ERROR(dwError); } LsaConvertTimeUnix2Nt(current_tv.tv_sec, &u64current_NTtime); if (pUser->userInfo.bPasswordNeverExpires || pUser->userInfo.qwPwdExpires == 0) { //password never expires pUserInfo2->dwDaysToPasswordExpiry = 0LL; } else if (pUser->userInfo.bPasswordExpired || u64current_NTtime >= pUser->userInfo.qwPwdExpires) { //password is expired already pUserInfo2->dwDaysToPasswordExpiry = 0LL; } else { pUserInfo2->dwDaysToPasswordExpiry = (pUser->userInfo.qwPwdExpires - u64current_NTtime) / (10000000LL * 24*60*60); } pUserInfo2->bPasswordNeverExpires = pUser->userInfo.bPasswordNeverExpires; pUserInfo2->bPasswordExpired = pUser->userInfo.bPasswordExpired; pUserInfo2->bPromptPasswordChange = pUser->userInfo.bPromptPasswordChange; pUserInfo2->bUserCanChangePassword = pUser->userInfo.bUserCanChangePassword; pUserInfo2->bAccountDisabled = pUser->userInfo.bAccountDisabled; pUserInfo2->bAccountExpired = pUser->userInfo.bAccountExpired; pUserInfo2->bAccountLocked = pUser->userInfo.bAccountLocked; } else { pUserInfo2->dwDaysToPasswordExpiry = 0LL; pUserInfo2->bPasswordExpired = FALSE; pUserInfo2->bPasswordNeverExpires = TRUE; pUserInfo2->bPromptPasswordChange = FALSE; pUserInfo2->bUserCanChangePassword = FALSE; pUserInfo2->bAccountDisabled = FALSE; pUserInfo2->bAccountExpired = FALSE; pUserInfo2->bAccountLocked = FALSE; } } *ppUserInfo = pUserInfo; cleanup: return dwError; error: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); pUserInfo = NULL; } *ppUserInfo = NULL; goto cleanup; }
DWORD LsaMarshalUserModInfoToUserModInfo2( HANDLE hLsa, PLSA_USER_MOD_INFO pModInfo1, PLSA_USER_MOD_INFO_2* ppModInfo2 ) { DWORD dwError = 0; PLSA_USER_MOD_INFO_2 pModInfo2 = NULL; LSA_QUERY_LIST QueryList; PLSA_SECURITY_OBJECT* ppObjects = NULL; DWORD dwUid = (DWORD) pModInfo1->uid; DWORD dwGid = (DWORD) pModInfo1->gid; dwError = LwAllocateMemory(sizeof(*pModInfo2), OUT_PPVOID(&pModInfo2)); BAIL_ON_LSA_ERROR(dwError); memcpy(&pModInfo2->actions, &pModInfo1->actions, sizeof(pModInfo2->actions)); QueryList.pdwIds = &dwUid; dwError = LsaFindObjects( hLsa, NULL, 0, LSA_OBJECT_TYPE_USER, LSA_QUERY_TYPE_BY_UNIX_ID, 1, QueryList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (ppObjects[0] == NULL) { dwError = LW_ERROR_NO_SUCH_USER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwStrDupOrNull(ppObjects[0]->pszObjectSid, &pModInfo2->pszSid); BAIL_ON_LSA_ERROR(dwError); LsaUtilFreeSecurityObjectList(1, ppObjects); BAIL_ON_LSA_ERROR(dwError); ppObjects = NULL; if (pModInfo1->actions.bSetPrimaryGroup) { QueryList.pdwIds = &dwGid; dwError = LsaFindObjects( hLsa, NULL, 0, LSA_OBJECT_TYPE_GROUP, LSA_QUERY_TYPE_BY_UNIX_ID, 1, QueryList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (ppObjects[0] == NULL) { dwError = LW_ERROR_NO_SUCH_GROUP; BAIL_ON_LSA_ERROR(dwError); } dwError = LwStrDupOrNull(ppObjects[0]->pszObjectSid, &pModInfo2->pszPrimaryGroupSid); BAIL_ON_LSA_ERROR(dwError); } dwError = LwStrDupOrNull(pModInfo1->pszAddToGroups, &pModInfo2->pszAddToGroups); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszRemoveFromGroups, &pModInfo2->pszRemoveFromGroups); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszExpiryDate, &pModInfo2->pszExpiryDate); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszHomedir, &pModInfo2->pszHomedir); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszShell, &pModInfo2->pszShell); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszGecos, &pModInfo2->pszGecos); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pModInfo1->pszPassword, &pModInfo2->pszPassword); BAIL_ON_LSA_ERROR(dwError); if (pModInfo1->pNtPasswordHash) { dwError = LsaDataBlobCopy(&pModInfo2->pNtPasswordHash, pModInfo1->pNtPasswordHash); BAIL_ON_LSA_ERROR(dwError); } if (pModInfo1->pLmPasswordHash) { dwError = LsaDataBlobCopy(&pModInfo2->pLmPasswordHash, pModInfo1->pLmPasswordHash); BAIL_ON_LSA_ERROR(dwError); } *ppModInfo2 = pModInfo2; cleanup: LsaUtilFreeSecurityObjectList(1, ppObjects); return dwError; error: *ppModInfo2 = NULL; if (pModInfo2) { LsaFreeUserModInfo2(pModInfo2); } goto cleanup; }
DWORD LsaMarshalGroupInfo1( HANDLE hLsa, LSA_FIND_FLAGS FindFlags, PLSA_SECURITY_OBJECT pGroup, DWORD dwMemberCount, PLSA_SECURITY_OBJECT* ppMembers, DWORD dwGroupInfoLevel, PVOID* ppGroupInfo ) { DWORD dwError = 0; PVOID pGroupInfo = NULL; PLSA_GROUP_INFO_1 pGroupInfo1 = NULL; /* The variable represents pGroupInfo casted to different types. Do not * free these values directly, free pGroupInfo instead. */ size_t sIndex = 0; size_t sEnabled = 0; *ppGroupInfo = NULL; BAIL_ON_INVALID_POINTER(pGroup); if (pGroup->type != LSA_OBJECT_TYPE_GROUP) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } if (!pGroup->enabled) { dwError = LW_ERROR_NO_SUCH_GROUP; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateMemory( sizeof(LSA_GROUP_INFO_1), (PVOID*)&pGroupInfo); BAIL_ON_LSA_ERROR(dwError); pGroupInfo1 = (PLSA_GROUP_INFO_1) pGroupInfo; pGroupInfo1->gid = pGroup->groupInfo.gid; dwError = LwAllocateString( pGroup->groupInfo.pszUnixName, &pGroupInfo1->pszName); BAIL_ON_LSA_ERROR(dwError); // Optional values use LwStrDupOrNull. Required values use // LwAllocateString. dwError = LwStrDupOrNull( pGroup->groupInfo.pszPasswd, &pGroupInfo1->pszPasswd); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pGroup->pszObjectSid, &pGroupInfo1->pszSid); BAIL_ON_LSA_ERROR(dwError); if (pGroup->pszDN) { dwError = LwAllocateString( pGroup->pszDN, &pGroupInfo1->pszDN); BAIL_ON_LSA_ERROR(dwError); } for (sIndex = 0; sIndex < dwMemberCount; sIndex++) { if (ppMembers[sIndex]) { if (ppMembers[sIndex]->enabled) { sEnabled++; } if (ppMembers[sIndex]->type != LSA_OBJECT_TYPE_USER) { dwError = LW_ERROR_INVALID_PARAMETER; BAIL_ON_LSA_ERROR(dwError); } } } dwError = LwAllocateMemory( //Leave room for terminating null pointer sizeof(PSTR) * (sEnabled+1), (PVOID*)&pGroupInfo1->ppszMembers); BAIL_ON_LSA_ERROR(dwError); sEnabled = 0; for (sIndex = 0; sIndex < dwMemberCount; sIndex++) { if (ppMembers[sIndex]) { if (ppMembers[sIndex]->enabled) { dwError = LwAllocateString( ppMembers[sIndex]->userInfo.pszUnixName, &pGroupInfo1->ppszMembers[sEnabled++]); BAIL_ON_LSA_ERROR(dwError); } } } *ppGroupInfo = pGroupInfo; cleanup: return dwError; error: if (pGroupInfo) { LsaFreeGroupInfo(dwGroupInfoLevel, pGroupInfo); pGroupInfo = NULL; } *ppGroupInfo = NULL; goto cleanup; }
DWORD LsaMarshalUserInfo0ToUserAddInfo( HANDLE hLsa, PLSA_USER_INFO_0 pUserInfo, PLSA_USER_ADD_INFO* ppAddInfo ) { DWORD dwError = 0; PLSA_USER_ADD_INFO pAddInfo = NULL; LSA_QUERY_LIST QueryList; PLSA_SECURITY_OBJECT* ppObjects = NULL; DWORD dwGid = (DWORD) pUserInfo->gid; dwError = LwAllocateMemory(sizeof(*pAddInfo), OUT_PPVOID(&pAddInfo)); BAIL_ON_LSA_ERROR(dwError); pAddInfo->uid = pUserInfo->uid; dwError = LwStrDupOrNull(pUserInfo->pszName, &pAddInfo->pszName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pUserInfo->pszPasswd, &pAddInfo->pszPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pUserInfo->pszGecos, &pAddInfo->pszGecos); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pUserInfo->pszShell, &pAddInfo->pszShell); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull(pUserInfo->pszHomedir, &pAddInfo->pszHomedir); BAIL_ON_LSA_ERROR(dwError); if (pUserInfo->gid > 0) { QueryList.pdwIds = &dwGid; dwError = LsaFindObjects( hLsa, NULL, 0, LSA_OBJECT_TYPE_GROUP, LSA_QUERY_TYPE_BY_UNIX_ID, 1, QueryList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (ppObjects[0] == NULL) { dwError = LW_ERROR_NO_SUCH_GROUP; BAIL_ON_LSA_ERROR(dwError); } dwError = LwAllocateString(ppObjects[0]->pszObjectSid, &pAddInfo->pszPrimaryGroupSid); BAIL_ON_LSA_ERROR(dwError); } *ppAddInfo = pAddInfo; cleanup: return dwError; error: *ppAddInfo = NULL; if (pAddInfo) { LsaFreeUserAddInfo(pAddInfo); } goto cleanup; }
/** * Actions execute method. */ DWORD ExecuteAdtLookupObjectAction(IN AdtActionTP action) { DWORD dwError = 0; AppContextTP appContext = (AppContextTP) ((AdtActionBaseTP) action)->opaque; INT i, j; AttrValsT *avp = NULL; if(appContext->gopts.isPrintDN) { PrintResult(appContext, LogLevelNone, "%s\n", action->lookupObject.dn); goto cleanup; } PrintStderr(appContext, LogLevelVerbose, "%s: Looking up object attributes ...\n", appContext->actionName); if(action->lookupObject.attr) { dwError = LwAllocateMemory(2 * sizeof(AttrValsT), OUT_PPVOID(&avp)); ADT_BAIL_ON_ALLOC_FAILURE(!dwError); dwError = LwStrDupOrNull((PCSTR) action->lookupObject.attr, &(avp[0].attr)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); dwError = GetObjectAttrs(appContext, action->lookupObject.dn, avp); ADT_BAIL_ON_ERROR_NP(dwError); } else { dwError = GetAllObjectAttrs(appContext, action->lookupObject.dn, &avp); ADT_BAIL_ON_ERROR_NP(dwError); } if (!appContext->gopts.isQuiet) { if (action->lookupObject.attr) { for (j = 0; avp && avp[0].vals && avp[0].vals[j]; ++j) { PrintResult(appContext, LogLevelNone, "%s\n", (PSTR) avp[0].vals[j]); } } else { for (i = 0; avp && avp[i].attr; ++i) { PrintResult(appContext, LogLevelNone, "%s: ", (PSTR) avp[i].attr); for (j = 0; avp[i].vals && avp[i].vals[j]; ++j) { PrintResult(appContext, LogLevelNone, j ? ";%s" : "%s", (PSTR) avp[i].vals[j]); } PrintResult(appContext, LogLevelNone, "\n"); } } } PrintStderr(appContext, LogLevelVerbose, "%s: Looking up object attributes - done\n", appContext->actionName); cleanup: if (avp) { for (i = 0; avp[i].attr; ++i) { LW_SAFE_FREE_MEMORY(avp[i].attr); if(avp[i].vals) { for (j = 0; avp[i].vals[j]; ++j) { LW_SAFE_FREE_MEMORY(avp[i].vals[j]); } LW_SAFE_FREE_MEMORY(avp[i].vals); } } LW_SAFE_FREE_MEMORY(avp); } return dwError; error: goto cleanup; }
DWORD ADCacheDuplicateObject( OUT PLSA_SECURITY_OBJECT* ppDest, IN PLSA_SECURITY_OBJECT pSrc ) { DWORD dwError = 0; PLSA_SECURITY_OBJECT pDest = NULL; dwError = LwAllocateMemory( sizeof(*pDest), (PVOID*)&pDest); BAIL_ON_LSA_ERROR(dwError); pDest->version = pSrc->version; dwError = LwAllocateString( pSrc->pszObjectSid, &pDest->pszObjectSid); BAIL_ON_LSA_ERROR(dwError); pDest->enabled = pSrc->enabled; dwError = LwAllocateString( pSrc->pszNetbiosDomainName, &pDest->pszNetbiosDomainName); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( pSrc->pszSamAccountName, &pDest->pszSamAccountName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->pszDN, &pDest->pszDN); BAIL_ON_LSA_ERROR(dwError); pDest->type = pSrc->type; if (pDest->type == LSA_OBJECT_TYPE_USER) { pDest->userInfo.uid = pSrc->userInfo.uid; pDest->userInfo.gid = pSrc->userInfo.gid; dwError = LwStrDupOrNull( pSrc->userInfo.pszPrimaryGroupSid, &pDest->userInfo.pszPrimaryGroupSid); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszUPN, &pDest->userInfo.pszUPN); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszAliasName, &pDest->userInfo.pszAliasName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszUnixName, &pDest->userInfo.pszUnixName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszPasswd, &pDest->userInfo.pszPasswd); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszGecos, &pDest->userInfo.pszGecos); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszShell, &pDest->userInfo.pszShell); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszHomedir, &pDest->userInfo.pszHomedir); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszWindowsHomeFolder, &pDest->userInfo.pszWindowsHomeFolder); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszLocalWindowsHomeFolder, &pDest->userInfo.pszLocalWindowsHomeFolder); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->userInfo.pszDisplayName, &pDest->userInfo.pszDisplayName); BAIL_ON_LSA_ERROR(dwError); pDest->userInfo.qwPwdLastSet = pSrc->userInfo.qwPwdLastSet; pDest->userInfo.qwPwdExpires = pSrc->userInfo.qwPwdExpires; pDest->userInfo.qwAccountExpires = pSrc->userInfo.qwAccountExpires; pDest->userInfo.bIsGeneratedUPN = pSrc->userInfo.bIsGeneratedUPN; pDest->userInfo.bIsAccountInfoKnown = pSrc->userInfo.bIsAccountInfoKnown; pDest->userInfo.bPasswordExpired = pSrc->userInfo.bPasswordExpired; pDest->userInfo.bPasswordNeverExpires = pSrc->userInfo.bPasswordNeverExpires; pDest->userInfo.bPromptPasswordChange = pSrc->userInfo.bPromptPasswordChange; pDest->userInfo.bUserCanChangePassword = pSrc->userInfo.bUserCanChangePassword; pDest->userInfo.bAccountDisabled = pSrc->userInfo.bAccountDisabled; pDest->userInfo.bAccountExpired = pSrc->userInfo.bAccountExpired; pDest->userInfo.bAccountLocked = pSrc->userInfo.bAccountLocked; } else if (pDest->type == LSA_OBJECT_TYPE_GROUP) { pDest->groupInfo.gid = pSrc->groupInfo.gid; dwError = LwStrDupOrNull( pSrc->groupInfo.pszAliasName, &pDest->groupInfo.pszAliasName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->groupInfo.pszUnixName, &pDest->groupInfo.pszUnixName); BAIL_ON_LSA_ERROR(dwError); dwError = LwStrDupOrNull( pSrc->groupInfo.pszPasswd, &pDest->groupInfo.pszPasswd); BAIL_ON_LSA_ERROR(dwError); } *ppDest = pDest; cleanup: return dwError; error: ADCacheSafeFreeObject(&pDest); *ppDest = NULL; goto cleanup; }