Subversion Repositories Aucun

Rev

Rev 71 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
70 ixe013 1
// SecurityHelper.cpp
2
//
3
// Routines that interface with Win32 and LSA security APIs
4
//
5
 
6
#include "SecurityHelper.h"
7
#include "trace.h"
8
 
9
 
10
// be sure we link with the LSA import library
11
#pragma comment(lib, "secur32.lib")
12
 
13
// excerpt from a DDK header file that we can't easily include here
14
#define STATUS_PASSWORD_EXPIRED         ((NTSTATUS)0xC0000071L)
15
 
16
int _stringLenInBytes(const wchar_t* s)
17
{
18
   if (!s) return 0;
19
   return (lstrlen(s)+1) * sizeof *s;
20
}
21
 
22
void _initUnicodeString(UNICODE_STRING* target, wchar_t* source, USHORT cbMax)
23
{
24
   target->Length        = cbMax - sizeof *source;
25
   target->MaximumLength = cbMax;
26
   target->Buffer        = source;
27
}
28
 
29
MSV1_0_INTERACTIVE_LOGON* _allocLogonRequest(
30
   const wchar_t* domain,
31
   const wchar_t* user,
32
   const wchar_t* pass,
33
   DWORD* pcbRequest)
34
{
35
 
36
   const DWORD cbHeader = sizeof(MSV1_0_INTERACTIVE_LOGON);
37
   const DWORD cbDom    = _stringLenInBytes(domain);
38
   const DWORD cbUser   = _stringLenInBytes(user);
39
   const DWORD cbPass   = _stringLenInBytes(pass);
40
 
41
   // sanity check string lengths
42
   if (cbDom > USHRT_MAX || cbUser > USHRT_MAX || cbPass > USHRT_MAX)
43
   {
44
      TRACE(L"Input string was too long");
45
      return 0;
46
   }
47
 
48
   *pcbRequest = cbHeader + cbDom + cbUser + cbPass;
49
 
50
   MSV1_0_INTERACTIVE_LOGON* pRequest = (MSV1_0_INTERACTIVE_LOGON*)new char[*pcbRequest];
51
   if (!pRequest)
52
   {
53
      TRACE(L"Out of memory, wtf?");
54
 
55
   }
56
 
57
   pRequest->MessageType = MsV1_0InteractiveLogon;
58
59
   char* p = (char*)(pRequest + 1); // point past MSV1_0_INTERACTIVE_LOGON header
60
 
61
   wchar_t* pDom  = (wchar_t*)(p);
62
   wchar_t* pUser = (wchar_t*)(p + cbDom);
63
   wchar_t* pPass = (wchar_t*)(p + cbDom + cbUser);
64
 
65
   CopyMemory(pDom,  domain, cbDom);
66
   CopyMemory(pUser, user,   cbUser);
67
   CopyMemory(pPass, pass,   cbPass);
68
 
69
   _initUnicodeString(&pRequest->LogonDomainName, pDom, (USHORT)cbDom);
70
   _initUnicodeString(&pRequest->UserName,        pUser, (USHORT)cbUser);
71
 
72
73
   return pRequest;
74
}
75
 
76
BOOL _newLsaString(LSA_STRING* target, const char* source)
77
{
78
 
79
80
   const int cch = lstrlenA(source);
81
 
82
83
   // UNICODE_STRINGs have a size limit
84
   if (cchWithNullTerminator * sizeof(*source) > USHRT_MAX) return FALSE;
85
86
 
87
   if (!newStr)
88
   {
89
      TRACE(L"Out of memory, wtf?");
90
 
91
   }
92
93
 
94
95
   target->Length        = (USHORT)cch                   * sizeof *newStr;
96
   target->MaximumLength = (USHORT)cchWithNullTerminator * sizeof *newStr;
97
   target->Buffer        = newStr;
98
99
 
100
}
101
102
void _deleteLsaString(LSA_STRING* target)
103
 
