mercredi, janvier 13, 2010

Récupérer un SID ou un login Windows depuis NatStar


DiggIt! Enregistrer sur Del.icio.us

J’ai du récemment pour un projet ajouter une authentification intégrée à une application NatStar : l’objectif étant d’éviter à l’utilisateur de saisir un mot de passe si l’utilisateur Windows connecté est autorisé à utiliser l’application.

L’utilisateur “Windows” peut recouvrir des informations techniques différentes, dans ce billet j’en traiterai deux :

  • le login Windows : le nom du compte Windows utilisé pour se connecter
  • le SID de l’utilisateur : il s’agit d’une notion MicroSoft qui permet l’identification unique d’un utilisateur (Security ID)

La récupération de ces informations et leur utilisation depuis NatStar n’étant pas très simple, je vous propose dans ce billet une description détaillée du mécanisme.

L’exercice est fait dans le cadre de NatStar, il ne doit pas être très compliqué de l’adapter au cas NS-DK.

Les fonctions MicroSoft

Nous allons utiliser 5 fonctions MicroSoft depuis NatStar :

  • advapi32.GetUserNameA : Cette fonction est le point de départ du mécanisme, elle retourne le login de l’utilisateur connecté.
  • advapi32.LookupAccountNameA : cette fonction permet à partir du login récupéré grâce à la fonction précédente de récupérer un pointeur sur la structure SID.
  • Advapi32.ConvertSidToStringSidA : cette fonction permet d’obtenir à partir du SID récupéré par la fonction précédente une chaine de caractère correspondant au SID.
  • KERNEL32.GetLastError : cette fonction permet de récupérer le dernier message d’erreur levé par Windows. Les 3 fonctions précédentes positionnent en cas de problème cette erreur.
  • KERNEL32.LocalFree : cette fonction permet de libérer certaines ressources allouées par Windows. Elle permettra de libérer l’information renvoyée par ConvertSidToStringSidA.

Les 3 premières fonctions retournent 0 en cas d’erreur et une valeur différente de 0 si tout se passe bien.

LookupAccountNameA

Cette fonction a un mode opératoire peu commun : il faut l’appeler deux fois :

  1. une première fois avec une taille à 0 et un pointeur SID à 0 : la fonction retourne alors la taille du SID. Il faut allouer alors l’espace nécessaire pour le SID.
  2. le second appel prend l’adresse de l’espace alloué et la taille du buffer pour le remplir. On dispose alors d’un SID.

Pour corser le tout, dans le premier cas, il ne faut pas tenir compte de la valeur retournée : il y a une erreur si la taille est nulle.

Mapping des fonctions MicroSoft dans NatStar

Pour pouvoir utiliser les fonctions précédemment définies depuis NatStar nous avons du définir des fonctions Externes. Pour ce faire, nous avons créé une librairie de service dans laquelle nous avons ajouter 5 fonctions.

Pour ces 5 fonctions, le radio bouton External de la définition devra être coché.

MSGETLASTERROR% pour GetLastError

Nous commençons par la plus simple. Nous éditons la définition de la fonction. Dans la dialogue “Modify Definition for Function or Instruction MSGETLASTERROR%” on précise :

  • Result Type : INT (4)
  • External : KERNEL32.GetLastError

Cette fonction ne prenant pas paramètre, il n’y a rien d’autre à faire.

MSLOCALFREE pour LocalFree

Nous continuons par un peu plus compliqué. Nous éditons la définition de la fonction. Dans la dialogue “Modify Definition for Function or Instruction MSLOCALFREE” on précise :

  • Result Type : POINTER
  • External : KERNEL32.LocalFree

Cette fonction possède un paramètre, on ajoute donc dans l’onglet Parameters le paramètre suivant :

  • Name : HMEM
  • On coche : In
  • Type : POINTER
  • Size : vide

Pensez à appuyer sur Append puis Ok afin de ne pas perdre vos modifications.

MSGETUSERNAME% pour GetUserNameA

Nous continuons par un peu plus compliqué. Nous éditons la définition de la fonction. Dans la dialogue “Modify Definition for Function or Instruction MSGETUSERNAME%” on précise :

  • Result Type : INT (4)
  • External : advapi32.GetUserNameA

Cette fonction possède 2 paramètres, on ajoute donc dans l’onglet Parameters les paramètres suivant :

  • Name : LPBUFFER$
  • On coche : Out
  • Type : CSTRING
  • Size : 255

Pensez à appuyer sur Append puis Ok afin de ne pas perdre vos modifications, puis ajoutez le second :

  • Name : NSIZE%
  • On coche : In et Out
  • Type : INT
  • Size : 4

C’est le premier paramètre LPBuffer$ qui contiendra le nom du compte Windows de l’utilisateur.

MSGETUSERSID% pour LookupAccountNameA

Nous continuons par un peu plus compliqué. Nous éditons la définition de la fonction. Dans la dialogue “Modify Definition for Function or Instruction MSGETUSERSID%” on précise :

  • Result Type : INT (4)
  • External : advapi32.LookupAccountNameA

