8
votes

Vérification de la connexion à l'utilisateur de l'administrateur dans les installations non anglaise de Windows

J'ai quelques petites questions ... J'ai un programme qui stocke une liste d'utilisateurs dans une base de données et compare sur le démarrage du programme si l'utilisateur est dans la liste ou est un administrateur avant de les laisser utiliser. Pour le moment, la façon dont j'utilise pour vérifier si l'utilisateur est un administrateur est simplement en comparant le nom d'utilisateur à une constante de chaîne appelée «administrateur». Cela fonctionnera-t-il sur un système non rigoureux? C'EST À DIRE. Windows utilise-t-il une version spécifique à une langue d'administrateur? Ou peut-être existe-t-il une version énumérée de l'utilisateur administrateur que je peux utiliser pour vérifier au lieu de ma chaîne d'administrateur? (Vous savez, tout comme comment les dossiers Windows sont énumérés). J'utilise Delphi 2009 au fait. Merci d'avance!


2 commentaires

Le nom de l'administrateur change, et il peut même être désactivé sur certains systèmes où les stratégies exigent que chaque administrateur connexe avec son propre compte.


Bon vous avez posé cette question!


5 Réponses :


7
votes

Non, ne le faites pas de cette façon. Cela va sûrement casser. Vous pouvez obtenir une liste de tous les groupes que l'utilisateur est membre et vérifier si l'un des SID est S-1-5-32-544 , qui est le SID du groupe Administrateurs. Il y a une liste de SIDS bien connus. Il y a aussi un SID pour le compte d'administrateur d'origine.

Voici la liste:

http://support.microsoft.com/kb/243330


2 commentaires

-1: Une simple recherche d'une adhésion retournera un résultat incorrect dans> = Vista parce que l'adhésion aux administrateurs est généralement limitée dans le jeton. L'adhésion est disponible mais Windows vérifiera le drapeau Deny_only_SID de l'adhésion dans le jeton et refuser toujours l'accès. Les jetons restreints ont été disponibles depuis Windows 2000 mais étaient rarement utilisés.


