SERVER.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:16k
源码类别:

Windows编程

开发平台:

Visual C++

  1. /*++
  2. Copyright 1996 - 1997 Microsoft Corporation
  3. Module Name:
  4.     server.c
  5. Abstract:
  6.     This sample illustrates the new DuplicateTokenEx() API for Windows NT 4.0.
  7.     This portion of the sample illustrates how to implement a remote cmd
  8.     server which launches cmd.exe in the security context of the client that
  9.     connected to the server.
  10.     When a client connects to the named-pipe, the server process impersonates
  11.     the client and then saves a copy of the impersonation token.  This token
  12.     is then duplicated via DuplicateTokenEx() to a primary level access token.
  13.     The primary level token is then supplied to CreateProcessAsUser(), which
  14.     launches a process in the security context of the client.  All threads in
  15.     the new process inherit the security context of the client.  All processes
  16.     created by the new process inherit the security context of the client.  The
  17.     input and output of the new process will be redirected to the client over
  18.     the named-pipes, which provides a means of interactively executing commands
  19.     remotely in a secure manner.
  20.     In order to allow processes to run in a different security context than
  21.     the server process, it is necessary to adjust the security on the
  22.     windowstation and desktop objects associated with the server process.
  23.     This is required to allow for proper console initialization.
  24.     For simplicity, this sample applies a Null Dacl to the windowstation and
  25.     desktop objects, which is generally not appropriate in a production
  26.     environment.  The best approach to selectively secure the windowstation and
  27.     desktop objects is to extract the Logon Sid from the access token returned
  28.     by DuplicateTokenEx().  The Logon Sid can be extracted with
  29.     GetTokenInformation() and then added to an access allowed ace which is
  30.     supplied in a Dacl which contains the existing access allowed aces in
  31.     addition to the new access allowed ace.
  32.     Some additional considerations with this sample follow:
  33.     * The account which the server process runs in needs the following two
  34.       privileges granted, to allow for the call to CreateProcessAsUser() to
  35.       succeed:
  36.       SeAssignPrimaryTokenPrivilege (Replace a process level token)
  37.       SeIncreateQuotaPrivilege (Increase quotas)
  38.     * The security on the initial server side named-pipe allows for Everyone
  39.       to connect.  This may present security problems in the event that the
  40.       server's file system has not be secured appropriately.  In a production
  41.       environment, it may be appropriate to apply a more restrictive Dacl on
  42.       the initial named-pipe.
  43.     * This sample only allows one client to be connected at a time.
  44.     * Any child processes started by the launched process will not be terminated
  45.       when the initial child process exits.
  46. Author:
  47.     Scott Field (sfield)    02-Apr-96
  48. --*/
  49. #include <windows.h>
  50. #include <stdio.h>
  51. BOOL
  52. BuildNamedPipeAcl(
  53.     PACL pAcl,
  54.     PDWORD cbAclSize
  55.     );
  56. BOOL
  57. SetupNamedPipes(
  58.     PSECURITY_ATTRIBUTES saInbound,
  59.     PSECURITY_ATTRIBUTES saOutbound,
  60.     PHANDLE hFileIn,
  61.     PHANDLE hFileOut
  62.     );
  63. BOOL
  64. SetWinstaDesktopSecurity(
  65.     void
  66.     );
  67. void
  68. DisplayLastError(
  69.     LPSTR szAPI // pointer to Ansi function name
  70.     );
  71. //
  72. // this defines the commandline that the server will execute in the security
  73. // context of the client.  The /Q switch tells cmd.exe not to echo the
  74. // entered commands back to the client.
  75. //
  76. #define COMMANDLINE     TEXT("cmd.exe /Q")
  77. #define INBOUND_PIPE    TEXT("\\.\pipe\rcmd_in")
  78. #define OUTBOUND_PIPE   TEXT("\\.\pipe\rcmd_out")
  79. #define RTN_OK 0
  80. #define RTN_ERROR 13
  81. int
  82. __cdecl
  83. main(
  84.     void
  85.     )
  86. {
  87.     HANDLE hPipeInbound;
  88.     HANDLE hPipeOutbound;
  89.     SECURITY_ATTRIBUTES saInbound;
  90.     SECURITY_DESCRIPTOR sd;
  91.     BYTE AclBuf[ 64 ];
  92.     DWORD cbAclSize = 64;
  93.     PACL pAcl = (PACL)AclBuf;
  94.     SECURITY_ATTRIBUTES saOutbound;
  95.     HANDLE hImpersonationToken;
  96.     HANDLE hPrimaryToken;
  97.     STARTUPINFO si;
  98.     PROCESS_INFORMATION pi;
  99.     //
  100.     // suppress errors regarding startup directory, etc
  101.     //
  102.     SetErrorMode(SEM_FAILCRITICALERRORS);
  103.     //
  104.     // build security attributes for named-pipes
  105.     //
  106.     // the inbound pipe will grant Everyone the ability to connect,
  107.     // and the server process full control over the pipe.  This way,
  108.     // only the server process has the ability to update security
  109.     // on the resource; clients can only connect.
  110.     //
  111.     // the outbound pipe will be built after the server impersonates
  112.     // the client, and will inherit the default security of the client
  113.     // as a result.  This allows only the same user that connected to
  114.     // the inbound pipe to connect to the outbound pipe
  115.     //
  116.     if(!BuildNamedPipeAcl(pAcl, &cbAclSize)) {
  117.         DisplayLastError("BuildNamedPipeAcl");
  118.         return RTN_ERROR;
  119.     }
  120.     if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
  121.         DisplayLastError("InitializeSecurityDescriptor");
  122.         return RTN_ERROR;
  123.     }
  124.     if(!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
  125.         DisplayLastError("SetSecurityDescriptorDacl");
  126.         return RTN_ERROR;
  127.     }
  128.     saInbound.nLength = sizeof(SECURITY_ATTRIBUTES);
  129.     saInbound.lpSecurityDescriptor = &sd;
  130.     saInbound.bInheritHandle = TRUE;
  131.     saOutbound.nLength = sizeof(SECURITY_ATTRIBUTES);
  132.     saOutbound.lpSecurityDescriptor = NULL; // default Dacl of caller
  133.     saOutbound.bInheritHandle = TRUE;
  134.     //
  135.     // modify security on windowstation and desktop to allow for correct
  136.     // process and console initialization by arbitrary clients
  137.     //
  138.     if(!SetWinstaDesktopSecurity()) {
  139.         DisplayLastError("SetWinstaDesktopSecurity");
  140.         return RTN_ERROR;
  141.     }
  142.     while(1) {
  143.     hPipeInbound = INVALID_HANDLE_VALUE;
  144.     hPipeOutbound = INVALID_HANDLE_VALUE;
  145.     hImpersonationToken = INVALID_HANDLE_VALUE;
  146.     hPrimaryToken = INVALID_HANDLE_VALUE;
  147.     pi.hProcess = INVALID_HANDLE_VALUE;
  148.     pi.hThread = INVALID_HANDLE_VALUE;
  149.     if(!SetupNamedPipes(
  150.         &saInbound,
  151.         &saOutbound,
  152.         &hPipeInbound,
  153.         &hPipeOutbound
  154.         )) {
  155.         //
  156.         // just bail out on failure for now
  157.         //
  158.         DisplayLastError("SetupNamedPipes");
  159.         break;
  160.     }
  161.     //
  162.     // obtain the impersonation token from the current thread
  163.     //
  164.     if(!OpenThreadToken(
  165.         GetCurrentThread(),
  166.         TOKEN_DUPLICATE,
  167.         TRUE,
  168.         &hImpersonationToken
  169.         )) {
  170.         DisplayLastError("OpenThreadToken");
  171.         goto cleanup;
  172.     }
  173.     //
  174.     // duplicate the impersonation token to primary
  175.     // since we are impersonating the client, the token will get the
  176.     // default Dacl of the client
  177.     //
  178.     if(!DuplicateTokenEx(
  179.         hImpersonationToken,
  180.         TOKEN_IMPERSONATE | TOKEN_READ |
  181.         TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE,
  182.         NULL,
  183.         SecurityImpersonation,
  184.         TokenPrimary,
  185.         &hPrimaryToken
  186.         )) {
  187.         DisplayLastError("DuplicateTokenEx");
  188.         goto cleanup;
  189.     }
  190.     RevertToSelf();
  191.     CloseHandle(hImpersonationToken);
  192.     hImpersonationToken = INVALID_HANDLE_VALUE;
  193.     //
  194.     // setup STARTUPINFO structure
  195.     //
  196.     si.cb = sizeof(STARTUPINFO);
  197.     si.lpReserved = NULL;
  198.     si.lpDesktop = NULL;
  199.     si.lpTitle = NULL;
  200.     si.cbReserved2 = 0;
  201.     si.lpReserved2 = NULL;
  202.     si.dwFlags = STARTF_USESTDHANDLES;
  203.     si.hStdOutput = hPipeOutbound;
  204.     si.hStdError = hPipeOutbound;
  205.     si.hStdInput = hPipeInbound;
  206.     //
  207.     // create a process running as the user
  208.     //
  209.     if(!CreateProcessAsUser(
  210.         hPrimaryToken,
  211.         NULL,
  212.         COMMANDLINE, // commandline to execute
  213.         NULL,   // process sa
  214.         NULL,   // thread sa
  215.         TRUE,   // inherit handles?
  216.         0,      // process creation flags (inherit existing console)
  217.         NULL,   // environment
  218.         NULL,   // current directory
  219.         &si,    // startupinfo
  220.         &pi     // processinfo
  221.         )) {
  222.         DisplayLastError("CreateProcessAsUser");
  223.         goto cleanup;
  224.     }
  225. cleanup:
  226.     if(hImpersonationToken != INVALID_HANDLE_VALUE) {
  227.         RevertToSelf();
  228.         CloseHandle(hImpersonationToken);
  229.     }
  230.     if(hPrimaryToken != INVALID_HANDLE_VALUE) {
  231.         CloseHandle(hPrimaryToken);
  232.     }
  233.     if(pi.hThread != INVALID_HANDLE_VALUE) {
  234.         CloseHandle(pi.hThread);
  235.     }
  236.     if(pi.hProcess != INVALID_HANDLE_VALUE) {
  237.         WaitForSingleObject(pi.hProcess, INFINITE);
  238.         CloseHandle(pi.hProcess);
  239.         //
  240.         // TODO kill any child by enumerating process tokens in the system
  241.         // by looking at the tokenID
  242.         //
  243.     }
  244.     if(hPipeInbound != INVALID_HANDLE_VALUE) {
  245.         DisconnectNamedPipe(hPipeInbound);
  246.         CloseHandle(hPipeInbound);
  247.     }
  248.     if(hPipeOutbound != INVALID_HANDLE_VALUE) {
  249.         DisconnectNamedPipe(hPipeOutbound);
  250.         CloseHandle(hPipeOutbound);
  251.     }
  252.     } // while
  253.     return RTN_OK;
  254. }
  255. /***
  256.  If the function succeeds, the return value is TRUE.  The parameters
  257.  hFileIn and hFileOut will point to the Inbound and Outbound named-pipe
  258.  handles.  The current thread will be impersonating the client.
  259.  If the function fails, the return value is FALSE.  The current thread will
  260.  not be impersonating the client.
  261. ***/
  262. BOOL
  263. SetupNamedPipes(
  264.     PSECURITY_ATTRIBUTES saInbound, // security attributes for Inbound pipe
  265.     PSECURITY_ATTRIBUTES saOutbound,// security attributes for Outbound pipe
  266.     PHANDLE hFileIn,                // resultant Inbound pipe handle on success
  267.     PHANDLE hFileOut                // resultant Outbound pipe handle on success
  268.     )
  269. {
  270.     HANDLE hPipeInbound = INVALID_HANDLE_VALUE;
  271.     HANDLE hPipeOutbound = INVALID_HANDLE_VALUE;
  272.     BOOL bSuccess = FALSE; // assume this function fails
  273.     //
  274.     // create Inbound named-pipe
  275.     //
  276.     hPipeInbound = CreateNamedPipe(
  277.         INBOUND_PIPE,
  278.         PIPE_ACCESS_INBOUND,
  279.         PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
  280.         1, // number of instances
  281.         4096,
  282.         4096,
  283.         NMPWAIT_USE_DEFAULT_WAIT,
  284.         saInbound
  285.         );
  286.     if(hPipeInbound == INVALID_HANDLE_VALUE) {
  287.         DisplayLastError("CreateNamedPipe");
  288.         return FALSE;
  289.     }
  290.     printf("Waiting for connection... ");
  291.     //
  292.     // wait for somebody to connect to the Inbound named pipe
  293.     //
  294.     ConnectNamedPipe(hPipeInbound, NULL);
  295.     //
  296.     // impersonate the client
  297.     //
  298.     if(!ImpersonateNamedPipeClient(hPipeInbound)) {
  299.         DisplayLastError("ImpersonateNamedPipeClient");
  300.         goto cleanup;
  301.     }
  302.     //
  303.     // create Outbound named-pipe
  304.     // the security on this named-pipe will be inherited from the default Dacl
  305.     // of the client that just connected, because this pipe instance is
  306.     // created in that users security context.  This prevents other users
  307.     // from connecting to the Outbound pipe.
  308.     //
  309.     hPipeOutbound = CreateNamedPipe(
  310.         OUTBOUND_PIPE,
  311.         PIPE_ACCESS_OUTBOUND,
  312.         PIPE_TYPE_MESSAGE | PIPE_WAIT,
  313.         1, // number of instances
  314.         4096,
  315.         4096,
  316.         NMPWAIT_USE_DEFAULT_WAIT,
  317.         saOutbound
  318.         );
  319.     if(hPipeOutbound == INVALID_HANDLE_VALUE) {
  320.         DisplayLastError("CreateNamedPipe");
  321.         goto cleanup;
  322.     }
  323.     //
  324.     // wait for the client to connect to the Outbound named pipe
  325.     //
  326.     ConnectNamedPipe(hPipeOutbound, NULL);
  327.     printf("Client connected!n");
  328.     *hFileIn = hPipeInbound;
  329.     *hFileOut = hPipeOutbound;
  330.     bSuccess = TRUE;
  331. cleanup:
  332.     if(!bSuccess) {
  333.         RevertToSelf();
  334.         if(hPipeInbound != INVALID_HANDLE_VALUE)
  335.             CloseHandle(hPipeInbound);
  336.         if(hPipeOutbound != INVALID_HANDLE_VALUE)
  337.             CloseHandle(hPipeOutbound);
  338.     }
  339.     return bSuccess;
  340. }
  341. /**
  342. This function builds a Dacl which grants the creator of the objects
  343. FILE_ALL_ACCESS and Everyone FILE_GENERIC_READ and FILE_GENERIC_WRITE
  344. access to the object.
  345. This Dacl allows for higher security than a NULL Dacl, which is common for
  346. named-pipes, as this only grants the creator/owner write access to the
  347. security descriptor, and grants Everyone the ability to "use" the named-pipe.
  348. This scenario prevents a malevolent user from disrupting service by preventing
  349. arbitrary access manipulation.
  350. **/
  351. BOOL
  352. BuildNamedPipeAcl(
  353.     PACL pAcl,
  354.     PDWORD cbAclSize
  355.     )
  356. {
  357.     DWORD dwAclSize;
  358.     SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY;
  359.     SID_IDENTIFIER_AUTHORITY siaCreator = SECURITY_CREATOR_SID_AUTHORITY;
  360.     BYTE BufEveryoneSid[32];
  361.     BYTE BufOwnerSid[32];
  362.     PSID pEveryoneSid = (PSID)BufEveryoneSid;
  363.     PSID pOwnerSid = (PSID)BufOwnerSid;
  364.     //
  365.     // compute size of acl
  366.     //
  367.     dwAclSize = sizeof(ACL) +
  368.         2 * ( sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD) ) +
  369.         GetSidLengthRequired( 1 ) + // well-known Everyone Sid
  370.         GetSidLengthRequired( 1 ) ; // well-known Creator Owner Sid
  371.     if(*cbAclSize < dwAclSize) {
  372.         *cbAclSize = dwAclSize;
  373.         return FALSE;
  374.     }
  375.     *cbAclSize = dwAclSize;
  376.     //
  377.     // intialize well known sids
  378.     //
  379.     if(!InitializeSid(pEveryoneSid, &siaWorld, 1)) return FALSE;
  380.     *GetSidSubAuthority(pEveryoneSid, 0) = SECURITY_WORLD_RID;
  381.     if(!InitializeSid(pOwnerSid, &siaCreator, 1)) return FALSE;
  382.     *GetSidSubAuthority(pOwnerSid, 0) = SECURITY_CREATOR_OWNER_RID;
  383.     if(!InitializeAcl(pAcl, dwAclSize, ACL_REVISION))
  384.         return FALSE;
  385.     //
  386.     //
  387.     if(!AddAccessAllowedAce(
  388.         pAcl,
  389.         ACL_REVISION,
  390.         FILE_GENERIC_READ | FILE_GENERIC_WRITE,
  391.         pEveryoneSid
  392.         ))
  393.         return FALSE;
  394.     //
  395.     //
  396.     return AddAccessAllowedAce(
  397.         pAcl,
  398.         ACL_REVISION,
  399.         FILE_ALL_ACCESS,
  400.         pOwnerSid
  401.         );
  402. }
  403. /**
  404. This function adjusts the security on the current windowstation and desktop,
  405. to allow arbitrary client to have access to the windowstation and desktop.
  406. This is necessary to allow for correct process and console initialization.
  407. Note that in a secure environment, it would be appropriate to create
  408. a specific desktop and launch the process spawned by the client on that
  409. desktop, rather than opening up the current desktop to the client.
  410. This function simply applies a NULL Dacl to the current windowstation and
  411. desktop in order to reduce the size of this sample.  Applying NULL Dacls
  412. to objects is generally not a wise idea.
  413. TODO revist this function later to better address security on the windowstation
  414. and desktop.
  415. **/
  416. BOOL
  417. SetWinstaDesktopSecurity(
  418.     void
  419.     )
  420. {
  421.     HWINSTA hWinsta;
  422.     HDESK hDesk;
  423.     SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
  424.     SECURITY_DESCRIPTOR sd;
  425.     hWinsta = GetProcessWindowStation();
  426.     if(hWinsta == NULL) return FALSE;
  427.     hDesk = GetThreadDesktop(GetCurrentThreadId());
  428.     if(hDesk == NULL) return FALSE;
  429.     InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
  430.     SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE);
  431.     if(!SetUserObjectSecurity(hWinsta, &si, &sd)) return FALSE;
  432.     return SetUserObjectSecurity(hDesk, &si, &sd);
  433. }
  434. void
  435. DisplayLastError(
  436.     LPSTR szAPI // pointer to Ansi function name
  437.     )
  438. {
  439.     LPSTR MessageBuffer;
  440.     DWORD dwBufferLength;
  441.     //
  442.     // TODO get this fprintf out of here!
  443.     //
  444.     fprintf(stderr,"%s error!n", szAPI);
  445.     if(dwBufferLength=FormatMessageA(
  446.             FORMAT_MESSAGE_ALLOCATE_BUFFER |
  447.             FORMAT_MESSAGE_FROM_SYSTEM,
  448.             NULL,
  449.             GetLastError(),
  450.             MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  451.             (LPSTR) &MessageBuffer,
  452.             0,
  453.             NULL
  454.             ))
  455.     {
  456.         DWORD dwBytesWritten; // unused
  457.         //
  458.         // Output message string on stderr
  459.         //
  460.         WriteFile(
  461.                 GetStdHandle(STD_ERROR_HANDLE),
  462.                 MessageBuffer,
  463.                 dwBufferLength,
  464.                 &dwBytesWritten,
  465.                 NULL
  466.                 );
  467.         //
  468.         // free the buffer allocated by the system
  469.         //
  470.         LocalFree(MessageBuffer);
  471.     }
  472. }