Cette fonction possède 7 paramètres, on ajoute donc dans l’onglet Parameters les paramètres suivant :

  • Name : LPSYSTEMNAME
  • On coche : In
  • Type : CSTRING
  • Size : 255

Pensez à appuyer sur Append puis Ok afin de ne pas perdre vos modifications, puis ajoutez le second :

  • Name : LPACCOUNTNAME
  • On coche : In
  • Type : CSTRING
  • Size : 255

Passons au troisième :

  • Name : SID
  • On coche : In
  • Type : POINTER
  • Size : vide

Passons au quatrième :

  • Name : CBSID
  • On coche : In et Out
  • Type : INT
  • Size : 4

Passons au cinquième :

  • Name : REFERENCEDDOMAINNAME
  • On coche : Out
  • Type : CSTRING
  • Size : 255

Passons au sixième :

  • Name : CCHREFERENCEDDOMAINNAME
  • On coche : In et Out
  • Type : INT
  • Size : 4

Passons au septième :

  • Name : PEUSE
  • On coche : Out
  • Type : INT
  • Size : 4

C’est le 3ieme paramètre SID qui correspond au pointeur SID de Windows. C’est cette valeur qui sera utilisée avec MSCONVERTSIDTOSTRINGSID% pour récupérer la chaine de caractères correspondante.

 

MSCONVERTSIDTOSTRINGSID% pour ConvertSidToStringSidA

Nous finissons par la plus compliquée. Avant d’éditer la définition de cette fonction,nous allons créer un nouveau segment PSTRSID qui sera utilisé pour récupérer la valeur qui contient la chaine de caractère.

Il faut savoir que la fonction MicroSoft, déclare ce paramètre en C++ comme :

BOOL ConvertSidToStringSid( 

__in PSID Sid

__out LPTSTR *StringSid

);

Le StringSid est donc un pointeur sur un pointeur d’une chaine. Pour faire la même chose, nous avons besoin d’un segment intermédiaire.

Ce segment est très simple, il ne possède qu’un seul Field :

  • Name : STRINGSID$
  • Type : CSTRING
  • Size : 255
  • On coche la case Reference (attention ce point est très important)

Nous pouvons maintenant éditer la définition de la fonction. Dans la dialogue “Modify Definition for Function or Instruction MSCONVERTSIDTOSTRINGSID%” on précise :

  • Result Type : INT (4)
  • External : advapi32.ConvertSidToStringSidA

Cette fonction possède 2 paramètres, on ajoute donc dans l’onglet Parameters les paramètres suivant :

  • Name : SID
  • On coche : In
  • Type : POINTER
  • Size : vide

Pensez à appuyer sur Append puis Ok afin de ne pas perdre vos modifications, puis ajoutez le second :

  • Name : STRINGSID
  • On coche : Out
  • Type : PSTRSID (c’est le segment créé précédemment il apparait dans la partie qui est en ordre alphabétique)
  • Size : vide

C’est le 1er paramètre SID qui correspond au pointeur SID de Windows récupéré dans la fonction précédente.

Le second paramètre permettra de récupérer la chaine de caractère grâce à : L_SID_USER_WINDOWS.STRINGSID$ si L_SID_USER_WINDOWS est la valeur récupérée comme second paramètre.

Utilisation des fonctions

Nous avons définis les fonctions externes qui permettent l’appel des services MicroSoft, nous allons maintenant utiliser ces services.

Récupération du login Windows

Cette première étape est simple :

  1. On initialise les variables
  2. On appelle la fonction MSGETUSERNAME%
  3. On vérifie qu’il n’y a pas eu d’erreur

Ce qui donne en NCL :

LOCAL CSTRING Buffer$
LOCAL int SizeBuffer%
LOCAL int Retour%

Local Cstring USER_WINDOWS$(255)

USER_WINDOWS$ = ''
L_LPBUFFER$ = ''
SizeBuffer% = 255 ; taille du buffer
Retour% = 0

Retour% = MSGETUSERNAME%(Buffer$, SizeBuffer%)

; Si le retour est différent de 0 alors tout est OK
If Retour% <> 0
    USER_WINDOWS$ = skip L_LPBUFFER$

Endif

La valeur récupérée dans la variable USER_WINDOWS$ est le login Windows de l’utilisateur. Il peut être utilisé pour vérifier en base de données si cet utilisateur a le droit de se connecter à votre application.

Récupération du SID Windows

Le login Windows est simple à récupérer, il possède une signification fonctionnelle pour les utilisateurs mais il est moins permanent que le SID : si l’utilisateur change de login suite à un mariage par exemple, le compte Windows est modifié mais pas son SID. Son inconvénient est que le SID est un numéro incompréhensible sans aucun sens fonctionnel.

Il peut être également intéressant de récupérer le SID pour interroger d’autres services de sécurité, mais nous n’aborderons pas ce point.

Pour utiliser le SID, il faut d’abord récupérer le USER_WINDOWS$. On ajoute ensuite le code suivant :

