File: SessionIDManager.cs

package info (click to toggle)
mono 5.18.0.240%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,253,216 kB
  • sloc: cs: 10,925,936; xml: 2,804,987; ansic: 643,970; cpp: 120,384; perl: 59,272; asm: 21,383; sh: 20,162; makefile: 18,157; python: 4,715; pascal: 924; sql: 859; sed: 16; php: 1
file content (513 lines) | stat: -rw-r--r-- 18,139 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
//------------------------------------------------------------------------------
// <copyright file="SessionIDManager.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

/*
 * SessionIDManager
 *
 * Copyright (c) 1998-1999, Microsoft Corporation
 *
 */

namespace System.Web.SessionState {

    using System;
    using System.Collections;
    using System.IO;
    using System.Web.Util;
    using System.Web.Configuration;
    using System.Security.Cryptography;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Text;
    using System.Web.Security;
    using System.Web.Management;
    using System.Web.Hosting;

    public interface ISessionIDManager {

        bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIDReissue);

        // Get the session id from Context or Cookies.
        // Called by session state module in AcquireState event.
        String GetSessionID(HttpContext context);

        // Create a session id.
        String CreateSessionID(HttpContext context);

        // Save the session id to either URL or cookies.
        // For URL case, the param "redirected" will be set
        // to true, meaning the caller should call HttpApplication.CompleteRequest()
        // and return. Called by session state module at the end of AcquireState event
        // and if a session state is successfully retrieved.

