Subversion Repositories Aucun

Rev

Rev 207 | Rev 215 | 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
 
121 ixe013 6
//#include <security.h>
70 ixe013 7
#include "SecurityHelper.h"
8
#include "trace.h"
121 ixe013 9
#include <security.h>
133 ixe013 10
#include <sddl.h>
70 ixe013 11
 
137 ixe013 12
#include <lm.h>
13
#include "randpasswd.h"
14
 
207 ixe013 15
#include <stdlib.h>
16
 
70 ixe013 17
// excerpt from a DDK header file that we can't easily include here
18
#define STATUS_PASSWORD_EXPIRED         ((NTSTATUS)0xC0000071L)
19
 
20
int _stringLenInBytes(const wchar_t* s)
21
{
22
   if (!s) return 0;
23
   return (lstrlen(s)+1) * sizeof *s;
24
}
25
 
26
void _initUnicodeString(UNICODE_STRING* target, wchar_t* source, USHORT cbMax)
27
{
28
   target->Length        = cbMax - sizeof *source;
29
   target->MaximumLength = cbMax;
30
   target->Buffer        = source;
31
}
32
 
33
MSV1_0_INTERACTIVE_LOGON* _allocLogonRequest(
34
   const wchar_t* domain,
35
   const wchar_t* user,
36
   const wchar_t* pass,
37
   DWORD* pcbRequest)
38
{
39
 
40
   const DWORD cbHeader = sizeof(MSV1_0_INTERACTIVE_LOGON);
41
   const DWORD cbDom    = _stringLenInBytes(domain);
42
   const DWORD cbUser   = _stringLenInBytes(user);
43
   const DWORD cbPass   = _stringLenInBytes(pass);
44
 
45
   // sanity check string lengths
46
   if (cbDom > USHRT_MAX || cbUser > USHRT_MAX || cbPass > USHRT_MAX)
47
   {
122 ixe013 48
      TRACE(eERROR, L"Input string was too long.\n");
70 ixe013 49
      return 0;
50
   }
51
 
52
   *pcbRequest = cbHeader + cbDom + cbUser + cbPass;
53
 
54
   MSV1_0_INTERACTIVE_LOGON* pRequest = (MSV1_0_INTERACTIVE_LOGON*)new char[*pcbRequest];
55
   if (!pRequest)
56
   {
161 ixe013 57
      TRACE(eERROR, L"Out of memory, wtf?.\n");
70 ixe013 58
      return 0;
59
   }
60
 
61
   pRequest->MessageType = MsV1_0InteractiveLogon;
62
 
63
   char* p = (char*)(pRequest + 1); // point past MSV1_0_INTERACTIVE_LOGON header
64
 
65
   wchar_t* pDom  = (wchar_t*)(p);
66
   wchar_t* pUser = (wchar_t*)(p + cbDom);
67
   wchar_t* pPass = (wchar_t*)(p + cbDom + cbUser);
68
 
69
   CopyMemory(pDom,  domain, cbDom);
70
   CopyMemory(pUser, user,   cbUser);
71
   CopyMemory(pPass, pass,   cbPass);
72
 
73
   _initUnicodeString(&pRequest->LogonDomainName, pDom, (USHORT)cbDom);
74
   _initUnicodeString(&pRequest->UserName,        pUser, (USHORT)cbUser);
75
   _initUnicodeString(&pRequest->Password,        pPass, (USHORT)cbPass);
76
 
77
   return pRequest;
78
}
79
 
80
BOOL _newLsaString(LSA_STRING* target, const char* source)
81
{
82
   if (0 == source) return FALSE;
83
 
84
   const int cch = lstrlenA(source);
85
   const int cchWithNullTerminator = cch + 1;
86
 
87
   // UNICODE_STRINGs have a size limit
88
   if (cchWithNullTerminator * sizeof(*source) > USHRT_MAX) return FALSE;
89
 
90
   char* newStr = new char[cchWithNullTerminator];
91
   if (!newStr)
92
   {
161 ixe013 93
      TRACE(eERROR, L"Out of memory, wtf?.\n");
70 ixe013 94
      return FALSE;
95
   }
96
 
97
   CopyMemory(newStr, source, cchWithNullTerminator * sizeof *newStr);
98
 
99
   target->Length        = (USHORT)cch                   * sizeof *newStr;
100
   target->MaximumLength = (USHORT)cchWithNullTerminator * sizeof *newStr;
101
   target->Buffer        = newStr;
102
 
103
   return TRUE;
104
}
105
 