104
   delete target->Buffer;
105
   target->Buffer = 0;
106
 
107
108
BOOL RegisterLogonProcess(const char* logonProcessName, HANDLE* phLsa)
109
 
110
   *phLsa = 0;
111
 
112
   LSA_STRING name;
113
   if (!_newLsaString(&name, logonProcessName)) return FALSE;
114
115
   LSA_OPERATIONAL_MODE unused;
116
   NTSTATUS status = LsaRegisterLogonProcess(&name, phLsa, &unused);
117
118
   _deleteLsaString(&name);
119
120
 
121
   {
122
      *phLsa = 0;
123
      TRACE(L"LsaRegisterLogonProcess failed: %d", LsaNtStatusToWinError(status));
124
 
125
   }
126
   return TRUE;
127
}
128
 
129
BOOL CallLsaLogonUser(HANDLE hLsa, const wchar_t* domain, const wchar_t* user, const wchar_t* pass,
130
 
131
{
132
133
   BOOL result      = FALSE;
134
   DWORD win32Error = 0;
135
 
136
137
   LUID ignoredLogonSessionId;
138
139
   // optional arguments
140
   if (ppProfile)        *ppProfile   = 0;
141
   if (pWin32Error)      *pWin32Error = 0;
142
   if (!pLogonSessionId) pLogonSessionId = &ignoredLogonSessionId;
143
 
144
   LSA_STRING logonProcessName            = { 0 };
145
   TOKEN_SOURCE sourceContext             = { 's', 'a', 'm', 'p', 'l', 'e' };
146
   MSV1_0_INTERACTIVE_PROFILE* pProfile = 0;
147
   ULONG cbProfile = 0;
148
   QUOTA_LIMITS quotaLimits;
149
   NTSTATUS substatus;
150
   DWORD cbLogonRequest;
151
 
152
   MSV1_0_INTERACTIVE_LOGON* pLogonRequest =
153
      _allocLogonRequest(domain, user, pass, &cbLogonRequest);
154
   if (!pLogonRequest)
155
   {
156
      win32Error = ERROR_NOT_ENOUGH_MEMORY;
157
 
158
   }
159
160
   if (!_newLsaString(&logonProcessName, LOGON_PROCESS_NAME))
161
   {
162
      win32Error = ERROR_NOT_ENOUGH_MEMORY;
163
      goto cleanup;
164
   }
165
166
   // LsaLogonUser - the function from hell
167
   NTSTATUS status = LsaLogonUser(
168
                        hLsa,
169
                        &logonProcessName,  // we use our logon process name for the "origin name"
170
                        logonType,
171
                        LOGON32_PROVIDER_DEFAULT, // we use MSV1_0 or Kerb, whichever is supported
172
                        pLogonRequest,
173
                        cbLogonRequest,
174
 
175
                        &sourceContext,
176
                        (void**)&pProfile,  // caller must free this via LsaFreeReturnBuffer
177
                        &cbProfile,
178
 
179
                        phToken,
180
                        &quotaLimits,       // we ignore this, but must pass in anyway
181
                        &substatus);
182
183
 
184
   {
185
      win32Error = LsaNtStatusToWinError(status);
186
187
 
188
      {
189
         win32Error = ERROR_PASSWORD_EXPIRED;
190
 
191
192
      *phToken = 0;
193
      pProfile = 0;
194
      TRACE(L"LsaLogonUser failed. Status = %d, substatus = 0x%08X", win32Error, substatus);
195
196
      goto cleanup;
197
 
198
199
   if (ppProfile)
200
   {
201
 
202
      pProfile = 0;
203
   }
204
   result = TRUE;
205
206
cleanup:
207
   // if caller cares about the details, pass them on
208
   if (pWin32Error) *pWin32Error = win32Error;
209
 
210
   if (pLogonRequest)
211
   {
212
 
213
      delete pLogonRequest;
214
   }
215
   if (pProfile) LsaFreeReturnBuffer(pProfile);
216
   _deleteLsaString(&logonProcessName);
217
218
   return result;
219
}
220
221
BOOL GetLogonSid(HANDLE htok, void* psid, DWORD cbMax)
222
{
223
   DWORD cb;
224
   GetTokenInformation(htok, TokenGroups, 0, 0, &cb);
225
   TOKEN_GROUPS* ptg = (TOKEN_GROUPS*)LocalAlloc(LMEM_FIXED, cb);
226
   if (!ptg)
227
   {
228
      TRACE(L"Out of memory, wtf?");
229
      return FALSE;
230
   }
231
232
   BOOL success = FALSE;
233
   if (GetTokenInformation(htok, TokenGroups, ptg, cb, &cb))
234
   {
235
      DWORD i;
236
      // search for the logon SID
237
      for (i = 0; i < ptg->GroupCount; ++i)
238
      {
239
         if (ptg->Groups[i].Attributes & SE_GROUP_LOGON_ID)
240
         {
241
            void* logonSid = ptg->Groups[i].Sid;
242
            const DWORD cb = GetLengthSid(logonSid);
243
            if (cbMax < cb) return FALSE; // sanity check caller's buffer size
244
            if (!CopySid(cb, psid, logonSid))
245
            {
246
               TRACE(L"CopySid failed: %d", GetLastError());
247
 
248
            }
249
 
250
            break;
251
         }
252
 
253
      if (i == ptg->GroupCount)
254
      {
255
         TRACE(L"Failed to find a logon SID in the user's access token!");
256
      }
257
   }
258
   else TRACE(L"GetTokenInformation(TokenGroups) failed: %d", GetLastError());
259
260
   LocalFree(ptg);
261
262
   return success;
263
}
264
265
BOOL GetLogonSessionId(HANDLE htok, LUID* pluid)
266
{
267
   TOKEN_STATISTICS stats;
268
 
269
   if (GetTokenInformation(htok, TokenStatistics, &stats, cb, &cb))
270
   {
271
      *pluid = stats.AuthenticationId;
272
      return TRUE;
273
   }
274
   else
275
   {
276
      TRACE(L"GetTokenInformation(TokenStatistics) failed: %d", GetLastError());
277
      return FALSE;
278
   }
279
}
280
 
281
// caller must free *ppProfilePath using LocalFree
282
BOOL ExtractProfilePath(wchar_t** ppProfilePath, MSV1_0_INTERACTIVE_PROFILE* pProfile)
283
{
284
   *ppProfilePath = 0;
285
   if (0 == pProfile->ProfilePath.Length)
286
   {
287
      // no profile path was specified, so return a null pointer to WinLogon
288
 
289
      return TRUE;
290
   }
291
   BOOL result = FALSE;
292
293
 
294
   wchar_t* profilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * (cch + 1)); // I never assume a UNICODE_STRING is null terminated
295
   if (profilePath)
296
   {
297
      // copy the string data and manually null terminate it
298
      CopyMemory(profilePath, pProfile->ProfilePath.Buffer, pProfile->ProfilePath.Length);
299
      profilePath[cch] = L'\0';
300
301
      *ppProfilePath = profilePath;
302
 
303
   else TRACE(L"Out of memory, wtf?");
304
305
   return result;
306
}
307
308
 
309
{
310
 
311
   *ppWinLogonProfile = 0;
312
   if (!profilePath)
313
   {
314
      // no profile path was specified, so return a null pointer to WinLogon
315
      // to indicate that *it* should figure out the appropriate path
316
 
317
   }
318
   BOOL result = FALSE;
319
 
320
   // must use LocalAlloc for this - WinLogon will use LocalFree
321
   WLX_PROFILE_V1_0* profile = (WLX_PROFILE_V1_0*)LocalAlloc(LMEM_FIXED, sizeof(WLX_PROFILE_V1_0));
322
   if (profile)
323
   {
324
      profile->dwType = WLX_PROFILE_TYPE_V1_0;
325
326
      const int cch = lstrlen(profilePath) + 1;
327
328
      wchar_t* newProfilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, cch * sizeof *newProfilePath);
329
      if (newProfilePath)
330
      {
331
 
332
         CopyMemory(newProfilePath, profilePath, cch * sizeof *newProfilePath);
333
334
         profile->pszProfile = newProfilePath;
335
         *ppWinLogonProfile = profile;
336
337
         result = TRUE;
338
      }
339
      else TRACE(L"Out of memory, wtf?");
340
   }
341
   else TRACE(L"Out of memory, wtf?");
342
343
 
344
}
345
 
346
BOOL CreateProcessAsUserOnDesktop(HANDLE hToken, wchar_t* programImage, wchar_t* desktop, void* env)
347
{
348
   // impersonate the user to ensure that they are allowed
349
 
350
   if (!ImpersonateLoggedOnUser(hToken))
351
   {
352
      TRACE(L"ImpersonateLoggedOnUser failed: %d", GetLastError());
353
      return FALSE;
354
   }
355
356
   STARTUPINFO si = { sizeof si, 0, desktop };
357
   PROCESS_INFORMATION pi;
358
   if (!CreateProcessAsUser(hToken, programImage, programImage, 0, 0, FALSE,
359
                            CREATE_UNICODE_ENVIRONMENT, env, 0, &si, &pi))
360
   {
361
      RevertToSelf();
362
 
363
      return FALSE;
364
   }
365
 
366
   CloseHandle(pi.hThread);
367
368
   RevertToSelf();
369
370
   TRACE(L"Successfully launched %s", programImage);
371
 
372
}
373
374
BOOL ImpersonateAndGetUserName(HANDLE hToken, wchar_t* name, int cch)
375
 
376
   BOOL result = FALSE;
377
   if (ImpersonateLoggedOnUser(hToken))
378
   {
379
      DWORD cchName = cch;
380
      if (GetUserName(name, &cchName))
381
         result = TRUE;
382
      else TRACE(L"GetUserName failed: %d", GetLastError());
383
      RevertToSelf();
384
 
385
   else TRACE(L"ImpersonateLoggedOnUser failed: %d", GetLastError());
386
387
 
388
}
389
390
// checks user SID in both tokens for equality
391
BOOL IsSameUser(HANDLE hToken1, HANDLE hToken2, BOOL* pbIsSameUser)
392
 
393
   *pbIsSameUser = FALSE;
394
   BOOL result = FALSE;
395
396
   const DWORD bufSize = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
397
   char buf1[bufSize];
398
   char buf2[bufSize];
399
400
   DWORD cb;
401
   if (GetTokenInformation(hToken1, TokenUser, buf1, bufSize, &cb) &&
402
         GetTokenInformation(hToken2, TokenUser, buf2, bufSize, &cb))
403
   {
404
      *pbIsSameUser = EqualSid(((TOKEN_USER*)buf1)->User.Sid, ((TOKEN_USER*)buf2)->User.Sid) ? TRUE : FALSE;
405
 
406
   }
407
   else TRACE(L"GetTokenInformation failed: %d", GetLastError());
408
409
   return result;
410
}
411
412
void* _administratorsAlias()
413
{
414
   const int subAuthCount = 2;
415
   static char sid[sizeof(SID) + subAuthCount * sizeof(DWORD)];
416
417
   SID* psid = (SID*)sid;
418
   if (0 == psid->Revision)
419
   {
420
      // first time called, initialize the sid
421
      psid->IdentifierAuthority.Value[5] = 5; // NT Authority
422
 
423
      psid->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
424
      psid->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
425
      psid->Revision = 1;
426
 
427
   return sid;
428
}