Modules/ComputerManagementDsc.Common/SetTimeZone.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
// This file enables setting of the machine time zone
// using .NET framework classes.
// It can be used to set the time zone when the
// PowerShell time zone cmdlets are not available.
//
// It creates a namespace Microsoft.PowerShell.TimeZone
// containing the classes and structures required to
// set the time zone.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace Microsoft.PowerShell.TimeZone
{
    [StructLayoutAttribute(LayoutKind.Sequential)]
    public struct SystemTime
    {
        [MarshalAs(UnmanagedType.U2)]
        public short Year;
        [MarshalAs(UnmanagedType.U2)]
        public short Month;
        [MarshalAs(UnmanagedType.U2)]
        public short DayOfWeek;
        [MarshalAs(UnmanagedType.U2)]
        public short Day;
        [MarshalAs(UnmanagedType.U2)]
        public short Hour;
        [MarshalAs(UnmanagedType.U2)]
        public short Minute;
        [MarshalAs(UnmanagedType.U2)]
        public short Second;
        [MarshalAs(UnmanagedType.U2)]
        public short Milliseconds;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct TimeZoneInformation
    {
        [MarshalAs(UnmanagedType.I4)]
        public int Bias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
        public string StandardName;
        public SystemTime StandardDate;
        [MarshalAs(UnmanagedType.I4)]
        public int StandardBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
        public string DaylightName;
        public SystemTime DaylightDate;
        [MarshalAs(UnmanagedType.I4)]
        public int DaylightBias;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct DynamicTimeZoneInformation
    {
        public int Bias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string StandardName;
        public SystemTime StandardDate;
        public int StandardBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        public string DaylightName;
        public SystemTime DaylightDate;
        public int DaylightBias;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string TimeZoneKeyName;
        [MarshalAs(UnmanagedType.U1)]
        public bool DynamicDaylightTimeDisabled;
    }
    [StructLayout(LayoutKind.Sequential)]
    public struct RegistryTimeZoneInformation
    {
        [MarshalAs(UnmanagedType.I4)]
        public int Bias;
        [MarshalAs(UnmanagedType.I4)]
        public int StandardBias;
        [MarshalAs(UnmanagedType.I4)]
        public int DaylightBias;
        public SystemTime StandardDate;
        public SystemTime DaylightDate;
        public RegistryTimeZoneInformation(TimeZoneInformation tzi)
        {
            this.Bias = tzi.Bias;
            this.StandardDate = tzi.StandardDate;
            this.StandardBias = tzi.StandardBias;
            this.DaylightDate = tzi.DaylightDate;
            this.DaylightBias = tzi.DaylightBias;
        }
        public RegistryTimeZoneInformation(byte[] bytes)
        {
            if ((bytes == null) || (bytes.Length != 0x2c))
            {
                throw new ArgumentException("Argument_InvalidREG_TZI_FORMAT");
            }
            this.Bias = BitConverter.ToInt32(bytes, 0);
            this.StandardBias = BitConverter.ToInt32(bytes, 4);
            this.DaylightBias = BitConverter.ToInt32(bytes, 8);
            this.StandardDate.Year = BitConverter.ToInt16(bytes, 12);
            this.StandardDate.Month = BitConverter.ToInt16(bytes, 14);
            this.StandardDate.DayOfWeek = BitConverter.ToInt16(bytes, 0x10);
            this.StandardDate.Day = BitConverter.ToInt16(bytes, 0x12);
            this.StandardDate.Hour = BitConverter.ToInt16(bytes, 20);
            this.StandardDate.Minute = BitConverter.ToInt16(bytes, 0x16);
            this.StandardDate.Second = BitConverter.ToInt16(bytes, 0x18);
            this.StandardDate.Milliseconds = BitConverter.ToInt16(bytes, 0x1a);
            this.DaylightDate.Year = BitConverter.ToInt16(bytes, 0x1c);
            this.DaylightDate.Month = BitConverter.ToInt16(bytes, 30);
            this.DaylightDate.DayOfWeek = BitConverter.ToInt16(bytes, 0x20);
            this.DaylightDate.Day = BitConverter.ToInt16(bytes, 0x22);
            this.DaylightDate.Hour = BitConverter.ToInt16(bytes, 0x24);
            this.DaylightDate.Minute = BitConverter.ToInt16(bytes, 0x26);
            this.DaylightDate.Second = BitConverter.ToInt16(bytes, 40);
            this.DaylightDate.Milliseconds = BitConverter.ToInt16(bytes, 0x2a);
        }
    }
    public class TokenPrivilegesAccess
    {
        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        public static extern int 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 int LookupPrivilegeValue(string lpsystemname, string lpname,
        [MarshalAs(UnmanagedType.Struct)] ref LUID lpLuid);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto)]
        public static extern int AdjustTokenPrivileges(int tokenhandle, int disableprivs,
            [MarshalAs(UnmanagedType.Struct)]ref TOKEN_PRIVILEGE Newstate, int bufferlength,
            int PreivousState, int Returnlength);
        public const int TOKEN_ASSIGN_PRIMARY = 0x00000001;
        public const int TOKEN_DUPLICATE = 0x00000002;
        public const int TOKEN_IMPERSONATE = 0x00000004;
        public const int TOKEN_QUERY = 0x00000008;
        public const int TOKEN_QUERY_SOURCE = 0x00000010;
        public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
        public const int TOKEN_ADJUST_GROUPS = 0x00000040;
        public const int TOKEN_ADJUST_DEFAULT = 0x00000080;
        public const UInt32 SE_PRIVILEGE_ENABLED_BY_DEFAULT = 0x00000001;
        public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002;
        public const UInt32 SE_PRIVILEGE_REMOVED = 0x00000004;
        public const UInt32 SE_PRIVILEGE_USED_FOR_ACCESS = 0x80000000;
        public static bool EnablePrivilege(string privilege)
        {
            try
            {
                int token = 0;
                int retVal = 0;
                TOKEN_PRIVILEGE TP = new TOKEN_PRIVILEGE();
                LUID LD = new LUID();
                retVal = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
                retVal = LookupPrivilegeValue(null, privilege, ref LD);
                TP.PrivilegeCount = 1;
                var luidAndAtt = new LUID_AND_ATTRIBUTES();
                luidAndAtt.Attributes = SE_PRIVILEGE_ENABLED;
                luidAndAtt.Luid = LD;
                TP.Privilege = luidAndAtt;
                retVal = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
                return true;
            }
            catch
            {
                return false;
            }
        }
        public static bool DisablePrivilege(string privilege)
        {
            try
            {
                int token = 0;
                int retVal = 0;
                TOKEN_PRIVILEGE TP = new TOKEN_PRIVILEGE();
                LUID LD = new LUID();
                retVal = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref token);
                retVal = LookupPrivilegeValue(null, privilege, ref LD);
                TP.PrivilegeCount = 1;
                // TP.Attributes should be none (not set) to disable privilege
                var luidAndAtt = new LUID_AND_ATTRIBUTES();
                luidAndAtt.Luid = LD;
                TP.Privilege = luidAndAtt;
                retVal = AdjustTokenPrivileges(token, 0, ref TP, 1024, 0, 0);
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct LUID
    {
        internal uint LowPart;
        internal uint HighPart;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct LUID_AND_ATTRIBUTES
    {
        internal LUID Luid;
        internal uint Attributes;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct TOKEN_PRIVILEGE
    {
        internal uint PrivilegeCount;
        internal LUID_AND_ATTRIBUTES Privilege;
    }
    public class TimeZone
    {
        public const int ERROR_ACCESS_DENIED = 0x005;
        public const int CORSEC_E_MISSING_STRONGNAME = -2146233317;
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool SetTimeZoneInformation([In] ref TimeZoneInformation lpTimeZoneInformation);
        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool SetDynamicTimeZoneInformation([In] ref DynamicTimeZoneInformation lpTimeZoneInformation);
        public static void Set(string name)
        {
            var regTimeZones = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones");
            var subKey = regTimeZones.GetSubKeyNames().Where(s => s == name).First();
            string daylightName = (string)regTimeZones.OpenSubKey(subKey).GetValue("Dlt");
            string standardName = (string)regTimeZones.OpenSubKey(subKey).GetValue("Std");
            byte[] tzi = (byte[])regTimeZones.OpenSubKey(subKey).GetValue("TZI");
            var regTzi = new RegistryTimeZoneInformation(tzi);
            TokenPrivilegesAccess.EnablePrivilege("SeTimeZonePrivilege");
            bool didSet;
            if (Environment.OSVersion.Version.Major < 6)
            {
                var tz = new TimeZoneInformation();
                tz.Bias = regTzi.Bias;
                tz.DaylightBias = regTzi.DaylightBias;
                tz.StandardBias = regTzi.StandardBias;
                tz.DaylightDate = regTzi.DaylightDate;
                tz.StandardDate = regTzi.StandardDate;
                tz.DaylightName = daylightName;
                tz.StandardName = standardName;
                didSet = TimeZone.SetTimeZoneInformation(ref tz);
            }
            else
            {
                var tz = new DynamicTimeZoneInformation();
                tz.Bias = regTzi.Bias;
                tz.DaylightBias = regTzi.DaylightBias;
                tz.StandardBias = regTzi.StandardBias;
                tz.DaylightDate = regTzi.DaylightDate;
                tz.StandardDate = regTzi.StandardDate;
                tz.DaylightName = daylightName;
                tz.StandardName = standardName;
                tz.TimeZoneKeyName = subKey;
                tz.DynamicDaylightTimeDisabled = false;
                didSet = TimeZone.SetDynamicTimeZoneInformation(ref tz);
            }
            int lastError = Marshal.GetLastWin32Error();
            TokenPrivilegesAccess.DisablePrivilege("SeTimeZonePrivilege");
            if (! didSet)
            {
                if (lastError == TimeZone.ERROR_ACCESS_DENIED)
                {
                    throw new SecurityException("Access denied changing System TimeZone.");
                }
                else if (lastError == TimeZone.CORSEC_E_MISSING_STRONGNAME)
                {
                    throw new SystemException("Application is not signed.");
                }
                else
                {
                    throw new SystemException("Win32Error: " + lastError + "\nHRESULT: " + Marshal.GetHRForLastWin32Error());
                }
            }
        }
    }
}