static DWORD TestAddUser( HANDLE hLsaConnection, PSTR pszUser ) { DWORD dwError = LW_ERROR_SUCCESS; DWORD dwUserInfoLevel = 0; PVOID pUserInfo = NULL; PSTR pszShell = "/bin/sh"; PSTR pszHomedir = "/home"; gid_t gid = 0; LSA_FIND_FLAGS FindFlags = 0; PCSTR pszTestDescription = "Verify LsaAddUser adds a user"; PCSTR pszTestAPIs = "LsaAddUser"; char szTestMsg[512] = { 0 }; dwError = GetGroupId( hLsaConnection, pszUser, &gid); BAIL_ON_TEST_BROKE(dwError); dwError = BuildUserInfo(0, gid, pszUser, pszShell, pszHomedir, (PLSA_USER_INFO_0*)&pUserInfo); BAIL_ON_TEST_BROKE(dwError); dwError = LsaAddUser( hLsaConnection, pUserInfo, dwUserInfoLevel); BAIL_ON_TEST_BROKE(dwError); dwError = LsaFindUserByName( hLsaConnection, pszUser, FindFlags, &pUserInfo); BAIL_ON_TEST_BROKE(dwError); if( !pUserInfo ) { dwError = LW_ERROR_TEST_FAILED; snprintf(szTestMsg, sizeof(szTestMsg), "unexpected result while adding the user %s", pszUser); LWT_LOG_TEST(szTestMsg); } cleanup: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); } return dwError; error: goto cleanup; }
NSS_STATUS LsaNssCommonPasswdGetpwuid( PLSA_NSS_CACHED_HANDLE pConnection, uid_t uid, struct passwd * pResultUser, char * pszBuf, size_t bufLen, int * pErrorNumber ) { int ret = NSS_STATUS_SUCCESS; HANDLE hLsaConnection = NULL; PVOID pUserInfo = NULL; DWORD dwUserInfoLevel = 0; ret = MAP_LSA_ERROR(NULL, LsaNssCommonEnsureConnected(pConnection)); BAIL_ON_NSS_ERROR(ret); hLsaConnection = pConnection->hLsaConnection; ret = MAP_LSA_ERROR(pErrorNumber, LsaFindUserById( hLsaConnection, uid, dwUserInfoLevel, &pUserInfo)); BAIL_ON_NSS_ERROR(ret); ret = MAP_LSA_ERROR(pErrorNumber, LsaNssWriteUserInfo( dwUserInfoLevel, pUserInfo, pResultUser, &pszBuf, bufLen)); BAIL_ON_NSS_ERROR(ret); cleanup: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); } return ret; error: if (ret != NSS_STATUS_TRYAGAIN && ret != NSS_STATUS_NOTFOUND) { LsaNssCommonCloseConnection(pConnection); } goto cleanup; }
int LsaNssChangePassword( PSTR pszUser, PSTR pszOldPass, PSTR pszNewPass, PSTR* ppszError) { DWORD dwError = LW_ERROR_SUCCESS; PLSA_USER_INFO_0 pInfo = NULL; const DWORD dwInfoLevel = 0; *ppszError = NULL; dwError = LsaNssCommonEnsureConnected(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaNssFindUserByAixName( lsaConnection.hLsaConnection, pszUser, dwInfoLevel, (PVOID*)&pInfo); BAIL_ON_LSA_ERROR(dwError); dwError = LsaChangePassword( lsaConnection.hLsaConnection, pInfo->pszName, pszNewPass, pszOldPass); BAIL_ON_LSA_ERROR(dwError); cleanup: if (pInfo != NULL) { LsaFreeUserInfo( dwInfoLevel, (PVOID)pInfo); } if(dwError != LW_ERROR_SUCCESS) { LsaNssMapErrorCode(dwError, &errno); return -1; } return 0; error: LsaNssCommonCloseConnection(&lsaConnection); goto cleanup; }
static DWORD BuildUserInfo( uid_t uid, gid_t gid, PCSTR pszLoginId, PCSTR pszShell, PCSTR pszHomedir, PLSA_USER_INFO_0* ppUserInfo ) { DWORD dwError = 0; PLSA_USER_INFO_0 pUserInfo = NULL; DWORD dwUserInfoLevel = 0; dwError = LwAllocateMemory( sizeof(LSA_USER_INFO_0), (PVOID*)&pUserInfo ); BAIL_ON_TEST_BROKE(dwError); pUserInfo->uid = uid; pUserInfo->gid = gid; dwError = LwAllocateString(pszLoginId, &pUserInfo->pszName); BAIL_ON_TEST_BROKE(dwError); dwError = LwAllocateString(pszShell, &pUserInfo->pszShell); BAIL_ON_TEST_BROKE(dwError); dwError = LwAllocateString(pszHomedir, &pUserInfo->pszHomedir); BAIL_ON_TEST_BROKE(dwError); // TODO: Gecos *ppUserInfo = pUserInfo; cleanup: return dwError; error: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); } goto cleanup; }
static VOID FreeUserInfo( DWORD dwLevel, PVOID pUserInfo ) { if ( pUserInfo ) { LsaFreeUserInfo( dwLevel, pUserInfo); pUserInfo = NULL; } return; }
/* * VerifyNullHandling * * Make sure NULL does not crash server. */ static DWORD VerifyNullHandling( HANDLE hLsaConnection ) { PCSTR pszTestDescription = "LsaFindUserByName returns error given NULL user name."; PCSTR pszTestAPIs = "LsaFindUserByName"; char szTestMsg[128] = { 0 }; DWORD dwError = LW_ERROR_SUCCESS; DWORD dwLocalError = LW_ERROR_SUCCESS; LSA_USER_INFO_0 *pUserInfo = NULL; size_t i; for ( i = 0; i < 3; i++ ) { dwLocalError = LsaFindUserByName( hLsaConnection, NULL, i, (void**) &pUserInfo); if ( pUserInfo ) { LsaFreeUserInfo(i, pUserInfo); } if ( dwLocalError != LW_ERROR_INVALID_PARAMETER ) { dwError = LW_ERROR_TEST_FAILED; goto error; } } cleanup: LWT_LOG_TEST(szTestMsg); return dwError; error: goto cleanup; }
/* * This is where we check if the password expired. * If the password is correct, but has expired, we return * PAM_NEW_AUTHTOK_REQD instead of PAM_SUCCESS */ int pam_sm_acct_mgmt( pam_handle_t* pamh, int flags, int argc, const char** argv ) { DWORD dwError = 0; PPAMCONTEXT pPamContext = NULL; HANDLE hLsaConnection = (HANDLE)NULL; PLSA_USER_INFO_2 pUserInfo = NULL; DWORD dwUserInfoLevel = 2; PSTR pszLoginId = NULL; PLSA_PAM_CONFIG pConfig = NULL; int iPamError = 0; PSTR pszExpireDone; LSA_LOG_PAM_DEBUG("pam_sm_acct_mgmt::begin"); dwError = LsaPamGetConfig(&pConfig); BAIL_ON_LSA_ERROR(dwError); LsaPamSetLogLevel(pConfig->dwLogLevel); dwError = LsaPamGetContext( pamh, flags, argc, argv, &pPamContext); BAIL_ON_LSA_ERROR(dwError); dwError = LsaPamGetLoginId( pamh, pPamContext, &pszLoginId, TRUE); BAIL_ON_LSA_ERROR(dwError); if (LsaShouldIgnoreUser(pszLoginId)) { LSA_LOG_PAM_DEBUG("By passing lsassd for local account"); dwError = LW_ERROR_NOT_HANDLED; BAIL_ON_LSA_ERROR(dwError); } dwError = LsaOpenServer(&hLsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaCheckUserInList( hLsaConnection, pszLoginId, NULL); if (dwError && dwError != LW_ERROR_NO_SUCH_USER) { if (dwError == LW_ERROR_NO_SUCH_USER) { BAIL_ON_LSA_ERROR(dwError); } LSA_LOG_PAM_ERROR("User %s is denied access because they are not in the 'require membership of' list", LSA_SAFE_LOG_STRING(pszLoginId)); if (!LW_IS_NULL_OR_EMPTY_STR(pConfig->pszAccessDeniedMessage)) { LsaPamConverse(pamh, pConfig->pszAccessDeniedMessage, PAM_TEXT_INFO, NULL); } } BAIL_ON_LSA_ERROR(dwError); dwError = LsaValidateUser( hLsaConnection, pszLoginId, NULL); if (dwError == LW_ERROR_PASSWORD_EXPIRED) { dwError = 0; pPamContext->bPasswordExpired = TRUE; } BAIL_ON_LSA_ERROR(dwError); if (pPamContext->bPasswordExpired) { // If during pam_sm_authenticate, // we detected that the password expired, // we handle it here if (!pPamContext->bPasswordMessageShown) { LsaPamConverse( pamh, "Your password has expired", PAM_ERROR_MSG, NULL); pPamContext->bPasswordMessageShown = TRUE; } dwError = LW_ERROR_PASSWORD_EXPIRED; BAIL_ON_LSA_ERROR(dwError); } iPamError = pam_get_data( pamh, PAM_LSASS_EXPIRE_WARNING_DONE, (PAM_GET_DATA_TYPE)&pszExpireDone); if (iPamError == PAM_NO_MODULE_DATA) { dwError = LsaFindUserByName( hLsaConnection, pszLoginId, dwUserInfoLevel, (PVOID*)&pUserInfo); BAIL_ON_LSA_ERROR(dwError); if (pUserInfo->bPromptPasswordChange == TRUE && pUserInfo->bPasswordExpired == FALSE && pUserInfo->bPasswordNeverExpires == FALSE) { CHAR szMessage[512]; switch (pUserInfo->dwDaysToPasswordExpiry) { case 0: sprintf(szMessage, "Your password will expire today\n"); break; case 1: sprintf(szMessage, "Your password will expire in 1 day\n"); break; default: sprintf(szMessage, "Your password will expire in %u days\n", pUserInfo->dwDaysToPasswordExpiry); break; } LsaPamConverse(pamh, szMessage, PAM_TEXT_INFO, NULL); } dwError = LsaPamSetDataString( pamh, PAM_LSASS_EXPIRE_WARNING_DONE, "TRUE"); BAIL_ON_LSA_ERROR(dwError); } cleanup: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, (PVOID)pUserInfo); } if (hLsaConnection != (HANDLE)NULL) { LsaCloseServer(hLsaConnection); } if (pConfig) { LsaPamFreeConfig(pConfig); } LW_SAFE_FREE_STRING(pszLoginId); LSA_LOG_PAM_DEBUG("pam_sm_acct_mgmt::end"); return LsaPamOpenPamFilterAcctMgmt( LsaPamMapErrorCode(dwError, pPamContext)); error: if (dwError == LW_ERROR_NO_SUCH_USER || dwError == LW_ERROR_NOT_HANDLED) { LSA_LOG_PAM_WARNING("pam_sm_acct_mgmt failed [login:%s][error code:%u]", LSA_SAFE_LOG_STRING(pszLoginId), dwError); } else { LSA_LOG_PAM_ERROR("pam_sm_acct_mgmt failed [login:%s][error code:%u]", LSA_SAFE_LOG_STRING(pszLoginId), dwError); if (pszLoginId && !strcmp(pszLoginId, "root")) { dwError = LW_ERROR_NO_SUCH_USER; LSA_LOG_PAM_ERROR("Converting error to %u for root", dwError); } } goto cleanup; }
/* * FindUserByName1 * * Check that LsaFindUserByName gets LSA_USER_INFO_1 for given user. */ static DWORD FindUserByName1( HANDLE hLsaConnection, PLWTUSER pUser, PCSTR pszLookedUpBy, PLSA_USER_INFO_1 *ppUserInfo1 ) { PCSTR pszTestDescription = "LsaFindUserByName retrieved LSA_USER_INFO_1 for given user."; PCSTR pszTestAPIs = "LsaFindUserByName"; char szTestMsg[128] = { 0 }; DWORD dwError = LW_ERROR_SUCCESS; DWORD dwLocalError = LW_ERROR_SUCCESS; PLSA_USER_INFO_1 pUserInfo1 = NULL; snprintf( szTestMsg, sizeof(szTestMsg), "\n\tAccount %s.\n", pszLookedUpBy); dwLocalError = LsaFindUserByName( hLsaConnection, pszLookedUpBy, 1, (PVOID*)&pUserInfo1); if ( dwLocalError ) { char buf[128]; char szErrorMsg[128]; LwGetErrorString( dwLocalError, szErrorMsg, sizeof(szErrorMsg)); snprintf( buf, sizeof(buf), "\tLsaFindUserByName reports %lu (%s)\n", (unsigned long)dwLocalError, szErrorMsg); Lwt_strcat(szTestMsg, sizeof(szTestMsg), buf); dwError = LW_ERROR_TEST_FAILED; goto error; } cleanup: *ppUserInfo1 = pUserInfo1; LWT_LOG_TEST(szTestMsg); return dwError; error: if ( pUserInfo1 ) { LsaFreeUserInfo(1, pUserInfo1); pUserInfo1 = NULL; } goto cleanup; }
DWORD Lwt_LsaFindUserByName( HANDLE hLsaConnection, PTESTDATA pTestData ) { DWORD dwError = LW_ERROR_SUCCESS; DWORD dwLocalError = LW_ERROR_SUCCESS; size_t nCurrentUser; PLWTUSER pUser = NULL; PLSA_USER_INFO_0 pUserInfo0 = NULL; PLSA_USER_INFO_1 pUserInfo1 = NULL; if ( ! pTestData ) { dwError = LW_ERROR_TEST_SKIPPED; goto error; } /* For each user (line), verify the information is correct. */ for ( nCurrentUser = 0; nCurrentUser < pTestData->dwNumUsers; nCurrentUser++ ) { dwLocalError = GetUser( pTestData, nCurrentUser, &pUser); BAIL_ON_TEST_BROKE(dwLocalError); if ( pUser->pszNTName ) { dwLocalError = FindUserByName0( hLsaConnection, pUser, pUser->pszNTName, &pUserInfo0); BAIL_ON_TEST_BROKE(dwLocalError); } if ( pUserInfo0 ) { dwLocalError = MatchUserInfo0( pUser, pUser->pszNTName, pUserInfo0); BAIL_ON_TEST_BROKE(dwLocalError); LsaFreeUserInfo(0, pUserInfo0); pUserInfo0 = NULL; } if ( pUser->pszUserPrincipalName ) { dwLocalError = FindUserByName0( hLsaConnection, pUser, pUser->pszUserPrincipalName, &pUserInfo0); BAIL_ON_TEST_BROKE(dwLocalError); } if ( pUserInfo0 ) { dwLocalError = MatchUserInfo0( pUser, pUser->pszUserPrincipalName, pUserInfo0); BAIL_ON_TEST_BROKE(dwLocalError); LsaFreeUserInfo(0, pUserInfo0); pUserInfo0 = NULL; } if ( pUser->pszNTName ) { dwLocalError = FindUserByName1( hLsaConnection, pUser, pUser->pszNTName, &pUserInfo1); BAIL_ON_TEST_BROKE(dwLocalError); } if ( pUserInfo1 ) { LsaFreeUserInfo(1, pUserInfo1); pUserInfo1 = NULL; } FreeUser(&pUser); } dwLocalError = VerifyNullHandling(hLsaConnection); BAIL_ON_TEST_BROKE(dwLocalError); dwLocalError = ValidateForInvalidParams(hLsaConnection, pTestData); BAIL_ON_TEST_BROKE(dwLocalError); cleanup: if ( pUserInfo0 ) { LsaFreeUserInfo(0, pUserInfo0); pUserInfo0 = NULL; } if ( pUserInfo1 ) { LsaFreeUserInfo(1, pUserInfo1); pUserInfo1 = NULL; } if ( pUser ) { FreeUser(&pUser); } return dwError; error: goto cleanup; }
LSASS_API DWORD LsaGetGidsForUserByName( HANDLE hLsaConnection, PCSTR pszUserName, PDWORD pdwGroupFound, gid_t** ppGidResults ) { DWORD dwError = 0; PVOID pUserInfo = NULL; DWORD dwUserInfoLevel = 0; static const DWORD dwGroupInfoLevel = 0; DWORD dwGroupFound = 0; gid_t* pGidResult = NULL; PVOID* ppGroupInfoList = NULL; DWORD iGroup = 0; BAIL_ON_INVALID_HANDLE(hLsaConnection); BAIL_ON_INVALID_STRING(pszUserName); BAIL_ON_INVALID_POINTER(ppGidResults); dwError = LsaValidateUserName(pszUserName); BAIL_ON_LSA_ERROR(dwError); dwError = LsaFindUserByName( hLsaConnection, pszUserName, dwUserInfoLevel, &pUserInfo); BAIL_ON_LSA_ERROR(dwError); dwError = LsaGetGroupsForUserById( hLsaConnection, ((PLSA_USER_INFO_0)pUserInfo)->uid, LSA_FIND_FLAGS_NSS, dwGroupInfoLevel, &dwGroupFound, &ppGroupInfoList); BAIL_ON_LSA_ERROR(dwError); dwError = LwAllocateMemory( sizeof(gid_t) * dwGroupFound, (PVOID*)&pGidResult); BAIL_ON_LSA_ERROR(dwError); for (iGroup = 0; iGroup < dwGroupFound; iGroup++) { *(pGidResult+iGroup) = ((PLSA_GROUP_INFO_0)(*(ppGroupInfoList+iGroup)))->gid; } *ppGidResults = pGidResult; *pdwGroupFound = dwGroupFound; cleanup: if (pUserInfo) { LsaFreeUserInfo(dwUserInfoLevel, pUserInfo); } if (ppGroupInfoList) { LsaFreeGroupInfoList(dwGroupInfoLevel, (PVOID*)ppGroupInfoList, dwGroupFound); } return dwError; error: *ppGidResults = NULL; *pdwGroupFound = 0; goto cleanup; }
int LsaNssIsPasswordExpired( PSTR pszUser, PSTR* ppszMessage ) { PLSA_USER_INFO_2 pInfo = NULL; const DWORD dwInfoLevel = 2; DWORD dwError = LW_ERROR_SUCCESS; int iError = 0; LSA_LOG_PAM_DEBUG("Lsass LAM checking expired password for user [%s]", pszUser? pszUser: "******"); *ppszMessage = NULL; dwError = LsaNssCommonEnsureConnected(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaNssFindUserByAixName( lsaConnection.hLsaConnection, pszUser, dwInfoLevel, (PVOID*)&pInfo); BAIL_ON_LSA_ERROR(dwError); if (pInfo->bPasswordExpired) { dwError = LwAllocateStringPrintf( ppszMessage, "%s's password is expired", pszUser); BAIL_ON_LSA_ERROR(dwError); dwError = LW_ERROR_PASSWORD_EXPIRED; BAIL_ON_LSA_ERROR(dwError); } if (pInfo->bPromptPasswordChange && pInfo->dwDaysToPasswordExpiry) { dwError = LwAllocateStringPrintf( ppszMessage, "Your password will expire in %u days\n", pInfo->dwDaysToPasswordExpiry); BAIL_ON_LSA_ERROR(dwError); } cleanup: if (pInfo != NULL) { LsaFreeUserInfo( dwInfoLevel, (PVOID)pInfo); } switch(dwError) { case LW_ERROR_SUCCESS: iError = 0; break; case LW_ERROR_PASSWORD_EXPIRED: iError = 1; break; case LW_ERROR_NO_SUCH_USER: iError = -1; errno = ENOENT; break; default: // password is expired and cannot login LsaNssMapErrorCode(dwError, &errno); iError = 2; break; } LSA_LOG_PAM_DEBUG("Lsass LAM expired password check finishing with likewise code %u and LAM code %d", dwError, iError); return iError; error: LsaNssCommonCloseConnection(&lsaConnection); goto cleanup; }
int LsaNssAuthenticate( PSTR pszUser, PSTR pszResponse, int* pReenter, PSTR* ppszOutputMessage ) { int iError = 0; DWORD dwError = LW_ERROR_SUCCESS; PLSA_USER_INFO_0 pInfo = NULL; const DWORD dwInfoLevel = 0; PSTR pszMessage = NULL; PLSA_USER_INFO_2 pInfo2 = NULL; LSA_LOG_PAM_DEBUG("Lsass LAM authenticating user [%s]", pszUser? pszUser: "******"); dwError = LsaNssCommonEnsureConnected(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaNssFindUserByAixName( lsaConnection.hLsaConnection, pszUser, dwInfoLevel, (PVOID*)&pInfo); BAIL_ON_LSA_ERROR(dwError); dwError = LsaAuthenticateUser( lsaConnection.hLsaConnection, pInfo->pszName, pszResponse, &pszMessage); if (dwError == LW_ERROR_PASSWORD_EXPIRED) { // Double check that the user's password is marked as expired dwError = LsaFindUserByName( lsaConnection.hLsaConnection, pInfo->pszName, 2, (PVOID*)&pInfo2); BAIL_ON_LSA_ERROR(dwError); if (!pInfo2->bPasswordExpired) { // Something went wrong in lsassd -- don't let the user login dwError = LW_ERROR_PASSWORD_EXPIRED; } } BAIL_ON_LSA_ERROR(dwError); dwError = LsaCheckUserInList( lsaConnection.hLsaConnection, pInfo->pszName, NULL); BAIL_ON_LSA_ERROR(dwError); // Need to ensure that home directories are created. dwError = LsaOpenSession( lsaConnection.hLsaConnection, pInfo->pszName); BAIL_ON_LSA_ERROR(dwError); cleanup: if (ppszOutputMessage) { *ppszOutputMessage = pszMessage; } else { LW_SAFE_FREE_STRING(pszMessage); } if (pInfo != NULL) { LsaFreeUserInfo( dwInfoLevel, pInfo); } if (pInfo2 != NULL) { LsaFreeUserInfo( 2, pInfo2); } switch(dwError) { case LW_ERROR_SUCCESS: iError = AUTH_SUCCESS; break; case LW_ERROR_NOT_HANDLED: case LW_ERROR_NO_SUCH_USER: iError = AUTH_NOTFOUND; break; case LW_ERROR_ACCOUNT_EXPIRED: case LW_ERROR_ACCOUNT_DISABLED: case LW_ERROR_ACCOUNT_LOCKED: iError = AUTH_FAILURE; break; default: iError = AUTH_UNAVAIL; break; } LSA_LOG_PAM_DEBUG("Lsass LAM authenticate finishing with likewise code %u and LAM code %d", dwError, iError); return iError; error: LsaNssCommonCloseConnection(&lsaConnection); 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 ADUKrb5GetUserCachePathAndSID( PCSTR pszUserUPN, PSTR* ppszCachePath, PSTR* ppszHomeDirPath, PSTR* ppszSID, uid_t* pUid ) { DWORD dwError = 0; char szPath[PATH_MAX]; PSTR pszCachePath = NULL; PSTR pszHomeDirPath = NULL; PSTR pszSID = NULL; HANDLE hLsaConnection = (HANDLE) NULL; PLSA_USER_INFO_0 pUserInfo_0 = NULL; dwError = LsaOpenServer(&hLsaConnection); BAIL_ON_MAC_ERROR(dwError); dwError = LsaFindUserByName(hLsaConnection, pszUserUPN, 0, (PVOID*) &pUserInfo_0); BAIL_ON_MAC_ERROR(dwError); memset(szPath, 0, sizeof(szPath)); sprintf(szPath, "FILE:/tmp/krb5cc_%ld",(long)pUserInfo_0->uid); dwError = LwAllocateString(szPath, &pszCachePath); BAIL_ON_MAC_ERROR(dwError); dwError = LwAllocateString(pUserInfo_0->pszSid, &pszSID); BAIL_ON_MAC_ERROR(dwError); dwError = LwAllocateString(pUserInfo_0->pszHomedir, &pszHomeDirPath); BAIL_ON_MAC_ERROR(dwError); if (ppszCachePath) { *ppszCachePath = pszCachePath; pszCachePath = NULL; } if (ppszSID) { *ppszSID = pszSID; pszSID = NULL; } if (ppszHomeDirPath) { *ppszHomeDirPath = pszHomeDirPath; pszHomeDirPath = NULL; } if (pUid) { *pUid = pUserInfo_0->uid; } cleanup: if (pszCachePath) LwFreeString(pszCachePath); if (pszSID) LwFreeString(pszSID); if (pszHomeDirPath) LwFreeString(pszHomeDirPath); if (pUserInfo_0) LsaFreeUserInfo(0, pUserInfo_0); if (hLsaConnection != (HANDLE)NULL) LsaCloseServer(hLsaConnection); return dwError; error: if (ppszCachePath) *ppszCachePath = NULL; if (ppszSID) *ppszSID = NULL; if (ppszHomeDirPath) *ppszHomeDirPath = NULL; if (pUid) *pUid = -1; goto cleanup; }
PVOID LADSFindUserById( PVOID pData ) { DWORD dwError = 0; PVOID pResult = NULL; uid_t userId = 0; DWORD dwInfoLevel = gLADSStressData[LADS_FIND_USER_BY_ID].dwInfoLevel; PVOID pUserInfo = NULL; HANDLE hProvider = (HANDLE)NULL; while (!LADSProcessShouldStop()) { DWORD iUid = 0; for (iUid = 0; (!LADSProcessShouldStop() && (iUid < gLADSStressData[LADS_FIND_USER_BY_ID].dwNumItems)); iUid++) { userId = gLADSStressData[LADS_FIND_USER_BY_ID].data.pUidArray[iUid]; dwError = gpAuthProvider->pFnTable->pfnOpenHandle( geteuid(), getegid(), getpid(), &hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = gpAuthProvider->pFnTable->pfnLookupUserById( hProvider, userId, dwInfoLevel, &pUserInfo); BAIL_ON_LSA_ERROR(dwError); gpAuthProvider->pFnTable->pfnCloseHandle(hProvider); hProvider = (HANDLE)NULL; LsaFreeUserInfo(dwInfoLevel, pUserInfo); pUserInfo = NULL; if (gLADSStressData[LADS_FIND_USER_BY_ID].dwSleepMSecs) { sleep(gLADSStressData[LADS_FIND_USER_BY_ID].dwSleepMSecs); } } } cleanup: if (pUserInfo) { LsaFreeUserInfo(dwInfoLevel, pUserInfo); } if (hProvider != (HANDLE)NULL) { gpAuthProvider->pFnTable->pfnCloseHandle(hProvider); } return pResult; error: LSA_LOG_ERROR("Failed to find user by id [%ld] [error code: %u]", (long)userId, dwError); LADSStopProcess(); goto cleanup; }
int LsaNssNormalizeUsername( PSTR pszInput, PSTR pszOutput ) { DWORD dwError = LW_ERROR_SUCCESS; PLSA_USER_INFO_0 pInfo = NULL; const DWORD dwInfoLevel = 0; uint64_t qwConvert = 0; int iDigit = 0; PSTR pszPos = NULL; if (strlen(pszInput) < S_NAMELEN) { strcpy(pszOutput, pszInput); goto cleanup; } dwError = LsaNssCommonEnsureConnected(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaFindUserByName( lsaConnection.hLsaConnection, pszInput, dwInfoLevel, (PVOID*)&pInfo); BAIL_ON_LSA_ERROR(dwError); qwConvert = pInfo->uid; pszPos = pszOutput + S_NAMELEN - 1; *pszPos-- = 0; if (qwConvert < 10000000) { // Mangle the username with the old rules while(pszPos > pszOutput) { iDigit = qwConvert % 10; *pszPos = iDigit + '0'; qwConvert /= 10; pszPos--; } } else { // Mangle the username with the new rules. The mangled user name will // start with _ and the second character will be a letter. The uid // number (with padding) will be in base 32. qwConvert += 10737418240ull; while(pszPos > pszOutput) { iDigit = qwConvert % 32; if (iDigit < 10) { *pszPos = iDigit + '0'; } else { *pszPos = iDigit + 'a' - 10; } qwConvert /= 32; pszPos--; } } *pszPos = '_'; cleanup: if (pInfo != NULL) { LsaFreeUserInfo( dwInfoLevel, (PVOID)pInfo); } if (dwError != LW_ERROR_SUCCESS) { LsaNssMapErrorCode(dwError, &errno); return 0; } return strlen(pszOutput); error: *pszOutput = 0; LsaNssCommonCloseConnection(&lsaConnection); goto cleanup; }
PVOID LADSFindUserByName( PVOID pData ) { DWORD dwError = 0; PVOID pResult = NULL; PCSTR pszName = NULL; DWORD dwInfoLevel = gLADSStressData[LADS_FIND_USER_BY_NAME].dwInfoLevel; PVOID pUserInfo = NULL; HANDLE hProvider = (HANDLE)NULL; while (!LADSProcessShouldStop()) { DWORD iName = 0; for (iName = 0; (!LADSProcessShouldStop() && (iName < gLADSStressData[LADS_FIND_USER_BY_NAME].dwNumItems)); iName++) { pszName = gLADSStressData[LADS_FIND_USER_BY_NAME].data.ppszNames[iName]; dwError = gpAuthProvider->pFnTable->pfnOpenHandle( geteuid(), getegid(), getpid(), &hProvider); BAIL_ON_LSA_ERROR(dwError); dwError = gpAuthProvider->pFnTable->pfnLookupUserByName( hProvider, pszName, dwInfoLevel, &pUserInfo); BAIL_ON_LSA_ERROR(dwError); gpAuthProvider->pFnTable->pfnCloseHandle(hProvider); hProvider = (HANDLE)NULL; LsaFreeUserInfo(dwInfoLevel, pUserInfo); pUserInfo = NULL; if (gLADSStressData[LADS_FIND_USER_BY_NAME].dwSleepMSecs) { sleep(gLADSStressData[LADS_FIND_USER_BY_NAME].dwSleepMSecs); } } } cleanup: if (pUserInfo) { LsaFreeUserInfo(dwInfoLevel, pUserInfo); } if (hProvider != (HANDLE)NULL) { gpAuthProvider->pFnTable->pfnCloseHandle(hProvider); } return pResult; error: LSA_LOG_ERROR("Failed to find user by name [%s] [error code: %u]", (LW_IS_NULL_OR_EMPTY_STR(pszName) ? "" : pszName), dwError); LADSStopProcess(); goto cleanup; }
int main(int argc, const char **argv) { poptContext poptContext; int poptResult; uid_t uid; int kq; HANDLE lsaConnection = (HANDLE) NULL; PVOID pUserInfo = NULL; struct kevent event = { 0 }; int numChanges = 1; krb5_context krb5Context = NULL; char krb5FileCachePath[PATH_MAX]; krb5_ccache krb5FileCache = NULL; krb5_ccache krb5MemoryCache = NULL; krb5_cc_cursor krb5Cursor = NULL; krb5_creds krb5Credentials = { 0 }; krb5_principal krb5Principal = NULL; krb5_error_code krb5Error; int exitStatus = 0; DWORD dwError = LW_ERROR_SUCCESS; poptContext = poptGetContext(NULL, argc, argv, Options, 0); while ((poptResult = poptGetNextOpt(poptContext)) >= 0) { /* All options are processed automatically. */ } if (poptResult < -1) { fprintf(stderr, "%s: %s: %s\n", getprogname(), poptBadOption(poptContext, POPT_BADOPTION_NOALIAS), poptStrerror(poptResult)); exitStatus = 1; goto error; } uid = getuid(); /* Make sure we're running as an AD user. */ dwError = LsaOpenServer(&lsaConnection); BAIL_ON_LSA_ERROR(dwError); dwError = LsaFindUserById( lsaConnection, uid, 0, &pUserInfo); if (dwError == LW_ERROR_NO_SUCH_USER) { /* * Running as a non-AD user; exit 0 so launchd doesn't restart * the ticketcopy program (see com.beyondtrust.pbis.ticketcopy.plist). */ LSA_LOG_DEBUG( "uid %lu is not an AD user; exiting", (unsigned long) uid); dwError = LW_ERROR_SUCCESS; goto cleanup; } BAIL_ON_LSA_ERROR(dwError); kq = kqueue(); BAIL_ON_UNIX_ERROR(kq == -1); krb5Error = krb5_init_context(&krb5Context); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5Error = krb5_cc_default(krb5Context, &krb5MemoryCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); snprintf( krb5FileCachePath, sizeof(krb5FileCachePath), "FILE:/tmp/krb5cc_%lu", (unsigned long) uid); while (1) /* Forever (or until an error occurs) */ { while ((event.ident = open(krb5FileCachePath + 5, O_RDONLY)) == -1) { sleep(5); } event.filter = EVFILT_VNODE; event.flags = EV_ADD | EV_ENABLE | EV_CLEAR; event.fflags = NOTE_DELETE | NOTE_WRITE; numChanges = 1; krb5Error = krb5_cc_resolve( krb5Context, krb5FileCachePath, &krb5FileCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); while (1) /* While the file continues to exist. */ { /* * Turn off KRB5_TC_OPENCLOSE so the file will be opened once * and kept open. This causes it to actually attempt to open * the file, so this is where we check for the file not * existing and retry after sleeping a bit. */ krb5Error = krb5_cc_set_flags(krb5Context, krb5FileCache, 0); if (krb5Error == KRB5_FCC_NOFILE) { break; } BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* Copy all credentials from the file to the memory cache. */ krb5Error = krb5_cc_start_seq_get( krb5Context, krb5FileCache, &krb5Cursor); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); while ((krb5Error = krb5_cc_next_cred( krb5Context, krb5FileCache, &krb5Cursor, &krb5Credentials)) == 0) { krb5Error = krb5_cc_store_cred( krb5Context, krb5MemoryCache, &krb5Credentials); if (krb5Error == KRB5_FCC_NOFILE) { krb5Error = krb5_cc_get_principal( krb5Context, krb5FileCache, &krb5Principal); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* The memory cache was destroyed; re-create it. */ krb5Error = krb5_cc_initialize( krb5Context, krb5MemoryCache, krb5Principal); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5_free_principal(krb5Context, krb5Principal); krb5Principal = NULL; krb5Error = krb5_cc_store_cred( krb5Context, krb5MemoryCache, &krb5Credentials); } BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5_free_cred_contents(krb5Context, &krb5Credentials); } if (krb5Error != KRB5_CC_END) { BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); } krb5Error = krb5_cc_end_seq_get( krb5Context, krb5FileCache, &krb5Cursor); krb5Cursor = NULL; BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* * Turn KRB5_TC_OPENCLOSE back on; this will cause * the file to be closed and any locks to be * released. */ krb5Error = krb5_cc_set_flags( krb5Context, krb5FileCache, KRB5_TC_OPENCLOSE); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); /* * Wait for the file to be modified or deleted. The first * time this is called after the file is opened, numChanges * will be 1, which will install the fd into the event * list. After that numChanges is changed to 0, so it will * just report events from the existing list. */ if (kevent(kq, &event, numChanges, &event, 1, NULL) != 1) { fprintf(stderr, "kevent failed\n"); exitStatus = 1; goto cleanup; } if (event.fflags & NOTE_DELETE) { break; } numChanges = 0; } krb5Error = krb5_cc_close(krb5Context, krb5FileCache); BAIL_ON_KRB5_ERROR(krb5Context, krb5Error, dwError); krb5FileCache = NULL; close(event.ident); event.ident = -1; /* * The cache file is usually removed as part of a * rename(2) system call, so only wait a short * time before the first attempt to re-open it. */ usleep(100000); } error: cleanup: krb5_free_cred_contents(krb5Context, &krb5Credentials); if (krb5Cursor) { krb5_cc_end_seq_get(krb5Context, krb5FileCache, &krb5Cursor); } if (krb5FileCache) { krb5_cc_close(krb5Context, krb5FileCache); } if (krb5Principal) { krb5_free_principal(krb5Context, krb5Principal); } if (krb5Context) { krb5_free_context(krb5Context); } if (event.ident != -1) { close(event.ident); } if (pUserInfo) { LsaFreeUserInfo(0, pUserInfo); } if (lsaConnection != (HANDLE) NULL) { LsaCloseServer(lsaConnection); } if (dwError) { exitStatus = 1; } return exitStatus; }