Subversion Repositories Aucun

Rev

Rev 71 | Rev 83 | Go to most recent revision | 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
219
BOOL GetLogonSid(HANDLE htok, void* psid, DWORD cbMax)
220
{
221
   DWORD cb;
222
   GetTokenInformation(htok, TokenGroups, 0, 0, &cb);
223
   TOKEN_GROUPS* ptg = (TOKEN_GROUPS*)LocalAlloc(LMEM_FIXED, cb);
224
   if (!ptg)
225
   {
71 ixe013 226
      TRACE(L"Out of memory, wtf?.\n");
70 ixe013 227
      return FALSE;
228
   }
229
230
   BOOL success = FALSE;
231
   if (GetTokenInformation(htok, TokenGroups, ptg, cb, &cb))
232
   {
233
      DWORD i;
234
      // search for the logon SID
235
      for (i = 0; i < ptg->GroupCount; ++i)
236
      {
237
         if (ptg->Groups[i].Attributes & SE_GROUP_LOGON_ID)
238
         {
239
            void* logonSid = ptg->Groups[i].Sid;
240
            const DWORD cb = GetLengthSid(logonSid);
241
            if (cbMax < cb) return FALSE; // sanity check caller's buffer size
242
            if (!CopySid(cb, psid, logonSid))
243
            {
71 ixe013 244
               TRACE(L"CopySid failed: %d\n", GetLastError());
70 ixe013 245
 
246
            }
247
 
248
            break;
249
         }
250
 
251
      if (i == ptg->GroupCount)
252
      {
71 ixe013 253
         TRACE(L"Failed to find a logon SID in the user's access token!.\n");
70 ixe013 254
      }
255
   }
71 ixe013 256
   else TRACE(L"GetTokenInformation(TokenGroups) failed: %d\n", GetLastError());
70 ixe013 257
258
   LocalFree(ptg);
259
260
   return success;
261
}
262
263
BOOL GetLogonSessionId(HANDLE htok, LUID* pluid)
264
{
265
   TOKEN_STATISTICS stats;
266
 
267
   if (GetTokenInformation(htok, TokenStatistics, &stats, cb, &cb))
268
   {
269
      *pluid = stats.AuthenticationId;
270
      return TRUE;
271
   }
272
   else
273
   {
71 ixe013 274
      TRACE(L"GetTokenInformation(TokenStatistics) failed: %d\n", GetLastError());
70 ixe013 275
      return FALSE;
276
   }
277
}
278
 
279
// caller must free *ppProfilePath using LocalFree
280
BOOL ExtractProfilePath(wchar_t** ppProfilePath, MSV1_0_INTERACTIVE_PROFILE* pProfile)
281
{
282
   *ppProfilePath = 0;
283
   if (0 == pProfile->ProfilePath.Length)
284
   {
285
      // no profile path was specified, so return a null pointer to WinLogon
286
 
287
      return TRUE;
288
   }
289
   BOOL result = FALSE;
290
291
 
292
   wchar_t* profilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * (cch + 1)); // I never assume a UNICODE_STRING is null terminated
293
   if (profilePath)
294
   {
295
      // copy the string data and manually null terminate it
296
      CopyMemory(profilePath, pProfile->ProfilePath.Buffer, pProfile->ProfilePath.Length);
297
      profilePath[cch] = L'\0';
298
299
      *ppProfilePath = profilePath;
300
 
71 ixe013 301
   else TRACE(L"Out of memory, wtf?.\n");
70 ixe013 302
303
   return result;
304
}
305
306
 
307
{
308
 
309
   *ppWinLogonProfile = 0;
310
   if (!profilePath)
311
   {
312
      // no profile path was specified, so return a null pointer to WinLogon
313
      // to indicate that *it* should figure out the appropriate path
314
 
315
   }
316
   BOOL result = FALSE;
317
 
318
   // must use LocalAlloc for this - WinLogon will use LocalFree
319
   WLX_PROFILE_V1_0* profile = (WLX_PROFILE_V1_0*)LocalAlloc(LMEM_FIXED, sizeof(WLX_PROFILE_V1_0));
320
   if (profile)
321
   {
322
      profile->dwType = WLX_PROFILE_TYPE_V1_0;
323
324
      const int cch = lstrlen(profilePath) + 1;
325
326
      wchar_t* newProfilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, cch * sizeof *newProfilePath);
327
      if (newProfilePath)
328
      {
329
 
330
         CopyMemory(newProfilePath, profilePath, cch * sizeof *newProfilePath);
331
332
         profile->pszProfile = newProfilePath;
333
         *ppWinLogonProfile = profile;
334
335
         result = TRUE;
336
      }
71 ixe013 337
      else TRACE(L"Out of memory, wtf?.\n");
70 ixe013 338
   }
71 ixe013 339
   else TRACE(L"Out of memory, wtf?.\n");
70 ixe013 340
341
 
342
}
343
 
