Jump to navigation

Poupou's Corner of the Web

Looking for perfect security? Try a wireless brick.
Otherwise you may find some unperfect stuff here...


Identity sneak preview

My security bible for .NET has always been .NET Framework Security. It's not a new book but quite frankly this is the book I still respect the most on .NET Security. However there is a big omission from the book, as it almost doesn't cover IIdentity and IPrincipal interfaces and related classes. While the basic for Identity/Principal are simple some aspects are more complex (e.g. WindowsIdentity, WindowsImpersonationContext ...) and can even be misleading. Thanksfully there is now an alternative and anyone interested in Identity and Principal should have a good look at the upcoming book by Keith Brown, A .NET Developer's Guide to Windows Security. A draft is available to read online.

WindowsIdentity implements the IIdentity interface (the simple part) but also adds some cool stuff like the ability to impersonate another user. This is really cool but, unless you have Windows 2003, it requires you to P/Invoke to get a user token - see Windows sample on MSDN.

Windows and UNIX have many differences in handling identities. Thanksfully the framework has a good abstraction for identities and principals, which should hide most differences for day to day usages. So the big questions becomes how do we get the cool stuff to work under Linux ?

Actually we can get a very nice mapping with WindowsIdentity even if there are some fundamental differences between the operating systems (so don't expect perfect portability here). Here's an example of what we can do...

using System; using System.IO; using System.Security.Principal; class WinId { static string file = "root.txt"; static void Show (string msg) { Console.WriteLine (msg); WindowsIdentity wi = WindowsIdentity.GetCurrent (); Console.WriteLine ("\tUserName:\t{0}", wi.Name); Console.WriteLine ("\tToken:\t\t{0}", wi.Token); Console.WriteLine (); } static void Show (Exception e) { Console.WriteLine ("Oops something has gone bad..."); Console.WriteLine (e.ToString ()); } static void DisplayFile (string filename) { Console.WriteLine ("Trying to read file {0}", filename); try { using (StreamReader sr = File.OpenText (filename)) { Console.WriteLine (sr.ReadToEnd ()); sr.Close (); } } catch (Exception e) { Show (e); } } static void DeleteFile (string filename) { Console.WriteLine ("Trying to delete file {0}", filename); try { File.Delete (filename); } catch (Exception e) { Show (e); } } [STAThread] static void Main (string[] args) { bool fileCreated = false; Show ("Current User"); if ((WindowsIdentity.GetCurrent ().Token == IntPtr.Zero) && ((int)Environment.OSVersion.Platform == 128)) { Console.WriteLine ("Cool you're root!"); Console.WriteLine ("Let's create a file {0}.", file); using (StreamWriter sw = new StreamWriter (file)) { sw.WriteLine ("FOR ROOT EYES ONLY"); sw.Close (); } fileCreated = true; DisplayFile (file); Console.WriteLine ("Now, who do you want to be today ? "); string username = Console.ReadLine (); WindowsIdentity nonroot = new WindowsIdentity (username); Console.WriteLine ("Trying to impersonate {0} (token: {1})", nonroot.Name, nonroot.Token); WindowsImpersonationContext wic = null; try { wic = nonroot.Impersonate (); Show ("Impersonated User"); DisplayFile (file); DeleteFile (file); } catch (Exception e) { Show (e); } finally { if (wic != null) { wic.Undo (); wic = null; } } Show ("Back to original User"); if (File.Exists (file)) { DisplayFile (file); DeleteFile (file); } else if (fileCreated) { Console.WriteLine ("Hey dude, where's my file ?"); } } else { Console.WriteLine ("Sorry, not much fun to have with non-root users."); } } }

First the sample will show you the current OS identity running the assembly. I say OS identity because this may be different than Thread.CurrentPrincipal.Identity something important to keep in mind when designing .NET applications dealing with identities.

The second part checks if you're running on a Unix system and, if so, if you are the super-user (root, which always has uid 0). The root user is important for this sample because it has the privilege to impersonate any user on the system. As the root we create a file (root.txt) and display it's content.

We now try to impersonate another user on the system. Note that root doesn't need the user password to impersonate the user - so, unlike Windows, no P/Invoke will be required here. As the new user we will try to display the file content, then try to delete the file.

Finally we will revert to the original process identity and, if the root.txt file still exists, display the file before deleting it.

Executing the sample

WARNING: Don't try this (yet) at home - that's what a sneak preview is for. While mcs, or even csc, can compile the sample code, neither Mono nor the MS runtime can execute it correctly right now (well MS will probably never run this under Linux). I'll update this entry when my patches are committed into CVS.

[root@rh9 home]# mono poupou/winid.exe Current User UserName: root Token: 0 Cool you're root! Let's create a file root.txt. Trying to read file root.txt FOR ROOT EYES ONLY Now, who do you want to be today ? nobody Trying to impersonate nobody (token: 99) Impersonated User UserName: nobody Token: 99 Trying to read file root.txt FOR ROOT EYES ONLY Trying to delete file root.txt Oops something has gone bad... System.UnauthorizedAccessException: Access to the path "root.txt" is denied. in <0x001df> System.IO.File:Delete (string) in <0x00024> WinId:DeleteFile (string) Back to original User UserName: root Token: 0 Trying to read file root.txt FOR ROOT EYES ONLY Trying to delete file root.txt

Ok, maybe the sample didn't work as you expected, like:

How could the user read the root file ?
Because by default the files are created as readable for everyone - umask
How could the user delete the root file ?
Just checking the file attributes isn't enough. If the user has write privileges in the directory he can delete the file (e.g. if the user is the directory's owner). Just be sure to run the sample if a directory where the impersonated user can't write to it (i.e. like /home/).
I got exceptions running the code ?
Some are normal. Remember that this will only works on POSIX compliant systems.

Ok, while this sample isn't portable (i.e. it won't work on Windows) it does have a big advantage over Windows - we didn't have to P/Invoke our way into any OS calls. Which means that the WindowsIdentity class work even better on Linux than on Windows :-).

4/5/2004 11:33:18 | Comments

The views expressed on this website/weblog are mine alone and do not necessarily reflect the views of my employer.