106
void _deleteLsaString(LSA_STRING* target)
107
{
207 ixe013 108
   delete[] target->Buffer;
70 ixe013 109
   target->Buffer = 0;
110
}
111
 
112
BOOL RegisterLogonProcess(const char* logonProcessName, HANDLE* phLsa)
113
{
114
   *phLsa = 0;
115
 
116
   LSA_STRING name;
117
   if (!_newLsaString(&name, logonProcessName)) return FALSE;
118
 
119
   LSA_OPERATIONAL_MODE unused;
120
   NTSTATUS status = LsaRegisterLogonProcess(&name, phLsa, &unused);
121
 
122
   _deleteLsaString(&name);
123
 
124
   if (status)
125
   {
126
      *phLsa = 0;
122 ixe013 127
      TRACE(eERROR, L"LsaRegisterLogonProcess failed: %d\n", LsaNtStatusToWinError(status));
70 ixe013 128
      return FALSE;
129
   }
130
   return TRUE;
131
}
132
 
207 ixe013 133
BOOL CallLsaLogonUser(HANDLE hLsa, const wchar_t* domain, const wchar_t* user, const wchar_t* pass, const wchar_t* authenticationPackageName,
133 ixe013 134
                      SECURITY_LOGON_TYPE logonType, LUID* pLogonSessionId, HANDLE* phToken, MSV1_0_INTERACTIVE_PROFILE** ppProfile, DWORD* pWin32Error)
70 ixe013 135
{
136
 
137
   BOOL result      = FALSE;
138
   DWORD win32Error = 0;
139
   *phToken         = 0;
140
 
141
   LUID ignoredLogonSessionId;
142
 
143
   // optional arguments
144
   if (ppProfile)        *ppProfile   = 0;
145
   if (pWin32Error)      *pWin32Error = 0;
146
   if (!pLogonSessionId) pLogonSessionId = &ignoredLogonSessionId;
147
 
148
   LSA_STRING logonProcessName            = { 0 };
76 ixe013 149
   TOKEN_SOURCE sourceContext             = { 'P', 'a', 'r', 'a', 'l', 'i', 'n', 't' };
150
 
70 ixe013 151
   MSV1_0_INTERACTIVE_PROFILE* pProfile = 0;
152
   ULONG cbProfile = 0;
153
   QUOTA_LIMITS quotaLimits;
154
   NTSTATUS substatus;
155
   DWORD cbLogonRequest;
156
 
157
   MSV1_0_INTERACTIVE_LOGON* pLogonRequest =
158
      _allocLogonRequest(domain, user, pass, &cbLogonRequest);
159
   if (!pLogonRequest)
160
   {
161
      win32Error = ERROR_NOT_ENOUGH_MEMORY;
162
      goto cleanup;
163
   }
164
 
165
   if (!_newLsaString(&logonProcessName, LOGON_PROCESS_NAME))
166
   {
167
      win32Error = ERROR_NOT_ENOUGH_MEMORY;
168
      goto cleanup;
169
   }
170
 
207 ixe013 171
   ULONG authenticationPackage = LOGON32_PROVIDER_DEFAULT;
172
 
173
    if(authenticationPackageName && *authenticationPackageName) {
174
       LSA_STRING customAuthenticationPackage            = { 0 };
175
       size_t n;
176
       char authenticationPackageAnsiName[127];
177
 
178
       wcstombs_s(&n, authenticationPackageAnsiName, sizeof(authenticationPackageAnsiName), authenticationPackageName, _TRUNCATE);
179
 
180
       if(_newLsaString(&customAuthenticationPackage, authenticationPackageAnsiName))
181
       {
182
            if(!LsaLookupAuthenticationPackage(hLsa, &customAuthenticationPackage, &authenticationPackage))
183
            {
184
                authenticationPackage = LOGON32_PROVIDER_DEFAULT; //safety
185
            }
186
 
187
            _deleteLsaString(&customAuthenticationPackage);
188
       }
189
    }
190
 
70 ixe013 191
   // LsaLogonUser - the function from hell
192
   NTSTATUS status = LsaLogonUser(
193
                        hLsa,
194
                        &logonProcessName,  // we use our logon process name for the "origin name"
195
                        logonType,
207 ixe013 196
                        authenticationPackage, // we use MSV1_0 or Kerb, whichever is supported
70 ixe013 197
                        pLogonRequest,
198
                        cbLogonRequest,
199
                        0,                  // we do not add any group SIDs
200
                        &sourceContext,
201
                        (void**)&pProfile,  // caller must free this via LsaFreeReturnBuffer
202
                        &cbProfile,
203
                        pLogonSessionId,
204
                        phToken,
205
                        &quotaLimits,       // we ignore this, but must pass in anyway
206
                        &substatus);
207
 
208
   if (status)
209
   {
210
      win32Error = LsaNtStatusToWinError(status);
211
 
212
      if ((ERROR_ACCOUNT_RESTRICTION == win32Error && STATUS_PASSWORD_EXPIRED == substatus))
213
      {
214
         win32Error = ERROR_PASSWORD_EXPIRED;
215
      }
216
 
217
      *phToken = 0;
218
      pProfile = 0;
76 ixe013 219
      TRACEMSG(win32Error);
70 ixe013 220
 
221
      goto cleanup;
222
   }
223
 
224
   if (ppProfile)
225
   {
226
      *ppProfile = (MSV1_0_INTERACTIVE_PROFILE*)pProfile;
227
      pProfile = 0;
228
   }
229
   result = TRUE;
230
 
231
cleanup:
232
   // if caller cares about the details, pass them on
233
   if (pWin32Error) *pWin32Error = win32Error;
234
 
235
   if (pLogonRequest)
236
   {
237
      SecureZeroMemory(pLogonRequest, cbLogonRequest);
238
      delete pLogonRequest;
239
   }
240
   if (pProfile) LsaFreeReturnBuffer(pProfile);
241
   _deleteLsaString(&logonProcessName);
242
 
243
   return result;
244
}
245
 
