SetLocalTime/SetSystemTime P/Invoke call fails under Vista (even witha requireAdministrator manifest

  • Thread starter Thread starter rdilipk
  • Start date Start date
R

rdilipk

I am posting at the end of this post some code that P/Invoke's
SetSystemTime to set the local system time. This call fails -- i.e
the time is not set and the API returns false. However calling
Marshal.GetLastWin32Error immediately afterwards returns an errorcode
of 0 indicating that the operation somehow executed successfully.

This is a plain vanilla console app written in C#. It runs on vista
and has a manifest embedded in it that has 'requireAdministrator'
privilege level on it. The logged on user belongs to the
administrator group and the UAC is set at "prompt for consent".

What am I missing? (FWIW, I didn't write the P/Invoke signatures
myself. I just lifted them out of various postings on the internet --
is there a mistake in that somewhere)?

It seems similar to what was reported here:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1807068&SiteID=1

/********** CODE ********************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace VistaUACTest
{
class Program
{
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct SystemTime
{
public short year;
public short month;
public short dayOfWeek;
public short day;
public short hour;
public short minute;
public short second;
public short milliseconds;
}

[DllImport("kernel32.dll")]
private static extern bool SetLocalTime(ref SystemTime
systime);

[DllImport("kernel32.dll")]
private static extern bool SetSystemTime(ref SystemTime
systime);

private const int ANYSIZE_ARRAY = 1;
private const string SE_SYSTEMTIME_NAME =
"SeSystemtimePrivilege";
private const int SE_PRIVILEGE_ENABLED = 0x00000002;
private const int TOKEN_QUERY = 0x0008;
private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;

[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public int LowPart;
public int HighPart;
}

[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public int Attributes;
}

[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
ANYSIZE_ARRAY)]
public LUID_AND_ATTRIBUTES[] Privileges;
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool OpenProcessToken(int ProcessHandle,
int DesiredAccess, ref int TokenHandle);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetCurrentProcess();

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LookupPrivilegeValue(string
lpSystemName, string lpName,

[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool AdjustTokenPrivileges(int
TokenHandle,
int
DisableAllPrivileges,

[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES NewState,
int
BufferLength,

[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES PreviousState,
ref
int ReturnLength);

static void Main(string[] args)
{
try
{
// because the docs for SetSystemTime says that the
SE_SYSTEMTIME_NAME privilege is disabled by default
// this call doesn't really help though
AdjustPrivileges();
SystemTime st = new SystemTime();
st.hour = 10;
if (!SetSystemTime(ref st))
{
// the return value says the call failed, however
Marshal.GetLastWin32Error() says the previous P/Invoke operation
// completed successfully (the default ctor of
Win32Exception calls GetLastWin32Error)
throw new Win32Exception();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}

public static bool AdjustPrivileges()
{
TOKEN_PRIVILEGES tkNew = new TOKEN_PRIVILEGES();
tkNew.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];
TOKEN_PRIVILEGES tkOld = new TOKEN_PRIVILEGES();
tkOld.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];

LUID luid = new LUID();
int token = -1;
int oldluidSize = 0;

if (LookupPrivilegeValue(null, SE_SYSTEMTIME_NAME, ref
luid))
{
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token))
{
tkNew.PrivilegeCount = 1;
tkNew.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED;
tkNew.Privileges[0].Luid = luid;
int luidSize =
Marshal.SizeOf(typeof(TOKEN_PRIVILEGES));
if (AdjustTokenPrivileges(token, 0, ref tkNew,
luidSize, ref tkOld, ref oldluidSize))
{
return true;
}
}
}
return false;
}
}
}
/************ CODE ************************/
 
Well, the signatures for the time APIs are correct (mostly) along with your
SystemTime struct. Win32 BOOL is actually an int. Yes I know the marshalling
will happen automatically, I'm just picky. Might want to turn off the UAC
and try it again to see if that's causing the problem, I had no problem on
my machine with UAC off. If so, there's a problem with how you're elevating
the privileges of the current user.


I am posting at the end of this post some code that P/Invoke's
SetSystemTime to set the local system time. This call fails -- i.e
the time is not set and the API returns false. However calling
Marshal.GetLastWin32Error immediately afterwards returns an errorcode
of 0 indicating that the operation somehow executed successfully.

This is a plain vanilla console app written in C#. It runs on vista
and has a manifest embedded in it that has 'requireAdministrator'
privilege level on it. The logged on user belongs to the
administrator group and the UAC is set at "prompt for consent".

What am I missing? (FWIW, I didn't write the P/Invoke signatures
myself. I just lifted them out of various postings on the internet --
is there a mistake in that somewhere)?

It seems similar to what was reported here:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1807068&SiteID=1

/********** CODE ********************/
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace VistaUACTest
{
class Program
{
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct SystemTime
{
public short year;
public short month;
public short dayOfWeek;
public short day;
public short hour;
public short minute;
public short second;
public short milliseconds;
}

[DllImport("kernel32.dll")]
private static extern bool SetLocalTime(ref SystemTime
systime);

[DllImport("kernel32.dll")]
private static extern bool SetSystemTime(ref SystemTime
systime);

private const int ANYSIZE_ARRAY = 1;
private const string SE_SYSTEMTIME_NAME =
"SeSystemtimePrivilege";
private const int SE_PRIVILEGE_ENABLED = 0x00000002;
private const int TOKEN_QUERY = 0x0008;
private const int TOKEN_ADJUST_PRIVILEGES = 0x0020;

[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public int LowPart;
public int HighPart;
}

[StructLayout(LayoutKind.Sequential)]
public struct LUID_AND_ATTRIBUTES
{
public LUID Luid;
public int Attributes;
}

[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_PRIVILEGES
{
public int PrivilegeCount;
[MarshalAs(UnmanagedType.ByValArray, SizeConst =
ANYSIZE_ARRAY)]
public LUID_AND_ATTRIBUTES[] Privileges;
}

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool OpenProcessToken(int ProcessHandle,
int DesiredAccess, ref int TokenHandle);

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetCurrentProcess();

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LookupPrivilegeValue(string
lpSystemName, string lpName,

[MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);

[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool AdjustTokenPrivileges(int
TokenHandle,
int
DisableAllPrivileges,

[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES NewState,
int
BufferLength,

[MarshalAs(UnmanagedType.Struct)] ref TOKEN_PRIVILEGES PreviousState,
ref
int ReturnLength);

static void Main(string[] args)
{
try
{
// because the docs for SetSystemTime says that the
SE_SYSTEMTIME_NAME privilege is disabled by default
// this call doesn't really help though
AdjustPrivileges();
SystemTime st = new SystemTime();
st.hour = 10;
if (!SetSystemTime(ref st))
{
// the return value says the call failed, however
Marshal.GetLastWin32Error() says the previous P/Invoke operation
// completed successfully (the default ctor of
Win32Exception calls GetLastWin32Error)
throw new Win32Exception();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.Read();
}

public static bool AdjustPrivileges()
{
TOKEN_PRIVILEGES tkNew = new TOKEN_PRIVILEGES();
tkNew.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];
TOKEN_PRIVILEGES tkOld = new TOKEN_PRIVILEGES();
tkOld.Privileges = new LUID_AND_ATTRIBUTES[ANYSIZE_ARRAY];

LUID luid = new LUID();
int token = -1;
int oldluidSize = 0;

if (LookupPrivilegeValue(null, SE_SYSTEMTIME_NAME, ref
luid))
{
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token))
{
tkNew.PrivilegeCount = 1;
tkNew.Privileges[0].Attributes =
SE_PRIVILEGE_ENABLED;
tkNew.Privileges[0].Luid = luid;
int luidSize =
Marshal.SizeOf(typeof(TOKEN_PRIVILEGES));
if (AdjustTokenPrivileges(token, 0, ref tkNew,
luidSize, ref tkOld, ref oldluidSize))
{
return true;
}
}
}
return false;
}
}
}
/************ CODE ************************/
 
Well, the signatures for the time APIs are correct (mostly) along with your
SystemTime struct. Win32 BOOL is actually an int. Yes I know the marshalling
will happen automatically, I'm just picky. Might want to turn off the UAC
and try it again to see if that's causing the problem, I had no problem on
my machine with UAC off. If so, there's a problem with how you're elevating
the privileges of the current user.

Jeff
Thanks for responding. The privilege adjusting code is there for no
particular reason. I believe the admin privileges already include
SE_SYSTEMTIME_NAME (which is enabled).

In any case, the funny thing is I can't get it to work even:

* if I run this application from a elevated command prompt
* if I mark the manifest as 'requireAdministrator' and grant consent
to the UAC dialog that pops up.

I thought the latter option was at least morally the same as turning
off UAC? I will turn off UAC and see how that goes.

There is another weird thing going on here. SetSystemTime seems to
return false indicating the call failed (and the time obviously isn't
set), however if I call Marshal.GetLastWin32Error() immediately after,
it returns 0 saying "the operation completed successfully".

Does that make sense to you?
 
Back
Top