1 /*
   2  * Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include <jni.h>
  27 #include "com_sun_security_auth_module_NTSystem.h"
  28 
  29 #include <windows.h>
  30 #include <stdio.h>
  31 #include <wchar.h>
  32 #include <ntsecapi.h>
  33 #include <lmerr.h>
  34 
  35 static BOOL debug = FALSE;
  36 
  37 BOOL getToken(PHANDLE);
  38 BOOL getUser(HANDLE tokenHandle, LPTSTR *userName,
  39         LPTSTR *domainName, LPTSTR *userSid, LPTSTR *domainSid);
  40 BOOL getPrimaryGroup(HANDLE tokenHandle, LPTSTR *primaryGroup);
  41 BOOL getGroups(HANDLE tokenHandle, PDWORD numGroups, LPTSTR **groups);
  42 BOOL getImpersonationToken(PHANDLE impersonationToken);
  43 BOOL getTextualSid(PSID pSid, LPTSTR TextualSid, LPDWORD lpdwBufferLen);
  44 void DisplayErrorText(DWORD dwLastError);
  45 
  46 static void throwIllegalArgumentException(JNIEnv *env, const char *msg) {
  47     jclass clazz = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
  48     if (clazz != NULL)
  49         (*env)->ThrowNew(env, clazz, msg);
  50 }
  51 
  52 JNIEXPORT jlong JNICALL
  53 Java_com_sun_security_auth_module_NTSystem_getImpersonationToken0
  54         (JNIEnv *env, jobject obj) {
  55     HANDLE impersonationToken = 0;      // impersonation token
  56     if (debug) {
  57         printf("getting impersonation token\n");
  58     }
  59     if (getImpersonationToken(&impersonationToken) == FALSE) {
  60         return 0;
  61     }
  62     return (jlong)impersonationToken;
  63 }
  64 
  65 JNIEXPORT void JNICALL
  66 Java_com_sun_security_auth_module_NTSystem_getCurrent
  67     (JNIEnv *env, jobject obj, jboolean debugNative) {
  68 
  69     long i, j = 0;
  70     HANDLE tokenHandle = INVALID_HANDLE_VALUE;
  71 
  72     LPTSTR userName = NULL;             // user name
  73     LPTSTR userSid = NULL;              // user sid
  74     LPTSTR domainName = NULL;           // domain name
  75     LPTSTR domainSid = NULL;            // domain sid
  76     LPTSTR primaryGroup = NULL;         // primary group sid
  77     DWORD numGroups = 0;                // num groups
  78     LPTSTR *groups = NULL;              // groups array
  79     long pIndex = -1;                   // index of primaryGroup in groups array
  80 
  81     jfieldID fid;
  82     jstring jstr;
  83     jobjectArray jgroups;
  84     jclass stringClass = 0;
  85     jclass cls = (*env)->GetObjectClass(env, obj);
  86 
  87     debug = debugNative;
  88 
  89     // get NT information first
  90 
  91     if (debug) {
  92         printf("getting access token\n");
  93     }
  94     if (getToken(&tokenHandle) == FALSE) {
  95         return;
  96     }
  97 
  98     if (debug) {
  99         printf("getting user info\n");
 100     }
 101     if (getUser
 102         (tokenHandle, &userName, &domainName, &userSid, &domainSid) == FALSE) {
 103         return;
 104     }
 105 
 106     if (debug) {
 107         printf("getting primary group\n");
 108     }
 109     if (getPrimaryGroup(tokenHandle, &primaryGroup) == FALSE) {
 110         return;
 111     }
 112 
 113     if (debug) {
 114         printf("getting supplementary groups\n");
 115     }
 116     if (getGroups(tokenHandle, &numGroups, &groups) == FALSE) {
 117         return;
 118     }
 119 
 120     // then set values into NTSystem
 121 
 122     fid = (*env)->GetFieldID(env, cls, "userName", "Ljava/lang/String;");
 123     if (fid == 0) {
 124         (*env)->ExceptionClear(env);
 125         throwIllegalArgumentException(env, "invalid field: userName");
 126         goto cleanup;
 127     }
 128     jstr = (*env)->NewStringUTF(env, userName);
 129     if (jstr == NULL)
 130         goto cleanup;
 131     (*env)->SetObjectField(env, obj, fid, jstr);
 132 
 133     fid = (*env)->GetFieldID(env, cls, "userSID", "Ljava/lang/String;");
 134     if (fid == 0) {
 135         (*env)->ExceptionClear(env);
 136         throwIllegalArgumentException(env, "invalid field: userSID");
 137         goto cleanup;
 138     }
 139     jstr = (*env)->NewStringUTF(env, userSid);
 140     if (jstr == NULL)
 141         goto cleanup;
 142     (*env)->SetObjectField(env, obj, fid, jstr);
 143 
 144     fid = (*env)->GetFieldID(env, cls, "domain", "Ljava/lang/String;");
 145     if (fid == 0) {
 146         (*env)->ExceptionClear(env);
 147         throwIllegalArgumentException(env, "invalid field: domain");
 148         goto cleanup;
 149     }
 150     jstr = (*env)->NewStringUTF(env, domainName);
 151     if (jstr == NULL)
 152         goto cleanup;
 153     (*env)->SetObjectField(env, obj, fid, jstr);
 154 
 155     if (domainSid != NULL) {
 156         fid = (*env)->GetFieldID(env, cls, "domainSID", "Ljava/lang/String;");
 157         if (fid == 0) {
 158             (*env)->ExceptionClear(env);
 159             throwIllegalArgumentException(env, "invalid field: domainSID");
 160             goto cleanup;
 161         }
 162         jstr = (*env)->NewStringUTF(env, domainSid);
 163         if (jstr == NULL)
 164             goto cleanup;
 165         (*env)->SetObjectField(env, obj, fid, jstr);
 166     }
 167 
 168     fid = (*env)->GetFieldID(env, cls, "primaryGroupID", "Ljava/lang/String;");
 169     if (fid == 0) {
 170         (*env)->ExceptionClear(env);
 171         throwIllegalArgumentException(env, "invalid field: PrimaryGroupID");
 172         goto cleanup;
 173     }
 174     jstr = (*env)->NewStringUTF(env, primaryGroup);
 175     if (jstr == NULL)
 176         goto cleanup;
 177     (*env)->SetObjectField(env, obj, fid, jstr);
 178 
 179     // primary group may or may not be part of supplementary groups
 180     for (i = 0; i < (long)numGroups; i++) {
 181         if (strcmp(primaryGroup, groups[i]) == 0) {
 182             // found primary group in groups array
 183             pIndex = i;
 184             break;
 185         }
 186     }
 187 
 188     if (numGroups == 0 || (pIndex == 0 && numGroups == 1)) {
 189         // primary group is only group in groups array
 190 
 191         if (debug) {
 192             printf("no secondary groups\n");
 193         }
 194     } else {
 195 
 196         // the groups array is non-empty,
 197         // and may or may not contain the primary group
 198 
 199         fid = (*env)->GetFieldID(env, cls, "groupIDs", "[Ljava/lang/String;");
 200         if (fid == 0) {
 201             (*env)->ExceptionClear(env);
 202             throwIllegalArgumentException(env, "groupIDs");
 203             goto cleanup;
 204         }
 205 
 206         stringClass = (*env)->FindClass(env, "java/lang/String");
 207         if (stringClass == NULL)
 208             goto cleanup;
 209 
 210         if (pIndex == -1) {
 211             // primary group not in groups array
 212             jgroups = (*env)->NewObjectArray(env, numGroups, stringClass, 0);
 213         } else {
 214             // primary group in groups array -
 215             // allocate one less array entry and do not add into new array
 216             jgroups = (*env)->NewObjectArray(env, numGroups-1, stringClass, 0);
 217         }
 218         if (jgroups == NULL)
 219             goto cleanup;
 220 
 221         for (i = 0, j = 0; i < (long)numGroups; i++) {
 222             if (pIndex == i) {
 223                 // continue if equal to primary group
 224                 continue;
 225             }
 226             jstr = (*env)->NewStringUTF(env, groups[i]);
 227             if (jstr == NULL)
 228                 goto cleanup;
 229             (*env)->SetObjectArrayElement(env, jgroups, j++, jstr);
 230         }
 231         (*env)->SetObjectField(env, obj, fid, jgroups);
 232     }
 233 
 234 cleanup:
 235     if (userName != NULL) {
 236         HeapFree(GetProcessHeap(), 0, userName);
 237     }
 238     if (domainName != NULL) {
 239         HeapFree(GetProcessHeap(), 0, domainName);
 240     }
 241     if (userSid != NULL) {
 242         HeapFree(GetProcessHeap(), 0, userSid);
 243     }
 244     if (domainSid != NULL) {
 245         HeapFree(GetProcessHeap(), 0, domainSid);
 246     }
 247     if (primaryGroup != NULL) {
 248         HeapFree(GetProcessHeap(), 0, primaryGroup);
 249     }
 250     if (groups != NULL) {
 251         for (i = 0; i < (long)numGroups; i++) {
 252             if (groups[i] != NULL) {
 253                 HeapFree(GetProcessHeap(), 0, groups[i]);
 254             }
 255         }
 256         HeapFree(GetProcessHeap(), 0, groups);
 257     }
 258     CloseHandle(tokenHandle);
 259 
 260     return;
 261 }
 262 
 263 BOOL getToken(PHANDLE tokenHandle) {
 264 
 265     // first try the thread token
 266     if (OpenThreadToken(GetCurrentThread(),
 267                         TOKEN_READ,
 268                         FALSE,
 269                         tokenHandle) == 0) {
 270         if (debug) {
 271             printf("  [getToken] OpenThreadToken error [%d]: ", GetLastError());
 272             DisplayErrorText(GetLastError());
 273         }
 274 
 275         // next try the process token
 276         if (OpenProcessToken(GetCurrentProcess(),
 277                         TOKEN_READ,
 278                         tokenHandle) == 0) {
 279             if (debug) {
 280                 printf("  [getToken] OpenProcessToken error [%d]: ",
 281                         GetLastError());
 282                 DisplayErrorText(GetLastError());
 283             }
 284             return FALSE;
 285         }
 286     }
 287 
 288     if (debug) {
 289         printf("  [getToken] got user access token\n");
 290     }
 291 
 292     return TRUE;
 293 }
 294 
 295 BOOL getUser(HANDLE tokenHandle, LPTSTR *userName,
 296         LPTSTR *domainName, LPTSTR *userSid, LPTSTR *domainSid) {
 297 
 298     BOOL error = FALSE;
 299     DWORD bufSize = 0;
 300     DWORD buf2Size = 0;
 301     DWORD retBufSize = 0;
 302     PTOKEN_USER tokenUserInfo = NULL;   // getTokenInformation
 303     SID_NAME_USE nameUse;               // LookupAccountSid
 304 
 305     PSID dSid = NULL;
 306     LPTSTR domainSidName = NULL;
 307 
 308     // get token information
 309     GetTokenInformation(tokenHandle,
 310                         TokenUser,
 311                         NULL,   // TokenInformation - if NULL get buffer size
 312                         0,      // since TokenInformation is NULL
 313                         &bufSize);
 314 
 315     tokenUserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, bufSize);
 316     if (GetTokenInformation(tokenHandle,
 317                         TokenUser,
 318                         tokenUserInfo,
 319                         bufSize,
 320                         &retBufSize) == 0) {
 321         if (debug) {
 322             printf("  [getUser] GetTokenInformation error [%d]: ",
 323                 GetLastError());
 324             DisplayErrorText(GetLastError());
 325         }
 326         error = TRUE;
 327         goto cleanup;
 328     }
 329 
 330     if (debug) {
 331         printf("  [getUser] Got TokenUser info\n");
 332     }
 333 
 334     // get userName
 335     bufSize = 0;
 336     buf2Size = 0;
 337     LookupAccountSid(NULL,      // local host
 338                 tokenUserInfo->User.Sid,
 339                 NULL,
 340                 &bufSize,
 341                 NULL,
 342                 &buf2Size,
 343                 &nameUse);
 344 
 345     *userName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
 346     *domainName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, buf2Size);
 347     if (LookupAccountSid(NULL,  // local host
 348                 tokenUserInfo->User.Sid,
 349                 *userName,
 350                 &bufSize,
 351                 *domainName,
 352                 &buf2Size,
 353                 &nameUse) == 0) {
 354         if (debug) {
 355             printf("  [getUser] LookupAccountSid error [%d]: ",
 356                 GetLastError());
 357             DisplayErrorText(GetLastError());
 358         }
 359         error = TRUE;
 360         goto cleanup;
 361     }
 362 
 363     if (debug) {
 364         printf("  [getUser] userName: %s, domainName = %s\n",
 365                 *userName, *domainName);
 366     }
 367 
 368     bufSize = 0;
 369     getTextualSid(tokenUserInfo->User.Sid, NULL, &bufSize);
 370     *userSid = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
 371     getTextualSid(tokenUserInfo->User.Sid, *userSid, &bufSize);
 372     if (debug) {
 373         printf("  [getUser] userSid: %s\n", *userSid);
 374     }
 375 
 376     // get domainSid
 377     bufSize = 0;
 378     buf2Size = 0;
 379     LookupAccountName(NULL,     // local host
 380                 *domainName,
 381                 NULL,
 382                 &bufSize,
 383                 NULL,
 384                 &buf2Size,
 385                 &nameUse);
 386 
 387     dSid = (PSID)HeapAlloc(GetProcessHeap(), 0, bufSize);
 388     domainSidName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, buf2Size);
 389     if (LookupAccountName(NULL, // local host
 390                 *domainName,
 391                 dSid,
 392                 &bufSize,
 393                 domainSidName,
 394                 &buf2Size,
 395                 &nameUse) == 0) {
 396         if (debug) {
 397             printf("  [getUser] LookupAccountName error [%d]: ",
 398                 GetLastError());
 399             DisplayErrorText(GetLastError());
 400         }
 401         // ok not to have a domain SID (no error)
 402         goto cleanup;
 403     }
 404 
 405     bufSize = 0;
 406     getTextualSid(dSid, NULL, &bufSize);
 407     *domainSid = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
 408     getTextualSid(dSid, *domainSid, &bufSize);
 409     if (debug) {
 410         printf("  [getUser] domainSid: %s\n", *domainSid);
 411     }
 412 
 413 cleanup:
 414     if (tokenUserInfo != NULL) {
 415         HeapFree(GetProcessHeap(), 0, tokenUserInfo);
 416     }
 417     if (dSid != NULL) {
 418         HeapFree(GetProcessHeap(), 0, dSid);
 419     }
 420     if (domainSidName != NULL) {
 421         HeapFree(GetProcessHeap(), 0, domainSidName);
 422     }
 423     if (error) {
 424         return FALSE;
 425     }
 426     return TRUE;
 427 }
 428 
 429 BOOL getPrimaryGroup(HANDLE tokenHandle, LPTSTR *primaryGroup) {
 430 
 431     BOOL error = FALSE;
 432     DWORD bufSize = 0;
 433     DWORD retBufSize = 0;
 434 
 435     PTOKEN_PRIMARY_GROUP tokenGroupInfo = NULL;
 436 
 437     // get token information
 438     GetTokenInformation(tokenHandle,
 439                         TokenPrimaryGroup,
 440                         NULL,   // TokenInformation - if NULL get buffer size
 441                         0,      // since TokenInformation is NULL
 442                         &bufSize);
 443 
 444     tokenGroupInfo = (PTOKEN_PRIMARY_GROUP)HeapAlloc
 445                         (GetProcessHeap(), 0, bufSize);
 446     if (GetTokenInformation(tokenHandle,
 447                         TokenPrimaryGroup,
 448                         tokenGroupInfo,
 449                         bufSize,
 450                         &retBufSize) == 0) {
 451         if (debug) {
 452             printf("  [getPrimaryGroup] GetTokenInformation error [%d]: ",
 453                 GetLastError());
 454             DisplayErrorText(GetLastError());
 455         }
 456         error = TRUE;
 457         goto cleanup;
 458     }
 459 
 460     if (debug) {
 461         printf("  [getPrimaryGroup] Got TokenPrimaryGroup info\n");
 462     }
 463 
 464     bufSize = 0;
 465     getTextualSid(tokenGroupInfo->PrimaryGroup, NULL, &bufSize);
 466     *primaryGroup = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
 467     getTextualSid(tokenGroupInfo->PrimaryGroup, *primaryGroup, &bufSize);
 468     if (debug) {
 469         printf("  [getPrimaryGroup] primaryGroup: %s\n", *primaryGroup);
 470     }
 471 
 472 cleanup:
 473     if (tokenGroupInfo != NULL) {
 474         HeapFree(GetProcessHeap(), 0, tokenGroupInfo);
 475     }
 476     if (error) {
 477         return FALSE;
 478     }
 479     return TRUE;
 480 }
 481 
 482 BOOL getGroups(HANDLE tokenHandle, PDWORD numGroups, LPTSTR **groups) {
 483 
 484     BOOL error = FALSE;
 485     DWORD bufSize = 0;
 486     DWORD retBufSize = 0;
 487     long i = 0;
 488 
 489     PTOKEN_GROUPS tokenGroupInfo = NULL;
 490 
 491     // get token information
 492     GetTokenInformation(tokenHandle,
 493                         TokenGroups,
 494                         NULL,   // TokenInformation - if NULL get buffer size
 495                         0,      // since TokenInformation is NULL
 496                         &bufSize);
 497 
 498     tokenGroupInfo = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), 0, bufSize);
 499     if (GetTokenInformation(tokenHandle,
 500                         TokenGroups,
 501                         tokenGroupInfo,
 502                         bufSize,
 503                         &retBufSize) == 0) {
 504         if (debug) {
 505             printf("  [getGroups] GetTokenInformation error [%d]: ",
 506                 GetLastError());
 507             DisplayErrorText(GetLastError());
 508         }
 509         error = TRUE;
 510         goto cleanup;
 511     }
 512 
 513     if (debug) {
 514         printf("  [getGroups] Got TokenGroups info\n");
 515     }
 516 
 517     if (tokenGroupInfo->GroupCount == 0) {
 518         // no groups
 519         goto cleanup;
 520     }
 521 
 522     // return group info
 523     *numGroups = tokenGroupInfo->GroupCount;
 524     *groups = (LPTSTR *)HeapAlloc
 525                 (GetProcessHeap(), 0, (*numGroups) * sizeof(LPTSTR));
 526     for (i = 0; i < (long)*numGroups; i++) {
 527         bufSize = 0;
 528         getTextualSid(tokenGroupInfo->Groups[i].Sid, NULL, &bufSize);
 529         (*groups)[i] = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
 530         getTextualSid(tokenGroupInfo->Groups[i].Sid, (*groups)[i], &bufSize);
 531         if (debug) {
 532             printf("  [getGroups] group %d: %s\n", i, (*groups)[i]);
 533         }
 534     }
 535 
 536 cleanup:
 537     if (tokenGroupInfo != NULL) {
 538         HeapFree(GetProcessHeap(), 0, tokenGroupInfo);
 539     }
 540     if (error) {
 541         return FALSE;
 542     }
 543     return TRUE;
 544 }
 545 
 546 BOOL getImpersonationToken(PHANDLE impersonationToken) {
 547 
 548     HANDLE dupToken;
 549 
 550     if (OpenThreadToken(GetCurrentThread(),
 551                         TOKEN_DUPLICATE,
 552                         FALSE,
 553                         &dupToken) == 0) {
 554         if (OpenProcessToken(GetCurrentProcess(),
 555                                 TOKEN_DUPLICATE,
 556                                 &dupToken) == 0) {
 557             if (debug) {
 558                 printf
 559                     ("  [getImpersonationToken] OpenProcessToken error [%d]: ",
 560                     GetLastError());
 561                 DisplayErrorText(GetLastError());
 562             }
 563             return FALSE;
 564         }
 565     }
 566 
 567     if (DuplicateToken(dupToken,
 568                         SecurityImpersonation,
 569                         impersonationToken) == 0) {
 570         if (debug) {
 571             printf("  [getImpersonationToken] DuplicateToken error [%d]: ",
 572                 GetLastError());
 573             DisplayErrorText(GetLastError());
 574         }
 575         return FALSE;
 576     }
 577     CloseHandle(dupToken);
 578 
 579     if (debug) {
 580         printf("  [getImpersonationToken] token = %p\n",
 581             (void *)*impersonationToken);
 582     }
 583     return TRUE;
 584 }
 585 
 586 BOOL getTextualSid
 587     (PSID pSid,                 // binary SID
 588     LPTSTR TextualSid,          // buffer for Textual representation of SID
 589     LPDWORD lpdwBufferLen) {    // required/provided TextualSid buffersize
 590 
 591     PSID_IDENTIFIER_AUTHORITY psia;
 592     DWORD dwSubAuthorities;
 593     DWORD dwSidRev=SID_REVISION;
 594     DWORD dwCounter;
 595     DWORD dwSidSize;
 596 
 597     // Validate the binary SID.
 598     if(!IsValidSid(pSid)) return FALSE;
 599 
 600     // Get the identifier authority value from the SID.
 601     psia = GetSidIdentifierAuthority(pSid);
 602 
 603     // Get the number of subauthorities in the SID.
 604     dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
 605 
 606     // Compute the buffer length.
 607     // S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL
 608     dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
 609 
 610     // Check input buffer length.
 611     // If too small, indicate the proper size and set last error.
 612     if (*lpdwBufferLen < dwSidSize) {
 613         *lpdwBufferLen = dwSidSize;
 614         SetLastError(ERROR_INSUFFICIENT_BUFFER);
 615         return FALSE;
 616     }
 617 
 618     // Add 'S' prefix and revision number to the string.
 619     dwSidSize=wsprintf(TextualSid, TEXT("S-%lu-"), dwSidRev );
 620 
 621     // Add SID identifier authority to the string.
 622     if ((psia->Value[0] != 0) || (psia->Value[1] != 0)) {
 623         dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
 624                 TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
 625                 (USHORT)psia->Value[0],
 626                 (USHORT)psia->Value[1],
 627                 (USHORT)psia->Value[2],
 628                 (USHORT)psia->Value[3],
 629                 (USHORT)psia->Value[4],
 630                 (USHORT)psia->Value[5]);
 631     } else {
 632         dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
 633                 TEXT("%lu"),
 634                 (ULONG)(psia->Value[5]  )   +
 635                 (ULONG)(psia->Value[4] <<  8)   +
 636                 (ULONG)(psia->Value[3] << 16)   +
 637                 (ULONG)(psia->Value[2] << 24)   );
 638     }
 639 
 640     // Add SID subauthorities to the string.
 641     for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
 642         dwSidSize+=wsprintf(TextualSid + dwSidSize, TEXT("-%lu"),
 643                 *GetSidSubAuthority(pSid, dwCounter) );
 644     }
 645 
 646     return TRUE;
 647 }
 648 
 649 void DisplayErrorText(DWORD dwLastError) {
 650     HMODULE hModule = NULL; // default to system source
 651     LPSTR MessageBuffer;
 652     DWORD dwBufferLength;
 653 
 654     DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
 655                         FORMAT_MESSAGE_IGNORE_INSERTS |
 656                         FORMAT_MESSAGE_FROM_SYSTEM ;
 657 
 658     //
 659     // If dwLastError is in the network range,
 660     //  load the message source.
 661     //
 662 
 663     if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
 664         hModule = LoadLibraryEx(TEXT("netmsg.dll"),
 665                                 NULL,
 666                                 LOAD_LIBRARY_AS_DATAFILE);
 667 
 668         if(hModule != NULL)
 669             dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
 670     }
 671 
 672     //
 673     // Call FormatMessage() to allow for message
 674     //  text to be acquired from the system
 675     //  or from the supplied module handle.
 676     //
 677 
 678     if(dwBufferLength = FormatMessageA(dwFormatFlags,
 679                 hModule, // module to get message from (NULL == system)
 680                 dwLastError,
 681                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
 682                 (LPSTR) &MessageBuffer,
 683                 0,
 684                 NULL)) {
 685         DWORD dwBytesWritten;
 686 
 687         //
 688         // Output message string on stderr.
 689         //
 690         WriteFile(GetStdHandle(STD_ERROR_HANDLE),
 691                 MessageBuffer,
 692                 dwBufferLength,
 693                 &dwBytesWritten,
 694                 NULL);
 695 
 696         //
 697         // Free the buffer allocated by the system.
 698         //
 699         LocalFree(MessageBuffer);
 700     }
 701 
 702     //
 703     // If we loaded a message source, unload it.
 704     //
 705     if(hModule != NULL)
 706         FreeLibrary(hModule);
 707 }
 708 
 709 /**
 710  * 1. comment out first two #includes
 711  * 2. set 'debug' to TRUE
 712  * 3. comment out 'getCurrent'
 713  * 4. uncomment 'main'
 714  * 5. cc -c nt.c
 715  * 6. link nt.obj user32.lib advapi32.lib /out:nt.exe
 716  */
 717 /*
 718 void main(int argc, char *argv[]) {
 719 
 720     long i = 0;
 721     HANDLE tokenHandle = INVALID_HANDLE_VALUE;
 722 
 723     LPTSTR userName = NULL;
 724     LPTSTR userSid = NULL;
 725     LPTSTR domainName = NULL;
 726     LPTSTR domainSid = NULL;
 727     LPTSTR primaryGroup = NULL;
 728     DWORD numGroups = 0;
 729     LPTSTR *groups = NULL;
 730     HANDLE impersonationToken = 0;
 731 
 732     printf("getting access token\n");
 733     if (getToken(&tokenHandle) == FALSE) {
 734         exit(1);
 735     }
 736 
 737     printf("getting user info\n");
 738     if (getUser
 739         (tokenHandle, &userName, &domainName, &userSid, &domainSid) == FALSE) {
 740         exit(1);
 741     }
 742 
 743     printf("getting primary group\n");
 744     if (getPrimaryGroup(tokenHandle, &primaryGroup) == FALSE) {
 745         exit(1);
 746     }
 747 
 748     printf("getting supplementary groups\n");
 749     if (getGroups(tokenHandle, &numGroups, &groups) == FALSE) {
 750         exit(1);
 751     }
 752 
 753     printf("getting impersonation token\n");
 754     if (getImpersonationToken(&impersonationToken) == FALSE) {
 755         exit(1);
 756     }
 757 
 758     printf("userName = %s, userSid = %s, domainName = %s, domainSid = %s\n",
 759         userName, userSid, domainName, domainSid);
 760     printf("primaryGroup = %s\n", primaryGroup);
 761     for (i = 0; i < numGroups; i++) {
 762         printf("Group[%d] = %s\n", i, groups[i]);
 763     }
 764     printf("impersonationToken = %ld\n", impersonationToken);
 765 
 766     if (userName != NULL) {
 767         HeapFree(GetProcessHeap(), 0, userName);
 768     }
 769     if (userSid != NULL) {
 770         HeapFree(GetProcessHeap(), 0, userSid);
 771     }
 772     if (domainName != NULL) {
 773         HeapFree(GetProcessHeap(), 0, domainName);
 774     }
 775     if (domainSid != NULL) {
 776         HeapFree(GetProcessHeap(), 0, domainSid);
 777     }
 778     if (primaryGroup != NULL) {
 779         HeapFree(GetProcessHeap(), 0, primaryGroup);
 780     }
 781     if (groups != NULL) {
 782         for (i = 0; i < numGroups; i++) {
 783             if (groups[i] != NULL) {
 784                 HeapFree(GetProcessHeap(), 0, groups[i]);
 785             }
 786         }
 787         HeapFree(GetProcessHeap(), 0, groups);
 788     }
 789     CloseHandle(impersonationToken);
 790     CloseHandle(tokenHandle);
 791 }
 792 */
 793 
 794 /**
 795  * extra main method for testing debug printing
 796  */
 797 /*
 798 void main(int argc, char *argv[]) {
 799     if(argc != 2) {
 800         fprintf(stderr,"Usage: %s <error number>\n", argv[0]);
 801     }
 802 
 803     DisplayErrorText(atoi(argv[1]));
 804 }
 805 */