LA SÉCURITÉ D'ACCÈS AU CODE

J-M Rabilloud

Cet article présente les concepts de sécurité d'accès au code utilisés par le Framework .NET. Il ne s'agit que d'une première approche de la sécurité, d'autres articles viendront compléter celui-ci. ♪

- Titre : LA SÉCURITÉ D'ACCÈS AU CODE

- Auteur : J-M Rabilloud

- Parution : 20 juillet 2004

- Licence : Ce document est issu de http://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. Concepts généraux

- II-A. Zones de sécurité

- II-B. Jeu d'autorisations

- II-C. Outil d'administration

- III. Principe de fonctionnement

- III-A. Preuves et autorisations

- III-B. Vérification des appelants

- IV. Utilisation

- IV-A. Vérification de ses autorisations

- IV-A-1. Sécurité déclarative

- IV-A-2. Sécurité impérative

- IV-B. Augmenter les privilèges

- IV-C. Parcours de pile

- IV-C-1. Assert

- IV-C-2. PermitOnly & Deny

- IV-C-3. Règles d'utilisation

- IV-C-4. Exemple

- IV-D. Autres demandes

- IV-D-1. Demande de liaison

- IV-D-2. InheritanceDemand

- V. Personnalisation

I - Introduction

La problématique de la sécurisation des applications a toujours été complexe par le fait qu'elle n'est pas dissociable de l'administration du poste de travail.

On distingue généralement dans les concepts de sécurité les cas suivants :

- qualité du code ;

- cryptographie ;

- authentification et privilège.

Qualité du code

Très souvent, les concepts de l'authentification et des privilèges font défaut. En effet, au niveau du poste de travail, c'est l'authentification qui donne accès à un certain nombre de privilèges selon l'appartenance à un groupe d'utilisateurs et/ou par attribution de droits spécifiques à un utilisateur donné.

Ceci induit un problème maintenant bien connu, puisqu'une application possède généralement les privilèges du compte qui l'exécute.

Il y a fort peu de chances que les utilisateurs deviennent spontanément les experts administrations ce qui est pourtant la condition sine qua non d'une véritable sécurité. Le Framework .Net utilise aussi une technique de gestion des privilèges appelée « sécurité d'accès au code »

II - Concepts généraux

La sécurité d'accès au code propose sur le principe de l'attribution de privilèges selon l'origine du code. On retrouve ce concept parfois sous le nom de preuve par l'origine. Comme pour la sécurité Windows, ce système de sécurité se doit d'être administré. Dans son principe général, le Framework considère l'existence de zones dites « de sécurité » permettant ou non l'accès à un certain nombre de privilèges. Évidemment, le compte de l'utilisateur doit posséder les privilèges nécessaires à l'exécution. Autrement dit, il s'agit d'un contrôle de sécurité qui ne préempte pas sur la sécurité système.

II-A - Zones de sécurité

Le Framework définit cinq zones de sécurité :

- sites sensibles : sites identifiés ainsi dans votre navigateur ;

- internet : application s'exécutant depuis Internet hors sites sensibles ou de confiance ;

- sites de confiance : sites identifiés ainsi dans votre navigateur ;

- intranet : applications installées sur l'Intranet du poste ;

- poste de travail : applications installées sur l'ordinateur.

Sites sensibles : sites identifiés ainsi dans votre navigateur

C'est zones de sécurité ne font rien par elle-même, elle détermine juste un niveau de confiance auquel est associé un jeu d'autorisations. C'est celui-ci qui détermine les ressources accessibles.

Comme nous le verrons un peu plus loin, il est possible d'ajouter de nouvelles zones de sécurité le cas échéant

II-B - Jeu d'autorisations

Parfois appelé aussi niveau de confiance. Il existe des jeux nommés déjà définis :

Jeu d'autorisations

Description

Nothing

Pas d'autorisations (le code ne s'exécute pas).

Execution

Autorisations d'exécuter sans utiliser des ressources protégées.

Internet

Jeu d'autorisations de la stratégie par défaut adaptée au contenu d'origine inconnue.

LocalIntranet

Jeu d'autorisations de la stratégie par défaut au sein d'une entreprise.

Everything

Toutes autorisations standard (intégrées), à l'exception de l'autorisation d'ignorer la vérification.

SkipVerification

Ignorer la vérification

FullTrust

Accès intégral à toutes les ressources.

