0

I would like to write a function to check if a given user is member of a group, in C language (it is based on a code which I found on stackoverflow How to programmatically figure out if a user account is a member of a particular group in Windows?). I've developped this function within the WinCC Software, which is a scada system.

Here's my code :

//Insert the header starting here
#include "WinApi.h"
#include "GlobalDefinitions.h" 

BOOL CheckGroupMembership(char* UserName, char* Password, char* Domain, char* Group)
{
    HRESULT result;
    SID_NAME_USE SIDType;
    WCHAR szDomain[256];
    DWORD dwSidSize=0;
    DWORD dwSize;
    HANDLE user;
    BOOL b=FALSE,returnval;
    SID* pSid;
    WCHAR szGroup[50];  
    // Convertit le nom du groupe en wide string
    result=MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,Group,strlen(Group),szGroup,50);
    printf("Nom du groupe : %ls\r\n",szGroup);
    //recupere SID groupe   
    //premier appel de lookup pour allouer la bonne taille de SID (remis à jour dans dwSidSize);
    LookupAccountNameW(NULL,szGroup,NULL,&dwSidSize,szDomain,&dwSize,&SIDType);
    if (dwSidSize)
    {
        pSid=(SID*)malloc(dwSidSize);
        //note : pas besoin de lui fournir un domaine si la machine est déjà intégrée dedans
        LookupAccountNameW(NULL,szGroup,pSid,&dwSidSize,szDomain,&dwSize,&SIDType);
        printf("dwSidSize %d \r\n",dwSidSize);
        printf("Infos SID : %x , %x, %x - %d \r\n", pSid->Revision,pSid->SubAuthorityCount,pSid->IdentifierAuthority,SIDType);
        // Récupère le handle de l'utilisateur  
        returnval=LogonUserA(UserName,Domain,Password,2,0,&user);   
        printf("User %d  - %x \r\n",user,returnval);
        returnval=CheckTokenMembership(user,pSid,&b);
    }
    printf("returnval %d - b : %d - %d\r\n",returnval,b,GetLastError()); 
    free(pSid);
    return b;
}

Whatever I do, the returnvalue outside CheckGroupMemberShip is false. I think LookupAccountNameW works well, if i put a valid group i've got some valid informations within the SID structure. If i put a group that doesn't exist, it just gives me zeros.

I think also LogonUserA works pretty well, as if i give valid informations the returnval is true, otherwise it is false.

Only checkGroupMembership doesn't seem to answer me.

There are some things I don't understand well though : Why is there a list of SIDs for one requested group and not only one ? Is there a place where I can compare the values within the SID structure and the SID corresponding to an existing group ? Whatever I give (group or user name) him it seems that the SIDType, is always the same at the output of LookupAccountNameW, Why ? Finally, GetLastError doesn't seem to help me. It always gives me 0, why ?

To be noticed : the "WinApi.h" is custom made, because I only wanted to import what was strictely neededd within my application. If you think that could be the problem, I could post it.

Thanks in advance for your answers,

Julien

  • for `CheckTokenMembership` - *The token must be an impersonation token.* and `LogonUser` *In most cases, the returned handle is a primary token* – RbMm May 05 '20 at 12:41
  • OK That's a good lead. Even by looking quickly on the internet, I do not understand quite the difference. How could I Modify/Replace LogonUser to get an impersonation token ? – Julien Freund May 05 '20 at 12:51
  • for what you print last error if you ignore it ? but it clear say to you - `ERROR_NO_IMPERSONATION_TOKEN` - *An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client.* – RbMm May 05 '20 at 12:53
  • you can call `DuplicateToken` for this – RbMm May 05 '20 at 12:54
  • Well that's part of the problem...last error gives me zero. But I'm guessing there's something wrong with the call of GetLastError too. Not so familiar with Windows API though – Julien Freund May 05 '20 at 12:56
  • this is because you call `GetLastError` too late. must do this after api call fail. but you in middle call `printf` also you have problem with memory free logic, not close returned token, etc – RbMm May 05 '20 at 12:58
  • Ok thanks for all those precious answers ! (also super quick) – Julien Freund May 05 '20 at 13:00
  • also you can use `LOGON32_LOGON_NETWORK` instead `LOGON32_LOGON_INTERACTIVE`(2) in call `LogonUserA` in this case api return to you already an impersonation token – RbMm May 05 '20 at 13:00
  • also use `LsaLookupNames` instead `LookupAccountNameW` if you need convert name to sid. this is much more efficient – RbMm May 05 '20 at 13:02
  • Changed from LOGON32_LOGON_INTERACTIVE to LOGON32_LOGON_NETWORK, works like a charm. I've modified GetLastError and called it before the printf but it still gives me zero though. Not sure to see where are the problems on the free logic. How do i close cleanly the token ? – Julien Freund May 05 '20 at 13:45
  • Check last error only when the function fails, for example like this `if (!CheckTokenMembership(user,pSid,&b)) { printf("error: %d\r\n",GetLastError()); } FreeSid(pSid);` And when you no longer need the token handle, close it by calling the `CloseHandle` function. – Rita Han May 06 '20 at 08:25
  • OK thanks for CloseHandle! Well it's true that now that there's no longer an error, so there is nothing to show - but I still don't understand why it wasn't showing it before. – Julien Freund May 06 '20 at 10:00

0 Answers0