• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 744
  • Last Modified:

LogonUser trouble

Hi, Im trying to use the function LogonUser, but I keep getting the error "Logon failure: unknown user name or bad password".

Im using LogonUser because I need to create a process running as another user, and CreateProcessAsUser needs a handle, which Im supposed to get from LogonUser. I have a service that starts a .bat file, and this file accesses a network path. This fails because the service runs as system account and cant access network resources. I want to duplicate the behaviour of the Windows Task Scheduler, where you can supply username+password, while the service still runs as system account. How do they do that?

I have of course verified that the username/password's I use are correct ;)

Regards,
Pede
0
pede
Asked:
pede
  • 5
  • 3
1 Solution
 
DaFoxCommented:
Hi pede.

Example:

procedure WinExecAsUser(FileName: string; username: string; password:
string; Visibility:
  integer);
var { V1 by Pat Ritchey, V2 by P.Below }
  zAppName          : array[0..512] of char;
  StartupInfo       : TStartupInfo;
  ProcessInfo       : TProcessInformation;
  h                 : thandle;
begin { WinExecAndWait32V2 }
  StrPCopy(zAppName, FileName);
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := Visibility;
  if not LogonUser(pchar(username), '.', pchar(Password),
LOGON32_LOGON_INTERACTIVE,
    LOGON32_PROVIDER_DEFAULT, h) then
    ShowMessage(SysErrorMessage(GetLastError));
  CreateProcessAsUser(h, nil,
    zAppName, { pointer to command line string }
    nil, { pointer to process security attributes }
    nil, { pointer to thread security attributes }
    false, { handle inheritance flag }
    CREATE_NEW_CONSOLE or { creation flags }
    NORMAL_PRIORITY_CLASS,
    nil, { pointer to new environment block }
    nil, { pointer to current directory name }
    StartupInfo, { pointer to STARTUPINFO }
    ProcessInfo); { pointer to PROCESS_INF }
  if GetLastError <> 0 then ShowMessage(SysErrorMessage(GetLastError));
end;

Watch out for your policies (SE_ASSIGNPRIMARYTOKEN_NAME...)!

Markus
0
 
pedeAuthor Commented:
Hi DaFox, thanks for your comment. Unfortunately Im already doing exactly what you suggested, but I never get past LogonUser, which fails with the error I described above.

It must be some security issue, but I dont know which and why. Im using Win2K server.

The exact scenario is this:

A service creates an out-of-process COM object (starting the COM server, which will terminate when it's done)

The COM object then calls LogonUser because it needs to spawn another process, but not running in the system account context - it must run as a specified user, just at the Task Scheduler does. This call to LogonUser fails. I've tried both with local administrator and the currently logged on user.


0
 
DaFoxCommented:
Hi Pede.

-
const
  SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege';
  SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege';
  SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege';
  SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege';
  SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege';
  SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege';
  SE_TCB_NAME = 'SeTcbPrivilege';
  SE_SECURITY_NAME = 'SeSecurityPrivilege';
  SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege';
  SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege';
  SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege';
  SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege';
  SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege';
  SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege';
  SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege';
  SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege';
  SE_BACKUP_NAME = 'SeBackupPrivilege';
  SE_RESTORE_NAME = 'SeRestorePrivilege';
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';
  SE_DEBUG_NAME = 'SeDebugPrivilege';
  SE_AUDIT_NAME = 'SeAuditPrivilege';
  SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege';
  SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege';
  SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege';
  SE_UNDOCK_NAME = 'SeUndockPrivilege';
  SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege';
  SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege';

function SetPrivilege(PrivilegeName: String; Enable: Boolean): Boolean;
var
  aTP      : TTokenPrivileges;
  aTPOld   : TTokenPrivileges;
  hToken   : THandle;
  dwRetLen : DWord;
begin
  Result := False;
  OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken);
  aTP.PrivilegeCount := 1;
  if LookupPrivilegeValue(nil, PChar(PrivilegeName), aTP.Privileges[0].LUID) then
  begin
    if Enable then aTP.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED else aTP.Privileges[0].Attributes := 0;
    dwRetLen := 0;
    Result := AdjustTokenPrivileges(hToken, False, aTP, SizeOf(aTPOld), aTPOld, dwRetLen);
  end;
  CloseHandle(hToken);
end;
-

I suppose the privileges you need are: SE_TCB_NAME, SE_ASSIGNPRIMARYTOKEN_NAME, SE_INCREASE_QUOTA_NAME.

Good luck ;-)
Markus
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
pedeAuthor Commented:
Well, it looked promising but it didnt help :( SetPrivilege succeeds but I still get the same error on LogonUser. To me it looks like he system account cant detect that the given user accounts exist, and therefore reports them as unknown. Is this possible?
0
 
kbb2Commented:
Listening... (muhaha)
0
 
pedeAuthor Commented:
I just tried the function CreateProcessWithLogonW to see if that would work, and I get the exact same error:

Logon failure: unknown user name or bad password

I tried with local administrator and Im 100% sure the password is correct.
0
 
pedeAuthor Commented:
There's something thats a bit strange about your SetPrivilege function. AdjustTokenPrivileges returns TRUE when I try to set SE_TCB_NAME, but it doesnt get set by the call. If you check the following way you will see that it actually fails:

   AdjustTokenPrivileges(hToken, False, aTP, SizeOf(aTPOld), aTPOld, dwRetLen);
   Result := GetLastError = ERROR_SUCCESS;

It fails with "Not all privileges referenced are assigned to the caller" which means that SE_TCB_NAME is not enabled for the account, and cannot be set.

I have absolutely no control over the accounts that is going to run this - all I have will be domain\username and password. So I guess LogonUser is not an option at all. I wonder how RunAs.exe and Task Scheduler does it.

Thanks for your help, but it looks like Im down the wrong track.

Regards,
Pede


0
 
pedeAuthor Commented:
Problem solved! I think I now have an understanding of how it all works ;) I cant call LogonUser as any user, because none of the users has the SE_TCB_NAME privilege. That is how its supposed to be, according to the Windows security model.

But - It DOES work when running from inside my service. Believe it or not, but the password was converted to uppercase somewhere along the way (it's a pretty big program and the password travels from disk, through a service and via DCOM to the final .exe that calls LogonUser).

Thank you for your suggestions in this thread :-)
0
 
DaFoxCommented:
I just came home from work and my plan was to work on your prob tonight! Seems like you were a little bit faster... ;-)

Thanks for the points and the explanation!

Markus
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

  • 5
  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now