# Set-ShareInfo.ps1
# Modifie les propriétés de sécurité d'un Share
# Adaptation des scripts suivants :
# http://mow001.blogspot.com/2005/11/replace-security-on-existing-share.html
# http://mow001.blogspot.com/2006/05/powershell-import-shares-and-security.html
# http://www.dotnetframework.de/lserver/CodeSampleDetails.aspx?c=4983
# ATTENTION : Seuls les membres du groupe local Administrateurs ou Opérateurs de compte ou
# ceux appartenant aux groupes Opérateurs de communication, d'impression ou de
# serveur peuvent exécuter la methode Win32_Share.SetShareInfo.
# Documents MSDN : Scripting Security Descriptors :
# http://download.microsoft.com/download/a/1/a/a1afc045-4b65-4a0a-817c-
9110b99f542d/Scripting_Security_Descriptors.doc
# How Security Descriptors and Access Control Lists Work:
# http://technet2.microsoft.com/windowsserver/en/library/9c7bc921-2517-4e7a-ba39-
d37c8b8202e31033.mspx?mfr=true
# Manage Access to Windows Objects with ACLs and the .NET Framework :
# http://msdn2.microsoft.com/fr-fr/magazine/cc163885(en-us).aspx
# http://support.microsoft.com/kb/243330
# http://support.microsoft.com/kb/278259/en-us
# Constante ErrorCodeWMI :
# http://msdn2.microsoft.com/en-us/library/aa394559.aspx
# Hey, Scripting Guy! We All Scream for Security Descriptors :
# http://technet.microsoft.com/fr-fr/magazine/cc160995(en-us).aspx
# Pour aller plus loin :
# The Windows Access Control Model :
# http://www.codeproject.com/KB/winsdk/accessctrl4.aspx
#
#
# Améliorations : Gestion des exceptions et codes d'erreur spécifiques à WMI ...
Set-Variable ErrWMi_Instance -value "Erreur lors de la création d'une instance de la classe " -option constant -scope Script
#A prioris sous .NET aucune énumération ne reprend ces valeurs.
Set-Variable SE_OWNER_DEFAULTED -value ([uint32] 0x1) -option constant -scope Script
Set-Variable SE_GROUP_DEFAULTED -value ([uint32] 0x2) -option constant -scope Script
Set-Variable SE_DACL_PRESENT -value ([uint32] 0x4) -option constant -scope Script
Set-Variable SE_DACL_DEFAULTED -value ([uint32] 0x8) -option constant -scope Script
Set-Variable SE_SACL_PRESENT -value ([uint32] 0x10) -option constant -scope Script
Set-Variable SE_SACL_DEFAULTED -value ([uint32] 0x20) -option constant -scope Script
Set-Variable SE_DACL_AUTO_INHERIT_REQ -value ([uint32] 0x100) -option constant -scope Script
Set-Variable SE_SACL_AUTO_INHERIT_REQ -value ([uint32] 0x200) -option constant -scope Script
Set-Variable SE_DACL_AUTO_INHERITED -value ([uint32] 0x400) -option constant -scope Script
Set-Variable SE_SACL_AUTO_INHERITED -value ([uint32] 0x800) -option constant -scope Script
Set-Variable SE_DACL_PROTECTED -value ([uint32] 0x1000) -option constant -scope Script
Set-Variable SE_SACL_PROTECTED -value ([uint32] 0x2000) -option constant -scope Script
Set-Variable SE_SELF_RELATIVE -value ([uint32] 0x8000) -option constant -scope Script
function Test-WinmgmtIsRunning
{
if ((Get-Service|where {$_.Name -eq "winmgmt"}).Status -eq "Stopped")
{Throw "Le service d'infrastructure de gestion Windows (winmgmt) est arrêté.`n`rImpossible de continuer."}
}
function Test-WMIVariable($WmiVariable, $ClassName)
{ #Vérifie si une instance WMI n'est pas nulle et si elle est bien de la classe attendue
#sinon déclenche une exception
#Récupére le nom de la variable à tester via la ligne de commande exécutée
#Pseudo la pile d'appel de la fonction en mode texte...
$VariableName=($MyInvocation.Line.Split())[1]
if ($WmiVariable -eq $Null)
{Throw "La variable $VariableName n'est pas renseignée."}
if ($WmiVariable.__CLASS -ne $ClassName)
{Throw "La variable $VariableName n'est pas une instance de la classe $Classname"}
# on ne renvoi rien si tout est correct
}
Function Get-SIDLocalUserAccount([String] $AccoutName)
{
#Renvoi, dans un format binaire, le SID d'un utilisateur local
Trap [System.Management.Automation.MethodInvocationException]
{ Throw "Le compte $AccoutName est inconnu."}
#Récupère un objet System.DirectoryServices.DirectoryEntry
$Account = New-Object System.Security.Principal.NTAccount($AccoutName)
#Pour AD voir http://www.microsoft.com/technet/scriptcenter/resources/pstips/feb08/pstip0201.mspx
# $Account = New-Object System.Security.Principal.NTAccount($DomainName,$AccoutName)
# Dans ce cas la suite du code reste identique
# Récupére le SID au format SDDL (S-1-1-0)
$SID = $Account.Translate([System.Security.Principal.SecurityIdentifier])
# Formate le SID au format SDDL en un 'SID binaire' (1,1,0,0,0,0,0,1,0,0,0,0)
[byte[]] $SIDArray = ,0 * $SID.BinaryLength
$SID.GetBinaryForm($SIDArray,0)
return $SIDArray
}
function New-Trustee([String] $DomainName, [String] $AccoutName)
{ # Crée une instance d'un tiers de confiance pour une entrée ACE
Trap [Exception]
{ Throw $ErrWMi_Instance+ "Win32_Trustee"
}
# CreateInstance permet de manipuler directement une instance d'une classes WMI
# et pas un objet l'encapsulant
$t = ([WMIClass] "Win32_Trustee").CreateInstance()
$t.Domain=$DomainName
$t.Name=$AccoutName
$t.SID=Get-SIDLocalUserAccount $AccoutName
return $t
}
function New-ACE([String]$DomainName,
[String]$AccoutName,
[int]$Mode,
[int]$TypeAce,
[int]$Flags)
{ # Crée une instance d'un 'Access Control Entry'
# $Mode : Mode d'accés [Full, Change, Read]
# $TypeAce : Définit le type de l'ACE [Access_Allowed, Access_Denied, System_Audit]
# Notes : System_Audit n'est pas utilisé avec un share. D'autres valeurs existent
pour ADSI.
# $Flags : Spécifie le type de propagation de l'héritage, de l'objet parent vers les objets
enfants.
Trap [Exception]
{ Throw $ErrWMi_Instance+ "Win32_Ace"
}
$a = ([WMIClass] "Win32_Ace").CreateInstance()
$a.AccessMask = $Mode
$a.AceFlags = $Flags
$a.AceType = $TypeAce
$a.Trustee = New-Trustee $DomainName $AccoutName
return $a
}
function New-SecurityDescriptor([System.Management.ManagementObject[]] $DACLs)
{ #Crée un descripteur de sécurité
# $DACLs : Liste des ayants droits
Trap [Exception]
{ Throw $ErrWMi_Instance+ "Win32_SecurityDescriptor"
}
$SD = ([WMIClass] "Win32_SecurityDescriptor").CreateInstance()
# Champs inutilisés pour un descripteur de sécurité d'un Share
# $SD.Owner
# $SD.SACL (il s'agit d'ACLs d'audit)
# $SD.Group (This information is used only by the POSIX subsystem)
$SD.DACL = $DACLs
# A propos de ControlFlags, voir les remarques importantes :
# http://msdn2.microsoft.com/en-us/library/aa394402.aspx
#
# SE_DACL_PRESENT : Indicates an SD that has a DACL. If this flag is not set, or if this flag
# is set and the DACL is NULL, the SD allows full access to everyone.
$SD.ControlFlags= ($SE_DACL_PRESENT -bor $SE_SELF_RELATIVE) #On positionne les bits du masque
return $SD
}
function Invoke-Win32_Share.SetShareInfo($Share,
[uint32] $MaximumAllowed,
[String] $Description,
$Access)
{ # Appel la méthode SetShareInfo d'une instance de la classe WMI Win32_Share
# Ici on attend un objet WMI "encapsulé" ce qui permet l'appel direct de méthode d'un objet WMI
# $Share : l'objet partage concerné
# $Description : Description du partage
# $Access : Liste des ACLs fournie par une instance de
SecurityDescriptor.PSobject.BaseObject
Test-WMIVariable $Share "Win32_Share"
Test-WMIVariable $Access "Win32_SecurityDescriptor"
$CodeRetour=($Share.SetShareInfo($MaximumAllowed,$Description,$Access)).ReturnValue
Switch ($CodeRetour) # Codes identiques à ceux renvoyés par la méthode Win32_Share.Create
{
0 {$MsgErreur="Opération terminée correctement.";Break}
2 {$MsgErreur="L'utilisateur n'a pas accès aux informations requises.";Break}
8 {$MsgErreur="Échec inconnu.";Break}
9 {$MsgErreur="Le nom du caractère ou du système de fichiers n'est pas valide.";Break}
10 {$MsgErreur="La valeur spécifiée pour le paramètre de niveau est non valide.";Break}
21 {$MsgErreur="Le paramètre spécifié est non valide.";Break}
22 {$MsgErreur="Le nom du partage est déjà en cours d'utilisation sur ce serveur.";Break}
23 {$MsgErreur="L'opération est non valide pour une ressource redirigée. Le nom de périphérique spécifié est
assigné à une ressource partagée.";Break}
24 {$MsgErreur="Le périphérique ou le répertoire n'existe pas.";Break}
25 {$MsgErreur="Le nom de partage n'existe pas.";Break}
Default {$MsgErreur="Référez-vous à la documentation concernant les codes d'erreur Win32. Code erreur : $CodeRetour"}
}
if ($CodeRetour -ne 0) {Throw $MsgErreur}
}
#----------------------------- Variables utilisées pour effectuer un test --------------
#Positionne un share existant avec le droit read pour le groupe "Tout le monde"
$NomDuShare="Share de test"
$DescriptionDuShare = "TestMAJ"
$NomDomaine=$Null
$NomCompte="Tout le monde" # Windows Français
#$NomCompte="Everyone" # Windows US
$Mode = "Read"
$NomTypes="AccessAllowed"
$NomFlags="ObjectInherit, ContainerInherit"
$MaximumAllowed=$Null
# Extraction possible via un fichier .CSV (cf. Excel)
#Nom de Domaine, Nom de compte, mode d'acccés, Type d'ace, Flags d'ace
#,"Tout le monde","Read","AccessAllowed","ObjectInherit, ContainerInherit"
#,"Administrateurs" "Full","AccessAllowed","ObjectInherit, ContainerInherit"
#,"Invités","Read,"AccessDenied","ObjectInherit, ContainerInherit"
#---------------------------------- Main ----------------------------------------------
Test-WinmgmtIsRunning
$AccessMask =0
# Création du masque d'accès (AccessMask)
# Synchronize :
# The right to specify a file handle in one of the wait functions.
# However, for asynchronous file I/O operations, you should wait on the event handle in
# an OVERLAPPED structure rather than using the file handle for synchronization.
switch ($Mode) {
"Full" {$AccessMask = [int][System.Security.AccessControl.FileSystemRights]"FullControl, Synchronize"}
"Read" {$AccessMask = [int][System.Security.AccessControl.FileSystemRights]"ReadAndExecute, Synchronize"}
"Change" {$AccessMask = [int][System.Security.AccessControl.FileSystemRights]"Modify, Synchronize"}
}
#Décompose la construction des paramètres utilisés par la méthode New-ACE
[int]$TypeAce =[System.Security.AccessControl.AceType]$NomTypes
[int]$FlagAce =[System.Security.AccessControl.AceFlags]$NomFlags
#Construit un tableau de DiscretionaryAcl et y ajoute le ou les ACEs désirés
# Il est recommandé d'ordonner les flags des ACEs, voir le paragraphe :
# "Canonical Order of ACEs in "How Security Descriptors and Access Control Lists Work"
$ACE=New-ACE $NomDomaine $NomCompte $AccessMask $TypeAce $FlagAce
#***************************************************************************************************
****
# ******* ACEs codés en dur à des fin de tests ***********
#Full control pour le groupe des administrateurs
$ACE2=New-ACE $NomDomaine "Administrateurs" ([int][System.Security.AccessControl.FileSystemRights]"FullControl, Synchronize") $TypeAce $FlagAce
#Aucun accés pour le groupe des invités
$ACE3=New-ACE $NomDomaine "Invités" ([int][System.Security.AccessControl.FileSystemRights]"Read, Synchronize") ([int] [System.Security.AccessControl.AceType]"AccessDenied") $FlagAce
# ********************************************************
#***************************************************************************************************
****
[System.Management.ManagementObject[]] $DACL=@($ACE,$ACE2,$ACE3)
#Creé un descripteur de sécurité contenant la collection des ayants droits précédemment créé
$SecurityDescriptor = New-SecurityDescriptor $DACL
# Obtient l'instance du partage à modifier
$Share = Get-WmiObject Win32_Share -filter "name='$NomDuShare'"
if ($Share -eq $Null)
{Throw "Le share nommé $NomDuShare n'existe pas."}
# Appel la méthode de modification/affectation des propriétés du share
# Ici on manipule un objet WMI "encapsulé" ce qui permet l'appel direct d'une méthode d'un objet
WMI
Invoke-Win32_Share.SetShareInfo $Share $MaximumAllowed $DescriptionDuShare $SecurityDescriptor.PSobject.BaseObject
|