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 :
- 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.
- 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 :
- On initialise les variables
- On appelle la fonction MSGETUSERNAME%
- 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 :
- 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.
- 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
- 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:
Enregistrer un commentaire