2009-08-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Security.Tokens / SspiSession.cs
1 using System;
2 using System.IO;
3 using System.Net;
4 using System.Net.NetworkInformation;
5 using System.Security.Cryptography;
6 using System.Text;
7 using Mono.Security;
8 using Mono.Security.Protocol.Ntlm;
9
10 namespace System.ServiceModel.Security
11 {
12         internal abstract class SspiSession
13         {
14                 internal static readonly byte [] NtlmSSP = new byte [] {
15                         0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00};
16
17                 public long Challenge, Context, ClientOSVersion, ServerOSVersion;
18                 public string ServerName, DomainName, DnsHostName, DnsDomainName;
19
20                 public bool Verify (byte [] expected, byte [] actual, int offset, int length)
21                 {
22                         if (expected.Length != length)
23                                 return false;
24                         for (int i = 0; i < length; i++)
25                                 if (expected [i] != actual [i + offset])
26                                         return false;
27                         return true;
28                 }
29
30                 public SspiSecurityBufferStruct ReadSecurityBuffer (BinaryReader reader)
31                 {
32                         return new SspiSecurityBufferStruct (
33                                 reader.ReadInt16 (),
34                                 reader.ReadInt16 (),
35                                 reader.ReadInt32 ());
36                 }
37         }
38
39         internal struct SspiSecurityBufferStruct
40         {
41                 public SspiSecurityBufferStruct (short length, short allocatedSpace, int offset)
42                 {
43                         Length = length;
44                         AllocatedSpace = allocatedSpace;
45                         Offset = offset;
46                 }
47
48                 public readonly short Length;
49                 public readonly short AllocatedSpace;
50                 public readonly int Offset;
51         }
52
53         internal class SspiClientSession : SspiSession
54         {
55                 Type2Message type2;
56                 Type3Message type3;
57
58                 // Class(60) {
59                 //   OID(spnego),
60                 //   Class(A0) {
61                 //     Class(30) {
62                 //       Class(A0) {
63                 //         Class(30) { OID,OID,OID} },
64                 //       Class(A2) { OctetStream } } } }
65                 public byte [] ProcessSpnegoInitialContextTokenRequest ()
66                 {
67                         Type1Message type1 = new Type1Message (NtlmVersion.Version3);
68                         type1.Flags = unchecked ((NtlmFlags) 0xE21882B7);
69                         type1.Domain = "WORKGROUP"; // FIXME: remove it
70
71                         ASN1 asn = new ASN1 (0x60);
72                         ASN1 asn2 = new ASN1 (0xA0);
73                         ASN1 asn21 = new ASN1 (0x30);
74                         ASN1 asn211 = new ASN1 (0xA0);
75                         ASN1 asn2111 = new ASN1 (0x30);
76                         asn211.Add (asn2111);
77                         asn2111.Add (ASN1Convert.FromOid (Constants.OidNtlmSsp));
78                         asn2111.Add (ASN1Convert.FromOid (Constants.OidKerberos5));
79                         asn2111.Add (ASN1Convert.FromOid (Constants.OidMIT));
80                         ASN1 asn212 = new ASN1 (0xA2);
81                         ASN1 asn2121 = new ASN1 (0x4);
82                         asn2121.Value = type1.GetBytes ();
83                         asn212.Add (asn2121);
84                         asn21.Add (asn211);
85                         asn21.Add (asn212);
86                         asn2.Add (asn21);
87                         asn.Add (ASN1Convert.FromOid (Constants.OidSpnego));
88                         asn.Add (asn2);
89                         return asn.GetBytes ();
90                 }
91
92                 // Example buffer:
93                 // A18181 307F A003
94                 //   0A0101
95                 //   A10C 060A2B06010401823702020A
96                 //   A26A 0468 NTLM
97                 //   NTLM = 4E544C4D53535000 0200000004000400 3800000035829AE2
98                 //    0D1A7FF0F171F339 0000000000000000 2C002C003C000000
99                 //    0501280A0000000F 5000430002000400 5000430001000400
100                 //    5000430004000400 5000430003000400 5000430006000400
101                 //    0100000000000000
102                 public void ProcessSpnegoInitialContextTokenResponse (byte [] raw)
103                 {
104                         ASN1 asn1 = new ASN1 (raw);
105                         // FIXME: check OIDs and structure
106                         ProcessMessageType2 (asn1 [0] [2] [0].Value);
107                 }
108
109                 // Class { Class { Class { OctetStream } } }
110                 public byte [] ProcessSpnegoProcessContextToken (string user, string pass)
111                 {
112                         ASN1 asn = new ASN1 (0xA1);
113                         ASN1 asn2 = new ASN1 (0x30);
114                         ASN1 asn3 = new ASN1 (0xA2);
115                         asn3.Add (new ASN1 (0x04, ProcessMessageType3 (user, pass)));
116                         asn2.Add (asn3);
117                         asn.Add (asn2);
118                         return asn.GetBytes ();
119                 }
120
121                 public byte [] ProcessMessageType1 ()
122                 {
123                         Type1Message type1 = new Type1Message (NtlmVersion.Version3);
124                         type1.Flags = unchecked ((NtlmFlags) 0xE21882B7);
125                         return type1.GetBytes ();
126                 }
127
128                 string TargetName;
129
130                 public void ProcessMessageType2 (byte [] raw)
131                 {
132                         type2 = new Type2Message (raw);
133                 }
134
135                 public byte [] ProcessMessageType3 (string user, string password)
136                 {
137                         TargetName = Environment.MachineName;
138                         ServerName = Environment.MachineName;
139                         // FIXME
140                         DomainName = ServerName;// IPGlobalProperties.GetIPGlobalProperties ().DomainName;
141                         DnsHostName = Dns.GetHostName ();
142                         DnsDomainName = DnsHostName; // FIXME
143
144                         type3 = new Type3Message (NtlmVersion.Version3);
145                         type3.Flags = (NtlmFlags) (unchecked ((int) 0xE2188235));
146                         type3.Domain = DomainName;
147                         type3.Host = DnsHostName;
148                         type3.Challenge = type2.Nonce;
149                         type3.Username = user;
150                         type3.Password = password;
151
152                         return type3.GetBytes ();
153                 }
154         }
155
156         internal class SspiServerSession : SspiSession
157         {
158                 public string TargetName;
159                 public long SuppliedDomain, SuppliedWorkstation;
160                 Type1Message type1;
161                 Type2Message type2;
162                 Type3Message type3;
163
164                 // Example buffer:
165                 // 6069 0606 2B0601050502 A05F 305D A024 3022
166                 //        060A 2B06010401823702020A
167                 //        0609 2A864882F712010202
168                 //        0609 2A864886F712010202
169                 // A235 0433 NTLM
170                 // NTLM = 4E544C4D53535000 01000000 B7B218E2 090009002A000000
171                 //  0200020028000000 0501280A0000000F 5043 574F524B47524F5550
172                 public void ProcessSpnegoInitialContextTokenRequest (byte [] raw)
173                 {
174                         ASN1 asn1 = new ASN1 (raw);
175                         // FIXME: check OIDs
176                         ProcessMessageType1 (asn1 [1] [0] [1] [0].Value);
177                 }
178
179                 // Class {
180                 //   Class {
181                 //     Class { Enum },
182                 //     Class { OID(NTLMSSP) },
183                 //     Class { OctetStream } } }
184                 public byte [] ProcessSpnegoInitialContextTokenResponse ()
185                 {
186                         ASN1 top = new ASN1 (0xA1);
187                         ASN1 asn = new ASN1 (0x30);
188                         ASN1 asn1 = new ASN1 (0xA0);
189                         // FIXME: what is this enum?
190                         asn1.Add (new ASN1 (0x0A, new byte [] {1})); // Enum whatever
191                         ASN1 asn2 = new ASN1 (0xA1);
192                         asn2.Add (ASN1Convert.FromOid (Constants.OidNtlmSsp));
193                         ASN1 asn3 = new ASN1 (0xA2);
194                         asn3.Add (new ASN1 (0x04, ProcessMessageType2 ()));
195                         asn.Add (asn1);
196                         asn.Add (asn2);
197                         asn.Add (asn3);
198                         top.Add (asn);
199                         return top.GetBytes ();
200                 }
201
202                 // Example buffer:
203                 // A181A7
204                 //   3081A4
205                 //     A281A1
206                 //       04819E
207                 // 4E544C4D53535000 03000000 
208                 // 180018005E000000 1800180076000000 0400040048000000
209                 // 0E000E004C000000 040004005A000000 100010008E000000
210                 // 358218E2 0501280A0000000F
211                 // 50004300 6100740073007500730068006900 50004300
212                 // [8 bytes LM] [16 bytes of 0s]
213                 // [24 bytes of NTLM]
214                 // C94EE2ADE7E32244 BD60D3B33609C167
215                 public void ProcessSpnegoProcessContextToken (byte [] raw)
216                 {
217                         ASN1 asn1 = new ASN1 (raw);
218                         // FIXME: check structure
219                         ProcessMessageType3 (asn1 [0] [0] [0].Value);
220                 }
221
222                 public void ProcessMessageType1 (byte [] raw)
223                 {
224                         type1 = new Type1Message (raw, NtlmVersion.Version3);
225                 }
226
227                 public byte [] ProcessMessageType2 ()
228                 {
229                         byte [] bytes = new byte [8];
230                         RandomNumberGenerator.Create ().GetNonZeroBytes (bytes);
231                         Challenge = bytes [0] << 24 + bytes [1] << 16 + bytes [2] << 8 + bytes [3];
232                         Context = 0; // FIXME
233                         ServerOSVersion = 0x0F00000A28010500; // FIXME
234                         TargetName = Environment.MachineName;
235                         ServerName = Environment.MachineName;
236                         // FIXME
237                         DomainName = ServerName;// IPGlobalProperties.GetIPGlobalProperties ().DomainName;
238                         DnsHostName = Dns.GetHostName ();
239                         DnsDomainName = DnsHostName; // FIXME
240
241                         type2 = new Type2Message (NtlmVersion.Version3);
242                         type2.Flags = (NtlmFlags) (unchecked ((int) 0xE21882B7));
243                         type2.TargetName = TargetName;
244                         type2.Target.ServerName = ServerName;
245                         type2.Target.DomainName = DomainName;
246                         type2.Target.DnsHostName = DnsHostName;
247                         type2.Target.DnsDomainName = DnsDomainName;
248                         return type2.GetBytes ();
249                 }
250
251                 public void ProcessMessageType3 (byte [] raw)
252                 {
253                         /*
254                         MemoryStream ms = new MemoryStream (raw);
255                         if (!Verify (NtlmSSP, raw, 0, 8))
256                                 throw new SecurityNegotiationException ("Expected NTLM SSPI header not found");
257                         BinaryReader reader = new BinaryReader (ms);
258                         reader.ReadInt64 (); // skip 8 bytes
259                         if (reader.ReadInt32 () != 3)
260                                 throw new SecurityNegotiationException ("SSPI type 3 message is expected");
261                         SspiSecurityBufferStruct lmResInfo = ReadSecurityBuffer (reader);
262                         SspiSecurityBufferStruct ntlmResInfo = ReadSecurityBuffer (reader);
263                         SspiSecurityBufferStruct targetNameInfo = ReadSecurityBuffer (reader);
264                         SspiSecurityBufferStruct userNameInfo = ReadSecurityBuffer (reader);
265                         SspiSecurityBufferStruct wsNameInfo = ReadSecurityBuffer (reader);
266                         SspiSecurityBufferStruct sessionKeyInfo = ReadSecurityBuffer (reader);
267                         int flags = reader.ReadInt32 ();
268                         ServerOSVersion = reader.ReadInt64 ();
269                         */
270                         type3 = new Type3Message (raw, NtlmVersion.Version3);
271                 }
272         }
273 }