#include "cvs.h"
#define WIN32_LEAN_AND_MEAN
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <shlwapi.h>
#define SECURITY_WIN32
#include <security.h>
#include <ntsecapi.h>
#include <ntdsapi.h>
#include <dsgetdc.h>
#include <lm.h>
#include <activeds.h>
#include <tchar.h>
#include <malloc.h>
#pragma comment(lib,"adsiid.lib")
#include <comdef.h>
struct _LARGE_INTEGER_X {
struct {
DWORD LowPart;
LONG HighPart;
} u;
};
// This stops the compiler from complaining about negative values in unsigned longs.
#pragma warning(disable:4146)
#pragma warning(disable:4192)
#import <activeds.tlb> rename("_LARGE_INTEGER","_LARGE_INTEGER_X") rename("GetObject","_GetObject")
#pragma warning(default:4146)
#pragma warning(default:4192)
#include "sid.h"
static MAKE_SID1(sidEveryone, 1, 0);
static MAKE_SID1(sidLocal, 2, 0);
static MAKE_SID1(sidNetwork, 5, 2);
static MAKE_SID1(sidBatch, 5, 3);
static MAKE_SID1(sidService, 5, 6);
static MAKE_SID1(sidAnonymous, 5, 7);
static MAKE_SID1(sidInteractive, 5, 4);
static MAKE_SID1(sidAuthenticated, 5, 11);
static MAKE_SID1(sidSystem, 5, 18);
static MAKE_SID2(sidAdministrators, 5, 32, 544);
/*
This is a funny bit of code... it emulates setuid on NT.
Any process with 'create token' can become any other user, with a bit of magic.
(This is normally just LocalSystem).
I did actually think of this independently, but realised very quickly that the
cygwin folks had already done something like this. This is based mostly on
their implementation, with a bit of re-interpretation of MSDN by me.
It leaks a bit (not 100% sure why) but it's only called a couple of times
during the execution of cvs so that's not a problem.
Prototype:
void nt_setuid_init(); -- Initialise
int nt_setuid(LPCSTR szMachine, LPCSTR szUser); -- Do the stuff
*/
#ifndef __cplusplus
typedef enum { false, true } bool;
#endif
typedef NTSTATUS (NTAPI *NtCreateToken_t)
(PHANDLE, /* Address of created token */
ACCESS_MASK, /* Access granted to object (TOKEN_ALL_ACCESS) */
PLSA_OBJECT_ATTRIBUTES,
TOKEN_TYPE, /* Token type (Primary/Impersonation) */
PLUID, /* Authentication Id (From GetTokenInformation) or SYSTEM_LUID */
PLARGE_INTEGER, /* exp - unknown 0x7FFFFFFFFFFFFFFFLL */
PTOKEN_USER, /* Owner SID for this token */
PTOKEN_GROUPS, /* Group SIDs in this token */
PTOKEN_PRIVILEGES, /* List of rights for this user */
PTOKEN_OWNER, /* Default SID for created objects */
PTOKEN_PRIMARY_GROUP, /* Primary group for created objects */
PTOKEN_DEFAULT_DACL, /* Default DACL for created objects */
PTOKEN_SOURCE /* Source of this token (App name) */
);
static NtCreateToken_t NtCreateToken=NULL;
static bool LookupSid(LPCWSTR szMachine, LPCWSTR szUser, PSID* pUserSid, SID_NAME_USE* Use)
{
DWORD UserSidSize=0,DomainSize=0;
wchar_t *szDomain = NULL;
*Use=SidTypeInvalid;
LookupAccountNameW(szMachine,szUser,NULL,&UserSidSize,NULL,&DomainSize,NULL);
if(!UserSidSize || !DomainSize)
return false;
*pUserSid=(PSID)malloc(UserSidSize);
szDomain=(wchar_t*)malloc(DomainSize*sizeof(wchar_t));
if(!LookupAccountNameW(szMachine,szUser,*pUserSid,&UserSidSize,szDomain,&DomainSize,Use))
{
free(szDomain);
free(pUserSid);
return false;
}
free(szDomain);
return true;
}
void nt_setuid_init()
{
HMODULE hNtDll;
hNtDll=LoadLibrary(_T("ntdll.dll"));
NtCreateToken = (NtCreateToken_t)GetProcAddress(hNtDll,"NtCreateToken");
}
static bool GetPrimaryGroup (LPCWSTR wszMachine, LPCWSTR wszUser, PSID UserSid, PSID* PrimarySid)
{
LPUSER_INFO_3 buf;
bool ret = false;
UCHAR count;
if (NetUserGetInfo (wszMachine, wszUser, 3, (LPBYTE *) &buf))
return false;
*PrimarySid=malloc(_msize(UserSid));
CopySid(_msize(UserSid),*PrimarySid,UserSid);
if (IsValidSid (*PrimarySid) && (count = *GetSidSubAuthorityCount (*PrimarySid)) > 1)
{
*GetSidSubAuthority (*PrimarySid, count - 1) = buf->usri3_primary_group_id;
ret = true;
}
NetApiBufferFree (buf);
return ret;
}
static bool GetDacl(PACL* pAcl, PSID UserSid, PTOKEN_GROUPS Groups)
{
int n;
*pAcl = (PACL)malloc(4096);
if (!InitializeAcl(*pAcl, 4096, ACL_REVISION))
{
free(*pAcl);
return false;
}
for(n=0; n<(int)Groups->GroupCount; n++)
{
if(EqualSid(Groups->Groups[n].Sid,&sidAdministrators))
{
if (!AddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, &sidAdministrators))
{
free(*pAcl);
return false;
}
break;
}
}
if (!AddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, UserSid))
{
free(*pAcl);
return false;
}
if (!AddAccessAllowedAce(*pAcl, ACL_REVISION, GENERIC_ALL, &sidSystem))
{
free(*pAcl);
return false;
}
return true;
}
static bool GetAuthLuid(LUID* pLuid)
{
HANDLE hToken;
TOKEN_STATISTICS stats;
DWORD size;
if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &hToken))
return false;
if (!GetTokenInformation (hToken, TokenStatistics, &stats, sizeof stats, &size))
return false;
*pLuid=stats.AuthenticationId;
return true;
}
int nt_setuid(LPCWSTR wszMachine, LPCWSTR wszUser, HANDLE *phToken)
{
int retval = -1;
PSID UserSid = NULL, pTmpSid;
LPGROUP_USERS_INFO_0 GlobalGroups = NULL;
LPGROUP_USERS_INFO_0 LocalGroups = NULL;
DWORD NumGlobalGroups=0,TotalGlobalGroups=0;
DWORD NumLocalGroups=0,TotalLocalGroups=0;
TOKEN_USER _TokenUser = {0};
TOKEN_OWNER _TokenOwner = {0};
PTOKEN_GROUPS pTokenGroups = NULL;
PTOKEN_PRIVILEGES TokenPrivs = NULL;
TOKEN_PRIMARY_GROUP _TokenPrimaryGroup = {0};
TOKEN_SOURCE _TokenSource = {0};
TOKEN_DEFAULT_DACL TokenDacl = {0};
wchar_t grName[256];
int n,j,p,q;
SID_NAME_USE Use;
LSA_OBJECT_ATTRIBUTES lsa = { sizeof(LSA_OBJECT_ATTRIBUTES) };
LSA_HANDLE hLsa=NULL;
LSA_UNICODE_STRING lsaMachine;
PLSA_UNICODE_STRING lsaUserRights;
DWORD NumUserRights;
LUID AuthLuid;
HANDLE hToken = INVALID_HANDLE_VALUE;
HANDLE hPrimaryToken = INVALID_HANDLE_VALUE;
HANDLE hProcessToken = INVALID_HANDLE_VALUE;
DWORD err;
SECURITY_QUALITY_OF_SERVICE sqos =
{ sizeof sqos, SecurityImpersonation, SECURITY_STATIC_TRACKING, FALSE };
LSA_OBJECT_ATTRIBUTES oa =
{ sizeof oa, 0, 0, 0, 0, &sqos };
SECURITY_ATTRIBUTES sa = { sizeof sa, NULL, TRUE };
LARGE_INTEGER exp = { 0xffffffff,0x7fffffff } ;
PTOKEN_PRIVILEGES NewToken;
DWORD NewTokenLength;
LUID TempLuid;
/* If init failed, or not called, then exit immediately */
if(!NtCreateToken)
return -1;
if(wszMachine)
{
lsaMachine.Length=wcslen(wszMachine)*sizeof(wchar_t);
lsaMachine.MaximumLength=lsaMachine.Length+sizeof(wchar_t);
lsaMachine.Buffer=(wchar_t*)wszMachine;
}
if(!LookupPrivilegeValue(NULL,SE_CREATE_TOKEN_NAME,&TempLuid))
{
goto nt_setuid_out;
}
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hProcessToken))
{
goto nt_setuid_out;
}
GetTokenInformation(hProcessToken,TokenPrivileges,NULL,0,&NewTokenLength);
NewToken=(PTOKEN_PRIVILEGES)malloc(NewTokenLength);
GetTokenInformation(hProcessToken,TokenPrivileges,NewToken,NewTokenLength,&NewTokenLength);
for(n=0; n<(int)NewToken->PrivilegeCount; n++)
{
if(!memcmp(&NewToken->Privileges[n].Luid,&TempLuid,sizeof(LUID)))
{
NewToken->Privileges[n].Attributes=SE_PRIVILEGE_ENABLED;
break;
}
}
if(n==(int)NewToken->PrivilegeCount)
{
goto nt_setuid_out;
}
if(!AdjustTokenPrivileges(hProcessToken,FALSE,NewToken,0,0,NULL))
{
goto nt_setuid_out;
}
if((err=LsaOpenPolicy(wszMachine?&lsaMachine:NULL,&lsa,POLICY_EXECUTE,&hLsa))!=ERROR_SUCCESS)
{
err=LsaNtStatusToWinError(err);
goto nt_setuid_out;
}
/* From Q185246 - username = domain name */
wsprintfW(grName,L"%s\\%s",wszUser,wszUser); // Used for domain/user clashes
/* Search on the specified PDC, then on the local domain, for the user.
This allows for trusted domains to work */
if((!LookupSid(wszMachine,wszUser,&UserSid,&Use) || Use!=SidTypeUser) &&
(!LookupSid(wszMachine,grName,&UserSid,&Use) || Use!=SidTypeUser) &&
(!LookupSid(NULL,wszUser,&UserSid,&Use) || Use!=SidTypeUser) &&
(!LookupSid(NULL,grName,&UserSid,&Use) || Use!=SidTypeUser))
{
goto nt_setuid_out;
}
_TokenOwner.Owner=UserSid;
_TokenUser.User.Attributes=0;
_TokenUser.User.Sid=UserSid;
NetUserGetGroups(wszMachine,wszUser,0,(LPBYTE*)&GlobalGroups,MAX_PREFERRED_LENGTH,&NumGlobalGroups,&TotalGlobalGroups);
NetUserGetLocalGroups(wszMachine,wszUser,0,0,(LPBYTE*)&LocalGroups,MAX_PREFERRED_LENGTH,&NumLocalGroups,&TotalLocalGroups);
pTokenGroups = (PTOKEN_GROUPS)malloc(sizeof(TOKEN_GROUPS)+sizeof(SID_AND_ATTRIBUTES)*(NumGlobalGroups + NumLocalGroups + NumGlobalGroups + 5));
pTokenGroups->GroupCount = NumGlobalGroups + NumLocalGroups + 5;
for(n=0,j=0; n<(int)NumGlobalGroups; n++)
{
if(LookupSid(wszMachine,GlobalGroups[n].grui0_name,&pTmpSid,&Use))
{
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT;
pTokenGroups->Groups[j].Sid=pTmpSid;
j++;
}
}
for(n=0; n<(int)NumLocalGroups; n++)
{
if(LookupSid(wszMachine,LocalGroups[n].grui0_name,&pTmpSid,&Use))
{
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=pTmpSid;
j++;
}
}
if(!GetAuthLuid(&AuthLuid))
{
goto nt_setuid_out;
}
if(!GetPrimaryGroup(wszMachine,wszUser,UserSid,&_TokenPrimaryGroup.PrimaryGroup))
{
goto nt_setuid_out;
}
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=&sidLocal;
j++;
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=&sidInteractive;
j++;
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=&sidAuthenticated;
j++;
pTokenGroups->Groups[j].Attributes=SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=&sidEveryone;
j++;
{
PSID pUserSid;
SID_IDENTIFIER_AUTHORITY nt = SECURITY_NT_AUTHORITY;
AllocateAndInitializeSid(&nt,3,SECURITY_LOGON_IDS_RID,AuthLuid.HighPart,AuthLuid.LowPart,0,0,0,0,0,&pUserSid);
pTokenGroups->Groups[j].Attributes=SE_GROUP_LOGON_ID|SE_GROUP_ENABLED|SE_GROUP_ENABLED_BY_DEFAULT|SE_GROUP_MANDATORY;
pTokenGroups->Groups[j].Sid=pUserSid;
j++;
}
TokenPrivs=(PTOKEN_PRIVILEGES)calloc(1,sizeof(TOKEN_PRIVILEGES));
j=0; NumUserRights=0;
if(LsaEnumerateAccountRights(hLsa,UserSid,&lsaUserRights,&NumUserRights)==ERROR_SUCCESS)
{
TokenPrivs->PrivilegeCount=NumUserRights;
TokenPrivs=(PTOKEN_PRIVILEGES)realloc(TokenPrivs,sizeof(TOKEN_PRIVILEGES)+sizeof(LUID_AND_ATTRIBUTES)*TokenPrivs->PrivilegeCount);
for(n=0,j=0; n<(int)NumUserRights; n++)
{
TokenPrivs->Privileges[j].Attributes=SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
LookupPrivilegeValueW(wszMachine,lsaUserRights->Buffer,&TokenPrivs->Privileges[j].Luid);
j++;
}
NetApiBufferFree(lsaUserRights);
}
TokenPrivs->PrivilegeCount=j;
for(n=0; n<(int)pTokenGroups->GroupCount; n++)
{
if(LsaEnumerateAccountRights(hLsa,pTokenGroups->Groups[n].Sid,&lsaUserRights,&NumUserRights)==ERROR_SUCCESS)
{
TokenPrivs->PrivilegeCount+=NumUserRights;
TokenPrivs=(PTOKEN_PRIVILEGES)realloc(TokenPrivs,sizeof(TOKEN_PRIVILEGES)+sizeof(LUID_AND_ATTRIBUTES)*TokenPrivs->PrivilegeCount);
for(p=0; p<(int)NumUserRights; p++)
{
LUID luid;
if(!wcscmp(lsaUserRights[p].Buffer,L"SeAssignPrimaryTokenPrivilege") ||
!wcscmp(lsaUserRights[p].Buffer,L"SeUndockPrivilege") ||
!wcscmp(lsaUserRights[p].Buffer,L"SeEnableDelegationPrivilege"))
continue; /* NTCreateToken will barf if these are set */
if(!LookupPrivilegeValueW(wszMachine,lsaUserRights[p].Buffer,&luid))
continue;
for(q=0; q<j; q++)
if(!memcmp(&luid,&TokenPrivs->Privileges[q].Luid,sizeof(luid)))
break;
if(q==j)
{
TokenPrivs->Privileges[j].Attributes=SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT;
TokenPrivs->Privileges[j].Luid=luid;
j++;
}
}
NetApiBufferFree(lsaUserRights);
}
}
TokenPrivs->PrivilegeCount=j;
TokenPrivs=(PTOKEN_PRIVILEGES)realloc(TokenPrivs,sizeof(TOKEN_PRIVILEGES)+sizeof(LUID_AND_ATTRIBUTES)*TokenPrivs->PrivilegeCount);
memset(&_TokenSource,0,sizeof(_TokenSource));
strcpy(_TokenSource.SourceName,"cvsnt");
_TokenSource.SourceIdentifier.HighPart = 0;
_TokenSource.SourceIdentifier.LowPart = 0x0101;
if(!GetDacl(&TokenDacl.DefaultDacl,UserSid,pTokenGroups))
{
goto nt_setuid_out;
}
if((err=NtCreateToken (&hToken, TOKEN_ALL_ACCESS, &oa, TokenImpersonation,
&AuthLuid, &exp, &_TokenUser, pTokenGroups, TokenPrivs, &_TokenOwner, &_TokenPrimaryGroup,
&TokenDacl, &_TokenSource))!=ERROR_SUCCESS)
{
err=LsaNtStatusToWinError(err);
goto nt_setuid_out;
}
if(!DuplicateTokenEx(hToken,TOKEN_ALL_ACCESS,&sa,SecurityImpersonation,TokenImpersonation,&hPrimaryToken))
{
goto nt_setuid_out;
}
if(phToken)
*phToken = hPrimaryToken;
else
CloseHandle(hPrimaryToken);
retval = 0;
nt_setuid_out:
free(UserSid);
NetApiBufferFree(GlobalGroups);
NetApiBufferFree(LocalGroups);
free(pTokenGroups);
free(TokenPrivs);
free(_TokenPrimaryGroup.PrimaryGroup);
free(NewToken);
if(hToken!=INVALID_HANDLE_VALUE)
CloseHandle(hToken);
if(hProcessToken!=INVALID_HANDLE_VALUE)
CloseHandle(hProcessToken);
free(TokenDacl.DefaultDacl);
LsaClose(hLsa);
return retval;
}
/* S4U call on Win2k3 */
/* XP actually implements this but drops out early in the processing
because it's in workstation mode not server */
/* Also for this to succeed your PDC needs to be a Win2k3 machine */
int nt_s4u(LPCWSTR wszDomain, LPCWSTR wszUser, HANDLE *phToken)
{
DWORD err;
struct
{
KERB_S4U_LOGON s4uLogon;
WCHAR Upn[UNLEN+DNLEN+2];
} Buf = { { KerbS4ULogon } };
LSA_STRING Origin = { 4,5, "CVSNT" };
TOKEN_SOURCE Source = { "CVSNT", 0, 101 };
LUID Luid, TempLuid;
NTSTATUS SubStatus;
BYTE* Profile;
ULONG ProfileLength;
HANDLE hLsa, hProcessToken;
LSA_OPERATIONAL_MODE Mode;
PTOKEN_PRIVILEGES NewToken;
ULONG NewTokenLength;
int n;
QUOTA_LIMITS Quotas;
TOKEN_GROUPS Tok = {0};
/* Enable seTcbName */
if(!LookupPrivilegeValue(NULL,SE_TCB_NAME,&TempLuid))
return GetLastError();
if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hProcessToken))
return GetLastError();
GetTokenInformation(hProcessToken,TokenPrivileges,NULL,0,&NewTokenLength);
NewToken=(PTOKEN_PRIVILEGES)malloc(NewTokenLength);
GetTokenInformation(hProcessToken,TokenPrivileges,NewToken,NewTokenLength,&NewTokenLength);
for(n=0; n<(int)NewToken->PrivilegeCount; n++)
{
if(!memcmp(&NewToken->Privileges[n].Luid,&TempLuid,sizeof(LUID)))
{
NewToken->Privileges[n].Attributes=SE_PRIVILEGE_ENABLED;
break;
}
}
if(n==(int)NewToken->PrivilegeCount)
{
CloseHandle(hProcessToken);
return ERROR_ACCESS_DENIED;
}
if(!AdjustTokenPrivileges(hProcessToken,FALSE,NewToken,0,0,NULL))
{
CloseHandle(hProcessToken);
return GetLastError();
}
CloseHandle(hProcessToken);
free(NewToken);
/* Logon */
err = LsaRegisterLogonProcess(&Origin, &hLsa, &Mode);
if(err)
return LsaNtStatusToWinError(err);
try
{
ActiveDs::IADsNameTranslatePtr info(CLSID_NameTranslate);
wsprintfW(Buf.Upn,L"%s\\%s",wszDomain,wszUser);
TRACE(3,"S4U untranslated name: %S",Buf.Upn);
info->Init(ADS_NAME_INITTYPE_GC,L"");
info->Set(ADS_NAME_TYPE_NT4,Buf.Upn);
lstrcpyW(Buf.Upn,info->Get(ADS_NAME_TYPE_USER_PRINCIPAL_NAME));
TRACE(3,"S4U UPN: %S",Buf.Upn);
}
catch(_com_error e)
{
TRACE(3,"IADS query failed: %S",e.ErrorMessage());
return ERROR_INVALID_FUNCTION;
}
Buf.s4uLogon.ClientUpn.Buffer=Buf.Upn;
Buf.s4uLogon.ClientUpn.MaximumLength=Buf.s4uLogon.ClientUpn.Length=wcslen(Buf.Upn)*sizeof(wchar_t);
err = LsaLogonUser(hLsa, &Origin, Network, LOGON32_PROVIDER_DEFAULT, &Buf, sizeof(Buf), &Tok, &Source, (PVOID*)&Profile, &ProfileLength, &Luid, phToken, &Quotas, &SubStatus);
LsaDeregisterLogonProcess(hLsa);
TRACE(3,"S4U: LsaLogonUser returned %08x (%08x)",err,LsaNtStatusToWinError(err));
return LsaNtStatusToWinError(err);
}
syntax highlighted by Code2HTML, v. 0.9.1