2005-08-02 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.Security.Principal / WindowsIdentity.cs
index c5d9cc7bf469d120ee748d4783c6c6c78c472173..4d87ce642e272f4cc19e3098917c08c683cbb86f 100644 (file)
@@ -3,20 +3,45 @@
 //
 // Authors:
 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
-//     Sebastien Pouliot (spouliot@motus.com)
+//     Sebastien Pouliot (sebastien@ximian.com)
 //
 // (C) 2002 Ximian, Inc (http://www.ximian.com)
 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Runtime.Serialization;
+using System.Security.Permissions;
 
 namespace System.Security.Principal {
 
        [Serializable]
 #if NET_1_0
        public class WindowsIdentity : IIdentity, IDeserializationCallback {
+#elif NET_2_0
+       [ComVisible (true)]
+       public class WindowsIdentity : IIdentity, IDeserializationCallback, ISerializable, IDisposable {
 #else
        public class WindowsIdentity : IIdentity, IDeserializationCallback, ISerializable {
 #endif
@@ -24,79 +49,139 @@ namespace System.Security.Principal {
                private string _type;
                private WindowsAccountType _account;
                private bool _authenticated;
+               private string _name;
+               private SerializationInfo _info;
+
+               static private IntPtr invalidWindows = IntPtr.Zero;
+               // that seems to be the value used for (at least) AIX and MacOSX
+               static private IntPtr invalidPosix = (IntPtr) unchecked (-2);
 
-               public WindowsIdentity (IntPtr userToken)
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
+               public WindowsIdentity (IntPtr userToken) 
+                       : this (userToken, null, WindowsAccountType.Normal, false)
                {
-                       _token = userToken;
-                       _type = "NTLM";
-                       _account = WindowsAccountType.Normal;
-                       _authenticated = false;
                }
 
-               public WindowsIdentity (IntPtr userToken, string type)
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
+               public WindowsIdentity (IntPtr userToken, string type) 
+                       : this (userToken, type, WindowsAccountType.Normal, false)
                {
-                       _token = userToken;
-                       _type = type;
-                       _account = WindowsAccountType.Normal;
-                       _authenticated = false;
                }
 
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
                public WindowsIdentity (IntPtr userToken, string type, WindowsAccountType acctType)
+                       : this (userToken, type, acctType, false)
                {
-                       _token = userToken;
-                       _type = type;
-                       _account = acctType;
-                       _authenticated = false;
                }
 
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
                public WindowsIdentity (IntPtr userToken, string type, WindowsAccountType acctType, bool isAuthenticated)
                {
-                       _token = userToken;
                        _type = type;
                        _account = acctType;
                        _authenticated = isAuthenticated;
+                       _name = null;
+                       // last - as it can override some fields
+                       SetToken (userToken);
                }
 #if !NET_1_0
-               [MonoTODO]
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
                public WindowsIdentity (string sUserPrincipalName) 
+                       : this (sUserPrincipalName, null)
                {
-                       throw new ArgumentException ("only for Windows Server 2003 +");
                }
 
-               [MonoTODO]
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
                public WindowsIdentity (string sUserPrincipalName, string type)
                {
-                       throw new ArgumentException ("only for Windows Server 2003 +");
+                       if (sUserPrincipalName == null)
+                               throw new NullReferenceException ("sUserPrincipalName");
+
+                       // TODO: Windows 2003 compatibility should be done in runtime
+                       IntPtr token = GetUserToken (sUserPrincipalName);
+                       if ((!IsPosix) && (token == IntPtr.Zero)) {
+                               throw new ArgumentException ("only for Windows Server 2003 +");
+                       }
+
+                       _authenticated = true;
+                       _account = WindowsAccountType.Normal;
+                       _type = type;
+                       // last - as it can override some fields
+                       SetToken (token);
                }
 
-               [MonoTODO]
-               public WindowsIdentity (SerializationInfo info, StreamingContext context) {}
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
+               public WindowsIdentity (SerializationInfo info, StreamingContext context)
+               {
+                       _info = info;
+               }
 #endif
-               [MonoTODO]
+
+#if NET_2_0
+               [ComVisible (false)]
+               public void Dispose ()
+               {
+                       _token = IntPtr.Zero;
+               }
+               
+               [ComVisible (false)]
+               protected virtual void Dispose (bool disposing)
+               {
+                       _token = IntPtr.Zero;
+               }
+#else
                ~WindowsIdentity ()
                {
-                       _token = (IntPtr) 0;
+                       // clear our copy but don't close it
+                       // http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
+                       _token = IntPtr.Zero;
                }
+#endif
+               // static methods
 
-               // methods
-
-               [MonoTODO]
                public static WindowsIdentity GetAnonymous ()
                {
-                       throw new NotImplementedException ();
+                       WindowsIdentity id = null;
+                       if (IsPosix) {
+                               id = new WindowsIdentity ("nobody");
+                               // special case
+                               id._account = WindowsAccountType.Anonymous;
+                               id._authenticated = false;
+                               id._type = String.Empty;
+                       }
+                       else {
+                               id = new WindowsIdentity (IntPtr.Zero, String.Empty, WindowsAccountType.Anonymous, false);
+                               // special case (don't try to resolve the name)
+                               id._name = String.Empty;
+                       }
+                       return id;
                }
 
-               [MonoTODO]
                public static WindowsIdentity GetCurrent ()
+               {
+                       return new WindowsIdentity (GetCurrentToken (), null, WindowsAccountType.Normal, true);
+               }
+#if NET_2_0
+               [MonoTODO ("need icall changes")]
+               public static WindowsIdentity GetCurrent (bool ifImpersonating)
                {
                        throw new NotImplementedException ();
                }
 
+               [MonoTODO ("need icall changes")]
+               public static WindowsIdentity GetCurrent (TokenAccessLevels desiredAccess)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+               // methods
+
                public virtual WindowsImpersonationContext Impersonate ()
                {
                        return new WindowsImpersonationContext (_token);
                }
 
+               [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
                public static WindowsImpersonationContext Impersonate (IntPtr userToken)
                {
                        return new WindowsImpersonationContext (userToken);
@@ -129,11 +214,14 @@ namespace System.Security.Principal {
                        get { return (_account == WindowsAccountType.System); }
                }
 
-               [MonoTODO]
                public virtual string Name
                {
                        get {
-                               throw new NotImplementedException ();
+                               if (_name == null) {
+                                       // revolve name (runtime)
+                                       _name = GetTokenName (_token);
+                               }
+                               return _name; 
                        }
                }
 
@@ -141,19 +229,107 @@ namespace System.Security.Principal {
                {
                        get { return _token; }
                }
+#if NET_2_0
+               [MonoTODO ("not implemented")]
+               public IdentityReferenceCollection Groups {
+                       get { throw new NotImplementedException (); }
+               }
+
+               [MonoTODO ("not implemented")]
+               [ComVisible (false)]
+               public TokenImpersonationLevel ImpersonationLevel {
+                       get { throw new NotImplementedException (); }
+               }
 
-               [MonoTODO]
+               [MonoTODO ("not implemented")]
+               [ComVisible (false)]
+               public SecurityIdentifier Owner {
+                       get { throw new NotImplementedException (); }
+               }
+
+               [MonoTODO ("not implemented")]
+               [ComVisible (false)]
+               public SecurityIdentifier User {
+                       get { throw new NotImplementedException (); }
+               }
+#endif
                void IDeserializationCallback.OnDeserialization (object sender)
                {
-                       throw new NotImplementedException ();
+                       _token = (IntPtr) _info.GetValue ("m_userToken", typeof (IntPtr));
+                       // can't trust this alone - we must validate the token
+                       _name = _info.GetString ("m_name");
+                       if (_name != null) {
+                               // validate token by comparing names
+                               string name = GetTokenName (_token);
+                               if (name != _name)
+                                       throw new SerializationException ("Token-Name mismatch.");
+                       }
+                       else {
+                               // validate token by getting name
+                               _name = GetTokenName (_token);
+                               if ((_name == String.Empty) || (_name == null))
+                                       throw new SerializationException ("Token doesn't match a user.");
+                       }
+                       _type = _info.GetString ("m_type");
+                       _account = (WindowsAccountType) _info.GetValue ("m_acctType", typeof (WindowsAccountType));
+                       _authenticated = _info.GetBoolean ("m_isAuthenticated");
                }
 #if !NET_1_0
-               [MonoTODO]
                void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
                {
-                       throw new NotImplementedException ();
+                       info.AddValue ("m_userToken", _token);
+                       // can be null when not resolved
+                       info.AddValue ("m_name", _name);
+                       info.AddValue ("m_type", _type);
+                       info.AddValue ("m_acctType", _account);
+                       info.AddValue ("m_isAuthenticated", _authenticated);
                }
 #endif
+               private static bool IsPosix {
+                       get { return ((int) Environment.Platform == 128); }
+               }
+
+               private void SetToken (IntPtr token) 
+               {
+                       if (IsPosix) {
+                               if (token == invalidPosix)
+                                       throw new ArgumentException ("Invalid token");
+
+                               _token = token;
+                               // apply defaults
+                               if (_type == null)
+                                       _type = "POSIX";
+                               // override user choice in this specific case
+                               if (_token == IntPtr.Zero)
+                                       _account = WindowsAccountType.System;
+                       }
+                       else {
+                               if ((token == invalidWindows) && (_account != WindowsAccountType.Anonymous))
+                                       throw new ArgumentException ("Invalid token");
+
+                               _token = token;
+                               // apply defaults
+                               if (_type == null)
+                                       _type = "NTLM";
+                       }
+               }
+
+               // see mono/mono/metadata/security.c for implementation
+
+               // Many people use reflection to get a user's roles - so many 
+               // that's it's hard to say it's an "undocumented" feature -
+               // so we also implement it in Mono :-/
+               // http://www.dotnet247.com/247reference/msgs/39/195403.aspx
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static string[] _GetRoles (IntPtr token);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               internal extern static IntPtr GetCurrentToken ();
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static string GetTokenName (IntPtr token);
+
+               [MethodImplAttribute (MethodImplOptions.InternalCall)]
+               private extern static IntPtr GetUserToken (string username);
        }
 }
-