Security Principles and Local Admin Rights in C# .Net
Having started work at a company where my local box did not have me as a local admin , got me to thinking about admin rights. The following code displays the current user stats and attempts to access a protected method that only local admins should access. The code should discriminate against all those that don’t have local admin rights. Within the code I demonstrate declarative security (using the Attribute over the method) Imperative security calls to specific security methods within the code. First off one needs these usings:
using System.Security.Principal;
using System.Security.Permissions;
Here is the code,
1: public static void GetRole()
2: {
3: System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
4:
5: WindowsIdentity curIdentity = WindowsIdentity.GetCurrent();
6: WindowsPrincipal myPrincipal = new WindowsPrincipal(curIdentity);
7:
8: List<string> groups = new List<string>();
9:
10: foreach (IdentityReference irc in curIdentity.Groups)
11: {
12: groups.Add(((NTAccount)irc.Translate(typeof(NTAccount))).Value);
13: }
14:
15: Console.WriteLine(
16: @"Name: {0},
17: System:
{1}
18: Authenticated: {2}
19: BuiltinAdmin: {3}
20: Identity: {4}
21: Groups: {5}"
22: curIdentity.Name,
23: curIdentity.IsSystem,
24: curIdentity.IsAuthenticated,
25: myPrincipal.IsInRole( WindowsBuiltInRole.Administrator ) ? "True" : "False",
26: myPrincipal.Identity,
27: string.Join(string.Format(",{0}\t\t", Environment.NewLine), groups.ToArray()));
28:
29:
30: try
31: {
32: Console.WriteLine(Environment.NewLine);
33: ManagersOnly();
34: }
35: catch (System.Security.SecurityException scx)
36: {
37: Console.WriteLine(scx.Message + " " + scx.FirstPermissionThatFailed.ToString());
38: }
39: Console.WriteLine(Environment.NewLine);
40:
41:
42: }
43:
44:
45: [PrincipalPermissionAttribute(SecurityAction.Demand, Role = @"BUILTIN\Administrators")]
46: private static void ManagersOnly()
47: {
48: Console.WriteLine("Key to the Executive Wash Room");
49: }
Here are some highlights
- Line 3 is the killer. Without it we become rudderless when a check is made. (Causes an exception to be thrown even though we are in the admins group). This line is setting the processing up to work with the OS system token which will be geared toward the current user and windows security on the current thread. This doesn’t actually come into play until we call ManagersOnly method on line 33. Otherwise if this was commented out we would get an exception “Request for principal permission failed” would occur on line 33. (The rub is line 25 would actually work, so be careful when using declarative or imperative security, they can behave differently).
- Line 5 of note the current identity will never have an actual user name (unless its the login name), password or other identifying aspects of the actual user.
- Line 12 is where we convert the numeric ids to a relatable group names that we are all familiar with.
- Line 15 informs us of what we are currently logged in as and will display different aspects of the account.
- Line 25 is in a way using imperative security, where we could take that check and programmatically divine where to allow access. What is nice is that the call is type safe unlike on line 45 where we can make a spelling mistake and it all fails.
- Line 33 calls the Declarative Security attributed method, any call to a role based security should be within a try catch block. Depending on where the code is, some problems exists as objects which are being disposed and the rights have changed on the GC thread which is trying to finalize objects.
- Line 45 is the declarative security as done in the attribute above the method. We could actually drill down even more to specify an exact user name if we wanted. We will stick with groups (roles) and look for the local admin. Note spelling mistakes will cost you so BUILTIN\Administrator is not BUILTIN\Administrators.
The final output looks like this:
Name: ORION\OmegaMan
System: False
Authenticated: True
BuiltinAdmin: True
Identity: System.Security.Principal.WindowsIdentity Groups: ORION\None,
Everyone,
BUILTIN\Administrators,
BUILTIN\Users,
NT AUTHORITY\INTERACTIVE,
NT AUTHORITY\Authenticated Users,
LOCAL
Key to the Executive Wash Room
The string.join line which has this format
“,{0}tt”
WordPress has stripped out the escape it should be
“,{0}\t\t”
But it will have no effect due to the new line, so simply have
“,{0}”
As the pattern.
Thanks Omega, you are super. It is very helpful. I hope you could help me with three other problems/posts.
I got into this thing as a diversion while waiting for other answers to materialize. However, I do want to establish a “subterrain” security tracking system in my major apps that will monitor things in the background for unexplained and unexpected changes in the system. It is a long term, slowly developing track.
Thanks – Alex
Hi mega,
I don’t know if you’ve tried this code in Vista, if not, you may be for a few nasty surprises.
I got “Request for Principal’s Permission Denied” or close. This Vista is a complete run around when it comes to security. I think it makes everything secure by simply welding all the doors solid.
I will have to test it on Vista, my primary machine is still XP. Thanks for the heads up!
Nice article.
(FYI, the line-numbers wreaks havoc when trying to copy-and-paste the code.)
(FYI, the scroll-bar in the code-box wreaks havoc when trying to print to PDF.)
Question…
…if one just wants to check like this…
System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsIdentity myCurrentIdentity = WindowsIdentity.GetCurrent();
if (myCurrentIdentity.User.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid))
{
//This is a local admin.
}
else
{
//This is not a local admin.
}
…then does one really need the line…
System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
…???
is there a way to list all the local users accounts on a given machine using c#?
error CS1022: Type or namespace definition, or end-of-file expected
Truly useful thank you very much.
I opened a Dos/cmd window “Run as administrator”
then I opened a Dos/cmd window under vista as “me”
Name: MACHINE\Rat1960,
System: False
Authenticated: True
BuiltinAdmin: False
Identity: System.Security.Principal.WindowsIdentity
Groups: MACHINE\None,
Everyone,
BUILTIN\Users,
NT AUTHORITY\INTERACTIVE,
NT AUTHORITY\Authenticated Users,
NT AUTHORITY\This Organization,
LOCAL,
NT AUTHORITY\NTLM Authentication
Request for principal permission failed.
I guess that
PrincipalPermission p = new PrincipalPermission(null, “BUILTIN\Administrator”);
p.Demand();
would fail the same.
Could you add some more information about line 45
“BUILTIN\Administrator” “BUILTIN\Administrators”
[2008 Express 3.5]
Oh, html …
Request for principal permission failed.
<IPermission class=”System.Security.Permissions.PrincipalPermission, mscorlib,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″
version=”1″>
<Identity Authenticated=”true”
Role=”BUILTIN\Administrators”/>
</IPermission>
This is a really well written article. Descriptive and succinct with a great code example. Thanks for taking the time to write it.
Only one observation for any people that says that sample does not work. Check the name for the role according with your local culture. For example, if you resides in Mexico, you must to use: @”BUILTIN\Administradores” intead of @”BUILTIN\Administrators”.
Regards