Retour% = 0
L_LPSYSTEMNAME$ = ''
SizeBuffer% = 0 ; taille du buffer
L_LPSYSTEMNAME$ = ''
L_CCHREFERENCEDDOMAINNAME% = 255 ; taille du buffer

; Le premier appel de la fonction avec un 3eme paramètre à 0, renvoie la taille de la structure PSID (SizeBuffer%)
Retour% = MSGETUSERSID%(L_LPSYSTEMNAME$, USER_WINDOWS$, 0, SizeBuffer%, L_REFERENCEDDOMAINNAME$, L_CCHREFERENCEDDOMAINNAME%, L_PSID_NAME_USE%)
things_trace "1ere appel SizeBuffer%= " & SizeBuffer%

If (SizeBuffer%<=0)
    things_trace "ERREUR lors du 1er MSGETUSERSID% GetLastError%="&  String(MsGetLastError%, 16)
Else
    New  SizeBuffer%,  L_PSID
    ; Récupère le pointer de la structure SID du USER Windows
    Retour% = MSGETUSERSID%(L_LPSYSTEMNAME$, USER_WINDOWS$, L_PSID, SizeBuffer%, L_REFERENCEDDOMAINNAME$, L_CCHREFERENCEDDOMAINNAME%, L_PSID_NAME_USE%)

    if (Retour%=0)
        things_trace "ERREUR lors du 2nd MSGETUSERSID% GetLastError%="&  String(MsGetLastError%, 16)
    Else
        ; Convertie le pointer de la structure SID du USER Windows en chaîne
        Retour% = MSCONVERTSIDTOSTRINGSID%(L_PSID, L_SID_USER_WINDOWS)
        if (Retour%=0)
            things_trace "ERREUR lors du MSCONVERTSIDTOSTRINGSID% GetLastError%="&  String(MsGetLastError%, 16)
        Else
            SID_USER_WINDOWS$ = L_SID_USER_WINDOWS.STRINGSID$
            things_trace "SID_USER_WINDOWS$=" & SID_USER_WINDOWS$

            ; Il faut liberer la chaine récupérée
            P = MSLocalFree(@L_SID_USER_WINDOWS.STRINGSID$)
            things_trace "Resultat LocalFree="& P && String(MsGetLastError%, 16)
        Endif

    Endif
    dispose L_PSID
Endif

 

Pour qu’il fonctionne, il faut ajouter au début les déclarations suivantes :

LOCAL pointer L_PSID, pointer P
Local int L_PSID_NAME_USE%
Local int L_CCHREFERENCEDDOMAINNAME%
Local CSTRING L_LPSYSTEMNAME$
Local CSTRING L_REFERENCEDDOMAINNAME$
Local PSTRSID L_SID_USER_WINDOWS

La valeur récupérée dans SID_USER_WINDOWS$  est la chaine de caractères correspondant à l’identifiant du SID.

On notera l’utilisation de la syntaxe : String(MsGetLastError%, 16). Le GetLastError renvoie un code d’erreur. Ce code d’erreur est un numérique mais apparait généralement dans la documentation sous forme hexadécimal. cette syntaxe permet de tracer directement le code hexadécimal.

Paramètre de compilation

La compilation du code ci-dessus demande d’être capable de se linker avec la librairie advapi32.dll. Cette DLL système est disponible sur tous les postes et ne nécessite aucun déploiement supplémentaire.

La compilation nécessite d’avoir accès à son LIB. Pour que cela soit possible, il faut modifier la variable d’environnement LIB et ajouter les deux répertoires suivants (dans le cas de MSVC 8) :

  • C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib
  • C:\Program Files\Microsoft Visual Studio 8\VC

Si vous utilisez NatStar directement (sans NsaConfig) cela suffit.

 

Dans le cadre d’une utilisation de NsaConfig où le lancement et l’environnement de travail de NatStar dépend de la configuration de NsaConfig, il est nécessaire de modifier le fichier C:\NSAC\INI\wspmgr.ini en :

  1. ajoutant dans la déclaration EnvVar les deux variables suivantes MsSdklib et COMPPATH (c’est indispensable pour que les définitions qui suivent soient prises en compte.
  2. puis en ajoutant les deux lignes de définition suivantes dans la partie qui suit la déclaration de EnvVar
    • MsSdklib=C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\Lib
    • COMPPATH=C:\Program Files\Microsoft Visual Studio 8\VC
  3. puis en modifiant la déclaration de LIB qui devient :
    • LIB=%NS-GLOB%\CLASSES\LIB;%NS-GLOB%\SERVICES\LIB;%COMPPATH%\LIB;%MsSdklib%;%NATSTARPATH%\LIB;%LIB%

 

Conclusion

J’espère que ce billet vous aura été utile. Il montre qu’il est possible de mettre en œuvre une authentification intégrée dans une application NatStar ou NS-DK même si cela n’a rien d’évident. On notera au passage que la complexité provient principalement des API MicroSoft mise à disposition (Je sais, je suis directeur technique de Nat System et donc probablement un peu partial).


DiggIt! Enregistrer sur Del.icio.us

0 commentaires: