src/jdk.security.auth/windows/native/libjaas/nt.c
changeset 47216 71c04702a3d5
parent 33653 c1ee09fe3274
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.security.auth/windows/native/libjaas/nt.c	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <jni.h>
+#include "jni_util.h"
+#include "com_sun_security_auth_module_NTSystem.h"
+
+#include <windows.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <ntsecapi.h>
+#include <lmerr.h>
+
+static BOOL debug = FALSE;
+
+BOOL getToken(PHANDLE);
+BOOL getUser(HANDLE tokenHandle, LPTSTR *userName,
+        LPTSTR *domainName, LPTSTR *userSid, LPTSTR *domainSid);
+BOOL getPrimaryGroup(HANDLE tokenHandle, LPTSTR *primaryGroup);
+BOOL getGroups(HANDLE tokenHandle, PDWORD numGroups, LPTSTR **groups);
+BOOL getImpersonationToken(PHANDLE impersonationToken);
+BOOL getTextualSid(PSID pSid, LPTSTR TextualSid, LPDWORD lpdwBufferLen);
+void DisplayErrorText(DWORD dwLastError);
+
+static void throwIllegalArgumentException(JNIEnv *env, const char *msg) {
+    jclass clazz = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
+    if (clazz != NULL)
+        (*env)->ThrowNew(env, clazz, msg);
+}
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
+JNIEXPORT jlong JNICALL
+Java_com_sun_security_auth_module_NTSystem_getImpersonationToken0
+        (JNIEnv *env, jobject obj) {
+    HANDLE impersonationToken = 0;      // impersonation token
+    if (debug) {
+        printf("getting impersonation token\n");
+    }
+    if (getImpersonationToken(&impersonationToken) == FALSE) {
+        return 0;
+    }
+    return (jlong)impersonationToken;
+}
+
+JNIEXPORT void JNICALL
+Java_com_sun_security_auth_module_NTSystem_getCurrent
+    (JNIEnv *env, jobject obj, jboolean debugNative) {
+
+    long i, j = 0;
+    HANDLE tokenHandle = INVALID_HANDLE_VALUE;
+
+    LPTSTR userName = NULL;             // user name
+    LPTSTR userSid = NULL;              // user sid
+    LPTSTR domainName = NULL;           // domain name
+    LPTSTR domainSid = NULL;            // domain sid
+    LPTSTR primaryGroup = NULL;         // primary group sid
+    DWORD numGroups = 0;                // num groups
+    LPTSTR *groups = NULL;              // groups array
+    long pIndex = -1;                   // index of primaryGroup in groups array
+
+    jfieldID fid;
+    jstring jstr;
+    jobjectArray jgroups;
+    jclass stringClass = 0;
+    jclass cls = (*env)->GetObjectClass(env, obj);
+
+    debug = debugNative;
+
+    // get NT information first
+
+    if (debug) {
+        printf("getting access token\n");
+    }
+    if (getToken(&tokenHandle) == FALSE) {
+        return;
+    }
+
+    if (debug) {
+        printf("getting user info\n");
+    }
+    if (getUser
+        (tokenHandle, &userName, &domainName, &userSid, &domainSid) == FALSE) {
+        return;
+    }
+
+    if (debug) {
+        printf("getting primary group\n");
+    }
+    if (getPrimaryGroup(tokenHandle, &primaryGroup) == FALSE) {
+        return;
+    }
+
+    if (debug) {
+        printf("getting supplementary groups\n");
+    }
+    if (getGroups(tokenHandle, &numGroups, &groups) == FALSE) {
+        return;
+    }
+
+    // then set values into NTSystem
+
+    fid = (*env)->GetFieldID(env, cls, "userName", "Ljava/lang/String;");
+    if (fid == 0) {
+        (*env)->ExceptionClear(env);
+        throwIllegalArgumentException(env, "invalid field: userName");
+        goto cleanup;
+    }
+    jstr = (*env)->NewStringUTF(env, userName);
+    if (jstr == NULL)
+        goto cleanup;
+    (*env)->SetObjectField(env, obj, fid, jstr);
+
+    fid = (*env)->GetFieldID(env, cls, "userSID", "Ljava/lang/String;");
+    if (fid == 0) {
+        (*env)->ExceptionClear(env);
+        throwIllegalArgumentException(env, "invalid field: userSID");
+        goto cleanup;
+    }
+    jstr = (*env)->NewStringUTF(env, userSid);
+    if (jstr == NULL)
+        goto cleanup;
+    (*env)->SetObjectField(env, obj, fid, jstr);
+
+    fid = (*env)->GetFieldID(env, cls, "domain", "Ljava/lang/String;");
+    if (fid == 0) {
+        (*env)->ExceptionClear(env);
+        throwIllegalArgumentException(env, "invalid field: domain");
+        goto cleanup;
+    }
+    jstr = (*env)->NewStringUTF(env, domainName);
+    if (jstr == NULL)
+        goto cleanup;
+    (*env)->SetObjectField(env, obj, fid, jstr);
+
+    if (domainSid != NULL) {
+        fid = (*env)->GetFieldID(env, cls, "domainSID", "Ljava/lang/String;");
+        if (fid == 0) {
+            (*env)->ExceptionClear(env);
+            throwIllegalArgumentException(env, "invalid field: domainSID");
+            goto cleanup;
+        }
+        jstr = (*env)->NewStringUTF(env, domainSid);
+        if (jstr == NULL)
+            goto cleanup;
+        (*env)->SetObjectField(env, obj, fid, jstr);
+    }
+
+    fid = (*env)->GetFieldID(env, cls, "primaryGroupID", "Ljava/lang/String;");
+    if (fid == 0) {
+        (*env)->ExceptionClear(env);
+        throwIllegalArgumentException(env, "invalid field: PrimaryGroupID");
+        goto cleanup;
+    }
+    jstr = (*env)->NewStringUTF(env, primaryGroup);
+    if (jstr == NULL)
+        goto cleanup;
+    (*env)->SetObjectField(env, obj, fid, jstr);
+
+    // primary group may or may not be part of supplementary groups
+    for (i = 0; i < (long)numGroups; i++) {
+        if (strcmp(primaryGroup, groups[i]) == 0) {
+            // found primary group in groups array
+            pIndex = i;
+            break;
+        }
+    }
+
+    if (numGroups == 0 || (pIndex == 0 && numGroups == 1)) {
+        // primary group is only group in groups array
+
+        if (debug) {
+            printf("no secondary groups\n");
+        }
+    } else {
+
+        // the groups array is non-empty,
+        // and may or may not contain the primary group
+
+        fid = (*env)->GetFieldID(env, cls, "groupIDs", "[Ljava/lang/String;");
+        if (fid == 0) {
+            (*env)->ExceptionClear(env);
+            throwIllegalArgumentException(env, "groupIDs");
+            goto cleanup;
+        }
+
+        stringClass = (*env)->FindClass(env, "java/lang/String");
+        if (stringClass == NULL)
+            goto cleanup;
+
+        if (pIndex == -1) {
+            // primary group not in groups array
+            jgroups = (*env)->NewObjectArray(env, numGroups, stringClass, 0);
+        } else {
+            // primary group in groups array -
+            // allocate one less array entry and do not add into new array
+            jgroups = (*env)->NewObjectArray(env, numGroups-1, stringClass, 0);
+        }
+        if (jgroups == NULL)
+            goto cleanup;
+
+        for (i = 0, j = 0; i < (long)numGroups; i++) {
+            if (pIndex == i) {
+                // continue if equal to primary group
+                continue;
+            }
+            jstr = (*env)->NewStringUTF(env, groups[i]);
+            if (jstr == NULL)
+                goto cleanup;
+            (*env)->SetObjectArrayElement(env, jgroups, j++, jstr);
+        }
+        (*env)->SetObjectField(env, obj, fid, jgroups);
+    }
+
+cleanup:
+    if (userName != NULL) {
+        HeapFree(GetProcessHeap(), 0, userName);
+    }
+    if (domainName != NULL) {
+        HeapFree(GetProcessHeap(), 0, domainName);
+    }
+    if (userSid != NULL) {
+        HeapFree(GetProcessHeap(), 0, userSid);
+    }
+    if (domainSid != NULL) {
+        HeapFree(GetProcessHeap(), 0, domainSid);
+    }
+    if (primaryGroup != NULL) {
+        HeapFree(GetProcessHeap(), 0, primaryGroup);
+    }
+    if (groups != NULL) {
+        for (i = 0; i < (long)numGroups; i++) {
+            if (groups[i] != NULL) {
+                HeapFree(GetProcessHeap(), 0, groups[i]);
+            }
+        }
+        HeapFree(GetProcessHeap(), 0, groups);
+    }
+    CloseHandle(tokenHandle);
+
+    return;
+}
+
+BOOL getToken(PHANDLE tokenHandle) {
+
+    // first try the thread token
+    if (OpenThreadToken(GetCurrentThread(),
+                        TOKEN_READ,
+                        FALSE,
+                        tokenHandle) == 0) {
+        if (debug) {
+            printf("  [getToken] OpenThreadToken error [%d]: ", GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+
+        // next try the process token
+        if (OpenProcessToken(GetCurrentProcess(),
+                        TOKEN_READ,
+                        tokenHandle) == 0) {
+            if (debug) {
+                printf("  [getToken] OpenProcessToken error [%d]: ",
+                        GetLastError());
+                DisplayErrorText(GetLastError());
+            }
+            return FALSE;
+        }
+    }
+
+    if (debug) {
+        printf("  [getToken] got user access token\n");
+    }
+
+    return TRUE;
+}
+
+BOOL getUser(HANDLE tokenHandle, LPTSTR *userName,
+        LPTSTR *domainName, LPTSTR *userSid, LPTSTR *domainSid) {
+
+    BOOL error = FALSE;
+    DWORD bufSize = 0;
+    DWORD buf2Size = 0;
+    DWORD retBufSize = 0;
+    PTOKEN_USER tokenUserInfo = NULL;   // getTokenInformation
+    SID_NAME_USE nameUse;               // LookupAccountSid
+
+    PSID dSid = NULL;
+    LPTSTR domainSidName = NULL;
+
+    // get token information
+    GetTokenInformation(tokenHandle,
+                        TokenUser,
+                        NULL,   // TokenInformation - if NULL get buffer size
+                        0,      // since TokenInformation is NULL
+                        &bufSize);
+
+    tokenUserInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    if (GetTokenInformation(tokenHandle,
+                        TokenUser,
+                        tokenUserInfo,
+                        bufSize,
+                        &retBufSize) == 0) {
+        if (debug) {
+            printf("  [getUser] GetTokenInformation error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        error = TRUE;
+        goto cleanup;
+    }
+
+    if (debug) {
+        printf("  [getUser] Got TokenUser info\n");
+    }
+
+    // get userName
+    bufSize = 0;
+    buf2Size = 0;
+    LookupAccountSid(NULL,      // local host
+                tokenUserInfo->User.Sid,
+                NULL,
+                &bufSize,
+                NULL,
+                &buf2Size,
+                &nameUse);
+
+    *userName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    *domainName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, buf2Size);
+    if (LookupAccountSid(NULL,  // local host
+                tokenUserInfo->User.Sid,
+                *userName,
+                &bufSize,
+                *domainName,
+                &buf2Size,
+                &nameUse) == 0) {
+        if (debug) {
+            printf("  [getUser] LookupAccountSid error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        error = TRUE;
+        goto cleanup;
+    }
+
+    if (debug) {
+        printf("  [getUser] userName: %s, domainName = %s\n",
+                *userName, *domainName);
+    }
+
+    bufSize = 0;
+    getTextualSid(tokenUserInfo->User.Sid, NULL, &bufSize);
+    *userSid = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    getTextualSid(tokenUserInfo->User.Sid, *userSid, &bufSize);
+    if (debug) {
+        printf("  [getUser] userSid: %s\n", *userSid);
+    }
+
+    // get domainSid
+    bufSize = 0;
+    buf2Size = 0;
+    LookupAccountName(NULL,     // local host
+                *domainName,
+                NULL,
+                &bufSize,
+                NULL,
+                &buf2Size,
+                &nameUse);
+
+    dSid = (PSID)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    domainSidName = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, buf2Size);
+    if (LookupAccountName(NULL, // local host
+                *domainName,
+                dSid,
+                &bufSize,
+                domainSidName,
+                &buf2Size,
+                &nameUse) == 0) {
+        if (debug) {
+            printf("  [getUser] LookupAccountName error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        // ok not to have a domain SID (no error)
+        goto cleanup;
+    }
+
+    bufSize = 0;
+    getTextualSid(dSid, NULL, &bufSize);
+    *domainSid = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    getTextualSid(dSid, *domainSid, &bufSize);
+    if (debug) {
+        printf("  [getUser] domainSid: %s\n", *domainSid);
+    }
+
+cleanup:
+    if (tokenUserInfo != NULL) {
+        HeapFree(GetProcessHeap(), 0, tokenUserInfo);
+    }
+    if (dSid != NULL) {
+        HeapFree(GetProcessHeap(), 0, dSid);
+    }
+    if (domainSidName != NULL) {
+        HeapFree(GetProcessHeap(), 0, domainSidName);
+    }
+    if (error) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+BOOL getPrimaryGroup(HANDLE tokenHandle, LPTSTR *primaryGroup) {
+
+    BOOL error = FALSE;
+    DWORD bufSize = 0;
+    DWORD retBufSize = 0;
+
+    PTOKEN_PRIMARY_GROUP tokenGroupInfo = NULL;
+
+    // get token information
+    GetTokenInformation(tokenHandle,
+                        TokenPrimaryGroup,
+                        NULL,   // TokenInformation - if NULL get buffer size
+                        0,      // since TokenInformation is NULL
+                        &bufSize);
+
+    tokenGroupInfo = (PTOKEN_PRIMARY_GROUP)HeapAlloc
+                        (GetProcessHeap(), 0, bufSize);
+    if (GetTokenInformation(tokenHandle,
+                        TokenPrimaryGroup,
+                        tokenGroupInfo,
+                        bufSize,
+                        &retBufSize) == 0) {
+        if (debug) {
+            printf("  [getPrimaryGroup] GetTokenInformation error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        error = TRUE;
+        goto cleanup;
+    }
+
+    if (debug) {
+        printf("  [getPrimaryGroup] Got TokenPrimaryGroup info\n");
+    }
+
+    bufSize = 0;
+    getTextualSid(tokenGroupInfo->PrimaryGroup, NULL, &bufSize);
+    *primaryGroup = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    getTextualSid(tokenGroupInfo->PrimaryGroup, *primaryGroup, &bufSize);
+    if (debug) {
+        printf("  [getPrimaryGroup] primaryGroup: %s\n", *primaryGroup);
+    }
+
+cleanup:
+    if (tokenGroupInfo != NULL) {
+        HeapFree(GetProcessHeap(), 0, tokenGroupInfo);
+    }
+    if (error) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+BOOL getGroups(HANDLE tokenHandle, PDWORD numGroups, LPTSTR **groups) {
+
+    BOOL error = FALSE;
+    DWORD bufSize = 0;
+    DWORD retBufSize = 0;
+    long i = 0;
+
+    PTOKEN_GROUPS tokenGroupInfo = NULL;
+
+    // get token information
+    GetTokenInformation(tokenHandle,
+                        TokenGroups,
+                        NULL,   // TokenInformation - if NULL get buffer size
+                        0,      // since TokenInformation is NULL
+                        &bufSize);
+
+    tokenGroupInfo = (PTOKEN_GROUPS)HeapAlloc(GetProcessHeap(), 0, bufSize);
+    if (GetTokenInformation(tokenHandle,
+                        TokenGroups,
+                        tokenGroupInfo,
+                        bufSize,
+                        &retBufSize) == 0) {
+        if (debug) {
+            printf("  [getGroups] GetTokenInformation error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        error = TRUE;
+        goto cleanup;
+    }
+
+    if (debug) {
+        printf("  [getGroups] Got TokenGroups info\n");
+    }
+
+    if (tokenGroupInfo->GroupCount == 0) {
+        // no groups
+        goto cleanup;
+    }
+
+    // return group info
+    *numGroups = tokenGroupInfo->GroupCount;
+    *groups = (LPTSTR *)HeapAlloc
+                (GetProcessHeap(), 0, (*numGroups) * sizeof(LPTSTR));
+    for (i = 0; i < (long)*numGroups; i++) {
+        bufSize = 0;
+        getTextualSid(tokenGroupInfo->Groups[i].Sid, NULL, &bufSize);
+        (*groups)[i] = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, bufSize);
+        getTextualSid(tokenGroupInfo->Groups[i].Sid, (*groups)[i], &bufSize);
+        if (debug) {
+            printf("  [getGroups] group %d: %s\n", i, (*groups)[i]);
+        }
+    }
+
+cleanup:
+    if (tokenGroupInfo != NULL) {
+        HeapFree(GetProcessHeap(), 0, tokenGroupInfo);
+    }
+    if (error) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+BOOL getImpersonationToken(PHANDLE impersonationToken) {
+
+    HANDLE dupToken;
+
+    if (OpenThreadToken(GetCurrentThread(),
+                        TOKEN_DUPLICATE,
+                        FALSE,
+                        &dupToken) == 0) {
+        if (OpenProcessToken(GetCurrentProcess(),
+                                TOKEN_DUPLICATE,
+                                &dupToken) == 0) {
+            if (debug) {
+                printf
+                    ("  [getImpersonationToken] OpenProcessToken error [%d]: ",
+                    GetLastError());
+                DisplayErrorText(GetLastError());
+            }
+            return FALSE;
+        }
+    }
+
+    if (DuplicateToken(dupToken,
+                        SecurityImpersonation,
+                        impersonationToken) == 0) {
+        if (debug) {
+            printf("  [getImpersonationToken] DuplicateToken error [%d]: ",
+                GetLastError());
+            DisplayErrorText(GetLastError());
+        }
+        return FALSE;
+    }
+    CloseHandle(dupToken);
+
+    if (debug) {
+        printf("  [getImpersonationToken] token = %p\n",
+            (void *)*impersonationToken);
+    }
+    return TRUE;
+}
+
+BOOL getTextualSid
+    (PSID pSid,                 // binary SID
+    LPTSTR TextualSid,          // buffer for Textual representation of SID
+    LPDWORD lpdwBufferLen) {    // required/provided TextualSid buffersize
+
+    PSID_IDENTIFIER_AUTHORITY psia;
+    DWORD dwSubAuthorities;
+    DWORD dwSidRev=SID_REVISION;
+    DWORD dwCounter;
+    DWORD dwSidSize;
+
+    // Validate the binary SID.
+    if(!IsValidSid(pSid)) return FALSE;
+
+    // Get the identifier authority value from the SID.
+    psia = GetSidIdentifierAuthority(pSid);
+
+    // Get the number of subauthorities in the SID.
+    dwSubAuthorities = *GetSidSubAuthorityCount(pSid);
+
+    // Compute the buffer length.
+    // S-SID_REVISION- + IdentifierAuthority- + subauthorities- + NULL
+    dwSidSize=(15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
+
+    // Check input buffer length.
+    // If too small, indicate the proper size and set last error.
+    if (*lpdwBufferLen < dwSidSize) {
+        *lpdwBufferLen = dwSidSize;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+
+    // Add 'S' prefix and revision number to the string.
+    dwSidSize=wsprintf(TextualSid, TEXT("S-%lu-"), dwSidRev );
+
+    // Add SID identifier authority to the string.
+    if ((psia->Value[0] != 0) || (psia->Value[1] != 0)) {
+        dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
+                TEXT("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
+                (USHORT)psia->Value[0],
+                (USHORT)psia->Value[1],
+                (USHORT)psia->Value[2],
+                (USHORT)psia->Value[3],
+                (USHORT)psia->Value[4],
+                (USHORT)psia->Value[5]);
+    } else {
+        dwSidSize+=wsprintf(TextualSid + lstrlen(TextualSid),
+                TEXT("%lu"),
+                (ULONG)(psia->Value[5]  )   +
+                (ULONG)(psia->Value[4] <<  8)   +
+                (ULONG)(psia->Value[3] << 16)   +
+                (ULONG)(psia->Value[2] << 24)   );
+    }
+
+    // Add SID subauthorities to the string.
+    for (dwCounter=0 ; dwCounter < dwSubAuthorities ; dwCounter++) {
+        dwSidSize+=wsprintf(TextualSid + dwSidSize, TEXT("-%lu"),
+                *GetSidSubAuthority(pSid, dwCounter) );
+    }
+
+    return TRUE;
+}
+
+void DisplayErrorText(DWORD dwLastError) {
+    HMODULE hModule = NULL; // default to system source
+    LPSTR MessageBuffer;
+    DWORD dwBufferLength;
+
+    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                        FORMAT_MESSAGE_IGNORE_INSERTS |
+                        FORMAT_MESSAGE_FROM_SYSTEM ;
+
+    //
+    // If dwLastError is in the network range,
+    //  load the message source.
+    //
+
+    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
+        hModule = LoadLibraryEx(TEXT("netmsg.dll"),
+                                NULL,
+                                LOAD_LIBRARY_AS_DATAFILE);
+
+        if(hModule != NULL)
+            dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+    }
+
+    //
+    // Call FormatMessage() to allow for message
+    //  text to be acquired from the system
+    //  or from the supplied module handle.
+    //
+
+    if(dwBufferLength = FormatMessageA(dwFormatFlags,
+                hModule, // module to get message from (NULL == system)
+                dwLastError,
+                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
+                (LPSTR) &MessageBuffer,
+                0,
+                NULL)) {
+        DWORD dwBytesWritten;
+
+        //
+        // Output message string on stderr.
+        //
+        WriteFile(GetStdHandle(STD_ERROR_HANDLE),
+                MessageBuffer,
+                dwBufferLength,
+                &dwBytesWritten,
+                NULL);
+
+        //
+        // Free the buffer allocated by the system.
+        //
+        LocalFree(MessageBuffer);
+    }
+
+    //
+    // If we loaded a message source, unload it.
+    //
+    if(hModule != NULL)
+        FreeLibrary(hModule);
+}
+
+/**
+ * 1. comment out first two #includes
+ * 2. set 'debug' to TRUE
+ * 3. comment out 'getCurrent'
+ * 4. uncomment 'main'
+ * 5. cc -c nt.c
+ * 6. link nt.obj user32.lib advapi32.lib /out:nt.exe
+ */
+/*
+void main(int argc, char *argv[]) {
+
+    long i = 0;
+    HANDLE tokenHandle = INVALID_HANDLE_VALUE;
+
+    LPTSTR userName = NULL;
+    LPTSTR userSid = NULL;
+    LPTSTR domainName = NULL;
+    LPTSTR domainSid = NULL;
+    LPTSTR primaryGroup = NULL;
+    DWORD numGroups = 0;
+    LPTSTR *groups = NULL;
+    HANDLE impersonationToken = 0;
+
+    printf("getting access token\n");
+    if (getToken(&tokenHandle) == FALSE) {
+        exit(1);
+    }
+
+    printf("getting user info\n");
+    if (getUser
+        (tokenHandle, &userName, &domainName, &userSid, &domainSid) == FALSE) {
+        exit(1);
+    }
+
+    printf("getting primary group\n");
+    if (getPrimaryGroup(tokenHandle, &primaryGroup) == FALSE) {
+        exit(1);
+    }
+
+    printf("getting supplementary groups\n");
+    if (getGroups(tokenHandle, &numGroups, &groups) == FALSE) {
+        exit(1);
+    }
+
+    printf("getting impersonation token\n");
+    if (getImpersonationToken(&impersonationToken) == FALSE) {
+        exit(1);
+    }
+
+    printf("userName = %s, userSid = %s, domainName = %s, domainSid = %s\n",
+        userName, userSid, domainName, domainSid);
+    printf("primaryGroup = %s\n", primaryGroup);
+    for (i = 0; i < numGroups; i++) {
+        printf("Group[%d] = %s\n", i, groups[i]);
+    }
+    printf("impersonationToken = %ld\n", impersonationToken);
+
+    if (userName != NULL) {
+        HeapFree(GetProcessHeap(), 0, userName);
+    }
+    if (userSid != NULL) {
+        HeapFree(GetProcessHeap(), 0, userSid);
+    }
+    if (domainName != NULL) {
+        HeapFree(GetProcessHeap(), 0, domainName);
+    }
+    if (domainSid != NULL) {
+        HeapFree(GetProcessHeap(), 0, domainSid);
+    }
+    if (primaryGroup != NULL) {
+        HeapFree(GetProcessHeap(), 0, primaryGroup);
+    }
+    if (groups != NULL) {
+        for (i = 0; i < numGroups; i++) {
+            if (groups[i] != NULL) {
+                HeapFree(GetProcessHeap(), 0, groups[i]);
+            }
+        }
+        HeapFree(GetProcessHeap(), 0, groups);
+    }
+    CloseHandle(impersonationToken);
+    CloseHandle(tokenHandle);
+}
+*/
+
+/**
+ * extra main method for testing debug printing
+ */
+/*
+void main(int argc, char *argv[]) {
+    if(argc != 2) {
+        fprintf(stderr,"Usage: %s <error number>\n", argv[0]);
+    }
+
+    DisplayErrorText(atoi(argv[1]));
+}
+*/