2005-08-02 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / corlib / System.Security.Principal / WindowsIdentity.cs
1 //
2 // System.Security.Principal.WindowsIdentity
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //      Sebastien Pouliot (sebastien@ximian.com)
7 //
8 // (C) 2002 Ximian, Inc (http://www.ximian.com)
9 // Portions (C) 2003 Motus Technologies Inc. (http://www.motus.com)
10 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System.Runtime.CompilerServices;
33 using System.Runtime.InteropServices;
34 using System.Runtime.Serialization;
35 using System.Security.Permissions;
36
37 namespace System.Security.Principal {
38
39         [Serializable]
40 #if NET_1_0
41         public class WindowsIdentity : IIdentity, IDeserializationCallback {
42 #elif NET_2_0
43         [ComVisible (true)]
44         public class WindowsIdentity : IIdentity, IDeserializationCallback, ISerializable, IDisposable {
45 #else
46         public class WindowsIdentity : IIdentity, IDeserializationCallback, ISerializable {
47 #endif
48                 private IntPtr _token;
49                 private string _type;
50                 private WindowsAccountType _account;
51                 private bool _authenticated;
52                 private string _name;
53                 private SerializationInfo _info;
54
55                 static private IntPtr invalidWindows = IntPtr.Zero;
56                 // that seems to be the value used for (at least) AIX and MacOSX
57                 static private IntPtr invalidPosix = (IntPtr) unchecked (-2);
58
59                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
60                 public WindowsIdentity (IntPtr userToken) 
61                         : this (userToken, null, WindowsAccountType.Normal, false)
62                 {
63                 }
64
65                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
66                 public WindowsIdentity (IntPtr userToken, string type) 
67                         : this (userToken, type, WindowsAccountType.Normal, false)
68                 {
69                 }
70
71                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
72                 public WindowsIdentity (IntPtr userToken, string type, WindowsAccountType acctType)
73                         : this (userToken, type, acctType, false)
74                 {
75                 }
76
77                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
78                 public WindowsIdentity (IntPtr userToken, string type, WindowsAccountType acctType, bool isAuthenticated)
79                 {
80                         _type = type;
81                         _account = acctType;
82                         _authenticated = isAuthenticated;
83                         _name = null;
84                         // last - as it can override some fields
85                         SetToken (userToken);
86                 }
87 #if !NET_1_0
88                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
89                 public WindowsIdentity (string sUserPrincipalName) 
90                         : this (sUserPrincipalName, null)
91                 {
92                 }
93
94                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
95                 public WindowsIdentity (string sUserPrincipalName, string type)
96                 {
97                         if (sUserPrincipalName == null)
98                                 throw new NullReferenceException ("sUserPrincipalName");
99
100                         // TODO: Windows 2003 compatibility should be done in runtime
101                         IntPtr token = GetUserToken (sUserPrincipalName);
102                         if ((!IsPosix) && (token == IntPtr.Zero)) {
103                                 throw new ArgumentException ("only for Windows Server 2003 +");
104                         }
105
106                         _authenticated = true;
107                         _account = WindowsAccountType.Normal;
108                         _type = type;
109                         // last - as it can override some fields
110                         SetToken (token);
111                 }
112
113                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
114                 public WindowsIdentity (SerializationInfo info, StreamingContext context)
115                 {
116                         _info = info;
117                 }
118 #endif
119
120 #if NET_2_0
121                 [ComVisible (false)]
122                 public void Dispose ()
123                 {
124                         _token = IntPtr.Zero;
125                 }
126                 
127                 [ComVisible (false)]
128                 protected virtual void Dispose (bool disposing)
129                 {
130                         _token = IntPtr.Zero;
131                 }
132 #else
133                 ~WindowsIdentity ()
134                 {
135                         // clear our copy but don't close it
136                         // http://www.develop.com/kbrown/book/html/whatis_windowsprincipal.html
137                         _token = IntPtr.Zero;
138                 }
139 #endif
140                 // static methods
141
142                 public static WindowsIdentity GetAnonymous ()
143                 {
144                         WindowsIdentity id = null;
145                         if (IsPosix) {
146                                 id = new WindowsIdentity ("nobody");
147                                 // special case
148                                 id._account = WindowsAccountType.Anonymous;
149                                 id._authenticated = false;
150                                 id._type = String.Empty;
151                         }
152                         else {
153                                 id = new WindowsIdentity (IntPtr.Zero, String.Empty, WindowsAccountType.Anonymous, false);
154                                 // special case (don't try to resolve the name)
155                                 id._name = String.Empty;
156                         }
157                         return id;
158                 }
159
160                 public static WindowsIdentity GetCurrent ()
161                 {
162                         return new WindowsIdentity (GetCurrentToken (), null, WindowsAccountType.Normal, true);
163                 }
164 #if NET_2_0
165                 [MonoTODO ("need icall changes")]
166                 public static WindowsIdentity GetCurrent (bool ifImpersonating)
167                 {
168                         throw new NotImplementedException ();
169                 }
170
171                 [MonoTODO ("need icall changes")]
172                 public static WindowsIdentity GetCurrent (TokenAccessLevels desiredAccess)
173                 {
174                         throw new NotImplementedException ();
175                 }
176 #endif
177                 // methods
178
179                 public virtual WindowsImpersonationContext Impersonate ()
180                 {
181                         return new WindowsImpersonationContext (_token);
182                 }
183
184                 [SecurityPermission (SecurityAction.Demand, ControlPrincipal=true)]
185                 public static WindowsImpersonationContext Impersonate (IntPtr userToken)
186                 {
187                         return new WindowsImpersonationContext (userToken);
188                 }
189
190                 // properties
191
192                 public virtual string AuthenticationType
193                 {
194                         get { return _type; }
195                 }
196
197                 public virtual bool IsAnonymous
198                 {
199                         get { return (_account == WindowsAccountType.Anonymous); }
200                 }
201
202                 public virtual bool IsAuthenticated
203                 {
204                         get { return _authenticated; }
205                 }
206
207                 public virtual bool IsGuest
208                 {
209                         get { return (_account == WindowsAccountType.Guest); }
210                 }
211
212                 public virtual bool IsSystem
213                 {
214                         get { return (_account == WindowsAccountType.System); }
215                 }
216
217                 public virtual string Name
218                 {
219                         get {
220                                 if (_name == null) {
221                                         // revolve name (runtime)
222                                         _name = GetTokenName (_token);
223                                 }
224                                 return _name; 
225                         }
226                 }
227
228                 public virtual IntPtr Token
229                 {
230                         get { return _token; }
231                 }
232 #if NET_2_0
233                 [MonoTODO ("not implemented")]
234                 public IdentityReferenceCollection Groups {
235                         get { throw new NotImplementedException (); }
236                 }
237
238                 [MonoTODO ("not implemented")]
239                 [ComVisible (false)]
240                 public TokenImpersonationLevel ImpersonationLevel {
241                         get { throw new NotImplementedException (); }
242                 }
243
244                 [MonoTODO ("not implemented")]
245                 [ComVisible (false)]
246                 public SecurityIdentifier Owner {
247                         get { throw new NotImplementedException (); }
248                 }
249
250                 [MonoTODO ("not implemented")]
251                 [ComVisible (false)]
252                 public SecurityIdentifier User {
253                         get { throw new NotImplementedException (); }
254                 }
255 #endif
256                 void IDeserializationCallback.OnDeserialization (object sender)
257                 {
258                         _token = (IntPtr) _info.GetValue ("m_userToken", typeof (IntPtr));
259                         // can't trust this alone - we must validate the token
260                         _name = _info.GetString ("m_name");
261                         if (_name != null) {
262                                 // validate token by comparing names
263                                 string name = GetTokenName (_token);
264                                 if (name != _name)
265                                         throw new SerializationException ("Token-Name mismatch.");
266                         }
267                         else {
268                                 // validate token by getting name
269                                 _name = GetTokenName (_token);
270                                 if ((_name == String.Empty) || (_name == null))
271                                         throw new SerializationException ("Token doesn't match a user.");
272                         }
273                         _type = _info.GetString ("m_type");
274                         _account = (WindowsAccountType) _info.GetValue ("m_acctType", typeof (WindowsAccountType));
275                         _authenticated = _info.GetBoolean ("m_isAuthenticated");
276                 }
277 #if !NET_1_0
278                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
279                 {
280                         info.AddValue ("m_userToken", _token);
281                         // can be null when not resolved
282                         info.AddValue ("m_name", _name);
283                         info.AddValue ("m_type", _type);
284                         info.AddValue ("m_acctType", _account);
285                         info.AddValue ("m_isAuthenticated", _authenticated);
286                 }
287 #endif
288                 private static bool IsPosix {
289                         get { return ((int) Environment.Platform == 128); }
290                 }
291
292                 private void SetToken (IntPtr token) 
293                 {
294                         if (IsPosix) {
295                                 if (token == invalidPosix)
296                                         throw new ArgumentException ("Invalid token");
297
298                                 _token = token;
299                                 // apply defaults
300                                 if (_type == null)
301                                         _type = "POSIX";
302                                 // override user choice in this specific case
303                                 if (_token == IntPtr.Zero)
304                                         _account = WindowsAccountType.System;
305                         }
306                         else {
307                                 if ((token == invalidWindows) && (_account != WindowsAccountType.Anonymous))
308                                         throw new ArgumentException ("Invalid token");
309
310                                 _token = token;
311                                 // apply defaults
312                                 if (_type == null)
313                                         _type = "NTLM";
314                         }
315                 }
316
317                 // see mono/mono/metadata/security.c for implementation
318
319                 // Many people use reflection to get a user's roles - so many 
320                 // that's it's hard to say it's an "undocumented" feature -
321                 // so we also implement it in Mono :-/
322                 // http://www.dotnet247.com/247reference/msgs/39/195403.aspx
323                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
324                 internal extern static string[] _GetRoles (IntPtr token);
325
326                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
327                 internal extern static IntPtr GetCurrentToken ();
328
329                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
330                 private extern static string GetTokenName (IntPtr token);
331
332                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
333                 private extern static IntPtr GetUserToken (string username);
334         }
335 }