/* Editor Settings: expandtabs and use 4 spaces for indentation * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: * */ /* * Copyright Likewise Software 2004-2008 * All rights reserved. * * 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, see * . * * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING * TERMS AS WELL. IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE. IF YOU * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT * license@likewisesoftware.com */ /* * Copyright (C) Likewise Software. All rights reserved. * * Module Name: * * lpmain.c * * Abstract: * * Likewise Security and Authentication Subsystem (LSASS) * * Local Authentication Provider * * Authors: Krishna Ganugapati (krishnag@likewisesoftware.com) * Sriram Nambakam (snambakam@likewisesoftware.com) * Gerald Carter */ #include "includes.h" static DWORD LocalInitializeProvider( OUT PCSTR* ppszProviderName, OUT PLSA_PROVIDER_FUNCTION_TABLE_2* ppFunctionTable ) { DWORD dwError = 0; LOCAL_CONFIG config; BOOLEAN bEventLogEnabled = FALSE; PWSTR pwszUserDN = NULL; PWSTR pwszCredentials = NULL; ULONG ulMethod = 0; memset(&config, 0, sizeof(config)); dwError = LocalCfgInitialize(&config); BAIL_ON_LSA_ERROR(dwError); dwError = LocalSyncDomainInfo( pwszUserDN, pwszCredentials, ulMethod, &gLPGlobals); BAIL_ON_LSA_ERROR(dwError); LocalCfgReadRegistry(&config); BAIL_ON_LSA_ERROR(dwError); dwError = LocalCfgTransferContents( &config, &gLPGlobals.cfg); BAIL_ON_LSA_ERROR(dwError); dwError = LocalCfgIsEventlogEnabled(&bEventLogEnabled); BAIL_ON_LSA_ERROR(dwError); if (bEventLogEnabled) { LocalEventLogServiceStart(dwError); } *ppszProviderName = gpszLocalProviderName; *ppFunctionTable = &gLocalProviderAPITable2; cleanup: return dwError; error: if (bEventLogEnabled) { LocalEventLogServiceStart(dwError); } LocalCfgFreeContents(&config); *ppszProviderName = NULL; *ppFunctionTable = NULL; goto cleanup; } DWORD LocalOpenHandle( HANDLE hServer, PHANDLE phProvider ) { DWORD dwError = 0; PLOCAL_PROVIDER_CONTEXT pContext = NULL; dwError = LwAllocateMemory( sizeof(LOCAL_PROVIDER_CONTEXT), (PVOID*)&pContext); BAIL_ON_LSA_ERROR(dwError); pthread_mutex_init(&pContext->mutex, NULL); pContext->pMutex = &pContext->mutex; LsaSrvGetClientId( hServer, &pContext->uid, &pContext->gid, &pContext->pid); pContext->localAdminState = LOCAL_ADMIN_STATE_NOT_DETERMINED; dwError = DirectoryOpen(&pContext->hDirectory); BAIL_ON_LSA_ERROR(dwError); *phProvider = (HANDLE)pContext; cleanup: return dwError; error: *(phProvider) = (HANDLE)NULL; if (pContext) { LocalCloseHandle((HANDLE)pContext); } goto cleanup; } void LocalCloseHandle( HANDLE hProvider ) { PLOCAL_PROVIDER_CONTEXT pContext = (PLOCAL_PROVIDER_CONTEXT)hProvider; if (pContext->hDirectory) { DirectoryClose(pContext->hDirectory); } if (pContext->pMutex) { pthread_mutex_destroy(&pContext->mutex); } if (pContext) { LwFreeMemory(pContext); } } BOOLEAN LocalServicesDomain( PCSTR pszDomain ) { BOOLEAN bResult = FALSE; if (!LW_IS_NULL_OR_EMPTY_STR(pszDomain) && (!strcasecmp(pszDomain, gLPGlobals.pszNetBIOSName) || !strcasecmp(pszDomain, gLPGlobals.pszLocalDomain) || !strcasecmp(pszDomain, gLPGlobals.pszBuiltinDomain))) { bResult = TRUE; } return bResult; } DWORD LocalAuthenticateUser( HANDLE hProvider, PCSTR pszLoginId, PCSTR pszPassword ) { DWORD dwError = 0; DWORD dwUpdateError = 0; PLOCAL_PROVIDER_CONTEXT pContext = (PLOCAL_PROVIDER_CONTEXT)hProvider; PWSTR pwszUserDN = NULL; PWSTR pwszPassword = NULL; PLSA_SECURITY_OBJECT pObject = NULL; DWORD dwBadPasswordCount = 0; dwError = LocalCheckForQueryAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); BAIL_ON_LSA_ERROR(dwError); dwError = LwMbsToWc16s( pObject->pszDN, &pwszUserDN); BAIL_ON_LSA_ERROR(dwError); /* Check for disable, expired, etc.. accounts */ dwError = LocalCheckAccountFlags(pObject); BAIL_ON_LSA_ERROR(dwError); dwError = LocalGetUserLogonInfo( hProvider, pObject->pszDN, NULL, &dwBadPasswordCount); BAIL_ON_LSA_ERROR(dwError); if (pszPassword) { dwError = LsaMbsToWc16s( pszPassword, &pwszPassword); BAIL_ON_LSA_ERROR(dwError); } dwError = DirectoryVerifyPassword( pContext->hDirectory, pwszUserDN, pwszPassword); if (dwError == LW_ERROR_PASSWORD_MISMATCH) { dwBadPasswordCount++; dwUpdateError = LocalSetUserLogonInfo( hProvider, pObject->pszDN, NULL, &dwBadPasswordCount, NULL, NULL); BAIL_ON_LSA_ERROR(dwUpdateError); } BAIL_ON_LSA_ERROR(dwError); cleanup: LsaUtilFreeSecurityObject(pObject); LW_SAFE_FREE_MEMORY(pwszUserDN); LW_SAFE_FREE_MEMORY(pwszPassword); if (dwUpdateError != ERROR_SUCCESS) { dwError = dwUpdateError; } return dwError; error: goto cleanup; } DWORD LocalAuthenticateUserEx( HANDLE hProvider, PLSA_AUTH_USER_PARAMS pUserParams, PLSA_AUTH_USER_INFO* ppUserInfo ) { return LocalAuthenticateUserExInternal(hProvider, pUserParams, ppUserInfo); } DWORD LocalValidateUser( HANDLE hProvider, PCSTR pszLoginId, PCSTR pszPassword ) { DWORD dwError = 0; PLSA_SECURITY_OBJECT pObject = NULL; dwError = LocalCheckForQueryAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); if (dwError != LW_ERROR_SUCCESS) { LSA_LOG_DEBUG( "Failed to find user '%s' while validating login " "[error code:%d]", pszLoginId, dwError); dwError = LW_ERROR_NOT_HANDLED; BAIL_ON_LSA_ERROR(dwError); } if (pObject->userInfo.bPasswordExpired) { dwError = LW_ERROR_PASSWORD_EXPIRED; BAIL_ON_LSA_ERROR(dwError); } cleanup: LsaUtilFreeSecurityObject(pObject); return dwError; error: goto cleanup; } DWORD LocalCheckUserInList( HANDLE hProvider, PCSTR pszLoginId, PCSTR pszListName ) { // require-membership-of is currently not supported // for the local provider. So just return success here. return LW_ERROR_SUCCESS; } DWORD LocalChangePassword( HANDLE hProvider, PCSTR pszLoginId, PCSTR pszPassword, PCSTR pszOldPassword ) { DWORD dwError = 0; PWSTR pwszUserDN = NULL; PWSTR pwszOldPassword = NULL; PWSTR pwszNewPassword = NULL; PLSA_SECURITY_OBJECT pObject = NULL; BAIL_ON_INVALID_HANDLE(hProvider); dwError = LocalCheckForQueryAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); BAIL_ON_LSA_ERROR(dwError); dwError = LwMbsToWc16s( pObject->pszDN, &pwszUserDN); BAIL_ON_LSA_ERROR(dwError); dwError = LocalCheckForPasswordChangeAccess( hProvider, pObject->userInfo.uid); BAIL_ON_LSA_ERROR(dwError); dwError = LocalCheckPasswordPolicy( pObject, pszPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LsaMbsToWc16s( (pszPassword ? pszPassword : ""), &pwszNewPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LsaMbsToWc16s( (pszOldPassword ? pszOldPassword : ""), &pwszOldPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirChangePassword( hProvider, pwszUserDN, pwszOldPassword, pwszNewPassword); BAIL_ON_LSA_ERROR(dwError); cleanup: LsaUtilFreeSecurityObject(pObject); LW_SAFE_FREE_MEMORY(pwszNewPassword); LW_SAFE_FREE_MEMORY(pwszOldPassword); LW_SAFE_FREE_MEMORY(pwszUserDN); return dwError; error: goto cleanup; } DWORD LocalSetPassword( HANDLE hProvider, PCSTR pszLoginId, PCSTR pszPassword ) { DWORD dwError = 0; PWSTR pwszUserDN = NULL; PWSTR pwszNewPassword = NULL; PLSA_SECURITY_OBJECT pObject = NULL; BAIL_ON_INVALID_HANDLE(hProvider); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); BAIL_ON_LSA_ERROR(dwError); dwError = LwMbsToWc16s( pObject->pszDN, &pwszUserDN); BAIL_ON_LSA_ERROR(dwError); dwError = LocalCheckPasswordPolicy( pObject, pszPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LsaMbsToWc16s( (pszPassword ? pszPassword : ""), &pwszNewPassword); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirSetPassword( hProvider, pwszUserDN, pwszNewPassword); BAIL_ON_LSA_ERROR(dwError); cleanup: LsaUtilFreeSecurityObject(pObject); LW_SAFE_FREE_MEMORY(pwszNewPassword); LW_SAFE_FREE_MEMORY(pwszUserDN); return dwError; error: goto cleanup; } DWORD LocalAddUser( HANDLE hProvider, PLSA_USER_ADD_INFO pUserAddInfo ) { DWORD dwError = 0; BAIL_ON_INVALID_HANDLE(hProvider); BAIL_ON_INVALID_POINTER(pUserAddInfo); dwError = LocalCheckForAddAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirAddUser( hProvider, pUserAddInfo); BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: goto cleanup; } DWORD LocalModifyUser( HANDLE hProvider, PLSA_USER_MOD_INFO_2 pUserModInfo ) { DWORD dwError = 0; dwError = LocalCheckForModifyAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirModifyUser( hProvider, pUserModInfo); BAIL_ON_LSA_ERROR(dwError); error: return dwError; } DWORD LocalDeleteObject( HANDLE hProvider, PCSTR pszSid ) { DWORD dwError = 0; PWSTR pwszDN = NULL; PLSA_SECURITY_OBJECT* ppObjects = NULL; LSA_QUERY_LIST QueryList; BAIL_ON_INVALID_HANDLE(hProvider); dwError = LocalCheckForDeleteAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); QueryList.ppszStrings = &pszSid; dwError = LocalFindObjects( hProvider, 0, LSA_OBJECT_TYPE_UNDEFINED, LSA_QUERY_TYPE_BY_SID, 1, QueryList, &ppObjects); BAIL_ON_LSA_ERROR(dwError); if (ppObjects[0] == NULL) { dwError = LW_ERROR_NO_SUCH_USER; BAIL_ON_LSA_ERROR(dwError); } dwError = LwMbsToWc16s( ppObjects[0]->pszDN, &pwszDN); BAIL_ON_LSA_ERROR(dwError); switch (ppObjects[0]->type) { case LSA_OBJECT_TYPE_USER: dwError = LocalDirDeleteUser( hProvider, pwszDN); BAIL_ON_LSA_ERROR(dwError); break; case LSA_OBJECT_TYPE_GROUP: dwError = LocalDirDeleteGroup( hProvider, pwszDN); BAIL_ON_LSA_ERROR(dwError); break; default: dwError = LW_ERROR_INTERNAL; BAIL_ON_LSA_ERROR(dwError); } cleanup: LsaUtilFreeSecurityObjectList(1, ppObjects); LW_SAFE_FREE_MEMORY(pwszDN); return dwError; error: goto cleanup; } DWORD LocalAddGroup( HANDLE hProvider, PLSA_GROUP_ADD_INFO pGroupAddInfo ) { DWORD dwError = 0; BAIL_ON_INVALID_HANDLE(hProvider); BAIL_ON_INVALID_POINTER(pGroupAddInfo); dwError = LocalCheckForAddAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirAddGroup( hProvider, pGroupAddInfo); BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: goto cleanup; } DWORD LocalModifyGroup( HANDLE hProvider, PLSA_GROUP_MOD_INFO_2 pGroupModInfo ) { DWORD dwError = 0; dwError = LocalCheckForModifyAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirModifyGroup( hProvider, pGroupModInfo); BAIL_ON_LSA_ERROR(dwError); error: return dwError; } DWORD LocalOpenSession( HANDLE hProvider, PCSTR pszLoginId ) { DWORD dwError = 0; BOOLEAN bCreateHomedir = FALSE; PLOCAL_PROVIDER_CONTEXT pContext = (PLOCAL_PROVIDER_CONTEXT)hProvider; PLSA_SECURITY_OBJECT pObject = NULL; DWORD dwLogonCount = 0; LONG64 llLastLogonTime = 0; dwError = LocalCheckForQueryAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); BAIL_ON_INVALID_HANDLE(hProvider); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); BAIL_ON_LSA_ERROR(dwError); // Allow directory creation only if this is // if ((pContext->uid != 0) && (pContext->uid != (pObject->userInfo.uid))) { dwError = LW_ERROR_ACCESS_DENIED; BAIL_ON_LSA_ERROR(dwError); } dwError = LocalCfgMustCreateHomedir(&bCreateHomedir); BAIL_ON_LSA_ERROR(dwError); if (bCreateHomedir) { dwError = LocalCreateHomeDirectory(pObject); BAIL_ON_LSA_ERROR(dwError); } dwError = LocalGetUserLogonInfo( hProvider, pObject->pszDN, &dwLogonCount, NULL); BAIL_ON_LSA_ERROR(dwError); dwLogonCount++; dwError = LwGetNtTime((PULONG64)&llLastLogonTime); BAIL_ON_LSA_ERROR(dwError); dwError = LocalSetUserLogonInfo( hProvider, pObject->pszDN, &dwLogonCount, NULL, &llLastLogonTime, NULL); BAIL_ON_LSA_ERROR(dwError); cleanup: LsaUtilFreeSecurityObject(pObject); return dwError; error: goto cleanup; } DWORD LocalCloseSession( HANDLE hProvider, PCSTR pszLoginId ) { DWORD dwError = 0; PLSA_SECURITY_OBJECT pObject = NULL; LONG64 llLastLogoffTime = 0; dwError = LocalCheckForQueryAccess(hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = LocalDirFindObjectByGenericName( hProvider, 0, LSA_OBJECT_TYPE_USER, pszLoginId, &pObject); BAIL_ON_LSA_ERROR(dwError); dwError = LwGetNtTime((PULONG64)&llLastLogoffTime); BAIL_ON_LSA_ERROR(dwError); dwError = LocalSetUserLogonInfo( hProvider, pObject->pszDN, NULL, NULL, NULL, &llLastLogoffTime); BAIL_ON_LSA_ERROR(dwError); cleanup: LsaUtilFreeSecurityObject(pObject); return dwError; error: goto cleanup; } DWORD LocalFindNSSArtefactByKey( HANDLE hProvider, PCSTR pszKeyName, PCSTR pszMapName, DWORD dwInfoLevel, LSA_NIS_MAP_QUERY_FLAGS dwFlags, PVOID* ppNSSArtefactInfo ) { *ppNSSArtefactInfo = NULL; return LW_ERROR_NOT_HANDLED; } DWORD LocalBeginEnumNSSArtefacts( HANDLE hProvider, DWORD dwInfoLevel, PCSTR pszMapName, LSA_NIS_MAP_QUERY_FLAGS dwFlags, PHANDLE phResume ) { DWORD dwError = 0; dwError = LW_ERROR_NOT_HANDLED; *phResume = (HANDLE)NULL; return dwError; } DWORD LocalEnumNSSArtefacts( HANDLE hProvider, HANDLE hResume, DWORD dwMaxNSSArtefacts, PDWORD pdwNSSArtefactsFound, PVOID** pppNSSArtefactInfoList ) { DWORD dwError = 0; dwError = LW_ERROR_NOT_HANDLED; *pdwNSSArtefactsFound = 0; *pppNSSArtefactInfoList = NULL; return dwError; } VOID LocalEndEnumNSSArtefacts( HANDLE hProvider, HANDLE hResume ) { return; } DWORD LocalGetStatus( HANDLE hProvider, PLSA_AUTH_PROVIDER_STATUS* ppProviderStatus ) { DWORD dwError = 0; PLSA_AUTH_PROVIDER_STATUS pProviderStatus = NULL; dwError = LwAllocateMemory( sizeof(LSA_AUTH_PROVIDER_STATUS), (PVOID*)&pProviderStatus); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateString( gpszLocalProviderName, &pProviderStatus->pszId); BAIL_ON_LSA_ERROR(dwError); pProviderStatus->mode = LSA_PROVIDER_MODE_LOCAL_SYSTEM; pProviderStatus->status = LSA_AUTH_PROVIDER_STATUS_ONLINE; *ppProviderStatus = pProviderStatus; cleanup: return dwError; error: *ppProviderStatus = NULL; if (pProviderStatus) { LocalFreeStatus(pProviderStatus); } goto cleanup; } DWORD LocalRefreshConfiguration( HANDLE hProvider ) { DWORD dwError = 0; LOCAL_CONFIG config = {0}; BOOLEAN bInLock = FALSE; dwError = LocalCfgReadRegistry(&config); BAIL_ON_LSA_ERROR(dwError); LOCAL_LOCK_MUTEX(bInLock, &gLPGlobals.mutex); dwError = LocalCfgTransferContents( &config, &gLPGlobals.cfg); BAIL_ON_LSA_ERROR(dwError); LOCAL_UNLOCK_MUTEX(bInLock, &gLPGlobals.mutex); LocalEventLogConfigReload(); cleanup: LOCAL_UNLOCK_MUTEX(bInLock, &gLPGlobals.mutex); return dwError; error: LocalCfgFreeContents(&config); goto cleanup; } VOID LocalFreeStatus( PLSA_AUTH_PROVIDER_STATUS pProviderStatus ) { LW_SAFE_FREE_STRING(pProviderStatus->pszId); LW_SAFE_FREE_STRING(pProviderStatus->pszDomain); LW_SAFE_FREE_STRING(pProviderStatus->pszForest); LW_SAFE_FREE_STRING(pProviderStatus->pszSite); LW_SAFE_FREE_STRING(pProviderStatus->pszCell); LwFreeMemory(pProviderStatus); } DWORD LocalIoControl( IN HANDLE hProvider, IN uid_t peerUID, IN gid_t peerGID, IN DWORD dwIoControlCode, IN DWORD dwInputBufferSize, IN PVOID pInputBuffer, OUT DWORD* pdwOutputBufferSize, OUT PVOID* ppOutputBuffer ) { DWORD dwError = 0; switch (dwIoControlCode) { default: dwError = LW_ERROR_NOT_HANDLED; break; } BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: *pdwOutputBufferSize = 0; *ppOutputBuffer = NULL; goto cleanup; } DWORD LocalFindObjects( IN HANDLE hProvider, IN LSA_FIND_FLAGS FindFlags, IN OPTIONAL LSA_OBJECT_TYPE ObjectType, IN LSA_QUERY_TYPE QueryType, IN DWORD dwCount, IN LSA_QUERY_LIST QueryList, OUT PLSA_SECURITY_OBJECT** pppObjects ) { DWORD dwError = 0; dwError = LocalDirFindObjects( hProvider, FindFlags, ObjectType, QueryType, dwCount, QueryList, pppObjects); BAIL_ON_LSA_ERROR(dwError); cleanup: return dwError; error: goto cleanup; } DWORD LocalOpenEnumObjects( IN HANDLE hProvider, OUT PHANDLE phEnum, IN LSA_FIND_FLAGS FindFlags, IN LSA_OBJECT_TYPE ObjectType, IN OPTIONAL PCSTR pszDomainName ) { return LocalDirOpenEnumObjects( hProvider, phEnum, FindFlags, ObjectType, pszDomainName); } DWORD LocalEnumObjects( IN HANDLE hEnum, IN DWORD dwMaxObjectsCount, OUT PDWORD pdwObjectsCount, OUT PLSA_SECURITY_OBJECT** pppObjects ) { return LocalDirEnumObjects( hEnum, dwMaxObjectsCount, pdwObjectsCount, pppObjects); } DWORD LocalOpenEnumMembers( IN HANDLE hProvider, OUT PHANDLE phEnum, IN LSA_FIND_FLAGS FindFlags, IN PCSTR pszSid ) { return LocalDirOpenEnumMembers( hProvider, phEnum, FindFlags, pszSid); } DWORD LocalEnumMembers( IN HANDLE hEnum, IN DWORD dwMaxMemberSidCount, OUT PDWORD pdwMemberSidCount, OUT PSTR** pppszMemberSids ) { return LocalDirEnumMembers( hEnum, dwMaxMemberSidCount, pdwMemberSidCount, pppszMemberSids); } VOID LocalCloseEnum( IN OUT HANDLE hEnum ) { return LocalDirCloseEnum(hEnum); } DWORD LocalQueryMemberOf( IN HANDLE hProvider, IN LSA_FIND_FLAGS FindFlags, IN DWORD dwSidCount, IN PSTR* ppszSids, OUT PDWORD pdwGroupSidCount, OUT PSTR** pppszGroupSids ) { return LocalDirQueryMemberOf( hProvider, FindFlags, dwSidCount, ppszSids, pdwGroupSidCount, pppszGroupSids); } DWORD LocalShutdownProvider( VOID ) { LW_SAFE_FREE_STRING(gLPGlobals.pszLocalDomain); LW_SAFE_FREE_STRING(gLPGlobals.pszNetBIOSName); return 0; } DWORD LsaInitializeProvider2( OUT PCSTR* ppszProviderName, OUT PLSA_PROVIDER_FUNCTION_TABLE_2* ppFunctionTable ) { return LocalInitializeProvider(ppszProviderName, ppFunctionTable); } /* local variables: mode: c c-basic-offset: 4 indent-tabs-mode: nil tab-width: 4 end: */