Les jeux d'autorisations Intranet et Internet possèdent des autorisations qui sont définies par les stratégies de sécurité du poste de travail, de l'entreprise et/ou de l'utilisateur.

Le Framework expose les autorisations suivantes :

Nom de classe de l'autorisation

Droit correspondant

AspNetHostingPermission

Accès aux ressources dans des environnements hébergés par ASP.NET.

DirectoryServicesPermission

Accès aux classes System.DirectoryServices.

DnsPermission

Accès au système de noms de domaine (DNS).

EnvironmentPermission

Lecture ou écriture de variables d'environnement.

EventLogPermission

Accès en lecture ou en écriture aux services de journaux des événements.

FileDialogPermission

Accès aux fichiers que l'utilisateur a sélectionné dans une boite de dialogue Ouvrir.

FileIOPermission

Lecture, ajout ou écriture de fichiers ou de répertoires.

IsolatedStorageFilePermission

Accès à un stockage isolé, c'est-à-dire un stockage associé à un utilisateur particulier et à certains aspects de l'identité du code, tel que son site Web, son éditeur ou sa signature.

MessageQueuePermission

Accès aux files d'attente de messages par le biais des interfaces MSMQ (Microsoft Message Queuing).

OdbcPermission

Accès à une source de données ODBC.

OleDbPermission

Accès à des bases de données utilisant OLE DB.

OraclePermission

Accès à une base de données Oracle.

PerformanceCounterPermission

Accès aux compteurs de performance.

PrintingPermission

Accès aux imprimantes.

ReflectionPermission

Identification d'informations se rapportant à un type au moment de l'exécution.

RegistryPermission

Lecture, écriture, création ou suppression de clés et de valeurs de la base de registres.

SecurityPermission

Exécution, assertion d'autorisations, appel dans du code non managé, omission de vérification et autres droits.

ServiceControllerPermission

Accès à des services en cours d'exécution ou arrêtés.

SocketPermission

Établissement ou acceptation de connexions sur une adresse de transport.

SqlClientPermission

Accès à des bases de données SQL.

UIPermission

Accès aux fonctionnalités de l'interface utilisateur.

WebPermission

Établissement ou acceptation de connexions sur une adresse Web.

Chacune de ces classes peut gérer un grand nombre d'autorisations spécifiques avec plusieurs types d'accès différents. Par exemple chaque variable d'environnement peut être définie séparément, pour un accès en lecture et/ou en écriture ou pour en interdire l'accès.

Le nombre de jeux de permissions réalisable est donc gigantesque.

II-C - Outil d'administration

Il existe de nombreux outils pour manipuler ces concepts de sécurité. Le plus simple à utiliser se trouve dans le panneau de configuration, c'est l'outil de configuration du Framework.

Il se présente sous la forme suivante :

À partir de ce gestionnaire, il est possible d'ajouter des zones de sécurité, de créer et/ou de modifier des jeux de permissions, etc.

Par exemple je peux créer assez facilement un jeu d'autorisation :

Pour cela il suffit de sélectionner « Jeux d'autorisations » dans le volet de gauche, de cliquer sur le lien « Créer un nouveau jeu d'autorisations ». Puis de suivre les instructions, par exemple :

III - Principe de fonctionnement

Généralement, c'est parce qu'on ne comprend pas bien le cheminement de l'évaluation qu'on se trompe.

III-A - Preuves et autorisations

Lors du chargement d'un assembly, le runtime commence par évaluer les preuves fournies pas l'assembly. Comme nous l'avons dit, celles-ci sont toujours aux moins fixées par la zone de sécurité d'où s'exécute l'assembly, mais il peut en posséder d'autres, telles qu'un nom fort, un chemin d'accès spécifique, un certificat, etc.

Selon ces preuves, le runtime calcule un jeu d'autorisation. Si à un moment votre code appelle une ressource sans en avoir l'autorisation, il y a levée d'une exception.

Commençons à définir, les règles de fonctionnement :

Règle n° 1 : C'est toujours la stratégie de sécurité qui attribue les autorisations

Autrement dit, vous n'avez pas la certitude qu'un code fonctionnant sur un poste avec ses preuves fonctionnera sur un autre poste avec les mêmes preuves, car la stratégie de sécurité peut différer d'un poste à l'autre.

Règle n° 2 : On ne peut pas obtenir par le code des autorisations non données par la stratégie de sécurité

Votre code ne peut pas augmenter ses privilèges.