134 ixe013 246
BOOL GetSIDFromToken(HANDLE token, PSID *ppsid)
70 ixe013 247
{
133 ixe013 248
    BOOL result = false;
249
   DWORD infolen = 0;
70 ixe013 250
 
133 ixe013 251
   if(GetTokenInformation(token, TokenUser, 0, 0, &infolen) == FALSE)
70 ixe013 252
   {
133 ixe013 253
      if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
70 ixe013 254
      {
133 ixe013 255
          void *buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, infolen);
256
          GetTokenInformation(token, TokenUser, buf, infolen, &infolen);
257
          *ppsid = ((TOKEN_USER*)buf)->User.Sid;
134 ixe013 258
            result = true;
70 ixe013 259
      }
260
   }
133 ixe013 261
    return result;
262
}
70 ixe013 263
 
134 ixe013 264
BOOL GetSIDFromUsername(LPTSTR username, PSID *sid)
133 ixe013 265
{
266
   BOOL result = FALSE;
267
    DWORD sidsize = 0;
268
   TCHAR domain[1024];
269
    DWORD domainsize = sizeof domain / sizeof *domain;
270
   SID_NAME_USE snu = SidTypeUser ;
271
   *sid = 0;
272
   PSID mysid = 0;
273
 
274
//This is done just to know the buffer size for SID as well as Domain name
275
   result = LookupAccountName(0, username, mysid, &sidsize, domain, &domainsize, &snu);
276
 
277
   if (sidsize)
278
   {
279
       mysid = (PSID) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sidsize);
280
      result = LookupAccountName(0, username, mysid, &sidsize, domain, &domainsize, &snu);
281
   }
282
 
283
   *sid = mysid;
284
 
285
   return result;
286
}
287
 
