SourceFiles/cMsmq.cs
/*
Author : Serge Nikalaichyk Version : 1.0.0 Date : 2015-09-30 */ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Security.Principal; namespace cMsmq { #region Enumerations [Flags] public enum MessageQueueAccessRights { DeleteMessage = 0x00000001, PeekMessage = 0x00000002, ReceiveMessage = (DeleteMessage | PeekMessage), WriteMessage = 0x00000004, DeleteJournalMessage = 0x00000008, ReceiveJournalMessage = (DeleteJournalMessage | PeekMessage), SetQueueProperties = 0x00000010, GetQueueProperties = 0x00000020, DeleteQueue = 0x00010000, GetQueuePermissions = 0x00020000, GenericWrite = (GetQueueProperties | GetQueuePermissions | WriteMessage), GenericRead = (GetQueueProperties | GetQueuePermissions | ReceiveMessage | ReceiveJournalMessage), ChangeQueuePermissions = 0x00040000, TakeQueueOwnership = 0x00080000, FullControl = (ReceiveMessage | ReceiveJournalMessage | WriteMessage | SetQueueProperties | GetQueueProperties | DeleteQueue | GetQueuePermissions | ChangeQueuePermissions | TakeQueueOwnership) }; [Flags] internal enum SecurityInformation : uint { Owner = 0x00000001, Group = 0x00000002, Dacl = 0x00000004, Sacl = 0x00000008, } internal enum ACL_INFORMATION_CLASS { AclRevisionInformation = 1, AclSizeInformation } #endregion #region Structures [StructLayoutAttribute(LayoutKind.Sequential)] internal class SECURITY_DESCRIPTOR { public byte revision; public byte size; public short control; public IntPtr owner; public IntPtr group; public IntPtr sacl; public IntPtr dacl; } [StructLayout(LayoutKind.Sequential)] internal struct ACL_SIZE_INFORMATION { public uint AceCount; public uint AclBytesInUse; public uint AclBytesFree; } [StructLayout(LayoutKind.Sequential)] internal struct ACE_HEADER { public byte AceType; public byte AceFlags; public short AceSize; } [StructLayout(LayoutKind.Sequential)] internal struct ACCESS_ALLOWED_ACE { public ACE_HEADER Header; public MessageQueueAccessRights Mask; public int SidStart; } #endregion public class QueuePath { private const string QueuePathFormat = @"Direct=OS:.\private$\{0}"; private string queueName; public QueuePath(string queueName) { this.queueName = queueName; } public override string ToString() { return string.Format(QueuePathFormat, queueName); } } public class Security { private const int MQ_OK = 0x0; private const uint MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL = 0xC00E0023; private const uint MQ_ERROR_ILLEGAL_FORMATNAME = 0xC00E001E; private const uint MQ_ERROR_ACCESS_DENIED = 0xC00E0025; private const uint MQ_ERROR_NO_DS = 0xC00E0013; private const uint MQ_ERROR_PRIVILEGE_NOT_HELD = 0xC00E0026; private const uint MQ_ERROR_UNSUPPORTED_FORMATNAME_OPERATION = 0xC00E0020; private const uint MQ_ERROR_QUEUE_NOT_FOUND = 0xC00E0003; private static readonly Dictionary<uint, string> ErrorMessages = new Dictionary<uint, string> { {MQ_ERROR_ILLEGAL_FORMATNAME, "MQ_ERROR_ILLEGAL_FORMATNAME"}, {MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL, "MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL"}, {MQ_ERROR_ACCESS_DENIED, "MQ_ERROR_ACCESS_DENIED"}, {MQ_ERROR_NO_DS, "MQ_ERROR_NO_DS"}, {MQ_ERROR_PRIVILEGE_NOT_HELD, "MQ_ERROR_PRIVILEGE_NOT_HELD"}, {MQ_ERROR_UNSUPPORTED_FORMATNAME_OPERATION, "MQ_ERROR_UNSUPPORTED_FORMATNAME_OPERATION"}, {MQ_ERROR_QUEUE_NOT_FOUND, "MQ_ERROR_QUEUE_NOT_FOUND"} }; public static MessageQueueAccessRights GetAccessMask(QueuePath queuePath, string userName) { var sid = TranslateUserNameToSid(userName); var gchSecurityDescriptor = GetSecurityDescriptorHandle(queuePath, (int)SecurityInformation.Dacl); var ace = GetAce(gchSecurityDescriptor.AddrOfPinnedObject(), sid); var aceMask = ace.Mask; gchSecurityDescriptor.Free(); return aceMask; } public static string GetOwner(QueuePath queuePath) { IntPtr pOwner; bool ownerDefaulted; var gchSecurityDescriptor = GetSecurityDescriptorHandle(queuePath, (int)SecurityInformation.Owner); Security.GetSecurityDescriptorOwner(gchSecurityDescriptor.AddrOfPinnedObject(), out pOwner, out ownerDefaulted); var ownerSid = new SecurityIdentifier(pOwner); string ownerUserName = TranslateSidToUserName(ownerSid); gchSecurityDescriptor.Free(); return ownerUserName; } private static string GetErrorMessage(uint errorCode) { return ErrorMessages[errorCode]; } private static string TranslateUserNameToSid(string userName) { var account = new NTAccount(userName); var sid = (SecurityIdentifier)account.Translate(typeof(SecurityIdentifier)); return sid.ToString(); } private static string TranslateSidToUserName(SecurityIdentifier sid) { var account = (NTAccount)sid.Translate(typeof(NTAccount)); return account.ToString(); } private static ACCESS_ALLOWED_ACE GetAce(IntPtr pSecurityDescriptor, string sid) { bool daclPresent; bool daclDefaulted; IntPtr pAcl = IntPtr.Zero; Security.GetSecurityDescriptorDacl(pSecurityDescriptor, out daclPresent, ref pAcl, out daclDefaulted); if (daclPresent) { ACL_SIZE_INFORMATION AclSize = new ACL_SIZE_INFORMATION(); Security.GetAclInformation(pAcl, ref AclSize, (uint)Marshal.SizeOf(typeof(ACL_SIZE_INFORMATION)), ACL_INFORMATION_CLASS.AclSizeInformation); for (int i = 0; i < AclSize.AceCount; i++) { IntPtr pAce; Security.GetAce(pAcl, i, out pAce); ACCESS_ALLOWED_ACE ace = (ACCESS_ALLOWED_ACE)Marshal.PtrToStructure(pAce, typeof(ACCESS_ALLOWED_ACE)); IntPtr iter = (IntPtr)((long)pAce + (long)Marshal.OffsetOf(typeof(ACCESS_ALLOWED_ACE), "SidStart")); byte[] sidBytes = null; int sidSize = (int)Security.GetLengthSid(iter); sidBytes = new byte[sidSize]; Marshal.Copy(iter, sidBytes, 0, sidSize); IntPtr pSid; Security.ConvertSidToStringSid(sidBytes, out pSid); string strSid = Marshal.PtrToStringAuto(pSid); if (strSid == sid) { return ace; } } throw new Exception(string.Format("No ACE for SID '{0}' found in Security Descriptor.", sid)); } else { throw new Exception("No DACL found in Security Descriptor."); } } private static GCHandle GetSecurityDescriptorHandle(QueuePath queuePath, int securityInformation) { byte[] securityDescriptorBytes; int length; int lengthNeeded; uint result; string formatName = queuePath.ToString(); result = Security.MQGetQueueSecurity(formatName, securityInformation, IntPtr.Zero, 0, out lengthNeeded); if (result != Security.MQ_ERROR_SECURITY_DESCRIPTOR_TOO_SMALL) { string message = "There was an error calling MQGetQueueSecurity." + Environment.NewLine + "Error Number: " + result.ToString() + Environment.NewLine + "Error Message: " + Security.GetErrorMessage(result); throw new Exception(message); } length = lengthNeeded; securityDescriptorBytes = new byte[length]; IntPtr pSecurityDescriptor = new IntPtr(); GCHandle gchSecurityDescriptor = GCHandle.Alloc(securityDescriptorBytes, GCHandleType.Pinned); pSecurityDescriptor = gchSecurityDescriptor.AddrOfPinnedObject(); result = Security.MQGetQueueSecurity(formatName, securityInformation, pSecurityDescriptor, length, out lengthNeeded); if (result != Security.MQ_OK) { gchSecurityDescriptor.Free(); string message = "There was an error calling MQGetQueueSecurity to read the Security Descriptor. " + Environment.NewLine + "Error Number: " + result.ToString() + Environment.NewLine + "Error Message: " + Security.GetErrorMessage(result); throw new Exception(message); } var securityDescriptor = new SECURITY_DESCRIPTOR(); Marshal.PtrToStructure(pSecurityDescriptor, securityDescriptor); return gchSecurityDescriptor; } #region P/Invoke Definitions [DllImport("mqrt.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern uint MQGetQueueSecurity( [MarshalAs(UnmanagedType.LPWStr)] string lpwcsFormatName, int SecurityInformation, IntPtr pSecurityDescriptor, int nLength, out int lpnLengthNeeded ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetSecurityDescriptorDacl( IntPtr pSecurityDescriptor, [MarshalAs(UnmanagedType.Bool)] out bool lpbDaclPresent, ref IntPtr pDacl, [MarshalAs(UnmanagedType.Bool)] out bool lpbDaclDefaulted ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetSecurityDescriptorOwner( IntPtr pSecurityDescriptor, out IntPtr pOwner, [MarshalAs(UnmanagedType.Bool)] out bool lpbOwnerDefaulted ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetAclInformation( IntPtr pAcl, ref ACL_SIZE_INFORMATION pAclInformation, uint nAclInformationLength, ACL_INFORMATION_CLASS dwAclInformationClass ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int GetAce( IntPtr pAcl, int dwAceIndex, out IntPtr pAce ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int GetLengthSid( IntPtr pSid ); [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool ConvertSidToStringSid( [MarshalAs(UnmanagedType.LPArray)] byte[] pSid, out IntPtr pStringSid ); #endregion } } |