~ifolder-dev/simias/trunk-packaging

« back to all changes in this revision

Viewing changes to src/core/HttpAuthentication/.svn/text-base/AuthHandler.cs.svn-base

  • Committer: Jorge O. Castro
  • Date: 2007-12-03 06:56:46 UTC
  • Revision ID: jorge@ubuntu.com-20071203065646-mupcnjcwgm5mnhyt
* Remove a bunch of .svn directories we no longer need.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/****************************************************************************
2
 
 |
3
 
 | Copyright (c) 2007 Novell, Inc.
4
 
 | All Rights Reserved.
5
 
 |
6
 
 | This program is free software; you can redistribute it and/or
7
 
 | modify it under the terms of version 2 of the GNU General Public License as
8
 
 | published by the Free Software Foundation.
9
 
 |
10
 
 | This program is distributed in the hope that it will be useful,
11
 
 | but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 | GNU General Public License for more details.
14
 
 |
15
 
 | You should have received a copy of the GNU General Public License
16
 
 | along with this program; if not, contact Novell, Inc.
17
 
 |
18
 
 | To contact Novell about this file by physical or electronic mail,
19
 
 | you may find current contact information at www.novell.com 
20
 
 |
21
 
 |  Author: Todd Throne - tthrone@novell.com
22
 
 |***************************************************************************/
23
 
 
24
 
using System;
25
 
using System.Collections;
26
 
using System.Collections.Specialized;
27
 
using System.Configuration;
28
 
using System.IO;
29
 
using System.Net;
30
 
using System.Threading;
31
 
using System.Web;
32
 
using System.Web.Services;
33
 
 
34
 
using Simias;
35
 
using Simias.Authentication;
36
 
using Simias.Client;
37
 
using Simias.Security.Web.AuthenticationService;
38
 
using Simias.Service;
39
 
using Simias.Storage;
40
 
using Simias.DomainServices;
41
 
 
42
 
namespace Simias.Security.Web
43
 