Règle n° 3 : C'est au développeur de gérer les exceptions de sécurité

Sans gestion de la sécurité, le programme s'arrêtera de fonctionner lors de l'appel de la ressource interdite.

Pour pouvoir anticiper sur ces erreurs éventuelles, le Framework met à votre disposition des mécanismes de demandes d'autorisation que nous verrons un peu plus loin.

III-B - Vérification des appelants

Comme vous l'avez surement remarqué, il y a un défaut à la cuirasse. Si la principale source de preuve est l'origine, tout assembly installé sur le poste de travail puis exécuté recevra le jeu d'autorisations maximales. Tout comme pour la sécurité du système d'exploitation, il incombe à l'utilisateur de savoir ce qu'il fait.

Par extension, on pourrait croire qu'un code s'exécutant d'une zone de sécurité restreinte peut augmenter ses privilèges en appelant un assembly installé sur le poste de travail. Ceci est faux, car le Framework implémente un contrôle de sécurité des appelants appelé communément « parcours de pile ».

Dans le principe, lors de l'appel d'une ressource protégée par un code managé, le CLR parcourt en remontant la pile des appelants de la méthode qui sollicite la ressource. Si un de ces appelants n'a pas l'autorisation nécessaire, l'appel de la méthode échoue et une exception est levée. Il est possible d'influer sur le parcours de pile par le code.

IV - Utilisation

Ne perdons pas de vue que dans certains cas, les vérifications de sécurité sont inutiles. Par exemple si votre code ne nécessite aucune ressource protégée ou si votre assembly ne peut pas être appelé par un code tierce.

IV-A - Vérification de ses autorisations

Comme la règle 2 nous le dit, le contrôle des autorisations ne change rien au fait qu'une ressource interdite le sera, mais permet uniquement de savoir quand l'exception sera levée et traitée.

Pour comprendre cela, envisageons le cas suivant. Je crée une application de type WinForms qui lit un fichier XML (un dataset) et l'affiche dans une grille. Le code est aussi simple que :

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim MonDS As New Data.DataSet("biblio")
MonDS.ReadXml("d:\tutoriel\pubs.xml", XmlReadMode.ReadSchema)
Me.DataGrid1.DataSource = MonDS.Tables(0)
End Sub

Comme le fichier existe bien et sur mon poste de travail le code s'exécute correctement, j'affiche bien ma table dans ma grille. Maintenant, j'upload l'exécutable sur Internet. Avec mon navigateur, je vais sélectionner cet exécutable.

Je vais recevoir un premier message d'alerte de la forme :

Comme nous l'avons vu, il est maintenant plus sûr d'exécuter le code distant que de l'enregistrer sur le disque, puisqu'une fois sur le disque dur, il sera considéré comme un code de confiance totale. Je clique donc sur le bouton « Exécuter ».

Le navigateur m'envoie un deuxième message d'erreur, tel que :

Celui-ci est dû à l'absence de certificat de mon exécutable. Il y aurait beaucoup à dire sur la sécurité et les certificats, cependant demander l'exécution d'un code sans certificat peut déclencher l'apparition de cette boite de dialogue selon le paramétrage du navigateur. Je clique donc une nouvelle fois sur « Exécuter » et j'obtiens :

Nous voyons bien que la stratégie de sécurité a joué son rôle et qu'un code issu de la zone de sécurité « Internet » ne peut pas utiliser des ressources de fichier locales. À ce moment l'erreur se produit bien sur la ligne :

MonDS.ReadXml("d:\tutoriel\pubs.xml", XmlReadMode.ReadSchema)

Si vous voulez en être sur, il suffit d'ajouter la ligne :

MessageBox.Show("je charge le dataset maintenant")

Juste avant l'appel de ReadXML. Dans ce cas vous verrez apparaître la boite de message puis l'erreur.

Le fait que cette boite de dialogue s'affiche alors que le code va et doit lever une exception n'est pas une bonne chose. Pour éviter le problème, on procède généralement à des vérifications d'autorisation.

IV-A-1 - Sécurité déclarative

La sécurité déclarative consiste à indiquer au niveau de l'assembly, les autorisations exigées, optionnelles ou refusées. On utilise pour cela, dans le manifeste de l'assembly, des attributs de permissions qui définissent celles-ci. La forme sera toujours :

<Assembly:ClassePermissionAttribute(SecurityAction.Request…,[Param])>

Les SecurityAction déclaratives sont généralement :

RequestMinimum