344
BOOL CreateProcessAsUserOnDesktop(HANDLE hToken, wchar_t* programImage, wchar_t* desktop, void* env)
345
{
346
   // impersonate the user to ensure that they are allowed
347
 
348
   if (!ImpersonateLoggedOnUser(hToken))
349
   {
71 ixe013 350
      TRACE(L"ImpersonateLoggedOnUser failed: %d\n", GetLastError());
70 ixe013 351
      return FALSE;
352
   }
353
354
   STARTUPINFO si = { sizeof si, 0, desktop };
355
   PROCESS_INFORMATION pi;
356
   if (!CreateProcessAsUser(hToken, programImage, programImage, 0, 0, FALSE,
357
                            CREATE_UNICODE_ENVIRONMENT, env, 0, &si, &pi))
358
   {
359
      RevertToSelf();
71 ixe013 360
 
70 ixe013 361
      return FALSE;
362
   }
363
 
364
   CloseHandle(pi.hThread);
365
366
   RevertToSelf();
367
71 ixe013 368
   TRACE(L"Successfully launched %s\n", programImage);
70 ixe013 369
 
370
}
371
372
BOOL ImpersonateAndGetUserName(HANDLE hToken, wchar_t* name, int cch)
373
 
374
   BOOL result = FALSE;
375
   if (ImpersonateLoggedOnUser(hToken))
376
   {
377
      DWORD cchName = cch;
378
      if (GetUserName(name, &cchName))
379
         result = TRUE;
380
      else TRACE(L"GetUserName failed: %d", GetLastError());
381
      RevertToSelf();
382
 
71 ixe013 383
   else TRACE(L"ImpersonateLoggedOnUser failed: %d\n", GetLastError());
70 ixe013 384
385
 
386
}
387
388
// checks user SID in both tokens for equality
389
BOOL IsSameUser(HANDLE hToken1, HANDLE hToken2, BOOL* pbIsSameUser)
390
 
391
   *pbIsSameUser = FALSE;
392
   BOOL result = FALSE;
393
394
   const DWORD bufSize = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
395
   char buf1[bufSize];
396
   char buf2[bufSize];
397
398
   DWORD cb;
399
   if (GetTokenInformation(hToken1, TokenUser, buf1, bufSize, &cb) &&
400
         GetTokenInformation(hToken2, TokenUser, buf2, bufSize, &cb))
401
   {
402
      *pbIsSameUser = EqualSid(((TOKEN_USER*)buf1)->User.Sid, ((TOKEN_USER*)buf2)->User.Sid) ? TRUE : FALSE;
403
 
404
   }
71 ixe013 405
   else TRACE(L"GetTokenInformation failed: %d\n", GetLastError());
70 ixe013 406
407
   return result;
408
}
409
410
void* _administratorsAlias()
411
{
412
   const int subAuthCount = 2;
413
   static char sid[sizeof(SID) + subAuthCount * sizeof(DWORD)];
414
415
   SID* psid = (SID*)sid;
416
   if (0 == psid->Revision)
417
   {
418
      // first time called, initialize the sid
419
      psid->IdentifierAuthority.Value[5] = 5; // NT Authority
420
 
421
      psid->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
422
      psid->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
423
      psid->Revision = 1;
424
 
425
   return sid;
426
}