{
44
 
        /// <summary>
45
 
        /// HttpModule used for Simias authentication
46
 
        /// </summary>
47
 
        public sealed class AuthenticationModule : IHttpModule
48
 
        {
49
 
                #region Class Members
50
 
 
51
 
                /// <summary>
52
 
                /// Used to log messages.
53
 
                /// </summary>
54
 
                private static readonly ISimiasLog log = SimiasLogManager.GetLogger( typeof( AuthenticationModule ) );
55
 
 
56
 
                /// <summary>
57
 
                /// Session tag used to store session information.
58
 
                /// </summary>
59
 
                private static readonly string sessionTag = "simias";
60
 
 
61
 
                // Response header set by the Http Authentication Module
62
 
                public readonly static string DomainIDHeader = "Domain-ID";
63
 
 
64
 
                /// <summary>
65
 
                /// Characters that are trimmed from the beginning and ending of the line.
66
 
                /// </summary>
67
 
                private readonly static char[] trimChars = new char[] { '\"', '/', '\\' };
68
 
 
69
 
                /// <summary>
70
 
                /// Enabled if ssl required is set in the web.config.
71
 
                /// </summary>
72
 
                private bool sslRequired = true;
73
 
 
74
 
                /// <summary>
75
 
                /// Default ssl port.
76
 
                /// </summary>
77
 
                private int sslPort = 443;
78
 
 
79
 
                /// <summary>
80
 
                /// Handle to the store.
81
 
                /// </summary>
82
 
                private Store store = null;
83
 
 
84
 
                /// <summary>
85
 
                /// Contains services specified in the web.config that require no
86
 
                /// authentication. These may include entire web services, http
87
 
                /// handlers and individual web service methods.
88
 
                /// </summary>
89
 
                private Hashtable unauthenticatedServices = new Hashtable();
90
 
 
91
 
                /// <summary>
92
 
                /// Hashtable that contains all the local addresses that this 
93
 
                /// machine is known as.
94
 
                /// </summary>
95
 
                private Hashtable localAddresses = new Hashtable();
96
 
 
97
 
                /// <summary>
98
 
                /// Manager singleton.
99
 
                /// </summary>
100
 
                private Service.Manager simiasManager = null;
101
 
 
102
 
                #endregion
103
 
 
104
 
                #region Properties
105
 
                /// <summary>
106
 
                /// Gets the store handle for the store.
107
 
                /// 
108
 
                /// NOTE: It seems that calling Store.GetStore() statically, in the
109
 
                /// constructor or in the Init() call causes the Mac client to fail
110
 
                /// because it cannot find the Flaim libraries.
111
 
                /// </summary>
112
 
                private Store StoreReference
113
 
                {
114
 
                        get 
115
 
                        { 
116
 
                                lock ( this )
117
 
                                {
118
 
                                        if ( store == null )
119
 
                                        {
120
 
                                                store = Store.GetStore();
121
 
                                        }
122
 
                                }
123
 
 
124
 
                                return store;
125
 
                        }
126
 
                }
127
 
 
128
 
                /// <summary>
129
 
                /// Gets a reference to the simias manager.
130
 
                /// </summary>
131
 
                private Service.Manager SimiasManager
132
 
                {
133
 
                        get
134
 
                        {
135
 
                                lock ( this )
136
 
                                {
137
 
                                        if ( simiasManager == null )
138
 
                                        {
139
 
                                                simiasManager = Service.Manager.GetManager();
140
 
                                        }
141
 
                                }
142
 
 
143
 
                                return simiasManager;
144
 
                        }
145
 
                }
146
 
                #endregion
147
 
 
148
 
                #region Private Methods
149
 
 
150
 
                /// <summary>
151
 
                /// Gets the domain ID from the realm if it exists.
152
 
                /// </summary>
153
 
                /// <param name="context">HttpContext object.</param>
154
 
                /// <returns>The domain ID if found, otherwise a null is returned.</returns>
155
 
                private string GetDomainIDFromRealm( HttpContext context )
156
 
                {
157
 
                        string domainID = null;
158
 
 
159
 
                        // Check for an authorization header.
160
 
                        string[] encodedCredentials = context.Request.Headers.GetValues( "Authorization" );
161
 
                        if ( ( encodedCredentials != null ) && ( encodedCredentials[ 0 ] != null ) )
162
 
                        {
163
 
                                // Make sure we are dealing with "Basic" credentials
164
 
                                if ( encodedCredentials[ 0 ].StartsWith( "Basic " ) )
165
 
                                {
166
 
                                        // The authHeader after the basic signature is encoded
167
 
                                        string authHeader = encodedCredentials[ 0 ].Remove( 0, 6 );
168
 
                                        byte[] credential = System.Convert.FromBase64String( authHeader );
169
 
                                        string decodedCredential = System.Text.Encoding.Default.GetString( credential, 0, credential.Length );
170
 
   
171
 
                                        // Clients that newed up a NetCredential object with a URL
172
 
                                        // come though on the authorization line in the following format:
173
 
                                        // http://domain:port/simias10/service.asmx\username:password
174
 
 
175
 
                                        int index = decodedCredential.LastIndexOf( '\\' );
176
 
                                        if ( index != -1 )
177
 
                                        {
178
 
                                                string tempDomainID = decodedCredential.Substring( 0, index );
179
 
                                                if ( ( tempDomainID != null ) && ( StoreReference.GetDomain( tempDomainID ) != null ) )
180
 
                                                {
181
 
                                                        domainID = tempDomainID;
182
 
                                                }
183
 
                                        }
184
 
                                }
185
 
                        }
186
 
 
187
 
                        return domainID;
188
 
                }
189
 
 
190
 
                /// <summary>
191
 
                /// Returns whether address is an address local to this machine.
192
 
                /// </summary>
193
 
                /// <param name="address">The address to test.</param>
194
 
                /// <returns>True if address is local, otherwise false is returned.</returns>
195
 
                private bool IsLocalAddress( string address )
196
 
                {
197
 
                        bool isLocal = false;
198
 
 
199
 
                        try
200
 
                        {
201
 
                                IPAddress hostAddress = IPAddress.Parse( address );
202
 
                                if ( IPAddress.IsLoopback( hostAddress ) || localAddresses.ContainsKey( address ) )
203
 
                                {
204
 
                                        isLocal = true;
205
 
                                }
206
 
                        }
207
 
                        catch ( FormatException )
208
 
                        {
209
 
                                // The address is a DNS name not a dotted-quad address.
210
 
                                if ( ( String.Compare( address, "loopback", true ) == 0 ) || 
211
 
                                         ( String.Compare( address, "localhost", true ) == 0 ) ||
212
 
                                         localAddresses.ContainsKey( address.ToLower() ) )
213
 
                                {
214
 
                                        isLocal = true;
215
 
                                }
216
 
                        }
217
 
 
218
 
                        return isLocal;
219
 
                }
220
 
 
221
 
                /// <summary>
222
 
                /// Occurs when ASP.NET acquires the current state (for example, session state) 
223
 
                /// associated with the current request.
224
 
                /// </summary>
225
 
                /// <param name="source">The source of the event.</param>
226
 
                /// <param name="eventArgs">An EventArgs that contains the event data.</param>
227
 
                private void OnAcquireRequestState( Object source, EventArgs eventArgs ) 
228
 
                {
229
 
                        // Get the context for the current request.
230
 
                        HttpContext context = HttpContext.Current;
231
 
                        if ( context.Session != null )
232
 
                        {
233
 
                                // See if the user has a session from a previous login.
234
 
                                Session simiasSession = context.Session[ sessionTag ] as Session;
235
 
                                if ( simiasSession != null )
236
 
                                {
237
 
                                        context.User = simiasSession.User;
238
 
                                        if ( context.User.Identity.IsAuthenticated )
239
 
                                        {
240
 
                                                // The user is authenticated, set it as the current principal on this thread.
241
 
                                                log.Debug("RAMESH: AUTHENTICATED USER");
242
 
                                                if( DomainAgent.blockedIPs != null)
243
 
                                                {
244
 
                                                        string soapPath = context.Request.Headers[ "SOAPAction" ];
245
 
                                                        string soapMethod = ( soapPath != null ) ? Path.GetFileName( soapPath.Trim( trimChars ) ) : null;
246
 
                                                        if ( soapMethod == null )
247
 
                                                        {
248
 
                                                                log.Debug("Getting the web method");
249
 
                                                                // See if it was specified as a query parameter.
250
 
                                                                soapMethod = context.Request.QueryString[ "op" ];
251
 
                        
252
 
                                                                // If there is no operation query parameter, then use the entire query
253
 
                                                                // string as an index. This will allow the exception file to use
254
 
                                                                // something like Simias.asmx:?WSDL to allow WSDL download without credentials.
255
 
                                                                if ( soapMethod == null )
256
 
                                                                {
257
 
                                                                        soapMethod = context.Request.Url.Query;
258
 
                                                                }
259
 
                                                        }
260
 
 
261
 
                                                        log.Debug("The web method is: {0}", soapMethod);
262
 
                                                        if( soapMethod.IndexOf( "IsUpdateAvailable") == -1 && soapMethod.IndexOf("CheckForUpdate") == -1)
263
 
                                                        {
264
 
                                                                if( DomainAgent.blockedIPs.ContainsKey(context.Request.UserHostAddress))//foreach( string cookie in DomainAgent.blockedIPs )
265
 
                                                                {
266
 
                                                                                log.Debug("The IP's match. {0}", context.Request.UserHostAddress);
267
 
                                                                                if( soapMethod == null || (soapMethod.IndexOf("GetUpdateFiles") == -1 && soapMethod.IndexOf("Platform") == -1 && soapMethod.IndexOf("File") == -1))
268
 
                                                                                {
269
 
                                                                                        // Block the request...  
270
 
                                                                                        log.Debug("Block the request");
271
 
                                                                                        context.Response.StatusCode = 401;
272
 
                                                                                        context.Response.StatusDescription = "Unauthorized";
273
 
                                                                                        context.ApplicationInstance.CompleteRequest();
274
 
                                                                                } 
275
 
                                                                }
276
 
                                                        }
277
 
                                                        else
278
 
                                                        {
279
 
                                                                log.Debug("Removing blocked ip new. continue for now");
280
 
                                                                if( DomainAgent.blockedIPs.ContainsKey(context.Request.UserHostAddress) )
281
 
                                                                        DomainAgent.blockedIPs.Remove(context.Request.UserHostAddress);
282
 
                                                                
283
 
                                                        }
284
 
                                                }
285
 
                                                //else
286
 
                                                        //log.Debug("Ramesh: blocked list is null");
287
 
                                                Thread.CurrentPrincipal = context.User;
288
 
                                        }
289
 
                                        else
290
 
                                        {
291
 
                                                // The user is not authenticated on this session. See if there are
292
 
                                                // credentials specified on the request.
293
 
                                                VerifyPrincipalFromRequest( context );
294
 
                                        }
295
 
                                }
296
 
                                else
297
 
                                {
298
 
                                        // A simias session from a previous login does not exist. See if there
299
 
                                        // are credentials specified on the request.
300
 
                                        VerifyPrincipalFromRequest( context );
301
 
                                }
302
 
                        }
303
 
                        else
304
 
                        {
305
 
                                // There is no session setup. Authenticate every time.
306
 
                                VerifyPrincipalFromRequest( context );
307
 
                        }
308
 
                }
309
 
 
310
 
                /// <summary>
311
 
                /// Occurs when a security module has established the identity of the user.
312
 
                /// </summary>
313
 
                /// <param name="source">The source of the event.</param>
314
 
                /// <param name="eventArgs">An EventArgs that contains the event data.</param>
315
 
                private void OnAuthenticateRequest( Object source, EventArgs eventArgs ) 
316
 
                {
317
 
                        // There is no way to access session information from the OnAuthenticateRequest
318
 
                        // event unless we implement our own cookie/session management.
319
 
                        // 
320
 
                        // We will handle authentication from our OnAcquireRequestState event instead
321
 
                        // so that we can take advantage of the SessionStateModule implementation.
322
 
 
323
 
                        // Verify that we are on a secure connection, if not redirect to https
324
 
                        HttpContext context = HttpContext.Current;
325
 
                        if ( ( IsLocalAddress( context.Request.UserHostAddress ) == false ) && 
326
 
                                 ( context.Request.IsSecureConnection == false ) && 
327
 
                                 ( sslRequired == true ) ) 
328
 
                        {
329
 
                                // Redirect over https
330
 
                                UriBuilder redirectedUri = new UriBuilder( context.Request.Url.ToString() );
331
 
                                redirectedUri.Scheme = "https";
332
 
                                redirectedUri.Port = sslPort;
333
 
 
334
 
                                //log.Debug( redirectedUri.Uri.ToString() );
335
 
 
336
 
                                // You must have an SSL certificate configured on your web server for this to work
337
 
                                context.Response.Redirect( redirectedUri.Uri.ToString() );
338
 
                        }
339
 
                }
340
 
 
341
 
                /// <summary>
342
 
                /// Occurs as the first event in the HTTP pipeline chain of execution when ASP.NET 
343
 
                /// responds to a request.
344
 
                /// </summary>
345
 
                /// <param name="source">The source of the event.</param>
346
 
                /// <param name="eventArgs">An EventArgs that contains the event data.</param>
347
 
                private void OnBeginRequest( Object source, EventArgs eventArgs ) 
348
 
                {
349
 
                        HttpApplication app = source as HttpApplication;
350
 
                        string physicalPath = app.Request.PhysicalPath;
351
 
 
352
 
                        if ( ( app.Request.Path.IndexOf( '\\' ) >= 0 ) || 
353
 
                                ( Path.GetFullPath( physicalPath ) != physicalPath ) )
354
 
                        {
355
 
                                //log.Debug( "AuthenticationModule.OnBeginRequest - Security attack detected!!" );
356
 
                                throw new HttpException( 404, "Not Found" );
357
 
                        }
358
 
 
359
 
                        // See if the simias services have been started.
360
 
                        if ( !SimiasManager.ServiceStarted )
361
 
                        {
362
 
                                HttpResponse response = app.Context.Response;
363
 
                                response.StatusCode = 503;
364
 
                                response.StatusDescription = "The server is not ready.";
365
 
                                app.CompleteRequest();
366
 
                        }
367
 
                }
368
 
 
369
 
                /// <summary>
370
 
                /// Parses the web.config appSettings values for AuthNotRequired.
371
 
                /// </summary>
372
 
                /// <param name="parseString">String that contains the service names.</param>
373
 
                private void ParseAuthNotRequiredServices( string parseString )
374
 
                {
375
 
                        string[] services = parseString.Split( new char[] { ',' } );
376
 
                        foreach ( string s in services )
377
 
                        {
378
 
                                // Add this web service or method to the table.
379
 
                                unauthenticatedServices.Add( s.Trim().ToLower(), null );
380
 
                        //      log.Debug("Ramesh: UnAuth services: {0}", s.Trim().ToLower());
381
 
                        }
382
 
                }
383
 
 
384
 
                /// <summary>
385
 
                /// Tries to authenticate the current request if authorization headers are present.
386
 
                /// </summary>
387
 
                /// <param name="context">HttpContext that represents the request.</param>
388
 
                private void VerifyPrincipalFromRequest( HttpContext context )
389
 
                {
390
 
                        // See if this request requires authentication.
391
 
                        string webService = Path.GetFileName( context.Request.FilePath );
392
 
                        if ( !unauthenticatedServices.ContainsKey( webService.ToLower() ) )
393
 
                        {
394
 
                                // See if this request method requires authentication.
395
 
                                string soapPath = context.Request.Headers[ "SOAPAction" ];
396
 
                                string soapMethod = ( soapPath != null ) ? Path.GetFileName( soapPath.Trim( trimChars ) ) : null;
397
 
                                if ( soapMethod == null )
398
 
                                {
399
 
                                        // See if it was specified as a query parameter.
400
 
                                        soapMethod = context.Request.QueryString[ "op" ];
401
 
 
402
 
                                        // If there is no operation query parameter, then use the entire query
403
 
                                        // string as an index. This will allow the exception file to use
404
 
                                        // something like Simias.asmx:?WSDL to allow WSDL download without credentials.
405
 
                                        if ( soapMethod == null )
406
 
                                        {
407
 
                                                soapMethod = context.Request.Url.Query;
408
 
                                        }
409
 
                                }
410
 
                                log.Debug("In verify[rincipalfromrequest: soapmethod is {0}", soapMethod);
411
 
 
412
 
                                if ( ( soapMethod == null ) || 
413
 
                                         !unauthenticatedServices.ContainsKey( String.Format( "{0}:{1}", webService, soapMethod ).ToLower() ) )
414
 
                                {
415
 
                                        // Check if the domain ID was specified in the basic realm.
416
 
                                        string domainID = GetDomainIDFromRealm( context );
417
 
                                        if ( domainID == null )
418
 
                                        {
419
 
                                                // Get the Domain ID.
420
 
                                                domainID = context.Request.Headers.Get( Http.DomainIDHeader );
421
 
                                                if ( domainID == null )
422
 
                                                {
423
 
                                                        if ( Store.IsEnterpriseServer )                         
424
 
                                                        {
425
 
                                                                // If this is an enterprise server use the default domain.
426
 
                                                                domainID = StoreReference.DefaultDomain;
427
 
                                                        }
428
 
                                                        else if ( IsLocalAddress( context.Request.UserHostAddress ) )
429
 
                                                        {
430
 
                                                                // If this address is loopback, set the local domain in the HTTP context.
431
 
                                                                domainID = StoreReference.LocalDomain;
432
 
                                                        }
433
 
                                                }
434
 
                                        }
435
 
 
436
 
                                        // Try and authenticate the request.
437
 
                                        if ( domainID != null )
438
 
                                        {
439
 
                                                if ( Http.GetMember( domainID, context ) != null )
440
 
                                                {
441
 
                                                        // Set the session to never expire on the local web service.
442
 
                                                        if ( context.Session != null )
443
 
                                                        {
444
 
                                                                if ( domainID == StoreReference.LocalDomain )
445
 
                                                                {
446
 
                                                                        // Set to a very long time.
447
 
                                                                        context.Session.Timeout = 60 * 24 * 365;
448
 
                                                                }
449
 
                                                                else
450
 
                                                                {
451
 
                                                                        // use the default session timeout
452
 
                                                                }
453
 
                                                        }
454
 
                                                }
455
 
                                        }
456
 
                                        else
457
 
                                        {
458
 
                                                string realmID = ( Store.IsEnterpriseServer ) ? StoreReference.DefaultDomain : StoreReference.LocalDomain;
459
 
                                                string realm = StoreReference.GetDomain( ( realmID != null ) ? realmID : StoreReference.LocalDomain ).Name;
460
 
                                                context.Response.StatusCode = 401;
461
 
                                                context.Response.StatusDescription = "Unauthorized";
462
 
                                                context.Response.AddHeader( "WWW-Authenticate", String.Concat( "Basic realm=\"", realm, "\"" ) );
463
 
                                                context.ApplicationInstance.CompleteRequest();
464
 
                                        }
465
 
                                }
466
 
                        }
467
 
                }
468
 
 
469
 
                #endregion
470
 
 
471
 
                #region IHttpModule Members
472
 
 
473
 
                /// <summary>
474
 
                /// Initializes a module and prepares it to handle requests.
475
 
                /// </summary>
476
 
                /// <param name="app">An HttpApplication that provides access to the methods, 
477
 
                /// properties, and events common to all application objects within an ASP.NET 
478
 
                /// application </param>
479
 
                public void Init( HttpApplication app ) 
480
 
                {
481
 
                        // Register for the interesting events in the HTTP life-cycle.
482
 
                        app.BeginRequest += new EventHandler( OnBeginRequest );
483
 
                        app.AuthenticateRequest += new EventHandler( OnAuthenticateRequest );
484
 
                        app.AcquireRequestState += new EventHandler( OnAcquireRequestState );
485
 
 
486
 
                        // Get the application settings from the Simias.config.
487
 
                        string setting = Store.Config.Get( "Authentication", "SimiasRequireSSL" );
488
 
                        if ( setting != null )
489
 
                        {
490
 
                                if ( String.Compare( setting, "no", true ) == 0 )
491
 
                                {
492
 
                                        sslRequired = false;
493
 
                                }
494
 
                        }
495
 
 
496
 
                        // Get the ssl port setting.
497
 
                        setting = Store.Config.Get( "Authentication", "SimiasSSLPort" );
498
 
                        if ( setting != null )
499
 
                        {
500
 
                                sslPort = Convert.ToInt32( setting );
501
 
                        }
502
 
 
503
 
                        // Get the services that do not need authentication.
504
 
                        setting = Store.Config.Get( "Authentication", "SimiasAuthNotRequired" );
505
 
                        if ( setting != null )
506
 
                        {
507
 
                                ParseAuthNotRequiredServices( setting );
508
 
                        }
509
 
 
510
 
                        // Get all the addresses that this host is known by.
511
 
                        string[] addresses = MyDns.GetHostAddresses();
512
 
                        foreach( string s in addresses )
513
 
                        {
514
 
                                localAddresses[ s.ToLower() ] = null;
515
 
                        }
516
 
                }
517
 
 
518
 
                /// <summary>
519
 
                /// Disposes of the resources (other than memory) used by the module that 
520
 
                /// implements IHttpModule. 
521
 
                /// </summary>
522
 
                public void Dispose() 
523
 
                {
524
 
                }
525
 
 
526
 
                #endregion
527
 
        }
528
 
}