288
/*
70 ixe013 289
BOOL GetLogonSessionId(HANDLE htok, LUID* pluid)
290
{
291
   TOKEN_STATISTICS stats;
292
   DWORD cb = sizeof stats;
293
   if (GetTokenInformation(htok, TokenStatistics, &stats, cb, &cb))
294
   {
295
      *pluid = stats.AuthenticationId;
296
      return TRUE;
297
   }
298
   else
299
   {
122 ixe013 300
      TRACE(eERROR, L"GetTokenInformation(TokenStatistics) failed: %d\n", GetLastError());
70 ixe013 301
      return FALSE;
302
   }
303
}
304
 
305
// caller must free *ppProfilePath using LocalFree
306
BOOL ExtractProfilePath(wchar_t** ppProfilePath, MSV1_0_INTERACTIVE_PROFILE* pProfile)
307
{
308
   *ppProfilePath = 0;
309
   if (0 == pProfile->ProfilePath.Length)
310
   {
311
      // no profile path was specified, so return a null pointer to WinLogon
312
      // to indicate that *it* should figure out the appropriate path
313
      return TRUE;
314
   }
315
   BOOL result = FALSE;
316
 
317
   const int cch = pProfile->ProfilePath.Length / sizeof(wchar_t);
318
   wchar_t* profilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, sizeof(wchar_t) * (cch + 1)); // I never assume a UNICODE_STRING is null terminated
319
   if (profilePath)
320
   {
321
      // copy the string data and manually null terminate it
322
      CopyMemory(profilePath, pProfile->ProfilePath.Buffer, pProfile->ProfilePath.Length);
323
      profilePath[cch] = L'\0';
324
 
325
      *ppProfilePath = profilePath;
326
   }
161 ixe013 327
   else TRACE(eERROR, L"Out of memory, wtf?.\n");
70 ixe013 328
 
329
   return result;
330
}
331
 
332
BOOL AllocWinLogonProfile(WLX_PROFILE_V1_0** ppWinLogonProfile, const wchar_t* profilePath)
333
{
334
 
335
   *ppWinLogonProfile = 0;
336
   if (!profilePath)
337
   {
338
      // no profile path was specified, so return a null pointer to WinLogon
339
      // to indicate that *it* should figure out the appropriate path
340
      return TRUE;
341
   }
342
   BOOL result = FALSE;
343
 
344
   // must use LocalAlloc for this - WinLogon will use LocalFree
345
   WLX_PROFILE_V1_0* profile = (WLX_PROFILE_V1_0*)LocalAlloc(LMEM_FIXED, sizeof(WLX_PROFILE_V1_0));
346
   if (profile)
347
   {
348
      profile->dwType = WLX_PROFILE_TYPE_V1_0;
349
 
350
      const int cch = lstrlen(profilePath) + 1;
351
 
352
      wchar_t* newProfilePath = (wchar_t*)LocalAlloc(LMEM_FIXED, cch * sizeof *newProfilePath);
353
      if (newProfilePath)
354
      {
355
         // copy the string data and manually null terminate it
356
         CopyMemory(newProfilePath, profilePath, cch * sizeof *newProfilePath);
357
 
358
         profile->pszProfile = newProfilePath;
359
         *ppWinLogonProfile = profile;
360
 
361
         result = TRUE;
362
      }
122 ixe013 363
      else TRACE(eERROR, L"Out of memory, wtf?.\n");
70 ixe013 364
   }
122 ixe013 365
   else TRACE(eERROR, L"Out of memory, wtf?.\n");
70 ixe013 366
367
   return result;
368
}
109 ixe013 369
*/
70 ixe013 370
109 ixe013 371
BOOL ImpersonateAndGetUserName(HANDLE hToken, wchar_t* name, int cch)
372
{
373
   BOOL result = FALSE;
374
   if (ImpersonateLoggedOnUser(hToken))
375
 
376
      DWORD cchName = cch;
121 ixe013 377
      if (GetUserNameEx(NameSamCompatible, name, &cchName))
109 ixe013 378
 
122 ixe013 379
      else TRACE(eERROR, L"GetUserNameEx failed: %d", GetLastError());
109 ixe013 380
      RevertToSelf();
381
   }
122 ixe013 382
   else TRACE(eERROR, L"ImpersonateLoggedOnUser failed: %d\n", GetLastError());
109 ixe013 383
384
   return result;
385
}
386
70 ixe013 387
BOOL CreateProcessAsUserOnDesktop(HANDLE hToken, wchar_t* programImage, wchar_t* desktop, void* env)
388
 
389
   // impersonate the user to ensure that they are allowed
390
 
391
   if (!ImpersonateLoggedOnUser(hToken))
392
 
122 ixe013 393
      TRACE(eERROR, L"ImpersonateLoggedOnUser failed: %d\n", GetLastError());
70 ixe013 394
      return FALSE;
395
   }
396
133 ixe013 397
   TCHAR path[MAX_PATH];
122 ixe013 398
133 ixe013 399
   ExpandEnvironmentStrings(programImage, path, MAX_PATH);
122 ixe013 400
70 ixe013 401
   STARTUPINFO si = { sizeof si, 0, desktop };
402
   PROCESS_INFORMATION pi;
