Subversion Repositories Aucun

Rev

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