Click here to Skip to main content
15,884,537 members
Articles / Programming Languages / Visual Basic

The Windows Access Control Model Part 3

Rate me:
Please Sign up or sign in to vote.
4.80/5 (28 votes)
1 Jul 200525 min read 232.4K   5.2K   126  
In the third part of this series, we will take a tour of the new access control classes coming in .NET v2.0.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.Win32;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace NetAccessControl
{
	public partial class ReadSD : Form
	{
		public ReadSD()
		{
			InitializeComponent();
		}

		protected RegistrySecurity ppSD;

		private void ReadSD_Load(object sender, EventArgs e)
		{
			this.ppSD = null;
			this.computerEdit.Text = System.Environment.MachineName;
			this.regHiveSelect.DataSource = Enum.GetValues(typeof(Microsoft.Win32.RegistryHive));
			this.sddlMsgButton.Tag = false;
		}

		private void OKButton_Click(object sender, EventArgs e)
		{
			this.sddlMsgButton.Tag = false;
			try
			{
				Microsoft.Win32.RegistryHive Hive = (Microsoft.Win32.RegistryHive)(this.regHiveSelect.SelectedValue);
				/* Normalize the computer name. */
				string ComputerName = this.computerEdit.Text.Trim(" \\".ToCharArray());
				Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Hive, ComputerName);

				regKey = regKey.OpenSubKey(this.regKeyEdit.Text, true);
				this.ppSD = regKey.GetAccessControl();
				regKey.Close();
				this.OutputGroupBox.Enabled = true;

				/* Owner */
				NTAccount OwnerUser = (NTAccount)(this.ppSD.GetOwner(typeof(NTAccount)));
				this.ownerEdit.Text = OwnerUser.ToString();
				this.ownerLbl.Enabled = true;
				this.ownerEdit.Enabled = true;

				/* Group */
				OwnerUser = (NTAccount)(this.ppSD.GetGroup(typeof(NTAccount)));
				this.groupEdit.Text = OwnerUser.ToString();
				this.groupLbl.Enabled = true;
				this.groupEdit.Enabled = true;

				/* Let user see SACL/DACL */
				this.sACLButton.Enabled = true;
				this.dACLButton.Enabled = true;
				this.sddlMsgButton.Enabled = true;
			}
			catch(System.Exception ex)
			{
				this.statusLabel1.Text = "Error occurred: " + ex.Message;
			}
		}

		private void sddlMsgButton_Click(object sender, EventArgs e)
		{
			try
			{
				/* Commit any changes from the Owner and group boxes */
				UpdateSDIfNecessary();
				MessageBox.Show(this, this.ppSD.GetSecurityDescriptorSddlForm(AccessControlSections.All),
					"Registry Key's String Security Descriptor",
					MessageBoxButtons.OK, MessageBoxIcon.None, MessageBoxDefaultButton.Button1);
				this.sddlMsgButton.Tag = true;
			}
			catch (System.Exception ex)
			{
				this.statusLabel1.Text = "Error occurred: " + ex.Message;
			}
		}

		private void closeButton_Click(object sender, EventArgs e)
		{
			this.Close();
		}

		private void ownerEdit_TextChanged(object sender, EventArgs e)
		{
			this.applyButton.Enabled = true;
			this.sddlMsgButton.Tag = false;
		}

		private void dACLButton_Click(object sender, EventArgs e)
		{
			DACLForm childFrm = new DACLForm(this.ppSD, this.regHiveSelect.Text + "\\" + this.regKeyEdit.Text);
			childFrm.ShowDialog(this);
			this.sddlMsgButton.Tag = false;
			this.statusLabel1.Text = "Your changes have not yet been applied. Press the Apply button " +
				"to commit the changes.";
		}

		private void sACLButton_Click(object sender, EventArgs e)
		{
			SACLForm childFrm = new SACLForm(this.ppSD, this.regHiveSelect.Text + "\\" + this.regKeyEdit.Text);
			childFrm.ShowDialog(this);
			this.sddlMsgButton.Tag = false;
			this.statusLabel1.Text = "Your changes have not yet been applied. Press the Apply button " +
				"to commit the changes.";
		}

		private void applyButton_Click(object sender, EventArgs e)
		{
			/* The final security descriptor is now available. Commit to the Registry Key */
			try
			{
				UpdateSDIfNecessary();

				/* Has the user seen the SDDL? */
				if ((bool)(this.sddlMsgButton.Tag) == (bool)(false))
				{
					MessageBox.Show(this, "Please review the SDDL of your security descriptor before committing it.",
						"SDDL not yet viewed", MessageBoxButtons.OK, MessageBoxIcon.Error,
						MessageBoxDefaultButton.Button1);
					return;
				}

				/* Reopen the Registry key. */
				Microsoft.Win32.RegistryHive Hive =
					(Microsoft.Win32.RegistryHive)(this.regHiveSelect.SelectedValue);
				/* Normalize the computer name. */
				string ComputerName = this.computerEdit.Text.Trim(" \\".ToCharArray());
				Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Hive, ComputerName);
				using(regKey = regKey.OpenSubKey(this.regKeyEdit.Text, true))
				{
					/* Has this feature been enabled in app.config? */
					if (Convert.ToBoolean(NetAccessControl.Properties.Settings.Default["WritableRegKey"]) == true)
					{
						/* Will the user get an access denied? */
						if (IsUserAllowedToChangeAcl(regKey))
						{
							/* Hand the user one last warning. Yes, the FUD is deliberate. */
							if (MessageBox.Show(this, "This feature has not been fully tested, so it may not correctly " +
								"update your registry key. In the worst case scenario you could lose access of " +
								"the registry key, leading to system instability. Make sure you have reviewed the " +
								"SDDL of the security descriptor and verified it is correct before proceeding. If " +
								"any errors occur reboot in safe mode and use regedit to reset the security " +
								"descriptor.\n\nIf you are not sure you want to continue, press cancel to halt " +
								"the update.",
								"Are you sure you want to apply the security descriptor?", MessageBoxButtons.OKCancel,
								MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2) == DialogResult.OK)
							{/* Point of no return: Let's hack the computer! */
								regKey.SetAccessControl(this.ppSD);
								/* update the status afterward. */
								this.OKButton.PerformClick();
							}
						}
						else
						{
							this.statusLabel1.Text = "The current ACL doesn't allow you to edit the security " +
								"descriptor. (Access is denied)";
						}
					}
					else
					{
						this.statusLabel1.Text = "Sorry. This feature has been disabled in the app.config.";
					}
					regKey.Close();
				}

			}
			catch (System.Exception ex)
			{
				this.statusLabel1.Visible = true;
				this.statusLabel1.Text = "Error Occurred: " + ex.Message;
			}
		}

		private bool IsUserAllowedToChangeAcl(RegistryKey regKey)
		{
			/* returns false if the user is likely to get Access denied. */
			IdentityReferenceCollection GroupList = WindowsIdentity.GetCurrent().Groups;

			/** This is a pseudo access check function. We need to test for the ChangePermissions access 
			* right. The good news is that we need not worry about multiple ACE grants, or SACL stuff.
			* But we do need to take into account group rights.
			**/
			foreach (RegistryAccessRule Entry in
				regKey.GetAccessControl().GetAccessRules(true, true, typeof(SecurityIdentifier)))
			{
				WindowsPrincipal userSID = new WindowsPrincipal(WindowsIdentity.GetCurrent());

				/* Remap generic rights. */
				uint accessMask = (uint)(Entry.RegistryRights);
				AccessToken.AccessCheckWrap.GENERIC_MAPPING genericReg = AccessToken.AccessCheckWrap.RegGeneric();
				AccessToken.AccessCheckWrap.MapGenericMask(ref accessMask, ref genericReg);

				if (userSID.IsInRole((SecurityIdentifier)(Entry.IdentityReference)))
				{
					/* This right applies to us. */
					if ((accessMask & (uint)(RegistryRights.ChangePermissions)) != 0)
					{/* This right holds our desired Registry permission. If its allow, true, otherwise false */
						return (Entry.AccessControlType == AccessControlType.Allow);
					}
				}
			}
			/* We didn't find the permission. Return false. */
			return false;
		}

		private void UpdateSDIfNecessary()
		{
			/* Check the current textboxes, and if their content changed, reflect the changes in ppSD. */
			if(!this.ppSD.GetOwner(typeof(NTAccount)).ToString().Equals(this.ownerEdit.Text))
				this.ppSD.SetOwner(new NTAccount(this.ownerEdit.Text));
			if (!this.ppSD.GetGroup(typeof(NTAccount)).ToString().Equals(this.groupEdit.Text))
				this.ppSD.SetGroup(new NTAccount(this.groupEdit.Text));
		}

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
Mr. Shah is a reclusive C++/C# developer lurking somewhere in the depths of the city of London. He learnt physics at Kings' College London and obtained a Master in Science there. Having earned an MCAD, he teeters on the brink of transitioning from C++ to C#, unsure of which language to jump to. Fortunately, he also knows how to use .NET interop to merge code between the two languages (which means he won't have to make the choice anytime soon).

His interests (apart from programming) are walking, football (the real one!), philosophy, history, retro-gaming, strategy gaming, and any good game in general.

He maintains a website / blog / FAQ / junk at shexec32.serveftp.net, where he places the best answers he's written to the questions you've asked. If you can find him, maybe you can hire Mr. Shah to help you with anything C++[/CLI]/C#/.NET related Smile | :) .

Comments and Discussions