Demande des autorisations minimales pour que le code fonctionne.

RequestOptional

Demande d'autorisations supplémentaires et facultatives (non requises pour le fonctionnement).

RequestRefuse

Demande pour que les autorisations qui peuvent être utilisées abusivement ne soient pas accordées au code appelant.

Comment cela fonctionne-t-il ?

Lors du chargement de l'assembly, le CLR calcule les autorisations que possède l'assembly. Si tout ou partie des autorisations requises ne peuvent être accordées, aucun code de l'assembly ne sera exécuté et une exception de sécurité sera levée. Si tout ou partie des autorisations optionnelles ne peuvent être accordées, il y a aussi levée d'une exception, mais le code de l'assembly peut s'exécuter.

Imaginons que dans mon code précédent, j'ajoute dans le manifeste de l'assembly les deux attributs suivants :

<Assembly: Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.RequestMinimum, _
Read:="d:\tutoriel\pubs.xml")>
<Assembly: Security.Permissions.FileIOPermission(Security.Permissions.SecurityAction.RequestOptional, _
Write:="d:\tutoriel\pubs.xml")>

Si j'exécute ce code depuis Internet, j'obtiens une erreur non récupérable (sauf à lancer le débogueur). Ce n'est pas tellement plus propre me direz-vous. En effet, on ne doit pas perdre de vue que toute cette gestion est basée sur la vérification des appelants. En l'occurrence, mon code n'a pas d'appelant, les exceptions au niveau assembly ne peuvent être interceptées, je dois donc gérer mes vérifications autrement.

IV-A-2 - Sécurité impérative

La sécurité impérative se gère au moment de l'exécution. Elle présente l'avantage d'être applicable au niveau de la classe ou de la méthode, mais elle induit que le code d'un assembly de confiance partielle va s'exécuter jusqu'au moment de la vérification.

