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.
Ces zones de sécurité ne font rien par elles-mêmes, elles déterminent 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). |
Exécution |
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és 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'autorisations. 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 sûrement 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 tiers.
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'uploade 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 sûr, 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édant, 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 lesquels on appliquera une accumulation de preuve donnant droit à des accès. Ainsi dans mon cas, je pourrais créer un jeu d'autorisations 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 et 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 permissions « 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 apparaître ma permission personnalisée.