SERVER.C
资源名称:MSDN_VC98.zip [点击查看]
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:16k
源码类别:
Windows编程
开发平台:
Visual C++
- /*++
- Copyright 1996 - 1997 Microsoft Corporation
- Module Name:
- server.c
- Abstract:
- This sample illustrates the new DuplicateTokenEx() API for Windows NT 4.0.
- This portion of the sample illustrates how to implement a remote cmd
- server which launches cmd.exe in the security context of the client that
- connected to the server.
- When a client connects to the named-pipe, the server process impersonates
- the client and then saves a copy of the impersonation token. This token
- is then duplicated via DuplicateTokenEx() to a primary level access token.
- The primary level token is then supplied to CreateProcessAsUser(), which
- launches a process in the security context of the client. All threads in
- the new process inherit the security context of the client. All processes
- created by the new process inherit the security context of the client. The
- input and output of the new process will be redirected to the client over
- the named-pipes, which provides a means of interactively executing commands
- remotely in a secure manner.
- In order to allow processes to run in a different security context than
- the server process, it is necessary to adjust the security on the
- windowstation and desktop objects associated with the server process.
- This is required to allow for proper console initialization.
- For simplicity, this sample applies a Null Dacl to the windowstation and
- desktop objects, which is generally not appropriate in a production
- environment. The best approach to selectively secure the windowstation and
- desktop objects is to extract the Logon Sid from the access token returned
- by DuplicateTokenEx(). The Logon Sid can be extracted with
- GetTokenInformation() and then added to an access allowed ace which is
- supplied in a Dacl which contains the existing access allowed aces in
- addition to the new access allowed ace.
- Some additional considerations with this sample follow:
- * The account which the server process runs in needs the following two
- privileges granted, to allow for the call to CreateProcessAsUser() to
- succeed:
- SeAssignPrimaryTokenPrivilege (Replace a process level token)
- SeIncreateQuotaPrivilege (Increase quotas)
- * The security on the initial server side named-pipe allows for Everyone
- to connect. This may present security problems in the event that the
- server's file system has not be secured appropriately. In a production
- environment, it may be appropriate to apply a more restrictive Dacl on
- the initial named-pipe.
- * This sample only allows one client to be connected at a time.
- * Any child processes started by the launched process will not be terminated
- when the initial child process exits.
- Author:
- Scott Field (sfield) 02-Apr-96
- --*/
- #include <windows.h>
- #include <stdio.h>
- BOOL
- BuildNamedPipeAcl(
- PACL pAcl,
- PDWORD cbAclSize
- );
- BOOL
- SetupNamedPipes(
- PSECURITY_ATTRIBUTES saInbound,
- PSECURITY_ATTRIBUTES saOutbound,
- PHANDLE hFileIn,
- PHANDLE hFileOut
- );
- BOOL
- SetWinstaDesktopSecurity(
- void
- );
- void
- DisplayLastError(
- LPSTR szAPI // pointer to Ansi function name
- );
- //
- // this defines the commandline that the server will execute in the security
- // context of the client. The /Q switch tells cmd.exe not to echo the
- // entered commands back to the client.
- //
- #define COMMANDLINE TEXT("cmd.exe /Q")
- #define INBOUND_PIPE TEXT("\\.\pipe\rcmd_in")
- #define OUTBOUND_PIPE TEXT("\\.\pipe\rcmd_out")
- #define RTN_OK 0
- #define RTN_ERROR 13
- int
- __cdecl
- main(
- void
- )
- {
- HANDLE hPipeInbound;
- HANDLE hPipeOutbound;
- SECURITY_ATTRIBUTES saInbound;
- SECURITY_DESCRIPTOR sd;
- BYTE AclBuf[ 64 ];
- DWORD cbAclSize = 64;
- PACL pAcl = (PACL)AclBuf;
- SECURITY_ATTRIBUTES saOutbound;
- HANDLE hImpersonationToken;
- HANDLE hPrimaryToken;
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- //
- // suppress errors regarding startup directory, etc
- //
- SetErrorMode(SEM_FAILCRITICALERRORS);
- //
- // build security attributes for named-pipes
- //
- // the inbound pipe will grant Everyone the ability to connect,
- // and the server process full control over the pipe. This way,
- // only the server process has the ability to update security
- // on the resource; clients can only connect.
- //
- // the outbound pipe will be built after the server impersonates
- // the client, and will inherit the default security of the client
- // as a result. This allows only the same user that connected to
- // the inbound pipe to connect to the outbound pipe
- //
- if(!BuildNamedPipeAcl(pAcl, &cbAclSize)) {
- DisplayLastError("BuildNamedPipeAcl");
- return RTN_ERROR;
- }
- if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
- DisplayLastError("InitializeSecurityDescriptor");
- return RTN_ERROR;
- }
- if(!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
- DisplayLastError("SetSecurityDescriptorDacl");
- return RTN_ERROR;
- }
- saInbound.nLength = sizeof(SECURITY_ATTRIBUTES);
- saInbound.lpSecurityDescriptor = &sd;
- saInbound.bInheritHandle = TRUE;
- saOutbound.nLength = sizeof(SECURITY_ATTRIBUTES);
- saOutbound.lpSecurityDescriptor = NULL; // default Dacl of caller
- saOutbound.bInheritHandle = TRUE;
- //
- // modify security on windowstation and desktop to allow for correct
- // process and console initialization by arbitrary clients
- //
- if(!SetWinstaDesktopSecurity()) {
- DisplayLastError("SetWinstaDesktopSecurity");
- return RTN_ERROR;
- }
- while(1) {
- hPipeInbound = INVALID_HANDLE_VALUE;
- hPipeOutbound = INVALID_HANDLE_VALUE;
- hImpersonationToken = INVALID_HANDLE_VALUE;
- hPrimaryToken = INVALID_HANDLE_VALUE;
- pi.hProcess = INVALID_HANDLE_VALUE;
- pi.hThread = INVALID_HANDLE_VALUE;
- if(!SetupNamedPipes(
- &saInbound,
- &saOutbound,
- &hPipeInbound,
- &hPipeOutbound
- )) {
- //
- // just bail out on failure for now
- //
- DisplayLastError("SetupNamedPipes");
- break;
- }
- //
- // obtain the impersonation token from the current thread
- //
- if(!OpenThreadToken(
- GetCurrentThread(),
- TOKEN_DUPLICATE,
- TRUE,
- &hImpersonationToken
- )) {
- DisplayLastError("OpenThreadToken");
- goto cleanup;
- }
- //
- // duplicate the impersonation token to primary
- // since we are impersonating the client, the token will get the
- // default Dacl of the client
- //
- if(!DuplicateTokenEx(
- hImpersonationToken,
- TOKEN_IMPERSONATE | TOKEN_READ |
- TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
- NULL,
- SecurityImpersonation,
- TokenPrimary,
- &hPrimaryToken
- )) {
- DisplayLastError("DuplicateTokenEx");
- goto cleanup;
- }
- RevertToSelf();
- CloseHandle(hImpersonationToken);
- hImpersonationToken = INVALID_HANDLE_VALUE;
- //
- // setup STARTUPINFO structure
- //
- si.cb = sizeof(STARTUPINFO);
- si.lpReserved = NULL;
- si.lpDesktop = NULL;
- si.lpTitle = NULL;
- si.cbReserved2 = 0;
- si.lpReserved2 = NULL;
- si.dwFlags = STARTF_USESTDHANDLES;
- si.hStdOutput = hPipeOutbound;
- si.hStdError = hPipeOutbound;
- si.hStdInput = hPipeInbound;
- //
- // create a process running as the user
- //
- if(!CreateProcessAsUser(
- hPrimaryToken,
- NULL,
- COMMANDLINE, // commandline to execute
- NULL, // process sa
- NULL, // thread sa
- TRUE, // inherit handles?
- 0, // process creation flags (inherit existing console)
- NULL, // environment
- NULL, // current directory
- &si, // startupinfo
- &pi // processinfo
- )) {
- DisplayLastError("CreateProcessAsUser");
- goto cleanup;
- }
- cleanup:
- if(hImpersonationToken != INVALID_HANDLE_VALUE) {
- RevertToSelf();
- CloseHandle(hImpersonationToken);
- }
- if(hPrimaryToken != INVALID_HANDLE_VALUE) {
- CloseHandle(hPrimaryToken);
- }
- if(pi.hThread != INVALID_HANDLE_VALUE) {
- CloseHandle(pi.hThread);
- }
- if(pi.hProcess != INVALID_HANDLE_VALUE) {
- WaitForSingleObject(pi.hProcess, INFINITE);
- CloseHandle(pi.hProcess);
- //
- // TODO kill any child by enumerating process tokens in the system
- // by looking at the tokenID
- //
- }
- if(hPipeInbound != INVALID_HANDLE_VALUE) {
- DisconnectNamedPipe(hPipeInbound);
- CloseHandle(hPipeInbound);
- }
- if(hPipeOutbound != INVALID_HANDLE_VALUE) {
- DisconnectNamedPipe(hPipeOutbound);
- CloseHandle(hPipeOutbound);
- }
- } // while
- return RTN_OK;
- }
- /***
- If the function succeeds, the return value is TRUE. The parameters
- hFileIn and hFileOut will point to the Inbound and Outbound named-pipe
- handles. The current thread will be impersonating the client.
- If the function fails, the return value is FALSE. The current thread will
- not be impersonating the client.
- ***/
- BOOL
- SetupNamedPipes(
- PSECURITY_ATTRIBUTES saInbound, // security attributes for Inbound pipe
- PSECURITY_ATTRIBUTES saOutbound,// security attributes for Outbound pipe
- PHANDLE hFileIn, // resultant Inbound pipe handle on success
- PHANDLE hFileOut // resultant Outbound pipe handle on success
- )
- {
- HANDLE hPipeInbound = INVALID_HANDLE_VALUE;
- HANDLE hPipeOutbound = INVALID_HANDLE_VALUE;
- BOOL bSuccess = FALSE; // assume this function fails
- //
- // create Inbound named-pipe
- //
- hPipeInbound = CreateNamedPipe(
- INBOUND_PIPE,
- PIPE_ACCESS_INBOUND,
- PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
- 1, // number of instances
- 4096,
- 4096,
- NMPWAIT_USE_DEFAULT_WAIT,
- saInbound
- );
- if(hPipeInbound == INVALID_HANDLE_VALUE) {
- DisplayLastError("CreateNamedPipe");
- return FALSE;
- }
- printf("Waiting for connection... ");
- //
- // wait for somebody to connect to the Inbound named pipe
- //
- ConnectNamedPipe(hPipeInbound, NULL);
- //
- // impersonate the client
- //
- if(!ImpersonateNamedPipeClient(hPipeInbound)) {
- DisplayLastError("ImpersonateNamedPipeClient");
- goto cleanup;
- }
- //
- // create Outbound named-pipe
- // the security on this named-pipe will be inherited from the default Dacl
- // of the client that just connected, because this pipe instance is
- // created in that users security context. This prevents other users
- // from connecting to the Outbound pipe.
- //
- hPipeOutbound = CreateNamedPipe(
- OUTBOUND_PIPE,
- PIPE_ACCESS_OUTBOUND,
- PIPE_TYPE_MESSAGE | PIPE_WAIT,
- 1, // number of instances
- 4096,
- 4096,
- NMPWAIT_USE_DEFAULT_WAIT,
- saOutbound
- );
- if(hPipeOutbound == INVALID_HANDLE_VALUE) {
- DisplayLastError("CreateNamedPipe");
- goto cleanup;
- }
- //
- // wait for the client to connect to the Outbound named pipe
- //
- ConnectNamedPipe(hPipeOutbound, NULL);
- printf("Client connected!n");
- *hFileIn = hPipeInbound;
- *hFileOut = hPipeOutbound;
- bSuccess = TRUE;
- cleanup:
- if(!bSuccess) {
- RevertToSelf();
- if(hPipeInbound != INVALID_HANDLE_VALUE)
- CloseHandle(hPipeInbound);
- if(hPipeOutbound != INVALID_HANDLE_VALUE)
- CloseHandle(hPipeOutbound);
- }
- return bSuccess;
- }
- /**
- This function builds a Dacl which grants the creator of the objects
- FILE_ALL_ACCESS and Everyone FILE_GENERIC_READ and FILE_GENERIC_WRITE
- access to the object.
- This Dacl allows for higher security than a NULL Dacl, which is common for
- named-pipes, as this only grants the creator/owner write access to the
- security descriptor, and grants Everyone the ability to "use" the named-pipe.
- This scenario prevents a malevolent user from disrupting service by preventing
- arbitrary access manipulation.
- **/
- BOOL
- BuildNamedPipeAcl(
- PACL pAcl,
- PDWORD cbAclSize
- )
- {
- DWORD dwAclSize;
- SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
- SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
- BYTE BufEveryoneSid[32];
- BYTE BufOwnerSid[32];
- PSID pEveryoneSid = (PSID)BufEveryoneSid;
- PSID pOwnerSid = (PSID)BufOwnerSid;
- //
- // compute size of acl
- //
- dwAclSize = sizeof(ACL) +
- 2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
- GetSidLengthRequired( 1 ) + // well-known Everyone Sid
- GetSidLengthRequired( 1 ) ; // well-known Creator Owner Sid
- if(*cbAclSize < dwAclSize) {
- *cbAclSize = dwAclSize;
- return FALSE;
- }
- *cbAclSize = dwAclSize;
- //
- // intialize well known sids
- //
- if(!InitializeSid(pEveryoneSid, &siaWorld, 1)) return FALSE;
- *GetSidSubAuthority(pEveryoneSid, 0) = SECURITY_WORLD_RID;
- if(!InitializeSid(pOwnerSid, &siaCreator, 1)) return FALSE;
- *GetSidSubAuthority(pOwnerSid, 0) = SECURITY_CREATOR_OWNER_RID;
- if(!InitializeAcl(pAcl, dwAclSize, ACL_REVISION))
- return FALSE;
- //
- //
- if(!AddAccessAllowedAce(
- pAcl,
- ACL_REVISION,
- FILE_GENERIC_READ | FILE_GENERIC_WRITE,
- pEveryoneSid
- ))
- return FALSE;
- //
- //
- return AddAccessAllowedAce(
- pAcl,
- ACL_REVISION,
- FILE_ALL_ACCESS,
- pOwnerSid
- );
- }
- /**
- This function adjusts the security on the current windowstation and desktop,
- to allow arbitrary client to have access to the windowstation and desktop.
- This is necessary to allow for correct process and console initialization.
- Note that in a secure environment, it would be appropriate to create
- a specific desktop and launch the process spawned by the client on that
- desktop, rather than opening up the current desktop to the client.
- This function simply applies a NULL Dacl to the current windowstation and
- desktop in order to reduce the size of this sample. Applying NULL Dacls
- to objects is generally not a wise idea.
- TODO revist this function later to better address security on the windowstation
- and desktop.
- **/
- BOOL
- SetWinstaDesktopSecurity(
- void
- )
- {
- HWINSTA hWinsta;
- HDESK hDesk;
- SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
- SECURITY_DESCRIPTOR sd;
- hWinsta = GetProcessWindowStation();
- if(hWinsta == NULL) return FALSE;
- hDesk = GetThreadDesktop(GetCurrentThreadId());
- if(hDesk == NULL) return FALSE;
- InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE);
- if(!SetUserObjectSecurity(hWinsta, &si, &sd)) return FALSE;
- return SetUserObjectSecurity(hDesk, &si, &sd);
- }
- void
- DisplayLastError(
- LPSTR szAPI // pointer to Ansi function name
- )
- {
- LPSTR MessageBuffer;
- DWORD dwBufferLength;
- //
- // TODO get this fprintf out of here!
- //
- fprintf(stderr,"%s error!n", szAPI);
- if(dwBufferLength=FormatMessageA(
- FORMAT_MESSAGE_ALLOCATE_BUFFER |
- FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,
- GetLastError(),
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPSTR) &MessageBuffer,
- 0,
- NULL
- ))
- {
- DWORD dwBytesWritten; // unused
- //
- // Output message string on stderr
- //
- WriteFile(
- GetStdHandle(STD_ERROR_HANDLE),
- MessageBuffer,
- dwBufferLength,
- &dwBytesWritten,
- NULL
- );
- //
- // free the buffer allocated by the system
- //
- LocalFree(MessageBuffer);
- }
- }