/* Copyright 2004 by the Massachusetts Institute of Technology All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the Massachusetts Institute of Technology (M.I.T.) not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include //#pragma keyword("interface",on) //#define interface struct #define SECURITY_WIN32 #if (_WIN32_WINNT < 0x0500) #error _WIN32_WINNT < 0x0500 #endif /**/ #include #include #include #include #include #include #include /**/ #include "afslogon.h" #include "..\afsrdr\common\AFSUserDefines.h" #include "..\afsrdr\common\AFSUserIoctl.h" #include "..\afsrdr\common\AFSUserStructs.h" #include "..\afsrdr\common\AFSProvider.h" #define SEC_ERR_VALUE(v) if (status==v) return #v char * _get_sec_err_text(SECURITY_STATUS status) { SEC_ERR_VALUE(SEC_E_OK); SEC_ERR_VALUE(SEC_I_CONTINUE_NEEDED); SEC_ERR_VALUE(SEC_I_COMPLETE_NEEDED); SEC_ERR_VALUE(SEC_I_COMPLETE_AND_CONTINUE); SEC_ERR_VALUE(SEC_E_INCOMPLETE_MESSAGE); SEC_ERR_VALUE(SEC_I_INCOMPLETE_CREDENTIALS); SEC_ERR_VALUE(SEC_E_INVALID_HANDLE); SEC_ERR_VALUE(SEC_E_TARGET_UNKNOWN); SEC_ERR_VALUE(SEC_E_LOGON_DENIED); SEC_ERR_VALUE(SEC_E_INTERNAL_ERROR); SEC_ERR_VALUE(SEC_E_NO_CREDENTIALS); SEC_ERR_VALUE(SEC_E_NO_AUTHENTICATING_AUTHORITY); SEC_ERR_VALUE(SEC_E_INSUFFICIENT_MEMORY); SEC_ERR_VALUE(SEC_E_INVALID_TOKEN); SEC_ERR_VALUE(SEC_E_UNSUPPORTED_FUNCTION); SEC_ERR_VALUE(SEC_E_WRONG_PRINCIPAL); return "Unknown"; } #undef SEC_ERR_VALUE DWORD LogonSSP(PLUID lpLogonId, PCtxtHandle outCtx) { DWORD code = 1; SECURITY_STATUS status; CredHandle creds; CtxtHandle ctxclient,ctxserver; TimeStamp expiry; BOOL cont = TRUE; BOOL first = TRUE; SecBufferDesc sdescc,sdescs; SecBuffer stokc,stoks; ULONG cattrs,sattrs; int iters = 10; outCtx->dwLower = 0; outCtx->dwUpper = 0; cattrs = 0; sattrs = 0; status = AcquireCredentialsHandle( NULL, "Negotiate", SECPKG_CRED_BOTH, lpLogonId, NULL, NULL, NULL, &creds, &expiry); if (status != SEC_E_OK) { DebugEvent("AcquireCredentialsHandle failed: %lX", status); goto ghp_0; } sdescc.cBuffers = 1; sdescc.pBuffers = &stokc; sdescc.ulVersion = SECBUFFER_VERSION; stokc.BufferType = SECBUFFER_TOKEN; stokc.cbBuffer = 0; stokc.pvBuffer = NULL; sdescs.cBuffers = 1; sdescs.pBuffers = &stoks; sdescs.ulVersion = SECBUFFER_VERSION; stoks.BufferType = SECBUFFER_TOKEN; stoks.cbBuffer = 0; stoks.pvBuffer = NULL; do { status = InitializeSecurityContext( &creds, ((first)? NULL:&ctxclient), NULL, ISC_REQ_DELEGATE | ISC_REQ_ALLOCATE_MEMORY, 0, SECURITY_NATIVE_DREP, ((first)?NULL:&sdescs), 0, &ctxclient, &sdescc, &cattrs, &expiry ); DebugEvent("InitializeSecurityContext returns status[%lX](%s)",status,_get_sec_err_text(status)); if (!first) FreeContextBuffer(stoks.pvBuffer); if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) { CompleteAuthToken(&ctxclient, &sdescc); } if (status != SEC_I_CONTINUE_NEEDED && status != SEC_I_COMPLETE_AND_CONTINUE) { cont = FALSE; } if (!stokc.cbBuffer && !cont) { DebugEvent("Breaking out after InitializeSecurityContext"); break; } status = AcceptSecurityContext( &creds, ((first)?NULL:&ctxserver), &sdescc, ASC_REQ_DELEGATE | ASC_REQ_ALLOCATE_MEMORY, SECURITY_NATIVE_DREP, &ctxserver, &sdescs, &sattrs, &expiry); DebugEvent("AcceptSecurityContext returns status[%lX](%s)", status, _get_sec_err_text(status)); FreeContextBuffer(stokc.pvBuffer); if (status == SEC_I_COMPLETE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) { CompleteAuthToken(&ctxserver,&sdescs); } if (status == SEC_I_CONTINUE_NEEDED || status == SEC_I_COMPLETE_AND_CONTINUE) { cont = TRUE; } if (!cont) FreeContextBuffer(stoks.pvBuffer); first = FALSE; iters--; /* just in case, hard limit on loop */ } while (cont && iters); if (sattrs & ASC_RET_DELEGATE) { DebugEvent("Received delegate context"); *outCtx = ctxserver; code = 0; } else { DebugEvent("Didn't receive delegate context"); outCtx->dwLower = 0; outCtx->dwUpper = 0; DeleteSecurityContext(&ctxserver); } DeleteSecurityContext(&ctxclient); FreeCredentialsHandle(&creds); ghp_0: return code; } DWORD QueryAdHomePathFromSid(char * homePath, size_t homePathLen, PSID psid, PWSTR domain) { DWORD code = 1; /* default is failure */ NTSTATUS rv = 0; HRESULT hr = S_OK; LPWSTR p = NULL; WCHAR adsPath[MAX_PATH] = L""; BOOL coInitialized = FALSE; CHAR ansidomain[256], *a; homePath[0] = '\0'; /* I trust this is an ASCII domain name */ for ( p=domain, a=ansidomain; *a = (CHAR)*p; p++, a++); DebugEvent("Domain: %s", ansidomain); if (ConvertSidToStringSidW(psid,&p)) { IADsNameTranslate *pNto; DebugEvent("Got SID string [%S]", p); hr = CoInitialize(NULL); if (SUCCEEDED(hr)) coInitialized = TRUE; hr = CoCreateInstance( CLSID_NameTranslate, NULL, CLSCTX_INPROC_SERVER, IID_IADsNameTranslate, (void**)&pNto); if (FAILED(hr)) { DebugEvent("Can't create nametranslate object"); } else { hr = pNto->Init(ADS_NAME_INITTYPE_GC,L""); if (FAILED(hr)) { DebugEvent("NameTranslate Init GC failed [%ld]", hr); if ( domain ) { hr = pNto->Init(ADS_NAME_INITTYPE_DOMAIN,domain); if (FAILED(hr)) { DebugEvent("NameTranslate Init Domain failed [%ld]", hr); } } } if (!FAILED(hr)) { hr = pNto->Set(ADS_NAME_TYPE_SID_OR_SID_HISTORY_NAME, p); if (FAILED(hr)) { DebugEvent("Can't set sid string"); } else { BSTR bstr; hr = pNto->Get(ADS_NAME_TYPE_1779, &bstr); if(SUCCEEDED(hr)) { hr = StringCchCopyW(adsPath, MAX_PATH, bstr); if(FAILED(hr)) { DebugEvent("Overflow while copying ADS path"); adsPath[0] = L'\0'; } SysFreeString(bstr); } } } pNto->Release(); } LocalFree(p); } else { DebugEvent("Can't convert sid to string"); } if (adsPath[0]) { WCHAR fAdsPath[MAX_PATH]; IADsUser *pAdsUser; BSTR bstHomeDir = NULL; hr = StringCchPrintfW(fAdsPath, MAX_PATH, L"LDAP://%s", adsPath); if (hr != S_OK) { DebugEvent("Can't format full adspath"); goto cleanup; } DebugEvent("Trying adsPath=[%S]", fAdsPath); hr = ADsGetObject( fAdsPath, IID_IADsUser, (LPVOID *) &pAdsUser); if (hr != S_OK) { DebugEvent("Can't open IADs object"); goto cleanup; } hr = pAdsUser->get_Profile(&bstHomeDir); if (hr != S_OK) { DebugEvent("Can't get profile directory"); goto cleanup_homedir_section; } wcstombs(homePath, bstHomeDir, homePathLen); DebugEvent("Got homepath [%s]", homePath); SysFreeString(bstHomeDir); code = 0; cleanup_homedir_section: pAdsUser->Release(); } cleanup: if (coInitialized) CoUninitialize(); return code; } /* Try to determine the user's AD home path. *homePath is assumed to be at least MAXPATH bytes. If successful, opt.flags is updated with LOGON_FLAG_AD_REALM to indicate that we are dealing with an AD realm. */ DWORD GetAdHomePath(char * homePath, size_t homePathLen, PLUID lpLogonId, LogonOptions_t * opt) { CtxtHandle ctx; DWORD code = 0; SECURITY_STATUS status; homePath[0] = '\0'; if (LogonSSP(lpLogonId,&ctx)) { DebugEvent("Failed LogonSSP"); return 1; } status = ImpersonateSecurityContext(&ctx); if (status == SEC_E_OK) { PSECURITY_LOGON_SESSION_DATA plsd; NTSTATUS rv; rv = LsaGetLogonSessionData(lpLogonId, &plsd); if (rv == 0) { PWSTR domain; domain = (PWSTR)malloc(sizeof(WCHAR) * (plsd->LogonDomain.Length+1)); memcpy(domain, plsd->LogonDomain.Buffer, sizeof(WCHAR) * (plsd->LogonDomain.Length)); domain[plsd->LogonDomain.Length] = 0; if (!QueryAdHomePathFromSid(homePath,homePathLen,plsd->Sid,domain)) { DebugEvent("Returned home path [%s]",homePath); opt->flags |= LOGON_FLAG_AD_REALM; } free(domain); LsaFreeReturnBuffer(plsd); } else { DebugEvent("LsaGetLogonSessionData failed [%lX]", rv); } RevertSecurityContext(&ctx); } else { DebugEvent("Can't impersonate context [%lX]",status); code = 1; } DeleteSecurityContext(&ctx); return code; } BOOL GetLocalShortDomain(PWSTR Domain, DWORD cbDomain) { HRESULT hr; IADsADSystemInfo *pADsys; BOOL coInitialized = FALSE; BOOL retval = FALSE; hr = CoInitialize(NULL); if (SUCCEEDED(hr)) coInitialized = TRUE; hr = CoCreateInstance( CLSID_ADSystemInfo, NULL, CLSCTX_INPROC_SERVER, IID_IADsADSystemInfo, (void**)&pADsys); if ( !FAILED(hr) ) { BSTR bstr; hr = pADsys->get_DomainShortName(&bstr); if ( !FAILED(hr) ) { hr = StringCbCopyW( Domain, cbDomain, bstr ); if(SUCCEEDED(hr)) { retval = TRUE; } SysFreeString(bstr); } pADsys->Release(); } if (coInitialized) CoUninitialize(); return retval; } static HANDLE OpenRedirector(void) { HANDLE hControlDevice = NULL; hControlDevice = CreateFileW( AFS_SYMLINK_W, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if( hControlDevice == INVALID_HANDLE_VALUE) { hControlDevice = NULL; DebugEvent("OpenRedirector Failed to open control device error: %d", GetLastError()); } return hControlDevice; } void AFSCreatePAG(PLUID lpLogonId) { BOOLEAN bRet = FALSE; HANDLE hControlDevice = NULL; DWORD dwCopyBytes = 0; AFSAuthGroupRequestCB *pAuthGroup = NULL; WCHAR * pwchSIDString = NULL; CtxtHandle ctx; SECURITY_STATUS status; PSECURITY_LOGON_SESSION_DATA plsd = NULL; NTSTATUS rv; BOOLEAN bImpersonated = FALSE; GUID stAuthGroup; unsigned char *pchGUID = NULL; DWORD bytesReturned; if (LogonSSP(lpLogonId, &ctx)) { DebugEvent("AFSCreatePAG unable to obtain LogonSSP context"); return; } status = ImpersonateSecurityContext(&ctx); if (status == SEC_E_OK) { bImpersonated = TRUE; rv = LsaGetLogonSessionData(lpLogonId, &plsd); if (rv == 0) { if( !ConvertSidToStringSidW( plsd->Sid, &pwchSIDString)) { DebugEvent("AFSCreatePAG Failed to convert sid to string Error %08X", GetLastError()); goto cleanup; } pAuthGroup = (AFSAuthGroupRequestCB *)LocalAlloc( LPTR, 0x1000); if( pAuthGroup == NULL) { DebugEvent0("AFSCreatePAG Failed auth group allocation"); goto cleanup; } memset( pAuthGroup, 0, 0x1000); pAuthGroup->SIDLength = (USHORT) (wcslen( pwchSIDString) * sizeof( WCHAR)); pAuthGroup->SessionId = plsd->Session; memcpy( pAuthGroup->SIDString, pwchSIDString, pAuthGroup->SIDLength); RevertSecurityContext(&ctx); bImpersonated = FALSE; hControlDevice = OpenRedirector(); if( hControlDevice == NULL) { DebugEvent0("AFSCreatePAG Failed to open redirector"); goto cleanup; } bRet = DeviceIoControl( hControlDevice, IOCTL_AFS_AUTHGROUP_SID_QUERY, NULL, 0, &stAuthGroup, sizeof( GUID), &bytesReturned, NULL); if( bRet == FALSE) { DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Error 0x%08X", GetLastError()); } else { if( UuidToString( (UUID *)&stAuthGroup, &pchGUID) == RPC_S_OK) { DebugEvent("AFSCreatePAG Initial AuthGroup %s\n", pchGUID); RpcStringFree( &pchGUID); } else { DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n"); } } ImpersonateSecurityContext(&ctx); bImpersonated = TRUE; bRet = DeviceIoControl( hControlDevice, IOCTL_AFS_AUTHGROUP_SID_QUERY, NULL, 0, &stAuthGroup, sizeof( GUID), &bytesReturned, NULL); if( bRet == FALSE) { DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Impersonation Error 0x%08X", GetLastError()); } else { if( UuidToString( (UUID *)&stAuthGroup, &pchGUID) == RPC_S_OK) { DebugEvent("AFSCreatePAG Initial Impersonation AuthGroup %s\n", pchGUID); RpcStringFree( &pchGUID); } else { DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n"); } } RevertSecurityContext(&ctx); bImpersonated = FALSE; bRet = DeviceIoControl( hControlDevice, IOCTL_AFS_AUTHGROUP_LOGON_CREATE, pAuthGroup, sizeof( AFSAuthGroupRequestCB) + pAuthGroup->SIDLength - 1, NULL, 0, &dwCopyBytes, NULL); if( bRet == FALSE) { DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_CREATE Error 0x%08X", GetLastError()); } else { bRet = DeviceIoControl( hControlDevice, IOCTL_AFS_AUTHGROUP_SID_QUERY, NULL, 0, &stAuthGroup, sizeof( GUID), &bytesReturned, NULL); if( bRet == FALSE) { DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Error 0x%08X", GetLastError()); } else { if( UuidToString( (UUID *)&stAuthGroup, &pchGUID) == RPC_S_OK) { DebugEvent("AFSCreatePAG New AuthGroup %s\n", pchGUID); RpcStringFree( &pchGUID); } else { DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n"); } } } ImpersonateSecurityContext(&ctx); bRet = DeviceIoControl( hControlDevice, IOCTL_AFS_AUTHGROUP_SID_QUERY, NULL, 0, &stAuthGroup, sizeof( GUID), &bytesReturned, NULL); if( bRet == FALSE) { DebugEvent("AFSCreatePAG Failed IOCTL_AFS_AUTHGROUP_SID_QUERY Impersonation Error 0x%08X", GetLastError()); } else { if( UuidToString( (UUID *)&stAuthGroup, &pchGUID) == RPC_S_OK) { DebugEvent("AFSCreatePAG New Impersonation AuthGroup %s\n", pchGUID); RpcStringFree( &pchGUID); } else { DebugEvent0("AFSCreatePAG Failed to convert GUID to string\n"); } } RevertSecurityContext(&ctx); } else { DebugEvent("AFSCreatePAG LsaGetLogonSessionData failed [%lX]", rv); } } else { DebugEvent("AFSCreatePAG cannot impersonate context [%lX]", status); } cleanup: if (bImpersonated) RevertSecurityContext(&ctx); DeleteSecurityContext(&ctx); if (plsd != NULL) LsaFreeReturnBuffer(plsd); if ( hControlDevice != NULL) CloseHandle( hControlDevice); if( pwchSIDString != NULL) LocalFree( pwchSIDString); }