        void SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded);

        // If cookie-ful, remove the session id from cookie.
        // Called by session state module in the ReleaseState event if a new
        // session was created but was unused

        void RemoveSessionID(HttpContext context);

        // Called by GetSessionID to make sure the ID sent by the browser is legitimate

        bool Validate(String id);

        void Initialize();
    }

    /*
     * The sesssion id manager provides session id services
     * for an application.
     */
    public class SessionIDManager : ISessionIDManager {

        // cookieless vars

        const int COOKIELESS_SESSION_LENGTH = SessionId.ID_LENGTH_CHARS + 2;

        internal const String COOKIELESS_SESSION_KEY = "AspCookielessSession";
        internal const String COOKIELESS_BOOL_SESSION_KEY = "AspCookielessBoolSession";
        internal const String ASP_SESSIONID_MANAGER_INITIALIZEREQUEST_CALLED_KEY = "AspSessionIDManagerInitializeRequestCalled";

        static string   s_appPath;
        static int      s_iSessionId;


        internal const HttpCookieMode       COOKIEMODE_DEFAULT = HttpCookieMode.UseCookies;
        internal const String               SESSION_COOKIE_DEFAULT = "ASP.NET_SessionId";
        internal const int                  SESSION_ID_LENGTH_LIMIT = 80;

        #pragma warning disable 0649
        static ReadWriteSpinLock            s_lock;
        #pragma warning restore 0649
        static SessionStateSection          s_config;  

        bool                                _isInherited;

        RandomNumberGenerator               _randgen;

        public SessionIDManager() {
        }

        public static int SessionIDMaxLength {
            get { return SESSION_ID_LENGTH_LIMIT; }
        }

        void OneTimeInit() {
            SessionStateSection config = RuntimeConfig.GetAppConfig().SessionState;

            s_appPath = HostingEnvironment.ApplicationVirtualPathObject.VirtualPathString;

            // s_iSessionId is pointing to the starting "("
            s_iSessionId = s_appPath.Length;

            s_config = config;
        }

        static SessionStateSection Config {
            get {
                if (s_config == null) {
                    throw new HttpException(SR.GetString(SR.SessionIDManager_uninit));
                }

                return s_config;
            }
        }

        public void Initialize() {
            if (s_config == null) {
                s_lock.AcquireWriterLock();
                try {
                    if (s_config == null) {
                        OneTimeInit();
                    }
                }
                finally {
                    s_lock.ReleaseWriterLock();
                }
            }

            _isInherited = !(this.GetType() == typeof(SessionIDManager));
            
            Debug.Trace("SessionIDManager", "cookieMode = " + Config.Cookieless +
                        ", cookieName = " + Config.CookieName +
                        ", inherited = " + _isInherited);
        }

        internal void GetCookielessSessionID(HttpContext context, bool allowRedirect, out bool cookieless) {
            HttpRequest     request;
            string          id;

            Debug.Trace("SessionIDManager", "Beginning SessionIDManager::GetCookielessSessionID");

            request = context.Request;

            // Please note that even if the page doesn't require session state, we still need
            // to read the session id because we have to update the state's timeout value

            cookieless = CookielessHelperClass.UseCookieless(context, allowRedirect, Config.Cookieless);
            context.Items[COOKIELESS_BOOL_SESSION_KEY] = cookieless;

            Debug.Trace("SessionIDManager", "cookieless=" + cookieless);

            if (cookieless) {
                /*
                 * Check if it's cookie-less session id
                 */

                id = context.CookielessHelper.GetCookieValue('S');
                if (id == null)
                    id = String.Empty;
                // Decode() is caled on all id's before saved to URL or cookie
                id = Decode(id);
                if (!ValidateInternal(id, false)) {
                    Debug.Trace("SessionIDManager", "No legitimate cookie on path\nReturning from SessionStateModule::GetCookielessSessionID");
                    return;
                }

                context.Items.Add(COOKIELESS_SESSION_KEY, id);

                Debug.Trace("SessionIDManager", "CookielessSessionModule found SessionId=" + id +
                            "\nReturning from SessionIDManager::GetCookielessSessionID");            
            }

        }

        static HttpCookie CreateSessionCookie(String id) {
            HttpCookie  cookie;

            cookie = new HttpCookie(Config.CookieName, id);
            cookie.Path = "/";

            // VSWhidbey 414687 Use HttpOnly to prevent client side script manipulation of cookie
            cookie.HttpOnly = true;

            return cookie;
        }

        internal static bool CheckIdLength(string id, bool throwOnFail) {
            bool    ret = true;
            
            if (id.Length > SESSION_ID_LENGTH_LIMIT) {
                if (throwOnFail) {
                    throw new HttpException(
                        SR.GetString(SR.Session_id_too_long,
                            SESSION_ID_LENGTH_LIMIT.ToString(CultureInfo.InvariantCulture), id));
                }
                else {
                    ret = false;
                }
            }

            return ret;
        }

        private bool ValidateInternal(string id, bool throwOnIdCheck) {
            return CheckIdLength(id, throwOnIdCheck) && Validate(id);
        }

        public virtual bool Validate(string id) {
            return SessionId.IsLegit(id);
        }

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        public virtual String Encode(String id) {
            // Need to do UrlEncode if the session id could be custom created.
            if (_isInherited) {
                Debug.Trace("SessionIDManager", "Encode is doing UrlEncode ");
                return HttpUtility.UrlEncode(id);
            }
            else {
                Debug.Trace("SessionIDManager", "Encode is doing nothing ");
                return id;
            }
        }

        public virtual String Decode(String id) {
            // Need to do UrlDecode if the session id could be custom created.
            if (_isInherited) {
                Debug.Trace("SessionIDManager", "Decode is doing UrlDecode ");
                return HttpUtility.UrlDecode(id);
            }
            else {
                Debug.Trace("SessionIDManager", "Decode is doing nothing");
                return id.ToLower(CultureInfo.InvariantCulture);
            }
        }

        internal bool UseCookieless(HttpContext context) {
            Debug.Assert(context.Items[ASP_SESSIONID_MANAGER_INITIALIZEREQUEST_CALLED_KEY] != null);
            
            if (Config.Cookieless == HttpCookieMode.UseCookies) {
                return false;
            }
            else {
                object o = context.Items[COOKIELESS_BOOL_SESSION_KEY];

                Debug.Assert(o != null, "GetCookielessSessionID should be run already");
                
                return (bool)o;
            }
        }

        void CheckInitializeRequestCalled(HttpContext context) {
            if (context.Items[ASP_SESSIONID_MANAGER_INITIALIZEREQUEST_CALLED_KEY] == null) {
                throw new HttpException(SR.GetString(SR.SessionIDManager_InitializeRequest_not_called));
            }
        }

        public bool InitializeRequest(HttpContext context, bool suppressAutoDetectRedirect, out bool supportSessionIDReissue) {
            // Note: We support cookie reissue only if we're using cookieless. VSWhidbey 384892
            
            if (context.Items[ASP_SESSIONID_MANAGER_INITIALIZEREQUEST_CALLED_KEY] != null) {
                supportSessionIDReissue = UseCookieless(context);
                return false;
            }
            
            context.Items[ASP_SESSIONID_MANAGER_INITIALIZEREQUEST_CALLED_KEY] = true;
                
            if (Config.Cookieless == HttpCookieMode.UseCookies) {
                supportSessionIDReissue = false;   
                return false;
            }
            else {
                bool    cookieless;
                GetCookielessSessionID(context, !suppressAutoDetectRedirect, out cookieless);

                supportSessionIDReissue = cookieless;       
                return context.Response.IsRequestBeingRedirected;
            }
        }

        // Get the session id from Context or Cookies.
        // Called by session state module in AcquireState event.
        public String GetSessionID(HttpContext context) {
            String      s = null;
            HttpCookie  cookie;

            CheckInitializeRequestCalled(context);

            if (UseCookieless(context)) {
                s = (String) context.Items[COOKIELESS_SESSION_KEY];
            }
            else {
                cookie = context.Request.Cookies[Config.CookieName];
                if (cookie != null && cookie.Value != null) {
                    s = Decode((String)cookie.Value);
                    if (s != null && !ValidateInternal(s, false)) {
                        s = null;
                    }
                }
            }

            return s;
        }

        // Create a session id.
        virtual public String CreateSessionID(HttpContext context) {
            return SessionId.Create(ref _randgen);
        }

        // Save the session id to either URL or cookies.
        // For URL case, the param "redirected" will be set
        // to true, and we've called HttpApplication.CompleteRequest().
        // The caller should return. Called by session state module at the end of AcquireState event
        // and if a session state is successfully retrieved.
        public void SaveSessionID(HttpContext context, String id, out bool redirected,
                                        out bool cookieAdded)
        {
            HttpCookie          cookie;
            String              idEncoded;

            redirected = false;
            cookieAdded = false;

            CheckInitializeRequestCalled(context);

            if (context.Response.HeadersWritten) {
                // We support setting the session ID in a cookie or by redirecting to a munged URL.
                // Both techniques require that response headers haven't yet been flushed.
                throw new HttpException(
                    SR.GetString(SR.Cant_save_session_id_because_response_was_flushed));
            }

            if (!ValidateInternal(id, true)) {
                // VSWhidbey 439376
                throw new HttpException(
                    SR.GetString(SR.Cant_save_session_id_because_id_is_invalid, id));
            }

            idEncoded = Encode(id);

            if (!UseCookieless(context)) {
                /*
                 * Set the cookie.
                 */
                Debug.Trace("SessionIDManager",
                            "Creating session cookie, id=" + id + ", idEncoded=" + idEncoded);

                cookie = CreateSessionCookie(idEncoded);
                context.Response.Cookies.Add(cookie);
                cookieAdded = true;
            }
            else {
                context.CookielessHelper.SetCookieValue('S', idEncoded);

                /*
                 * Redirect.
                 */
                HttpRequest request = context.Request;
                string path = request.Path;
                string qs = request.QueryStringText;
                if (!String.IsNullOrEmpty(qs)) {
                    path = path + "?" + qs;
                }

                Debug.Trace("SessionIDManager",
                            "Redirecting to create cookieless session, path=" + path + ", idEncoded=" + idEncoded +
                            "\nReturning from SessionIDManager::SaveSessionID");

                context.Response.Redirect(path, false);
                context.ApplicationInstance.CompleteRequest();

                // Caller has to return immediately.
                redirected = true;
            }

            return;
        }

        // If cookie-ful, remove the session id from cookie.
        // Called by session state module in the ReleaseState event if a new
        // session was created but was unused

        // If cookieless, we can't do anything.
        public void RemoveSessionID(HttpContext context) {
            Debug.Trace("SessionIDManager", "Removing session id cookie");
            context.Response.Cookies.RemoveCookie(Config.CookieName);
        }
    }

    /*
     * Provides and verifies the integrity of a session id.
     *
     * A session id is a logically 120 bit random number,
     * represented in a string of 20 characters from a
     * size 64 character set. The session id can be placed
     * in a url without url-encoding.
     */
    internal static class SessionId {
        internal const int  NUM_CHARS_IN_ENCODING = 32;
        internal const int  ENCODING_BITS_PER_CHAR = 5;
        internal const int  ID_LENGTH_BITS  = 120;
        internal const int  ID_LENGTH_BYTES = (ID_LENGTH_BITS / 8 );                        // 15
        internal const int  ID_LENGTH_CHARS = (ID_LENGTH_BITS / ENCODING_BITS_PER_CHAR);    // 24

        static char[] s_encoding = new char[NUM_CHARS_IN_ENCODING]
        {
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5'
        };

        static bool[] s_legalchars;

        static SessionId() {
            int     i;
            char    ch;

            s_legalchars = new bool[128];
            for (i = s_encoding.Length - 1; i >= 0; i--) {
                ch = s_encoding[i];
                s_legalchars[ch] = true;
            }
        }

        internal static bool IsLegit(String s) {
            int     i;
            char    ch;

            if (s == null || s.Length != ID_LENGTH_CHARS)
                return false;

            try {
                i = ID_LENGTH_CHARS;
                while (--i >= 0) {
                    ch = s[i];
                    if (!s_legalchars[ch])
                        return false;
                }

                return true;
            }
            catch (IndexOutOfRangeException) {
                return false;
            }
        }

        static String Encode(byte[] buffer) {
            int     i, j, k, n;
            char[]  chars = new char[ID_LENGTH_CHARS];

            Debug.Assert(buffer.Length == ID_LENGTH_BYTES);

            j = 0;
            for (i = 0; i < ID_LENGTH_BYTES; i += 5) {
                n =  (int) buffer[i] |
                     ((int) buffer[i+1] << 8)  |
                     ((int) buffer[i+2] << 16) |
                     ((int) buffer[i+3] << 24);

                k = (n & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 5) & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 10) & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 15) & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 20) & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 25) & 0x0000001F);
                chars[j++] = s_encoding[k];

                n = ((n >> 30) & 0x00000003) | ((int)buffer[i + 4] << 2);

                k = (n & 0x0000001F);
                chars[j++] = s_encoding[k];

                k = ((n >> 5) & 0x0000001F);
                chars[j++] = s_encoding[k];
            }

            Debug.Assert(j == ID_LENGTH_CHARS);

            return new String(chars);
        }

        static internal String Create(ref RandomNumberGenerator randgen) {
            byte[]  buffer;
            String  encoding;

            if (randgen == null) {
                randgen = new RNGCryptoServiceProvider();
            }

            buffer = new byte [15];
            randgen.GetBytes(buffer);
            encoding = Encode(buffer);
            return encoding;
        }
    }

}