Subversion Repositories Aucun

Rev

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