Articles   Members Online:
-Article/Tip Search
-News Group Search over 21 Million news group articles.
-Delphi/Pascal
-CBuilder/C++
-C#Builder/C#
-JBuilder/Java
-Kylix
Member Area
-Home
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Login/Logout
-Become a Member
-Why sign up!
-Newsletter
-Chat Online!
-Indexes NEW!!
Employment
-Build your resume
-Find a job
-Post a job
-Resume Search
Contacts
-Contacts
-Feedbacks
-Link to us
-Privacy/Disclaimer
Embarcadero
Visit Embarcadero
Embarcadero Community
JEDI
Links
How to Impersonate User on Windows NT is a three step process Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
14-Nov-02
Category
Security
Language
Delphi 5.x
Views
331
User Rating
No Votes
# Votes
0
Replies
0
Publisher:
DSP, Administrator
Reference URL:
DKB
			Author: Stewart Moss 

How do I simulate the unix SU command under windows NT. In other words I want to 
run an app under a different user...

Answer:
This code was not written by me.
The original copyright information is still intact.
1   
2   {*
3    SU.DPR for Delphi32 Pascal
4    by Fred - APIKing - de Jong, Heerlen, Netherlands 1997
5       home: frejon@worldonline.nl, office: fjng@cbs.nl
6   
7    su.cpp
8      UNIX-like Substitute User for Windows NT
9   
10   Usage:
11     su [NewDomain\][NewUser] [command-line]
12     where:
13       NewDomain\ is desired domain logon (\\ is ok also)
14       NewUser is the name of the user to be impersonated. Default is Administrator.
15       command-line is the command to be executed, with parameters. Default is CMD 
16  (Console)
17  
18   Authors:
19     David Wihl (wihl@shore.net)
20     Steffen Krause (skrause@informatik.hu-berlin.de)
21  
22   Revision History:
23   xx-JUL-1995.
24         - Removed restriction on command line (User can now specify anything)
25         - Added NewDomain logon on command line
26         - Added Unicode support but found bug in LogonUserW
27   03-JUL-1995. Initial public release
28  
29   Design:
30     Impersonating a User on Windows NT is a three step process:
31     1-  Logon the User to create a Security identifier
32     2-  Enabling access to the Windows Station so the newly logged on NewUser
33       can interact. This is necessary even if the Administrator is logging on.
34     3-  Creating a process using the Security identifier
35  
36     Different privileges are required for steps (1) and (3). Logging on a User
37     (LogonUser()) requires the SeTcbPrivilege. Creating a process as another User
38     CreateProcessAsUser()) requires SeAssignPrimary and SeIncreaseQuota privileges.
39     To grant these privileges, see the Installation Section.
40  
41     These two Security API calls were only stablized in NT 3.51, build 1057. SU will
42     not work with earlier versions.
43  
44     In NT, there is no direct equivalent of UNIX's rwsr-xr-x file permission.
45  
46   Restrictions and Limitations:
47     - There is no logging of failed or successful usage. A future may incorporate
48       writing to the Event Log.
49  
50   Installation:
51     The easiest way to selectively grant the three privileges required to use this
52     program is:
53  
54     1-  Start the User Manager (MUSRMGR)
55     2-  Create a new group (e.g. "SU Users")
56     3-  Add the three privileges to the group (via Policies\User Rights):
57         "Act as part of the operating system"  - SeTcbPrivilege
58         "Increase quotas"                      - SeIncreaseQuota
59         "Replace a process level token"        - SeAssignPrimaryToken
60  
61       NOTE: The three privileges will only be visible if you check
62       "Show Advanced User Rights" in the dialog box.
63     4-  Add the desired users to the new group (via User\Properties\Group)
64  
65     This program was compiled under Visual C++ 2.1 with the June '95 SDK
66  
67   For more information about Porting from UNIX to NT check the FAQ:
68   http://www.shore.net/~wihl/unix2nt.html
69  
70  *}
71  
72  program su;
73  {$APPTYPE CONSOLE}
74  
75  uses
76    Windows,
77    SysUtils { already has SysErrorMessage function };
78  
79  //{$R VersInfo.RES}
80  
81  //
82  // CUSTOMIZATION OPTIONS - put 'em here
83  const
84    DEFAULT_USER: string = 'Administrator';
85      // if we don't specify a username, who are we?
86    DEFAULT_CMD: string = 'cmd'; // if we don't specify a command, what do we do?
87  {$DEFINE VERBOSE} // quiet ?la UNIX, or chatty?
88    //
89    // END CUSTOMIZATION OPTIONS
90    //
91  const
92    SECURITY_DESCRIPTOR_REVISION = 1; // from winnt.h, missing in windows.pas
93    ////////////////////////////////////////////////////////////////////////
94    //                                                                    //
95    //               NT Defined Privileges                                //
96    //                                                                    //
97    ////////////////////////////////////////////////////////////////////////
98    SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege';
99    SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege';
100   SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege';
101   SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege';
102   SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege';
103   SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege';
104   SE_TCB_NAME = 'SeTcbPrivilege';
105   SE_SECURITY_NAME = 'SeSecurityPrivilege';
106   SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege';
107   SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege';
108   SE_system_PROFILE_NAME = 'SesystemProfilePrivilege';
109   SE_systemTIME_NAME = 'SesystemtimePrivilege';
110   SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege';
111   SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege';
112   SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege';
113   SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege';
114   SE_BACKUP_NAME = 'SeBackupPrivilege';
115   SE_RESTORE_NAME = 'SeRestorePrivilege';
116   SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
117   SE_DEBUG_NAME = 'SeDebugPrivilege';
118   SE_AUDIT_NAME = 'SeAuditPrivilege';
119   SE_system_ENVIRONMENT_NAME = 'SesystemEnvironmentPrivilege';
120   SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege';
121   SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege';
122 
123   { ------------------------------------------------- }
124 
125   { support standard Error output, besides standard Output/Input }
126 var
127   Error: TextFile;
128 
129 procedure InitErrorOutput;
130 begin
131   AssignFile(Error, EmptyStr);
132   Rewrite(Error);
133   TTextRec(Error).Handle := GetStdHandle(STD_ERROR_HANDLE);
134 end;
135 
136 var
137   _TokenizeStr: PChar = nil;
138   _TokenizeLast: PChar = nil;
139 
140 function Tokenize(const SourceText: string; const Delimiters: string): string;
141 { this is my Delphi version of C's strtok():
142     1st call: SourceText is not empty, next calls: SourceText is EmptyStr;
143  set of delimiters can change while tokenizing;
144  implicit string memory allocation is hidden for the outside:
145     Tokenize only parses one SourceText at a time. }
146 var
147   R, S: PChar;
148 begin
149   if length(SourceText) = 0 then
150     R := _TokenizeLast
151   else
152   begin { cleanup and (re)initialize }
153     _TokenizeLast := nil;
154     StrDispose(_TokenizeStr);
155     _TokenizeStr := StrNew(PChar(SourceText));
156     R := _TokenizeStr;
157   end;
158   if R <> nil then
159   begin
160     S := R; { find next delim }
161     while (S^ <> chr(0)) and (StrScan(PChar(Delimiters), S^) = nil) do
162       inc(S);
163     if S^ <> chr(0) then
164     begin
165       S^ := chr(0); { got delim, truncate R result }
166       inc(S); { skip over delims to set _TokenizeLast }
167       while (S^ <> chr(0)) and (StrScan(PChar(Delimiters), S^) <> nil) do
168         inc(S);
169       if S^ <> chr(0) then
170         _TokenizeLast := S;
171     end;
172     Result := string(R);
173     if S^ = chr(0) then
174     begin { cleanup early }
175       _TokenizeLast := nil;
176       StrDispose(_TokenizeStr);
177       _TokenizeStr := nil;
178     end
179   end
180   else
181     Result := EmptyStr
182 end;
183 
184 { -------------------------------------------------------------- }
185 const
186   DEFWINSTATION: string = 'WinSta0';
187   DEFDESKTOP: string = 'Default';
188   WHITESPACE: string = ' ' {SPACE} + chr(9) {TAB} + chr(10) {LF};
189   DOMUSERSEP: string = '\';
190 
191 procedure ErrorHandler(const errmsg: string);
192 var
193   err: dword;
194 begin
195   err := GetLastError;
196   writeln(Error, 'Error: ', errmsg, '.');
197   write(Error, SysErrorMessage(err));
198 end;
199 
200 function SetUserObjectAllAccess(hUserObject: THANDLE): boolean;
201 var
202   pSD: PSecurity_Descriptor;
203   si: Security_Information; { dword }
204 begin
205   (* Initialize a security descriptor. *)
206   pSD := PSecurity_Descriptor(
207     LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH));
208   if pSD = nil then
209   begin
210     ErrorHandler('Can''t Allocate Local Memory');
211     Result := FALSE;
212     exit;
213   end;
214   if not InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) then
215   begin
216     ErrorHandler('Can''t Initialize Security Descriptor');
217     LocalFree(HLOCAL(pSD));
218     Result := FALSE;
219     exit;
220   end;
221 
222  {* Add a NULL disc. ACL to the security descriptor. *}
223   if not SetSecurityDescriptorDacl(pSD,
224     TRUE, // specifying a disc. ACL
225     PACL(nil),
226     FALSE) then // not a default disc. ACL
227   begin
228     ErrorHandler('Can''t Set Security Descriptor DACL');
229     LocalFree(HLOCAL(pSD));
230     Result := FALSE;
231     exit;
232   end;
233 
234 {* Add the security descriptor to the userobject (like a window or a DDE
235    conversation), NOT to a kernelobject (like a process, thread or event). *}
236   si := DACL_SECURITY_INformATION;
237   Result := SetUserObjectSecurity(hUserObject, si, pSD);
238 
239   LocalFree(HLOCAL(pSD));
240   if not Result then
241     ErrorHandler('Can''t Set NewUser Object Security')
242 end;
243 
244 function GetUserObjectName(hUserObject: THandle; var Name: string): boolean;
245 var
246   dw: DWord;
247 begin
248   Name := EmptyStr;
249   GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Name), 0, dw);
250   SetLength(Name, dw + 1);
251   Result := GetUserObjectInformation(hUserObject, UOI_NAME, PChar(Name), dw, dw);
252   if Result then
253     SetLength(Name, dw - 1)
254   else
255     Name := EmptyStr;
256 end;
257 
258 function GetPrivilegeDisplayName(const PrivilegeName: string): string;
259 { PrivilegeName is of string type 'SE_'* }
260 var
261   dw, li: DWord;
262 begin
263   Result := EmptyStr;
264   dw := 0;
265   li := 0; { li:= dword(MAKELANGID(LANG_DEFAULT, LANG_USER)); }
266   if not LookupPrivilegeDisplayName(nil, PChar(PrivilegeName), PChar(Result), dw, 
267 li)
268     then
269     dw := 256;
270   SetLength(Result, dw + 1);
271   if LookupPrivilegeDisplayName(nil, PChar(PrivilegeName), PChar(Result), dw, li) 
272 then
273     SetLength(Result, StrLen(PChar(Result)))
274   else
275     Result := EmptyStr;
276 end;
277 
278 function GetAccountInfo(var CurUser, CurDomain: string): boolean;
279 var
280   dw, dw2: DWord;
281   pSD: PSecurity_Descriptor;
282   snu: Sid_Name_Use;
283 begin
284   Result := False;
285   dw := 255;
286   Setlength(CurUser, dw + 1);
287   if GetUserName(PChar(CurUser), dw) then
288   begin
289     SetLength(CurUser, dw - 1);
290     dw2 := 256;
291     SetLength(CurDomain, dw2);
292     snu := SidTypeUser;
293     pSD := nil;
294     dw := 0; { get needed length for SID }
295     LookUpAccountName(nil {LocalMachine}, PChar(CurUser),
296       pSD, dw, PChar(CurDomain), dw2, snu);
297     if dw <> 0 then
298     begin
299       pSD := PSecurity_Descriptor(LocalAlloc(LPTR, dw));
300       if pSD <> nil then
301       begin
302         if LookUpAccountName(nil, PChar(CurUser), { get the real thing }
303           pSD, dw, PChar(CurDomain), dw2, snu) then
304         begin
305           SetLength(CurDomain, dw2);
306           Result := True;
307         end
308         else
309           CurDomain := EmptyStr;
310         LocalFree(HLOCAL(pSD));
311       end;
312     end;
313   end
314   else
315     CurUser := EmptyStr;
316 end;
317 
318 function GetMachineName: string;
319 var
320   dw: DWord;
321 begin
322   dw := MAX_COMPUTERNAME_LENGTH + 1;
323   SetLength(Result, MAX_COMPUTERNAME_LENGTH + 1);
324   if GetComputerName(PChar(Result), dw) then
325     SetLength(Result, dw)
326   else
327     Result := EmptyStr;
328 end;
329 
330 { ---------------------------------------------------------- }
331 var
332   CurUser, // Current User
333   CurDomain, // Current Domain
334   pwstr, // password string
335   consoleTitle, // Title if new console only
336   NewDomUser, // NewDomain\NewUser combination
337   CommandLine, // command line we pass to the new process
338   NewDomain, // NewDomain to log onto
339   NewUser: string; // NewUser to log onto
340   startUpInfo: TStartupInfo;
341   procInfo: TProcessInformation; // child process info, from CreateProcessAsUser
342   hDesktop: HDESK;
343   hWindowStation: HWINSTA;
344   hUserToken, hConsIn: THANDLE;
345   OldConsInMode, NewConsInMode: DWORD;
346   NTversion: TOSVersionInfo;
347   S, DeskTopName, WinStaName: string;
348   RC: integer;
349 
350 begin { program }
351   InitErrorOutput; // Attach outputfile Error to STDERR
352 
353   // Make sure we are using the minimum OS version.
354   NTversion.dwOSVersionInfoSize := sizeof(TOSVersionInfo);
355   if not GetVersionEx(NTversion) then
356   begin
357     ErrorHandler('Unable to get OS version');
358     halt(1);
359   end;
360   if NTversion.dwPlatformId <> VER_PLATform_WIN32_NT then
361   begin
362     writeln(Error, 'SU will run only on Windows NT.');
363     halt(1);
364   end;
365   if NTversion.dwBuildNumber < 1057 then // Commercial 3.51 release
366   begin
367     writeln(Error, 'SU requires at minimum NT version 3.51 build 1057.');
368     halt(1);
369   end;
370 
371   //{$IFDEF DEBUG}
372   writeln('SU: NT Version ', NTversion.dwMajorVersion, '.',
373     NTversion.dwMinorVersion, ', build ', NTversion.dwBuildNumber);
374   // {$ENDIF}
375 
376   GetAccountInfo(CurUser, CurDomain);
377   writeln('You are ', CurDomain, '\', CurUser);
378 
379   // Process the command line parameters
380   Tokenize(string(CmdLine), WHITESPACE);
381   NewDomUser := Tokenize(EmptyStr, WHITESPACE);
382   if length(NewDomUser) = 0 then
383   begin
384     NewDomUser := DEFAULT_USER;
385     CommandLine := DEFAULT_CMD;
386   end
387   else
388   begin
389     CommandLine := Tokenize(EmptyStr, EmptyStr);
390     if length(CommandLine) = 0 then
391       CommandLine := DEFAULT_CMD;
392   end;
393   if Pos(DOMUSERSEP, NewDomUser) > 0 then
394   begin
395     NewDomain := Tokenize(NewDomUser, DOMUSERSEP);
396     NewUser := Tokenize(EmptyStr, DOMUSERSEP);
397     if length(NewUser) = 0 then
398       NewUser := DEFAULT_USER;
399   end
400   else
401   begin
402     NewDomain := EmptyStr;
403     NewUser := NewDomUser;
404   end;
405   if (length(NewDomain) = 0) and
406     ((NewUser = '-?') or (NewUser = '/?') or (NewUser = '?')) then
407   begin
408     writeln;
409     writeln('Runs Windows NT commands under another user''s account.');
410     writeln;
411     writeln('SU [newdomain\][newuser] [command-line]');
412     writeln;
413     writeln('  [newdomain\]   Specifies desired domain logon (\\ is ok also).');
414     writeln('  [newuser]      Specifies the name of the user to be impersonated.');
415     writeln('                   The default is Administrator.');
416     writeln('  [command-line] Specifies the command to be executed, with 
417 parameters.'
418     writeln('                   The default is CMD (a new NT Console).');
419     writeln;
420     writeln('Requires three extended NT privileges:');
421     writeln;
422     writeln('  ', GetPrivilegeDisplayName(SE_TCB_NAME), ',');
423     writeln('  ', GetPrivilegeDisplayName(SE_ASSIGNPRIMARYTOKEN_NAME), ' and');
424     writeln('  ', GetPrivilegeDisplayName(SE_INCREASE_QUOTA_NAME), '.');
425     writeln;
426     writeln('These can be granted as User Rights with NT User Manager.');
427     halt(0);
428   end;
429 
430   // Turn off console mode echo, since we don't want clear-screen passwords
431   system.Reset(Input); {GetStdHandle(STD_INPUT_HANDLE)}
432   hConsIn := TTextRec(Input).Handle;
433 
434   //if hConsIn = INVALID_HANDLE_values then
435   //begin
436   //  ErrorHandler ('Can''t get handle of STDIN'); halt(1);
437   //end;
438 
439   if not GetConsoleMode(hConsIn, OldConsInMode) then
440   begin
441     ErrorHandler('Can''t get current Console Mode');
442     halt(1);
443   end;
444   NewConsInMode := OldConsInMode and (not ENABLE_ECHO_INPUT);
445   if not SetConsoleMode(hConsIn, NewConsInMode) then
446   begin
447     ErrorHandler('Unable to turn off Echo');
448     halt(1);
449   end;
450 
451   // Ask for the password
452 {$IFDEF VERBOSE}
453   if length(NewDomain) = 0 then
454     S := CurDomain
455   else
456     S := NewDomain;
457   writeln('Logging onto ', S, ' domain as ', NewUser, '.');
458 {$ENDIF}
459   write('Enter password: ');
460   readln(pwstr);
461   // When echo is off and NewUser hits , CR-LF is not echoed, so do it for him
462   writeln;
463   if not SetConsoleMode(hConsIn, OldConsInMode) then
464   begin
465     ErrorHandler('Unable to reset previous console mode');
466     halt(1);
467   end;
468   CloseHandle(hConsIn);
469 
470   // Do the Logon
471   if not LogonUser(PChar(NewUser), PChar(NewDomain), PChar(pwstr),
472     LOGON32_LOGON_INTERACTIVE,
473     LOGON32_PROVIDER_DEFAULT, hUserToken) then
474   begin
475     case GetLastError of
476       ERROR_PRIVILEGE_NOT_HELD:
477         begin
478           writeln(Error,
479             'Error: you do not have the following extended User Right:');
480           writeln(Error, GetPrivilegeDisplayName(SE_TCB_NAME), '.');
481         end;
482       ERROR_LOGON_FAILURE:
483         ErrorHandler('LogonUser failed.');
484       ERROR_ACCESS_DENIED:
485         ErrorHandler('Access is denied');
486     else
487       ErrorHandler('Unable to logon');
488     end;
489     halt(2);
490   end;
491 
492   // give the NewUser access to the current WindowStation and Desktop
493   hWindowStation := GetProcessWindowStation;
494   if not GetUserObjectName(hWindowStation, WinStaName) then
495     WinStaName := DEFWINSTATION;
496   if not SetUserObjectAllAccess(hWindowStation) then
497   begin
498     write(Error, 'Can''t set WindowStation ', WinStaName, ' security.');
499     CloseHandle(hUserToken);
500     halt(3);
501   end;
502   hDesktop := GetThreadDesktop(GetCurrentThreadId);
503   if not GetUserObjectName(hDesktop, DeskTopName) then
504     DeskTopName := DEFDESKTOP;
505   if not SetUserObjectAllAccess(hDesktop) then
506   begin
507     write(Error, 'Can''t set Desktop ', DeskTopName, ' security.');
508     CloseHandle(hUserToken);
509     halt(3);
510   end;
511 
512   // Set the STARTUPINFO for the new process
513   if length(NewDomain) <> 0 then
514     NewDomain := NewDomain + '\';
515   consoleTitle := 'SU: ' + NewDomain + NewUser;
516   FillChar(startUpInfo, sizeof(startUpInfo), 0);
517   with startUpInfo do
518   begin
519     cb := sizeof(startUpInfo);
520     lpTitle := PChar(consoleTitle);
521     S := WinStaName + '\' + DeskTopName;
522     lpDesktop := PChar(S);
523   end;
524 
525   // Create the child process
526   if not CreateProcessAsUser(hUserToken,
527     nil, PChar(CommandLine), nil, nil, FALSE {no inherit handles},
528     CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP,
529     nil, nil, startUpInfo, procInfo) then
530   begin
531     case GetLastError of
532       ERROR_PRIVILEGE_NOT_HELD:
533         begin
534           writeln(Error, 'Error: missing (one of) following extended User Rights:');
535           writeln(Error, GetPrivilegeDisplayName(SE_ASSIGNPRIMARYTOKEN_NAME), ', 
536 or'
537           writeln(Error, GetPrivilegeDisplayName(SE_INCREASE_QUOTA_NAME), '.');
538           ErrorHandler(EmptyStr);
539         end;
540       ERROR_FILE_NOT_FOUND:
541         ErrorHandler('Error: command in ''' + CommandLine + ''' not found.');
542     else
543       ErrorHandler('Error: CreateProcessAsUser failed.');
544     end;
545     RC := 4;
546   end
547   else
548     RC := 0;
549 
550   CloseHandle(hWindowStation);
551   CloseHandle(hDesktop);
552   CloseHandle(hUserToken);
553 
554   if RC = 0 then
555   begin
556     CloseHandle(procInfo.hThread);
557     CloseHandle(procInfo.hProcess);
558   end;
559 
560   halt(RC);
561 
562 end.


			
Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10

 

Advertisement
Share this page
Advertisement
Download from Google

Copyright © Mendozi Enterprises LLC