122 ixe013 403
   if (!CreateProcessAsUser(hToken, path, path, 0, 0, FALSE,
70 ixe013 404
 
405
   {
406
 
122 ixe013 407
      TRACE(eERROR, L"CreateProcessAsUser failed for image %s with error code %d\n", programImage, GetLastError());
70 ixe013 408
      return FALSE;
409
   }
410
 
411
   CloseHandle(pi.hThread);
412
413
   RevertToSelf();
414
122 ixe013 415
   TRACE(eERROR, L"Successfully launched %s\n", programImage);
70 ixe013 416
 
417
}
418
419
// checks user SID in both tokens for equality
420
 
421
{
422
   *pbIsSameUser = FALSE;
423
   BOOL result = FALSE;
424
425
   const DWORD bufSize = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
426
   char buf1[bufSize];
427
   char buf2[bufSize];
428
429
 
430
   if (GetTokenInformation(hToken1, TokenUser, buf1, bufSize, &cb) &&
431
         GetTokenInformation(hToken2, TokenUser, buf2, bufSize, &cb))
432
 
433
      *pbIsSameUser = EqualSid(((TOKEN_USER*)buf1)->User.Sid, ((TOKEN_USER*)buf2)->User.Sid) ? TRUE : FALSE;
434
      result = TRUE;
435
   }
122 ixe013 436
   else TRACE(eERROR, L"GetTokenInformation failed: %d\n", GetLastError());
70 ixe013 437
438
 
439
}
440
83 ixe013 441
/*
70 ixe013 442
void* _administratorsAlias()
443
{
444
   const int subAuthCount = 2;
445
   static char sid[sizeof(SID) + subAuthCount * sizeof(DWORD)];
446
447
   SID* psid = (SID*)sid;
448
   if (0 == psid->Revision)
449
   {
450
      // first time called, initialize the sid
451
 
452
      psid->SubAuthorityCount = subAuthCount;
453
      psid->SubAuthority[0] = SECURITY_BUILTIN_DOMAIN_RID;
454
      psid->SubAuthority[1] = DOMAIN_ALIAS_RID_ADMINS;
455
      psid->Revision = 1;
456
   }
457
   return sid;
458
}
459
460
// checks for presence of local admin group (must have TOKEN_DUPLICATE perms on hToken)
461
BOOL IsAdmin(HANDLE hToken)
462
{
463
   // we can pretty much assume all tokens will be primary tokens in this application
464
   // and CheckTokenMembership requires an impersonation token (which is really annoying)
465
   // so we'll just duplicate any token we get into an impersonation token before continuing...
466
   BOOL isAdmin = FALSE;
467
   HANDLE hImpToken;
468
 
469
   {
470
      BOOL isMember;
471
      if (CheckTokenMembership(hImpToken, _administratorsAlias(), &isMember) && isMember)
472
 
473
         isAdmin = TRUE;
474
      }
122 ixe013 475
      else TRACE(eERROR, L"CheckTokenMembership failed: %d\n", GetLastError());
70 ixe013 476
477
      CloseHandle(hImpToken);
478
   }
122 ixe013 479
   else TRACE(eERROR, L"DuplicateTokenEx failed: %d\n", GetLastError());
70 ixe013 480
481
   return isAdmin;
482
}
83 ixe013 483
*/
484
int GetUsernameAndDomainFromToken(HANDLE token, wchar_t *domain, DWORD domain_len, wchar_t *username, DWORD username_len)
485
{
133 ixe013 486
   int result;
487
   TOKEN_USER *user;
488
   DWORD size = 0;
489
   GetTokenInformation(token, TokenUser, NULL, 0, &size);
490
   if (size)
491
   {
492
      user = (TOKEN_USER *)malloc(size);
493
      if (user)
494
      {
495
         if (GetTokenInformation(token, TokenUser, user, size, &size))
496
         {
497
            if (IsValidSid(user->User.Sid))
498
            {
499
               SID_NAME_USE name_use;
500
               *domain = *username = 0;
501
               if (LookupAccountSid(0, user->User.Sid, username, &username_len, domain, &domain_len, &name_use))
502
               {
503
                  result = (*username != 0) + (*domain != 0);
504
 
505
            }
506
         }
507
         free(user);
508
 
509
   }
510
   return result;
83 ixe013 511
 
137 ixe013 512
161 ixe013 513
const wchar_t *FindUserNameInString(const wchar_t *rawusername)
514
{
515
    const wchar_t *result = 0;
137 ixe013 516
 
161 ixe013 517
            //TODO : Replace this hack with CredUIParseUserName
162 ixe013 518
            result = wcsstr(rawusername, L"\\");
161 ixe013 519
 
162 ixe013 520
            if (!result)
161 ixe013 521
            {
162 ixe013 522
                result = rawusername; //No domain entered, so point directly to the supplied buffer
161 ixe013 523
            }
524
525
 
526
}
527
 
213 ixe013 528
int SetSelfservePassword(const wchar_t *username, const wchar_t *randpasswd)
137 ixe013 529
 
530
    int result = 0;
531
    USER_INFO_1003 ui;
213 ixe013 532
    wchar_t mutable_password[LM20_PWLEN];
137 ixe013 533
213 ixe013 534
 
137 ixe013 535
213 ixe013 536
    ui.usri1003_password = mutable_password;
537
 
137 ixe013 538