Reprenons notre code précédent, en utilisant la sécurité déclarative :

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim MonDS As New Data.DataSet("biblio")
MessageBox.Show("je charge le dataset maintenant")
Dim MaPerm As New Security.Permissions.FileIOPermission(Security.Permissions.PermissionState.Unrestricted, _
"d:\tutoriel\pubs.xml")
Try
MaPerm.Demand()
Catch ex As Exception
MessageBox.Show("vous n'avez pas l'autorisation nécessaire", "Erreur", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
Me.Close()
End Try
MonDS.ReadXml("d:\tutoriel\pubs.xml", XmlReadMode.ReadSchema)
Me.DataGrid1.DataSource = MonDS.Tables(0)
End Sub

Là à l'exécution je verrais bien apparaître ma boite de dialogue annonçant l'erreur et l'exécution s'arrêtera.

Cependant l'appel explicite d'une demande force très souvent un parcours de pile inutile. En effet, comme nous l'avons vu dans le premier exemple, il y a eu un parcours de pile généré par l'appel de la méthode ReadXML du dataset. On utilise généralement Demand de façon explicite lorsqu'on souhaite protéger des ressources qui ne sont pas protégées naturellement par le Framework.

IV-B - Augmenter les privilèges

Dans les cas comme celui-là, on a trop souvent tendance à prendre des mesures disproportionnées par rapport au problème. Bien sûr mon exemple est loin d'être un cas réel, car on se demande bien quel serait l'intérêt d'un tel code. Cependant pour contourner les problèmes, on a trop souvent tendance à vouloir modifier la stratégie de sécurité ou de forcer l'installation sur le poste de travail. Ce qui nous mène à la règle suivante :

Règle n° 4 : L'ignorance et la fainéantise sont les deux plus grandes failles de sécurité.

En effet, avant d'envisager forcément un octroi démesuré de droit sur la zone « Internet » ou de forcer l'installation locale, il est plus sage de définir un ou plusieurs nouveaux groupes de code sur lequel on appliquera une accumulation de preuve donnant droit à des accès. Ainsi dans mon cas, je pourrais créer un jeu d'autorisation donnant accès à mon fichier XML, puis créer un groupe de code qui vérifierait le nom fort et le site d'origine de l'appelant et en cas de concordance lui octroyer ce jeu d'autorisations. Bien sûr, cela demande d'intervenir sur la stratégie de sécurité du poste, ce qui peut être lourd si vous ciblez de nombreux postes, mais il est possible de préparer des fichiers XML permettant de simplifier l'opération.

Dans certains cas, on peut aussi envisager des stratégies de déploiement particulières, d'utiliser le stockage isolé, etc.

IV-C - Parcours de pile

La sécurité repose donc sur le parcours de la pile des appelants. Dans certains cas, il peut être intéressant de modifier l'action de celle-ci, sous réserve de prendre en charge les implications de sécurité. Ces opérations qu'on retrouve sous le nom de substitution de vérification de sécurité sont de deux sortes.

IV-C-1 - Assert

L'assertion indique que vous pouvez exécuter une tâche pour un appelant moins sécurisé. Autrement dit, vous savez que votre composant va interagir sans risque avec une ressource protégée et qu'aucun code appelant ne peut détourner votre code. Dans ce cas, vous pouvez faire un appel à Assert pour bloquer les vérifications de sécurité pour la ou les permissions désignées comme cible de l'assertion.

IV-C-2 - PermitOnly & Deny

Ce sont les substitutions inverses de l'assertion. Le but est de refuser des autorisations au code appelant. PermitOnly refuse toutes les autorisations qui ne sont pas comprises dans le jeu soumis à PermitOnly, Deny refuse toutes celles qui sont comprises dans ce jeu. On utilise l'un ou l'autre selon qu'il soit plus court de décrire un jeu de celles qui sont autorisées ou de celles qui ne le sont pas. PermitOnly permet aussi de résoudre certains problèmes de représentation canonique.

IV-C-3 - Règles d'utilisation

Les substitutions de sécurité doivent toujours être explicitement annulées à l'aide d'une des méthodes suivantes : RevertAll, RevertAssert, RevertDeny ou RevertPermitOnly.

Le code utilisant une substitution doit être hautement sécurisé. Les substitutions peuvent avoir des effets redoutables si vous n'en comprenez pas bien les implications. Par exemple un Appel de type PermitOnly sur le système de fichier peut interdire tout appel ultérieur par les appelants en aval de l'interface utilisateur. Vous ne devez donc substituer qu'en toute connaissance de cause.

IV-C-4 - Exemple

Jusqu'à présent, j'ai écrit du code de goret puisque le seul but était de montrer le principe des autorisations. Appliquons-nous maintenant, puisque pour la suite nous allons générer un composant de code. Créons la classe suivante :

Imports System.Security
Imports System.Data
Imports System.IO
<Assembly: AllowPartiallyTrustedCallers()>
Public Class DtsXML

Private m_FileName As String = ""

Public Sub New()

End Sub

Public Sub New(ByVal Fichier As String)
Me.NomFichier = Fichier
End Sub

Public Property NomFichier() As String
Get
Return m_FileName
End Get
Set(ByVal Value As String)
m_FileName = Value
End Set
End Property

Public Function ExtractDataset() As DataSet

If Me.NomFichier.Length = 0 OrElse Me.NomFichier.EndsWith("xml") = False Then
Throw New ArgumentException("Le nom de fichier n'est pas valide")
Return Nothing
End If
Dim MonDts As New DataSet
Dim pAssert As New Permissions.FileIOPermission(Permissions.FileIOPermissionAccess.Read, Me.NomFichier)
Dim pDeny As New Permissions.FileDialogPermission(Permissions.PermissionState.Unrestricted)
pAssert.Assert()
pDeny.Deny()
Try
MonDts.ReadXml(Me.NomFichier, XmlReadMode.ReadSchema)
Catch ex As FileNotFoundException
Throw New ApplicationException("Le fichier" & Me.NomFichier & " n'est pas accessible")
Catch ex As Xml.XmlException
Throw New ApplicationException("Le fichier " & Me.NomFichier & " n'est pas un fichier xml valide")
Finally
pAssert.RevertAssert()
pDeny.RevertDeny()
End Try
Return MonDts
End Function

End Class

Quelques remarques sur ce code. J'ai écrit un composant que je souhaite déployer dans le cache global d'assemblies (GAC). Dès lors je vais devoir lui donner un nom fort. Pour qu'un assembly ayant un nom fort puisse être invoqué par du code partiellement fiable, je dois lui ajouter l'attribut AllowPartiallyTrustedCallers.

Pour que ce composant soit utilisable d'une zone de confiance partielle, je vais donc faire une assertion sur l'accès en lecture au fichier passé en paramètre. Par ailleurs je fais aussi un Deny sur l'accès aux boites de dialogue de fichier qui n'a aucun autre intérêt que d'être un exemple de code.

En faisant mon assertion, je prends le risque de donner le droit de lecture sur le fichier cible à mon composant même si le code appelant ne possède pas ce droit. Je ne prends pas un gros risque puisqu'il s'agit :

- possédant des ACL compatibles avec la session utilisateur en cours ;

- d'un fichier XML interprétable comme un Dataset ;

- d'un accès en lecture seule.

Possédant des ACL compatibles avec la session utilisateur en cours

J'encapsule ma méthode ReadXML dans un try…Catch…Finally afin de pouvoir annuler mes substitutions en cas d'échec.

J'écris ensuite un projet WinForms pour faire un test. Dans ce projet j'ajoute une référence à mon composant de code. Je mets sur la feuille un bouton et une grille et j'écris le code :

Private Sub cmdChargement_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles cmdChargement.Click
Dim MonDS As New DataSet
Dim objExtr As New clsDtsXML.DtsXML("d:\tutoriel\pubs.xml")
Try
MonDS = objExtr.ExtractDataset
Me.DataGrid1.DataSource = MonDS.Tables(0)
Catch ex As Exception
MessageBox.Show("Echec de l'extraction " & vbCrLf & ex.Message, "Erreur", _
MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try

End Sub

Je génère cet exécutable. Même si je l'installe sur Internet, j'aurais bien remplissage de ma grille puisque l'assertion supprime le parcours de pile.

IV-D - Autres demandes

IV-D-1 - Demande de liaison

Une demande de liaison, LinkDemand, est une demande de sécurité un peu particulière, puisqu'elle ne vérifie que l'appelant immédiat. Elle se vérifie pendant la compilation JIT. L'avantage de ce type de demande est qu'elle est moins couteuse qu'une vérification complète, mais comme il ne s'agit pas d'une demande normale, elle suppose que l'appelant soit lui-même correctement sécurisé.

IV-D-2 - InheritanceDemand

Ce type de demande sert à vérifier qu'un héritier possède bien les droits nécessaires. Généralement, lorsqu'on veut sécuriser une classe dans des scénarios d'héritage, on tend à sceller celle-ci (NotInheritable en VB ou Sealed en C#). Si on veut pouvoir permettre l'héritage tout en vérifiant quand même la sécurité, on utilise alors InheritanceDemand.

V - Personnalisation

Pour finir, nous allons voir la création d'une autorisation personnalisée. Pour cela je vais créer une nouvelle bibliothèque de classe. Je dois générer un Assembly avec nom fort, j'utilise donc Sn.exe pour générer la signature que j'appelle dans le manifeste de l'assembly.

Cette permission est utilisée dans une entreprise pour accéder à des clés de cryptage d'entreprise, c'est-à-dire permettant à quiconque dans l'entreprise de crypter et décrypter des données avec la même paire de clés.

Je crée donc une classe SocietyPermission telle que :

Imports System.Security
Imports System.Security.Permissions

<Serializable()> Public NotInheritable Class PermissionSociety
Inherits CodeAccessPermission
Implements IUnrestrictedPermission

Private p_AllowAccess As Boolean
Private p_State As PermissionState

Public Sub New(ByVal State As PermissionState)
If State = PermissionState.Unrestricted Then Me.CanAccess = True
Me.p_State = PermissionState.Unrestricted
End Sub

Public Property CanAccess() As Boolean
Get
Return Me.p_AllowAccess
End Get
Set(ByVal Value As Boolean)
Me.p_AllowAccess = Value
If Value Then Me.p_State = PermissionState.Unrestricted Else Me.p_State = PermissionState.None
End Set
End Property

Public Overrides Function Copy() As System.Security.IPermission
Return New PermissionSociety(Me.p_State)
End Function

Public Overrides Function Intersect(ByVal target As System.Security.IPermission) As System.Security.IPermission
If target Is Nothing Then Return Nothing
If Not TypeOf target Is PermissionSociety Then
Throw New ArgumentException("Le paramètre doit être de type PermissionSociety")
End If
Dim TargetPerm As PermissionSociety = CType(target, PermissionSociety)
If TargetPerm.CanAccess And Me.CanAccess Then
Return New PermissionSociety(PermissionState.Unrestricted)
Else
Return New PermissionSociety(PermissionState.None)
End If
End Function

Public Overrides Function Union(ByVal other As System.Security.IPermission) As System.Security.IPermission

If other Is Nothing Then Return Me.Copy
If Not TypeOf other Is PermissionSociety Then
Throw New ArgumentException("Le paramètre doit être de type PermissionSociety")
End If
Dim OtherPerm As PermissionSociety = CType(other, PermissionSociety)
If OtherPerm.CanAccess Or Me.CanAccess Then
Return New PermissionSociety(PermissionState.Unrestricted)
Else
Return New PermissionSociety(PermissionState.None)
End If

End Function

Public Overrides Function IsSubsetOf(ByVal target As System.Security.IPermission) As Boolean
If target Is Nothing Then
If Me.CanAccess Then Return False Else Return True
End If
If Not TypeOf target Is PermissionSociety Then
Throw New ArgumentException("Le paramètre doit être de type PermissionSociety")
End If
Dim TargetPerm As PermissionSociety = CType(target, PermissionSociety)
If TargetPerm.CanAccess Xor Me.CanAccess Then
Return False
Else
Return True
End If
End Function

Public Overrides Sub FromXml(ByVal elem As System.Security.SecurityElement)
Dim strUnrestricted As String = elem.Attribute("Unrestricted")
If CBool(strUnrestricted) Then Me.CanAccess = True Else _
Me.CanAccess = False
End Sub

Public Overrides Function ToXml() As System.Security.SecurityElement
Dim elem As New SecurityElement("IPermission")
Dim Nom As String = Me.GetType.AssemblyQualifiedName.ToString
With elem
.AddAttribute("class", Nom)
.AddAttribute("version", CStr(1))
If Me.CanAccess Then
.AddAttribute("Unrestricted", Boolean.TrueString)
Else
.AddAttribute("Unrestricted", Boolean.FalseString)
End If
End With
Return elem
End Function

Public Function IsUnrestricted() As Boolean _
Implements System.Security.Permissions.IUnrestrictedPermission.IsUnrestricted
If Me.CanAccess Then Return True Else Return False
End Function

End Class

Comme vous le voyez, c'est une permission assez simple puisqu'elle est vraie ou fausse, autrement dit on l'a entièrement ou pas du tout.

Les méthodes écrites sont obligatoires (MustOverrides) sauf IsUnrestricted qui est donnée par IUnrestrictedPermission.

Je crée ensuite l'attribut qui permet la vérification de cette permission au niveau Assembly. Là encore il s'agit d'un code assez simple :

Imports System.Security
Imports System.Diagnostics
Imports System.Security.Permissions

<Serializable(), AttributeUsage(AttributeTargets.Assembly Or _
AttributeTargets.Class Or _
AttributeTargets.Method Or _
AttributeTargets.Constructor, AllowMultiple:=True, Inherited:=False)> _
Public NotInheritable Class PermissionSocietyAttribute
Inherits CodeAccessSecurityAttribute

Private m_Access As Boolean

Public Sub New(ByVal Action As SecurityAction)
MyBase.New(Action)
End Sub

Public Property CanAccess() As Boolean
Get
Return Me.m_Access
End Get
Set(ByVal Value As Boolean)
Me.m_Access = Value
End Set
End Property

Public Overrides Function CreatePermission() As System.Security.IPermission
If Unrestricted Then
Return New PermissionSociety(PermissionState.Unrestricted)
End If
If Me.CanAccess Then
Return New PermissionSociety(PermissionState.Unrestricted)
Else
Return New PermissionSociety(PermissionState.None)
End If
End Function
End Class

Enfin, je dois allouer cette permission. Dans ma configuration de sécurité, j'ai un groupe de code « Société », enfant du groupe LocalIntranet qui est obtenu pour tous les assemblies ayant une clé publique définie.

Je dois donc ajouter mon assembly de permission dans les assemblies de stratégie.

Mon jeu de permission « société » se trouve en fait dans le fichier :

C:\Windows\Microsoft.NET\Framework\v1.1.4322\CONFIG\security.config

Il faut alors ouvrir ce fichier. Dans la partie Security Class je dois ajouter :

<SecurityClass Name=« SocietyPermission » Description=« clsPermSociety.SocietyPermission, clsPermSociety, Version=1.0.0.1, Culture=neutral, PublicKeyToken=a3d67cf8fe365b39 »/>

Puis dans le jeu de permission “Societe”, ajouter :

<IPermission class=« PermissionSociety »

version=« 1 »

Unrestricted=« True »/>

Si je regarde maintenant les autorisations du groupe de code dans le manager de sécurité, je vois bien apparaitre ma permission personnalisée.