Votre suggestion est implémentée ici: Techtricks.com/delphi/isadmin.php est incorrect pour > = Vista (et fonctionne sur



1
votes

Il varie de la version Windows à la version Windows ... en pré-vista ... Administrateur Nom d'utilisateur est dans la langue de Windows principale ... Par exemple, en espagnol, il est Administrador .

Dans Post-Vista, il n'y a pas d'utilisateur administrateur. Vous stockerez et recherchez des privilèges utilisateur.

J'ai trouvé ce fonction isadmin et vous le trouverez peut-être aussi ... < / p>


1 commentaires

-1: Dans Post Vista, il y a un administrateur, mais il est désactivé. Chaque utilisateur avec l'adhésion aux administrateurs de groupe est de facto un administrateur. La fonction Isadmin est incorrecte, elle ne prend pas en compte le jeton restreint et ne vérifie que la disponibilité des administrateurs de groupe dans le jeton, mais ignore le drapeau Deny_only. Ces sources flottent beaucoup sur Internet, car elles sont simplement copiées d'un article ancien et révisé MSDN.



7
votes

Actualités

En 2010, @christianwimmer a critiqué mon style de codage. Maintenant, deux ans après, je dois utiliser la fonction à nouveau dans mon programme. Donc, j'ai décidé d'améliorer le style de codage de la fonction. P>

Vue d'ensemble h2>

Je choisis une petite partie de ma bibliothèque privée pour votre commodité. Pour tester si le compte d'utilisateur du jeton d'accès est membre du groupe de l'administrateur local, passe winbuiltinadministratorsid code> de jwawinnt code> à ewellnownSidType em> paramètre. Remarque, il nécessite Jedi API Libray parce que Delphi windows.pas Code> Unité n'a pas défini CreewellknownSide () Code>. P>

Mise en œuvre H2>
//------------------------------------------------------------------------------
// Purpose: Tests whether user account of the access token is a member of the
//   specified well known group, and report its elevation type.
// Parameter:
//   hToken [in,opt]
//     A handle to an access token having TOKEN_QUERY and TOKEN_DUPLICATE
//     access. If hToken is 0: if it is an impersonation token, the access token
//     of the calling thread is used; otherwise, the access token associated
//     with the process is used.
//   eWellKnownSidType [in]
//     Member of the WELL_KNOWN_SID_TYPE enumeration that specifies what Sid the
//     function will identify.
//   pDomainSid [in,opt]
//     A pointer to a SID that identifies the domain to use when identifying the
//     Sid. Pass nil to use the local computer.
//   peElevType [out,opt]
//     A pointer to a variable that receives the following elevation type of the
//     access token:
//       - TokenElevationTypeDefault: The access token does not have a linked
//         token. This value is reported under Windows prior to Windows Vista.
//       - TokenElevationTypeFull: The access token is an elevated token.
//       - TokenElevationTypeLimited: The access token is a limited token.
// Return value:
//   - True if user account of the access token is a member of the well known
//     group specified in eWellKnownSidType parameter.
//   - False, otherwise. To get error information, call GetLastError().
// Remarks:
//   To test whether user account of the access token is a member of local
//   administrators group, pass JwaWinNT.WinBuiltinAdministratorsSid to
//   eWellKnownSidType parameter.
// References:
//   - How To Determine Whether a Thread Is Running in User Context of
//     Local Administrator Account [MSDN]
//------------------------------------------------------------------------------
function Inu_IsMemberOfWellKnownGroup(const hToken: Windows.THandle;
    const eWellKnownSidType: JwaWinNT.WELL_KNOWN_SID_TYPE;
    const pDomainSid: JwaWinNT.PSID=nil;
    peElevType: PTokenElevationType=nil): Boolean;
var
  hAccessToken: Windows.THandle;
  rOSVerInfo: Windows.OSVERSIONINFO;
  eTET: Windows.TTokenElevationType;
  iReturnLen: Windows.DWORD;
  hTokenToCheck: Windows.THandle;
  iSidLen: Windows.DWORD;
  pGroupSid: JwaWinNT.PSID;
  bMemberOfWellKnownGroup: Windows.BOOL;
begin
  Result := False;
  hAccessToken := 0;
  hTokenToCheck := 0;
  pGroupSid := nil;
  try
    if hToken = 0 then begin // If the caller doesn't supply a token handle,
      // Get the calling thread's access token
      if not Windows.OpenThreadToken(Windows.GetCurrentThread(),
          Windows.TOKEN_QUERY or Windows.TOKEN_DUPLICATE,
          True, hAccessToken) then begin
        if Windows.GetLastError() <> Windows.ERROR_NO_TOKEN then
          Exit();
        // If no thread token exists, retry against process token
        if not Windows.OpenProcessToken(Windows.GetCurrentProcess(),
            Windows.TOKEN_QUERY or Windows.TOKEN_DUPLICATE, hAccessToken) then
          Exit();
      end;
    end
    else // If the caller supplies a token handle,
      hAccessToken := hToken;
    // Determine whether the system is running Windows Vista or later because
    // because they support linked tokens, previous versions don't.
    rOSVerInfo.dwOSVersionInfoSize := SizeOf(Windows.OSVERSIONINFO);
    if not Windows.GetVersionEx(rOSVerInfo) then
      Exit();
    if rOSVerInfo.dwMajorVersion >= 6 then begin
      // Retrieve information about the elevation level of the access token
      if not Windows.GetTokenInformation(hAccessToken,
          Windows.TokenElevationType, @eTET,
          SizeOf(Windows.TTokenElevationType), iReturnLen) then
        Exit();
      // If the access token is a limited token, retrieve the linked token
      // information from the access token.
      if eTET = Windows.TokenElevationTypeLimited then begin
        if not Windows.GetTokenInformation(hAccessToken,
            Windows.TokenLinkedToken, @hTokenToCheck,
            SizeOf(Windows.TTokenLinkedToken), iReturnLen) then
          Exit();
      end;
      // Report the elevation type if it is wanted
      if Assigned(peElevType) then
        peElevType^ := eTET;
    end
    else begin // if rOSVerInfo.dwMajorVersion < 6
      // There is no concept of elevation prior to Windows Vista
      if Assigned(peElevType) then
        peElevType^ := Windows.TokenElevationTypeDefault;
    end;
    // CheckTokenMembership() requires an impersonation token. If we just got a
    // linked token, it is already an impersonation token. Otherwise, duplicate
    // the original as an impersonation token for CheckTokenMembership().
    if (hTokenToCheck = 0) and (not Windows.DuplicateToken(hAccessToken,
        Windows.SecurityIdentification, @hTokenToCheck)) then
      Exit();
    // Allocate enough memory for the longest possible Sid
    iSidLen := JwaWinNT.SECURITY_MAX_SID_SIZE;
    pGroupSid := JwaWinNT.PSid(Windows.LocalAlloc(Windows.LMEM_FIXED, iSidLen));
    if not Assigned(pGroupSid) then
      Exit();
    // Create a Sid for the predefined alias as specified in eWellKnownSidType
    if not JwaWinBase.CreateWellKnownSid(eWellKnownSidType, pDomainSid,
        pGroupSid, iSidLen) then
      Exit();
    // Now, check presence of the created Sid in the user and group Sids of the
    // access token. In other words, it determines whether the user is a member
    // of the well known group specified in eWellKnownSidType parameter.
    if not JwaWinBase.CheckTokenMembership(hTokenToCheck, pGroupSid,
        bMemberOfWellKnownGroup) then
      Exit();
    Result := bMemberOfWellKnownGroup;
  finally
    // Close the access token handle
    if hAccessToken <> 0 then
      Windows.CloseHandle(hAccessToken);
    // Close the new duplicate token handle if exists
    if (hTokenToCheck <> 0) then
      Windows.CloseHandle(hTokenToCheck);
    // Free the allocated memory for the Sid created by CreateWellKnownSid()
    if Assigned(pGroupSid) then
      Windows.LocalFree(Windows.HLOCAL(pGroupSid));
  end;
end; // endfunction Inu_IsMemberOfWellKnownGroup
//==============================================================================


26 commentaires

+1: Pour utiliser Jedi-API -1: Pour utiliser Goto: Ceci n'est vraiment pas nécessaire ici et il semble que vous avez converti une source C une à une? Cela rend le code vraiment difficile à lire. (Je suis un essai / enfin suiveur) +1: Pour ajouter beaucoup de commentaires. Vous vérifiez les valeurs de retour de LocalFree et de Centherhandle! Cela n'a aucun sens imho. Si vous avez déjà un résultat BmemberofwellknownPonnuryGroup, vous pouvez simplement appeler les fonctions GRATUITES et Fermer la poignée et vous pouvez être heureux. S'ils échouent, il y a un vrai problème qui est déguisé néanmoins. Habituellement, nous avons dû vérifier tous les appels gratuits et fermés avec RaiselastosError.


J'utilise toujours essayer. > Ne comprend que ses propres exceptions. En ce qui concerne votre localfree () & CLOSEHANDLE () Commentaire, IMHO, c'est une question de préférence. J'ai vérifié leurs valeurs de retour car je voudrais peut-être savoir pourquoi ils échouent dans une circonstance étrange; Certaines personnes pourraient ne pas s'en soucier. Pour goto , je ne l'utilise jamais dans mon code pure / mixe pascal. Je l'utilise parfois dans le code Winapi pour réduire l'indentation et beaucoup de la même répétition de code, mais uniquement dans une fonction simple (non composée).


Je ne parle pas d'essayer / sauf parce qu'ils sont inutiles dans des appels d'API purs. Au lieu de cela, essayez / enfin peut être utilisé pour fermer des poignées ou une mémoire libre, même si vous appelez la sortie. De cette façon, vous n'avez pas besoin de clauses "si" sur votre code pour cela. Vérification de la temporisation et telle n'est que USuable en mode de débogage ou si vous prenez des efforts supplémentaires. Cependant, dans votre code, vous créez un résultat incorrect même si le résultat a été calculé correctement auparavant.


Non, je n'ai pas converti une source C sur une. Vous pouvez comparer ma fonction avec mon référence d'origine http://msdn.microsoft.com/en-us/windows/ff420334.aspx , n'est-ce pas très différent?


@Christian, en code Pure Winapi (sans aucun code RTL / VCL), nous n'avons même pas besoin d'un Essayer / enfin Juste pour que tout soit fermé. À propos de la vérification de la recherche, je suppose que vous ne remarquez pas que MSDN ait beaucoup un tel code, car, en le faisant, les utilisateurs finaux pourraient également remarquer une défaillance de clôture lorsqu'elle se produise afin qu'elle puisse la déboguer et / ou le signaler à son programmeur . BTW, pourriez-vous mentionner explicitement où est le résultat incorrect que vous vouliez dire?


Je ne te blâme pas! Je dis juste que cela semble être converti de c. Les stratégies de sortie GOTO sont vraiment utilisées souvent dans le code MS C dans MSDN. Mais essayez / définit enfin le code beaucoup plus clair pour lire à Delphi IMHO.


Si ce n'est pas de prèshandle (HaccessToken), puis quittez (); Résultat: = BmemberofwellknownknownnownPonnondGroup; Cette étroitehandle modifiera la valeur de résultat de la fonction même si Bmemberof ... est vrai. Couperhandle a échoué, vous ne pouvez rien faire à ce sujet, mais la fonction renvoie néanmoins fausse. Essayez / Enfin n'est pas pour l'appel Winapi, mais pour les appels de sortie () s'ils échouent. De cette façon, vous pouvez simplement sortir et enfin être appelé cependant. Pas besoin de goto ici.


De plus, si l'un des appels Winapi normaux échoue, vous venez de sortir avec FALSE. D'une certaine manière, vous mappez toutes les erreurs sur FALSE qui est également une valeur de retour correcte. Comment pouvez-vous distinguer entre eux? La solution est donc d'utiliser des exceptions. Si ce n'est pas WinCall, puis RaiselastosError. Il vient aussi essayer / enfin assez pratique.


Votre lien ( msdn.microsoft.com/en-us/windows/ff420334.aspx ) le rend même correct: si (WinCall) {lasterr = error_success;} else {lasterr = getlasterror ();}, puis si (error_success! = lasterr) {lancer (lasterr);} retour (fisadmin); Les valeurs de retour de tous les appels de la clientèle sont irnorées.


+1 Pour dire au mauvais endroit du résultat (j'ai raté cela, et je vais le corriger) :-). Cependant, Essayez / enfin ne sait jamais si une fonction API appelle le succès ou non. Sans aucun si ainsi que essayer..finalement dans un code PURE WinapI, le code restant sera toujours exécuté même si une fonction API échoue. Comme vous le voyez, il n'y avait même pas une déclaration RTL / VCL dans le code ci-dessus. Donc, essayer / enfin est juste un gâchis.


Mes fonctions de faible niveau ont été conçues pour fonctionner comme fonctionne comme des fonctions de Winapi. Pour obtenir le détail d'erreur, l'utilisateur peut appeler getlasterror () .


Comme je l'ai dit auparavant, vérifiez la valeur de retour de étroitehandle () ou localfree () a son utilisation, MSDN le fait dans de nombreux endroits, par exemple. Voir http://msdn.microsoft.com/en-us/library/ms724892%28vs.85%29. ASPX ; Encore une fois, c'est juste une question de préférence.


Il y a tant de style de codage, un code peut parfois être souligné sur son éligibilité et met parfois l'accent sur sa vitesse (par exemple projet FastCode). Pensez-vous que les personnes MSDN ne sont pas en mesure de ne pas utiliser goto dans leur code? La lisibilité parfois une vue relative, une personne peut dire qu'il est plus facile de lire des non imbriqués en profondeur si et la même répétition de code au lieu d'utiliser goto pour les réduire; Et quelqu'un pourrait dire que ce dernier est plus lisible.


"Mes fonctions de faible niveau ont été conçues pour fonctionner comme fonctionne comme des fonctions de Winapi. Pour obtenir le détail d'erreur, l'utilisateur peut appeler getlasterror ()". Non tu ne l'as pas fait! L'utilisateur ne peut pas appeler GetLasterRor sur votre fonction car la valeur de retour d'erreur FALSE fait partie d'une valeur de retour réussie. Et puisque getlasterror n'est jamais réinitialisé à 0 Votre code peut retourner false sur le succès mais GetLasterRor contient le code d'erreur à partir d'une panne 10 appels de pile avant votre fonction. Alors, comment un appelant peut-il savoir si l'élément donné est membre d'administrateurs ou de la fonction a échoué? WinapI utilise le paramètre Out pour renvoyer le résultat!


"Comme je l'ai déjà dit, vérifiant la valeur de retour de CLOSTHANDLE () ou localFree () a son utilisation, MSDN le fait dans de nombreux endroits, par exemple, voir" ... Bien sûr, vous pouvez également trouver autant d'exemples où ils ne le font pas . Mais pourquoi vérifiez-vous une seule tirette, mais laissez-vous 2 autres seuls? Sous un débogueur, vous n'avez pas besoin de le faire du tout parce que Collerhandle jette une exception en lisant MSDN. Le vérificateur d'application AFAIK peut également vérifier cette poignée.


"Cependant, essayez / finalement ne sait jamais si une fonction API appelle le succès ou non. Sans aucun autre que, si vous essayez de tout aussi bien que d'un code pur winapi, le code restant sera toujours exécuté même si une fonction API échoue." Avez-vous même lu mes commentaires du tout? J'ai déjà dit ça. IMHO, GOTO doit être remplacé dans Delphi avec sortie en combinaison avec TRY / FINAL. La vieille plaine c ne connaît pas les exceptions et donc de nombreux codes n'utilisent pas cette technique qui leur convient. Dans ma carrière, je pouvais lire beaucoup de tels codes C et j'étais toujours content de lire un code Delphi.


Conversion de ces codes à Delphi sans les adapter aux feuilles de Delphi ne sont pas utilisées qui sont là pour rendre le code mieux. Checkout Jedi Windows Code de sécurité Bibliothèque. De nombreuses fonctions Winapi sont appelées de cette manière en utilisant Essayer / enfin (et aussi sortir). Et cela fonctionne bien car les erreurs sont propagées à l'aide d'exceptions Delphes avec beaucoup d'informations supplémentaires que GetLasterRor ne peut pas fournir. Ils ne peuvent même pas échouer silencieusement comme votre fonction où un appelant ne sais pas si False est une valeur valide ou d'erreur.


-1 pour une mauvaise manipulation des erreurs et ignorer les commentaires / suggestions pour les améliorer.


@Remko, j'avais déjà admis le bogue dans mon commentaire avant, mais je n'avais pas assez de temps pour le réparer, mais maintenant c'est. Ne vous attendez pas à ce que chaque peuple puisse réparer le plus tôt possible, nous sommes des gens occupés. BTW Même si je ne l'ai pas réparais, le questionneur a déjà obtenu quelles mesures pour atteindre son objectif et que tout le monde peut réparer un code aussi simple.


@Christian, vous avez une chance de critiquer le résultat du code alors qu'il n'était pas encore réparé à l'époque. En ce qui concerne votre commentaire sur essayer..finalement , je tiens à dire, je ne la déteste pas, je l'aime, je l'utilise dans mes programmes comme je l'ai déjà dit, mais c'est un petit Fonction contenant des appels d'API pure, essayer..finalement il suffit de rendre la fonction plus lentement. Je sais que la fonction n'est pas criante à optimiser, mais j'ai d'autres fonctions de bas niveau dans la même bibliothèque qui sont critiques. Donc, le style de codage est juste pour la cohérence de style dans cette bibliothèque.


@Remko, -1 Pour avoir voté sur l'action, simplement parce que vous trouvez une faille ou que vous n'êtes pas d'accord avec la manière dont quelque chose est mis en œuvre.


@Vantomex: Ne me trompe pas. Je déteste critiquer sans montrer des moyens de solutions ni d'améliorations. J'essaie de ne pas faire tout mal. Mais sachez que, en affectant le code source, les gens le prennent pour acquis que votre code en tant qu'expert fonctionne également pour eux (sinon, le code que vérifie que l'administrateur ne serait pas copié de nouveau encore et encore ... Voir le lien dans un des réponses ici encore). Cependant, vous semblez déjà connaître les problèmes de votre code, mais je ne leur ai pas commis de commentaires. Cela le rend plus problématique. Imo, vous n'avez pas aidé mais je viens de le rendre pire.


essayer / enfin rend le code plus lent? Hmm, avez-vous une preuve de cela? Je peux donner une contre-preuve de Dr. Bob de drobob42.com/delphi/perform.htm "Enfin, les mesures du bloc TRA-enfin-enfin ont également été introduites avec Delphi Exceptions n'indiquent aucune perte de performance significative non plus. Ceci, combiné au fait qu'un blocage-enfin est un moyen très utile de protéger les ressources et de prévenir Toute fuite, fait l'essai-enfin-bloquer mon mode de programmation préféré! " C'est ce que je parlais de tout le temps!


"Les exceptions Delphes n'indiquent aucune perte de performance significative non plus." Oui, c'est vrai, c'est pourquoi j'ai dit "un peu plus lent". Vous pourriez dire que ce n'est que la différence nano / micro / milisecondes, mais il est toujours plus lent. J'ai vu le compilateur ajouter un code supplémentaire lorsque essayer..finalement a été appliqué. Néanmoins, je n'ai jamais dit essayer..finalement était mauvais. S'il vous plaît, pardonnez-moi, c'est juste pour la consistance de style dans cette bibliothèque.


@Christian, cependant, je dois dire merci pour votre explication sur ESSAY-EXIT-EXIT-enfin Trick, j'ai raté une telle pensée. :-)


@Christian, je viens de mettre à jour le style de codage comme vous l'avez suggéré. Merci. :-)



1
votes

Il y a le Fonction CreewellknownSid .

Mais la vérification explicite du compte administrateur peut ne pas être une bonne idée. Faites simplement l'opération et demandez une élévation si vous avez eu une erreur «accès refusé».


0 commentaires

2
votes

Ceci est un extrait de jwscltoken.pas de l'API de Jedi & WSCL. Les deux fonctions font le même chèque mais de différentes manières. Vous voyez comment peu de code est utilisé? Le même code dans la nature Winapi serait au moins 5 fois plus grand. Bien sûr, vous pouvez simplement appeler ces fonctions de l'unité elle-même. pas besoin de copier ici strong>!

function JwCheckAdministratorAccess: boolean;
var
  SD: TJwSecurityDescriptor;
begin
  if not Assigned(JwAdministratorsSID) then
    JwInitWellKnownSIDs;

  SD := TJwSecurityDescriptor.Create;
  try
    SD.PrimaryGroup := JwNullSID;
    SD.Owner   := JwAdministratorsSID;
    SD.OwnDACL := True;

    SD.DACL.Add(TJwDiscretionaryAccessControlEntryAllow.Create(nil,
      [], STANDARD_RIGHTS_ALL, JwAdministratorsSID, False));

    Result := TJwSecureGeneralObject.AccessCheck(SD, nil,
      STANDARD_RIGHTS_ALL, TJwSecurityGenericMapping);
  finally
    FreeAndNil(SD);
  end;
end;

function JwIsMemberOfAdministratorsGroup: boolean;
var
  Token: TJwSecurityToken;
begin
  Token := TJwSecurityToken.CreateTokenEffective(TOKEN_READ or
    TOKEN_DUPLICATE);
  try
    Token.ConvertToImpersonatedToken(SecurityImpersonation, MAXIMUM_ALLOWED);
    Result := Token.CheckTokenMembership(JwAdministratorsSID)
  finally
    FreeAndNil(Token);
  end;
end;


3 commentaires

Ne devriez-vous pas faire le attribué (jwadministratorsidsid) enregistrement dans la deuxième fonction? Ou est-il initialisé dans l'une des déclarations précédentes?


Bon point. Cela se fait par l'utilisateur, mais il devrait y avoir une vérification néanmoins. Je pense que j'ai ajouté un chèque dans une autre révision. Je viens de voir que j'ai attrapé une révision plus ancienne. Maintenant, vous voyez pourquoi la copie et la pâte ne devraient pas faire sans réfléchir ni utiliser directement la bibliothèque. Je vais le réparer dans le référentiel de succursales de subversion mais je la quitte ici.


Jedidi API utilise beaucoup d'inline ASM et qui n'est pas prise en charge par le compilateur Win64 de Delphi. EMG fournit uniquement un sous-ensemble de l'API Windows (j'ai uniquement XE7). Quelqu'un toute suggestion?