commit code to git
[raven/abandoned/asp.git] / Ucam_Webauth.vbs
1 <%
2
3 ' This VBScript class implements a Raven v3 agent for the University of Cambridge
4 ' Web Authentication System.
5 '
6 ' See http://raven.cam.ac.uk/project/ for more details
7 '
8 ' Loosely based on the PHP module for Raven
9 ' https://wiki.cam.ac.uk/raven/PHP_library
10 '
11 ' Copyright (c) 2004, 2005, 2008, 2014 University of Cambridge
12 '
13 ' This module is free software; you can redistribute it and/or modify
14 ' it under the terms of the GNU Lesser General Public License as
15 ' published by the Free Software Foundation; either version 2.1 of the
16 ' License, or (at your option) any later version.
17 '
18 ' The module is distributed in the hope that it will be useful, but
19 ' WITHOUT ANY WARRANTY; without even the implied warranty of
20 ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ' Lesser General Public License for more details.
22 '
23 ' You should have received a copy of the GNU Lesser General Public
24 ' License along with this toolkit; if not, write to the Free Software
25 ' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 ' USA
27 '
28 ' $Id: ucam_webauth.vbs, v1.0 2014/04/05 08:13:00 sh801
29 '
30 ' Version 1.0
31
32
33 Class Ucam_Webauth
34
35 ' ****************************************
36 '
37 ' ----------------------------------------
38 ' ---------- class Ucam_Webauth ----------
39 ' ----------------------------------------
40 '
41 ' ****************************************
42 '
43 ' Methods are listed in order of importance
44 ' with MAIN functions at top and lower-
45 ' level functions towards the end.
46 '
47 ' ****************************************
48
49
50 ' 'status_codes' is associative array of status codes of the form {"CODE" => "Description", ..}
51
52 Private status_codes
53
54 ' ****************************************
55 ' Default values for key parameters
56 ' ****************************************
57
58 Private PROTOCOL_VERSION
59 Private AUTHENTICATION_RESPONSE_VERSION
60 Private DEFAULT_AUTH_SERVICE
61 Private DEFAULT_KEY_DIR
62 Private DEFAULT_COOKIE_NAME
63 Private DEFAULT_TIMEOUT_MESSAGE
64 Private DEFAULT_HOSTNAME
65 Private WLS_LOGOUT
66 Private DEFAULT_LOG_FILE
67 Private AUTHENTICATIONCOOKIE_REDIRECT_WLS
68
69 ' ****************************************
70 ' Constants for tracking state
71 ' ****************************************
72 '
73 ' The 'Authenticate' function is called multiple
74 ' times for a successful authentication so we
75 ' need to track where we are in the authentication
76 ' process with some constants.
77
78 Private STATE_ERROR
79 Private STATE_NEW_AUTHENTICATION
80 Private STATE_WLS_RESPONSE_RECEIVED
81 private STATE_WAA_AUTHENTICATIONCOOKIE_SET
82
83 ' ****************************************
84 ' Constants for returning state
85 ' ****************************************
86 '
87 ' The 'Authenticate' function needs to
88 ' return relevant information as to where
89 ' it currently is in the authentication process.
90
91 Public AUTHENTICATE_INCOMPLETE
92 Public AUTHENTICATE_COMPLETE_ERROR
93 Public AUTHENTICATE_COMPLETE_AUTHENTICATED
94 Public AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED
95
96 ' ****************************************
97 ' Index numbers for the 'Authentication response' fields
98 ' ****************************************
99 '
100 ' See Raven specification documentation for descriptions of each parameter
101
102 Private AUTHENTICATION_RESPONSE_VER
103 Private AUTHENTICATION_RESPONSE_STATUS
104 Private AUTHENTICATION_RESPONSE_MSG
105 Private AUTHENTICATION_RESPONSE_ISSUE
106 Private AUTHENTICATION_RESPONSE_EXPIRE
107 Private AUTHENTICATION_RESPONSE_ID
108 Private AUTHENTICATION_RESPONSE_PRINCIPAL
109 Private AUTHENTICATION_RESPONSE_PTAGS
110 Private AUTHENTICATION_RESPONSE_AUTH
111 Private AUTHENTICATION_RESPONSE_SSO
112 Private AUTHENTICATION_RESPONSE_PARAMS
113 Private AUTHENTICATION_RESPONSE_SIG
114 Private AUTHENTICATION_RESPONSE_SIZE ' Size of required array
115
116 Private WLS_RESPONSE_VER
117 Private WLS_RESPONSE_STATUS
118 Private WLS_RESPONSE_MSG
119 Private WLS_RESPONSE_ISSUE
120 Private WLS_RESPONSE_ID
121 Private WLS_RESPONSE_URL
122 Private WLS_RESPONSE_PRINCIPAL
123 Private WLS_RESPONSE_PTAGS
124 Private WLS_RESPONSE_AUTH
125 Private WLS_RESPONSE_SSO
126 Private WLS_RESPONSE_LIFE
127 Private WLS_RESPONSE_PARAMS
128 Private WLS_RESPONSE_KID
129 Private WLS_RESPONSE_SIG
130 Private WLS_RESPONSE_SIZE ' Size of required array
131
132 ' ****************************************
133 ' General private member variables
134 ' ****************************************
135
136 Private m_response_timeout
137 Private m_authrequest_skew
138 Private m_max_session_life
139 Private m_redirected
140 Private m_use_authrequest_iact
141 Private m_authrequest_iact
142 Private m_authrequest_fail
143 Private m_authrequest_desc
144 Private m_authrequest_aauth
145 Private m_authrequest_params
146 Private m_authentication_response_string
147 Private m_authentication_cookie
148 Private m_auth_service
149 Private m_hostname
150 Private m_key_dir
151 Private m_timeout_message
152 Private m_cookie_key
153 Private m_cookie_path
154 Private m_cookie_name
155 Private m_cookie_domain
156 Private m_log_file
157 Private m_authentication_response
158
159 ' ****************************************
160 '
161 ' ----------------------------------------
162 ' ----------- MAIN FUNCTIONS -------------
163 ' ----------------------------------------
164 '
165 ' ****************************************
166
167 Private Sub Class_Initialize
168
169 ' Initialize all constants as it's not possible
170 ' to declare initial values in class definition, above.
171
172 PROTOCOL_VERSION = "3"
173 AUTHENTICATION_RESPONSE_VERSION = "3"
174 DEFAULT_AUTH_SERVICE = "https://raven.cam.ac.uk/auth/authenticate.html"
175 DEFAULT_KEY_DIR = "/etc/httpd/conf/webauth_keys"
176 DEFAULT_COOKIE_NAME = "Ucam-WebAuth-Session"
177 DEFAULT_TIMEOUT_MESSAGE = "your logon to the site has expired"
178 DEFAULT_HOSTNAME = "" ' must be supplied explicitly
179 WLS_LOGOUT = "Not-authenticated"
180 DEFAULT_LOG_FILE = "log.txt"
181 AUTHENTICATIONCOOKIE_REDIRECT_WLS = "REDIRECT_WLS"
182
183 STATE_ERROR = -1
184 STATE_NEW_AUTHENTICATION = 0
185 STATE_WLS_RESPONSE_RECEIVED = 1
186 STATE_WAA_AUTHENTICATIONCOOKIE_SET = 2
187
188 AUTHENTICATE_INCOMPLETE = 0
189 AUTHENTICATE_COMPLETE_ERROR = -1
190 AUTHENTICATE_COMPLETE_AUTHENTICATED = 1
191 AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED = 2
192
193 AUTHENTICATION_RESPONSE_VER = 0
194 AUTHENTICATION_RESPONSE_STATUS = 1
195 AUTHENTICATION_RESPONSE_MSG = 2
196 AUTHENTICATION_RESPONSE_ISSUE = 3
197 AUTHENTICATION_RESPONSE_EXPIRE = 4
198 AUTHENTICATION_RESPONSE_ID = 5
199 AUTHENTICATION_RESPONSE_PRINCIPAL = 6
200 AUTHENTICATION_RESPONSE_PTAGS = 7
201 AUTHENTICATION_RESPONSE_AUTH = 8
202 AUTHENTICATION_RESPONSE_SSO = 9
203 AUTHENTICATION_RESPONSE_PARAMS = 10
204 AUTHENTICATION_RESPONSE_SIG = 11
205 AUTHENTICATION_RESPONSE_SIZE = 12 ' Size of required array
206
207 WLS_RESPONSE_VER = 0
208 WLS_RESPONSE_STATUS = 1
209 WLS_RESPONSE_MSG = 2
210 WLS_RESPONSE_ISSUE = 3
211 WLS_RESPONSE_ID = 4
212 WLS_RESPONSE_URL = 5
213 WLS_RESPONSE_PRINCIPAL = 6
214 WLS_RESPONSE_PTAGS = 7
215 WLS_RESPONSE_AUTH = 8
216 WLS_RESPONSE_SSO = 9
217 WLS_RESPONSE_LIFE = 10
218 WLS_RESPONSE_PARAMS = 11
219 WLS_RESPONSE_KID = 12
220 WLS_RESPONSE_SIG = 13
221 WLS_RESPONSE_SIZE = 14 ' Size of required array
222
223 ' Set up 'status_codes' associative array
224 ' See Raven specification documentation for descriptions of each parameter
225
226 Set status_codes = CreateObject("Scripting.Dictionary")
227
228 status_codes.Add "200", "Successful authentication"
229 status_codes.Add "410", "The user cancelled the authentication request"
230 status_codes.Add "510", "No mutually acceptable authentication types available"
231 status_codes.Add "520", "Unsupported protocol version"
232 status_codes.Add "530", "General request parameter error"
233 status_codes.Add "540", "Interaction would be required"
234 status_codes.Add "560", "WAA not authorised"
235 status_codes.Add "570", "Authentication declined"
236
237 ' Set up array for holding authentication response parameters
238
239 ReDim m_authentication_response(AUTHENTICATION_RESPONSE_SIZE)
240
241 End Sub
242
243
244 Public Default Function Construct(ByVal args)
245
246 ' ****************************************
247 ' Constructor for Ucam_Webauth
248 '
249 ' We split the constructor that takes
250 ' parameters from the initializer as
251 ' VBScript doesn't allow initializers
252 ' to take parameters.
253 '
254 ' Arguments
255 ' ================
256 ' args: An associative array of arguments as name,value pairs, eg. {"auth_service" => "http://..", "hostname" => "www...", "log_file" => }.
257 '
258 ' To supply arguments:
259 ' Set args = CreateObject("Scripting.Dictionary")
260 ' args.Add auth_service", "https://..."
261 ' args.Add hostname", "www..."
262 ' args.Add log_file", "C:/logfile.txt"
263 ' args.Add key_dir", "C:/raven"
264 ' args.Add cookie_key", "Random string"
265 '
266 ' ****************************************
267
268
269 ' Set up default values
270
271 log_file = DEFAULT_LOG_FILE
272 response_timeout = 30
273 key_dir = DEFAULT_KEY_DIR
274 max_session_life = 2 * 60 * 60
275 timeout_message = DEFAULT_TIMEOUT_MESSAGE
276 hostname = DEFAULT_HOSTNAME
277 cookie_name = DEFAULT_COOKIE_NAME
278 cookie_path = "" ' *** SHOULD BE PATH RELATIVE PATH TO SCRIPT BY DEFAULT ***
279 cookie_domain = ""
280 auth_service = DEFAULT_AUTH_SERVICE
281 authrequest_skew = 5
282 authrequest_fail = False
283 authrequest_iact = False
284 use_authrequest_iact = False
285
286 ' If specific arguments are provided, then override default values
287
288 If (args.Exists("log_file")) Then log_file = args("log_file")
289 If (args.Exists("response_timeout")) Then response_timeout = Int(args("response_timeout"))
290 If (args.Exists("key_dir")) Then key_dir = args("key_dir")
291 If (args.Exists("max_session_life")) Then max_session_life = Int(args("max_session_life"))
292 If (args.Exists("timeout_message")) Then timeout_message = args("timeout_message")
293 If (args.Exists("hostname")) Then hostname = args("hostname")
294 If (args.Exists("cookie_key")) Then cookie_key = args("cookie_key")
295 If (args.Exists("cookie_name")) Then cookie_name = args("cookie_name")
296 If (args.Exists("cookie_path")) Then cookie_path = args("cookie_path")
297 If (args.Exists("cookie_domain")) Then cookie_domain = args("cookie_domain")
298 If (args.Exists("auth_service")) Then auth_service = args("auth_service")
299 If (args.Exists("authrequest_desc")) Then authrequest_desc = args("authrequest_desc")
300 If (args.Exists("authrequest_params")) Then authrequest_params = args("authrequest_params")
301 If (args.Exists("authrequest_skew")) Then authrequest_skew = Int(args("authrequest_skew"))
302 If (args.Exists("authrequest_fail")) Then authrequest_fail = CBool(args("authrequest_fail"))
303 If (args.Exists("authrequest_iact")) Then
304 authrequest_iact = CBool(args("authrequest_iact"))
305 use_authrequest_iact = True
306 End If
307
308 End Function
309
310 ' ****************************************
311 '
312 ' ----------------------------------------
313 ' -- PRIMARY FUNCTION - Authenticate() ---
314 ' ----------------------------------------
315 '
316 ' ****************************************
317
318 Public Function Authenticate()
319
320 ' ****************************************
321 ' Authenticate()
322 '
323 ' This function is called three times in
324 ' order to complete the authentication process.
325 '
326 ' STEP 1: A fresh user requests an Authenticated
327 ' resource with the URL 'X'. 'Authenticate'
328 ' redirects the user's browser to a Web Login
329 ' Service (WLS) via 'SendAuthenticationRequest()'.
330 '
331 ' STEP 2: The WLS processes the user's user id
332 ' and password and then redirects the user's
333 ' browser back to the client's Web Application
334 ' Agent (WAA) with a specific set of parameters
335 ' in a 'WLS-Response' GET variable.
336 ' 'Authenticate' is called again and validates
337 ' these values using 'ProcessAuthenticationRequest()'.
338 ' If they all check out, it creates a
339 ' local authentication cookie on the user's
340 ' browser and redirects the user's browser
341 ' back to the original URL 'X'.
342 '
343 ' STEP 3: We return to our original page, except
344 ' this time we have a local authentication cookie
345 ' set. 'Authenticate' calls
346 ' 'ProcessAuthenticationCookie' which checks
347 ' this cookie is valid and if the status code
348 ' of the cookie is '200', the user was successfully
349 ' verified at the WLS -
350 ' 'AUTHENTICATE_COMPLETE_AUTHENTICATED' is then
351 ' returned to the user.
352 '
353 ' ****************************************
354
355
356 ' First off, perform a sanity check on basic variables
357
358 If (CheckSetup() = False) Then
359 ResetState()
360 Authenticate = AUTHENTICATE_COMPLETE_ERROR
361 Exit Function
362 End If
363
364 ' Now work out where we are in the authentication process:
365 '
366 ' STATE_NEW_AUTHENTICATION
367 ' A completely fresh authentication attempt.
368 '
369 ' STATE_WLS_RESPONSE_RECEIVED
370 ' The user has already been redirected to the WLS, which has
371 ' then redirected them back here. They may have successfully
372 ' authenticated or simply pressed cancel. As long as it's a
373 ' valid, signed WLS-Response - regardless of whether it's a
374 ' successful authentication or not - we set a new
375 ' authentication cookie which indicates to the code
376 ' that the user has been processed by the WLS.
377 '
378 ' STATE_WAA_AUTHENTICATIONCOOKIE_SET
379 ' We have a valid authentication cookie and just need to
380 ' check what the status of that is. The user may have
381 ' successfully authenticated, the authentication token
382 ' may have expired, or the user may have skipped out
383 ' the authentication process. In the event of expiration,
384 ' 'ProcessAuthenticationCookie' redirects the user back
385 ' to the WLS.
386 '
387 ' STATE_ERROR
388 ' There was an error somewhere along the way.
389 ' If this happens, it's not a simple case of
390 ' an invalid authentication but something more serious
391 ' that falls outside the expected process flow.
392
393 state = GetCurrentState()
394
395 Select Case state
396
397 Case STATE_NEW_AUTHENTICATION
398
399 Authenticate = SendAuthenticationRequest("")
400 Exit Function
401
402 Case STATE_WLS_RESPONSE_RECEIVED
403
404 Authenticate = ProcessAuthenticationResponse()
405 Exit Function
406
407 Case STATE_WAA_AUTHENTICATIONCOOKIE_SET
408
409 Authenticate = ProcessAuthenticationCookie()
410 Exit Function
411
412 Case Else ' This includes the case STATE_ERROR
413
414 write_log("STATE_ERROR received: " & status() & ": " & msg())
415 ResetState()
416 Authenticate = AUTHENTICATE_COMPLETE_ERROR
417 Exit Function
418
419 End Select
420
421 End Function
422
423
424 ' ****************************************
425 '
426 ' ----------------------------------------
427 ' ---- SECONDARY FUNCTIONS TRIGGERED -----
428 ' ------- FROM 'Authenticate()' ----------
429 ' ----------------------------------------
430 '
431 ' 1. SendAuthenticationRequest()
432 ' 2. ProcessAuthenticationResponse()
433 ' 3. ProcessAuthenticationCookie()
434 '
435 ' ****************************************
436
437 Public Function SendAuthenticationRequest(msg)
438
439 ' ****************************************
440 ' 1. SendAuthenticationRequest()
441 '
442 ' Send an authentication request from the WAA to the WLS.
443 '
444 ' ****************************************
445
446
447 ' Write a long line of astericks to make log easier to read.
448
449 write_log("****************************************************************")
450 write_log("SendAuthenticationRequest: Starting...")
451
452 ' If the hostname from the request (Host: header) does not match the
453 ' server's preferred name for itself (which should be what's configured
454 ' as hostname), cookies are likely to break "randomly" (or more
455 ' accurately, the cookie may not be sent by the browser since it"s for
456 ' a different hostname) as a result of following links that use the
457 ' preferred name, or server-level redirects e.g. to fix "directory"
458 ' URLs lacking the trailing "/". Attempt to avoid that by redirecting
459 ' to an equivalent URL using the configured hostname.
460
461 http_host = Request.ServerVariables("HTTP_HOST")
462
463 If ((http_host <> "") And (LCase(hostname) <> LCase(http_host))) Then
464 write_log("SendAuthenticationRequest: Redirect to tidy up hostname mismatch")
465 Redirect(url())
466 SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE
467 Exit Function
468 End If
469
470 ' We set the authentication cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'
471 ' to keep a track of state.
472
473 write_log("SendAuthenticationRequest: Setting pre-session cookie")
474
475 authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS
476
477 ' Build the full 'Authentication Request' URL that will
478 ' redirect the user to the WLS.
479 '
480 ' Full information about each of these parameters can be
481 ' found in Raven v3 documentation.
482
483 wls_redirect = auth_service
484 wls_redirect = wls_redirect & "?ver=" & PROTOCOL_VERSION
485 wls_redirect = wls_redirect & "&url=" & Server.URLEncode(url())
486
487 If (authrequest_desc <> "") Then wls_redirect = wls_redirect & "&desc=" & Server.URLEncode(authrequest_desc)
488 If (authrequest_aauth <> "") Then wls_redirect = wls_redirect & "&aauth=" & Server.URLEncode(authrequest_aauth)
489
490 If use_authrequest_iact Then
491 If authrequest_iact Then
492 wls_redirect = wls_redirect & "&iact=yes"
493 Else
494 wls_redirect = wls_redirect & "&iact=no"
495 End If
496 End If
497
498 If (msg <> "") Then wls_redirect = wls_redirect & "&msg=" & Server.URLEncode(msg)
499 if (authrequest_params <> "") Then wls_redirect = wls_redirect & "&params=" & Server.URLEncode(authrequest_params)
500
501 wls_redirect = wls_redirect & "&date=" & Server.URLEncode(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))
502
503 ' If (clock_skew <> 0) THen wls_redirect = wls_redirect & "&skew=" & Server.URLEncode(Convert.ToString(clock_skew)) ' 'skew' parameter deprecated in v3
504
505 If (authrequest_fail = True) Then wls_redirect = wls_redirect & "&fail=yes"
506
507 write_log("SendAuthenticationRequest: Redirecting to WLS with URL=" & wls_redirect)
508
509 Redirect(wls_redirect)
510
511 SendAuthenticationRequest = AUTHENTICATE_INCOMPLETE
512
513 End Function
514
515
516 Public Function ProcessAuthenticationResponse()
517
518 ' ****************************************
519 ' 2. ProcessAuthenticationResponse()
520 '
521 ' Process the authentication response received from the WLS.
522 '
523 ' ****************************************
524
525 write_log("ProcessAuthenticationResponse: Starting...")
526 write_log("ProcessAuthenticationResponse: WLS response=" & authentication_response_string)
527
528 wls_response = Split(authentication_response_string, "!")
529
530 set_status "200", ""
531
532
533 If (UBound(wls_response) < 1) Then
534
535 ' Response is too short to have been signed or have a status.
536
537 set_status "620", "WLS response has too few parameters"
538 ResetState()
539 ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
540 Exit Function
541
542 End If
543
544 ' We only check signature if status = "200".
545
546 If (wls_response(WLS_RESPONSE_STATUS) = "200") Then
547
548 ' Get signature and key_id off the end of the wls_response array
549 ' and collapse the remaining array into a string.
550 ' It's this string that will have been signed by the WLS
551 ' and whose signature we then verify using the WLS
552 ' public certificate on our server.
553
554 signature = wls_response(UBound(wls_response))
555 key_id = wls_response(UBound(wls_response) - 1)
556 Redim Preserve wls_response (UBound(wls_response) - 2)
557 wls_response_signedstring = Join(wls_response, "!")
558
559 ' write_log("Signature=" & signature)
560 ' write_log("Signed string=" & wls_response_signedstring)
561
562 If (check_signature(wls_response_signedstring, signature, key_id) = False) Then
563
564 ' Signature is not correct for the wls_response
565
566 set_status "606", "Invalid WLS wls_response signature"
567 ResetState()
568 ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
569 Exit Function
570
571 End If
572
573 write_log("ProcessAuthenticationResponse: Signature is correct.")
574
575 End If
576
577
578 ' Expand 'wls_response' array to maximum size just in case it's too small
579 ' and subsequent calls to wls_response(WLS_RESPONSE_XXX) raise 'IndexOutOfRangeException'
580
581 Redim Preserve wls_response (WLS_RESPONSE_SIZE)
582
583
584 ' Remove the query part of our current url
585 ' and the url provided by the WLS and check
586 ' they match.
587
588 Set rgx = New RegExp
589 rgx.Global = True
590 rgx.IgnoreCase = False
591 rgx.Pattern = "\?.*$"
592 this_url = rgx.Replace(url(), "")
593
594 If (wls_response(WLS_RESPONSE_URL) = "") Then
595 set_status "607", "Null URL in response ticket doesn't match this URL: " & this_url
596 ResetState()
597 ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
598 Exit Function
599 End If
600
601 response_url = wls_response(WLS_RESPONSE_URL)
602 response_url = rgx.Replace(response_url, "")
603
604 If (this_url <> response_url) Then
605 set_status "607", "URL in response ticket doesn't match this URL: " & response_url & " != " & this_url
606 ResetState()
607 ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
608 Exit Function
609 End If
610
611
612 ' Go through the possible different response codes
613 ' from the WLS and set our status accordingly.
614
615 If (wls_response(WLS_RESPONSE_STATUS) = "410") Then
616
617 set_status "410", status_codes("410")
618
619 ElseIf ((wls_response(WLS_RESPONSE_VER) <> PROTOCOL_VERSION) And (wls_response(WLS_RESPONSE_STATUS) <> "520")) Then
620
621 set_status "608", "Wrong protocol version in authentication service reply"
622
623 ElseIf (wls_response(WLS_RESPONSE_STATUS) <> "200") Then
624
625 ' If status code != "200" then we have an error of some description.
626 ' First off, check that it's an error for which we have an error code / msg.
627
628 If (status_codes.Exists(wls_response(WLS_RESPONSE_STATUS)) = False) Then
629 write_log("No status code for error " & wls_response(WLS_RESPONSE_STATUS))
630 ProcessAuthenticationResponse = AUTHENTICATE_COMPLETE_ERROR
631 Exit Function
632 End If
633
634 ' If we have an error code / msg for the error, then set status to that code / msg.
635
636 set_status wls_response(WLS_RESPONSE_STATUS), status_codes(wls_response(WLS_RESPONSE_STATUS))
637
638 ' If the authentication response provided a response message then set status msg to that.
639
640 If (wls_response(WLS_RESPONSE_MSG) <> "") Then m_authentication_response(AUTHENTICATION_RESPONSE_MSG) = wls_response(WLS_RESPONSE_MSG)
641
642 Else
643
644 ' Status code must be '200' to get here.
645 ' But we need to check the issue time of the
646 ' authentication response is close to our current time.
647
648 timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow())
649 timestamp_issue = iso2time(wls_response(WLS_RESPONSE_ISSUE))
650
651 If (timestamp_issue = 0) Then
652
653 set_status "609", "Unable to read issue time in authentication service reply"
654
655 ElseIf (timestamp_issue > (timestamp_now + authrequest_skew + 1)) Then
656
657 set_status "610", "Authentication service reply apparently issued in the future: " & wls_response(WLS_RESPONSE_ISSUE)
658
659 ElseIf ((timestamp_now - authrequest_skew - 1) > (timestamp_issue + response_timeout)) Then
660
661 set_status "611", "Stale authentication service reply issue at " & wls_response(WLS_RESPONSE_ISSUE)
662
663 End If
664
665 End If
666
667
668 ' Calculate session expiry time
669
670 expiry = max_session_life
671
672 If (wls_response(WLS_RESPONSE_LIFE) <> "") Then
673
674 wls_token_life = Int(wls_response(WLS_RESPONSE_LIFE))
675
676 If ((wls_token_life > 0) And (wls_token_life < expiry)) Then expiry = wls_token_life
677
678 End If
679
680
681 ' Populate authentication response with information collected so far
682
683 m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow())))
684 m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE) = CStr(time2iso(DateDiff("s", "01/01/1970 00:00:00", UTCNow()) + expiry))
685 m_authentication_response(AUTHENTICATION_RESPONSE_ID) = wls_response(WLS_RESPONSE_ID)
686 m_authentication_response(AUTHENTICATION_RESPONSE_PRINCIPAL) = wls_response(WLS_RESPONSE_PRINCIPAL)
687 m_authentication_response(AUTHENTICATION_RESPONSE_AUTH) = wls_response(WLS_RESPONSE_AUTH)
688 m_authentication_response(AUTHENTICATION_RESPONSE_SSO) = wls_response(WLS_RESPONSE_SSO)
689 m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) = wls_response(WLS_RESPONSE_PARAMS)
690 m_authentication_response(AUTHENTICATION_RESPONSE_PTAGS) = wls_response(WLS_RESPONSE_PTAGS)
691 m_authentication_response(AUTHENTICATION_RESPONSE_VER) = AUTHENTICATION_RESPONSE_VERSION
692
693
694 ' Collapse parameters of authentication_response into a string
695 ' then create HMAC hash code of this string and append this code
696 ' onto the string to form the value of our authentication cookie.
697
698 authentication_cookie_value = ""
699
700 For i = 0 To (AUTHENTICATION_RESPONSE_PARAMS - 1)
701 If (m_authentication_response(i) <> "") Then authentication_cookie_value = authentication_cookie_value & m_authentication_response(i)
702 authentication_cookie_value = authentication_cookie_value & "!"
703 Next
704
705 If (m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS) <> "") Then
706 authentication_cookie_value = authentication_cookie_value & m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS)
707 End If
708
709 hmac_signature = hmac_sha1(cookie_key, authentication_cookie_value)
710 authentication_cookie_value = authentication_cookie_value & "!" & hmac_signature
711
712 write_log("ProcessAuthenticationResponse: Setting cookie=" & authentication_cookie_value)
713
714 authentication_cookie = authentication_cookie_value
715
716 ' Now the authentication cookie's in place, we
717 ' can redirect user back to the page they started from.
718 ' As long as our authentication cookie doesn't expire,
719 ' we then only need to call 'ProcessAuthenticationCookie'
720 ' to check the cookie is valid - ie. we don't need to
721 ' keep returning to the WLS for every single
722 ' authentication request.
723
724 write_log("ProcessAuthenticationResponse: Session cookie established, redirecting...")
725
726 Redirect(wls_response(WLS_RESPONSE_URL))
727
728 ProcessAuthenticationResponse = AUTHENTICATE_INCOMPLETE
729
730 End Function
731
732
733 Public Function ProcessAuthenticationCookie()
734
735 ' ****************************************
736 ' 3. ProcessAuthenticationCookie()
737 '
738 ' Check the signature of the cookie is valid and if so,
739 ' process/interpret the values of the authentication cookie.
740 '
741 ' ****************************************
742
743 write_log("ProcessAuthenticationCookie: Starting...")
744
745 ' Do some quick checks to ensure the authentication
746 ' cookie looks like a valid response from WLS.
747
748 If (authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) Then
749 write_log("ProcessAuthenticationCookie: Processing authentication cookie when it's still set to REDIRECT_WLS.")
750 set_status "612", "Processing authentication cookie when its still set to REDIRECT_WLS"
751 ResetState()
752 ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
753 Exit Function
754 End If
755
756 If (CheckValidAuthenticationCookie(authentication_cookie) = False) Then
757 write_log("ProcessAuthenticationCookie: Authentication cookie looks invalid.")
758 set_status "613", "Authentication cookie invalid"
759 ResetState()
760 ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
761 Exit Function
762 End If
763
764 write_log("ProcessAuthenticationCookie: Interpreting authentication cookie=" & authentication_cookie)
765
766 ' Split up authentication cookie into _authentication_response array
767 ' of response parameters using '!' as the delimiter.
768
769 m_authentication_response = Split(url_decode(authentication_cookie), "!")
770
771 ' ****************************************
772 ' Check authentication cookie has been correctly signed
773 ' ****************************************
774
775 ' Get signature from last element of authentication response array.
776
777 signature = m_authentication_response(UBound(m_authentication_response))
778
779 ' Copy m_authentication_response into a separate array
780 ' then remove the last element of that array
781 ' and bundle together as a string.
782
783 Dim values_for_verify()
784 ReDim values_for_verify(UBound(m_authentication_response) - 1)
785
786 For i = 0 To (UBound(values_for_verify))
787 values_for_verify(i) = m_authentication_response(i)
788 Next
789
790 values_for_verify_string = Join(values_for_verify, "!")
791
792 ' Check whether the hash of this shorter string matches the signature
793 ' we generated in the previous stage of the authentication process.
794
795 If (False = hmac_sha1_verify(cookie_key, values_for_verify_string, signature)) Then
796 write_log("ProcessAuthenticationCookie: AUTHENTICATION FAILED, session cookie signature invalid")
797 set_status "614", "Session cookie signature invalid"
798 ResetState()
799 ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_ERROR
800 Exit Function
801 End If
802
803 write_log("ProcessAuthenticationCookie: Existing authentication cookie verified")
804
805 ' ****************************************
806 ' Check authentication cookie hasn't expired
807 ' ****************************************
808
809 timestamp_issue = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE))
810 timestamp_expire = iso2time(m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE))
811 timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow()) ' Get current time as Unix timestamp
812
813 If ((timestamp_issue > timestamp_now) Or (timestamp_now >= timestamp_expire)) Then
814
815 ' Session has expired so send new authentication request to WLS.
816
817 write_log("ProcessAuthenticationCookie: Local session cookie expired. Issue/now/expire: " & Cstr(timestamp_issue) & "/" & CStr(timestamp_now) & "/" & CStr(timestamp_expire))
818
819 ' this.ResetState(); This won't work as we need to set the cookie during 'SendAuthenticationRequest'
820
821 ProcessAuthenticationCookie = SendAuthenticationRequest(timeout_message)
822 Exit Function
823
824 End If
825
826 ' ****************************************
827 ' Authentication process is COMPLETE
828 ' though the user may not have been
829 ' successfully authenticated.
830 ' ****************************************
831
832 If (m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) <> "200") Then
833 write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE but not successfully authenticated.")
834 write_log("****************************************************************")
835 ResetState()
836 ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_NOT_AUTHENTICATED
837 Exit Function
838 End If
839
840 write_log("ProcessAuthenticationCookie: AUTHENTICATION COMPLETE and user authenticated.")
841 write_log("****************************************************************")
842 ProcessAuthenticationCookie = AUTHENTICATE_COMPLETE_AUTHENTICATED
843
844 End Function
845
846
847 ' ****************************************
848 '
849 ' ----------------------------------------
850 ' --- AUTHENTICATE()-SPECIFIC FUNCTIONS --
851 ' ----------------------------------------
852 '
853 ' ****************************************
854
855 Public Function CheckSetup()
856
857 ' ****************************************
858 ' CheckSetup()
859 '
860 ' Check the essential variables are in place.
861 '
862 ' ****************************************
863
864 request_method = Request.ServerVariables("REQUEST_METHOD")
865
866 ' Check 'cookie_key' is defined, the key we will be using to create hash of cookie value.
867
868 If (cookie_key = "") Then
869 set_status "601", "No key defined for session cookie"
870 CheckSetup = False
871 Exit Function
872 End If
873
874 ' Log a warning if being used to authenticate POST requests.
875
876 If (request_method = "POST") Then
877 write_log("Ucam_Webauth agent invoked for POST request, which it doesn't really support")
878 End If
879
880 ' Check that the hostname is set explicitly (since we cannot trust
881 ' the Host: header); if it returns false (i.e. not set).
882
883 If (hostname = "") Then
884 write_log("hostname not set in Ucam_Webauth object, but is mandatory")
885 set_status "602", "Ucam_Webauth configuration error - mandatory hostname not defined"
886 CheckSetup = False
887 Exit Function
888 End If
889
890 ' If we have got this far without problems, then everything is good.
891
892 CheckSetup = True
893
894 End Function
895
896
897 Public Sub ResetState()
898
899 ' Reset state as if a new user has just loaded a
900 ' fresh browser window. Unfortunately we can't reset
901 ' any cookies that the remote WLS may have set.
902 ' This will not completely log you out of Raven as
903 ' the Raven WLS also stores a session cookie that
904 ' you cannot delete remotely. So accessing Raven
905 ' after logout may elicit a "You are already logged
906 ' in" from the Raven WLS.
907 '
908 ' As both local and WLS cookies are session cookies, the safest
909 ' thing to do is to quit the browser to remove both session cookies.
910 ' Note that some browsers with 'Restore session' functionality,
911 ' eg. Firefox, may not remove session cookies properly.
912 ' <returns>'true' whatever happens.</returns>
913
914 ' By setting the expiry to '1', we ensure the
915 ' authentication cookie is removed straightaway.
916
917 setcookie full_cookie_name(), "", 1, cookie_path, cookie_domain, using_https(), False
918
919 End Sub
920
921
922 Public Function GetCurrentState()
923
924 ' If no authentication cookie and no WLS-response, then we're
925 ' starting fresh so return 'STAGE_AUTHENTICATION_NEW'
926
927 If ((authentication_cookie = "") And (authentication_response_string = "")) Then
928 GetCurrentState = STATE_NEW_AUTHENTICATION
929 Exit Function
930 End If
931
932 ' If no cookie and a non-empty WLS-Response, then either (i) we have
933 ' returned from the WLS without the previous cookie that we set staying intact.
934 ' So there must have been a problem setting that earlier cookie.
935 ' Or (ii) a 'WLS-Response' GET variable was supplied to our initial
936 ' page to begin with, which is going to cause confusion.
937 ' This may be due to reloading the post-WLS page after an error.
938
939 If ((authentication_cookie = "") And (authentication_response_string <> "")) Then
940 set_status "610", "Either browser is not accepting session cookie or you supplied WLS-Response variable to initial page."
941
942 ' Don't do any redirecting back to the original
943 ' page as it might catch us in an infinite loop.
944 ' Instead show a page to the user with the
945 ' option to click on a particular link or
946 ' put something in code to go to a specific
947 ' safe page.
948
949 GetCurrentState = STATE_ERROR
950 Exit Function
951 End If
952
953 ' If authentication cookie = 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'
954 ' and WLS-Response is non-empty, then we've been redirected here
955 ' by the WLS (though you could simulate this redirection manually by
956 ' interrupting the WLS process and manually pasting in a URL with a
957 ' WLS-Response GET variable.)
958
959 If ((authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) And (authentication_response_string <> "")) Then
960 GetCurrentState = STATE_WLS_RESPONSE_RECEIVED
961 Exit Function
962 End If
963
964
965 ' If authentication cookie = 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'
966 ' and WLS-Response is empty, then the WLS is not working correctly.
967 ' So we trigger another authentication request.
968 ' Ideally we'd 'ResetState()' but this seems to create
969 ' confusion when we subsequently set the authentication
970 ' cookie to 'AUTHENTICATIONCOOKIE_REDIRECT_WLS' again -
971 ' so we leave it as is, with authentication cookie still
972 ' with this value.
973
974 If ((authentication_cookie = AUTHENTICATIONCOOKIE_REDIRECT_WLS) And (authentication_response_string = "")) Then
975 GetCurrentState = STATE_NEW_AUTHENTICATION
976 Exit Function
977 End If
978
979
980 ' If authentication cookie != "" and != 'AUTHENTICATIONCOOKIE_REDIRECT_WLS'
981 ' then there's a possibility we have a legitimate authentication cookie.
982 ' Run 'CheckValidAuthenticationCookie' to check it looks in the
983 ' right format.
984
985 If ((authentication_cookie <> "") And (authentication_cookie <> AUTHENTICATIONCOOKIE_REDIRECT_WLS)) Then
986
987 If (CheckValidAuthenticationCookie(authentication_cookie)) Then
988 GetCurrentState = STATE_WAA_AUTHENTICATIONCOOKIE_SET
989 Exit Function
990 End If
991
992 set_status "604", "Authentication cookie invalid"
993 GetCurrentState = STATE_ERROR
994 Exit Function
995 End If
996
997 ' If none of the conditions have been satisfied so far,
998 ' there must have been an error along the way.
999
1000 If (status() <> "200") Then set_status "605", "Miscellaneous error while getting current state."
1001
1002 GetCurrentState = STATE_ERROR
1003
1004 End Function
1005
1006
1007 Public Function CheckValidAuthenticationCookie(ByVal vAuthenticationCookie)
1008
1009 ' A quick check to ensure the cookie value looks the
1010 ' right format to be an authentication cookie.
1011 ' ie. it has the correct number of values, delimited by '!'.
1012
1013 If (vAuthenticationCookie = "") Then
1014 CheckValidAuthenticationCookie = False
1015 Exit Function
1016 End If
1017
1018 ' Split up authentication cookie into Array
1019 ' using '!' as the delimiter.
1020
1021 authentication_response = Split(url_decode(vAuthenticationCookie), "!")
1022
1023 If (UBound(authentication_response) <> (AUTHENTICATION_RESPONSE_SIZE - 1)) Then
1024 CheckValidAuthenticationCookie = False
1025 Exit Function
1026 End If
1027
1028 CheckValidAuthenticationCookie = True
1029
1030 End Function
1031
1032
1033 Public Sub Redirect(url)
1034
1035 ' Redirect user's browser making
1036 ' sure to set redirected flag accordingly
1037
1038 Response.Redirect(url)
1039 redirected = True
1040
1041 End Sub
1042
1043
1044 ' ****************************************
1045 '
1046 ' ----------------------------------------
1047 ' ------- MISCELLANEOUS FUNCTIONS --------
1048 ' ----------------------------------------
1049 '
1050 ' ****************************************
1051
1052 Public Sub write_log(ByVal message )
1053
1054 ' Write a message to the log file with a timestamp prefix on every line
1055 '
1056 ' Note relative paths, eg. 'logfile.txt', will be relative
1057 ' to the IIS server directories where permissions to create
1058 ' new files may not be available. So provide an absolute
1059 ' path in the initial arguments to Ucam_Webauth ("log_file" => Path)
1060
1061 Set objFSO = CreateObject("Scripting.FileSystemObject")
1062
1063 If (objFSO.FileExists(log_file) = False) Then
1064 Set objFile = objFSO.CreateTextFile(log_file)
1065 Else
1066 Set objFile = objFSO.OpenTextFile(log_file, 8)
1067 End If
1068
1069 date_prefix = "[" & lpad(Year(Date()), 4) & "-" & lpad(Month(Date()),2) & "-" & lpad(Day(Date()),2) & " " & _
1070 lpad(Hour(Time()), 2) & ":" & lpad(Minute(Time()),2) & ":" & lpad(Second(Time()),2) & "] "
1071
1072 objFile.WriteLine(date_prefix & message)
1073 objFile.Close
1074
1075 End Sub
1076
1077
1078 Public Sub set_status(response_status, response_msg)
1079
1080 ' Quick function to set 'status' and 'msg' in a single line.
1081 ' Makes the code look a little neater.
1082
1083 m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) = response_status
1084 m_authentication_response(AUTHENTICATION_RESPONSE_MSG) = response_msg
1085
1086 End Sub
1087
1088
1089 Public Function using_https()
1090
1091 ' Determines whether we're using https or Not
1092
1093 using_https = False
1094
1095 If (Request.ServerVariables("HTTPS") <> "") Then
1096 servertype = Request.ServerVariables("HTTPS")
1097 If (servertype = "on") Then using_https = True
1098 End If
1099
1100 End Function
1101
1102
1103 Public Function url()
1104
1105 ' Get the url to redirect the browser to after a successful authentication attempt.
1106 '
1107 ' This is typically identical to the url that first calls this script.
1108 ' But due to the possibility of faking 'HOST' variables in a server response,
1109 ' the 'hostname' must be supplied as a setup / default parameter in the code.
1110 '
1111 ' In versions 2 and 3, we must include the query component (GET parameters) of the
1112 ' original calling url if they exist.
1113
1114
1115 ' Strip out port number from hostname
1116
1117 Set rgx = New RegExp
1118 rgx.Global = True
1119 rgx.IgnoreCase = False
1120 rgx.Pattern = ":[0-9]+$"
1121
1122 basichostname = rgx.Replace(hostname, "")
1123 port = Request.ServerVariables("SERVER_PORT")
1124 protocol = "http://"
1125
1126 If (using_https()) Then
1127 protocol = "https://"
1128 If (port = "443") Then port = ""
1129 Else
1130 If (port = "80") Then port = ""
1131 End If
1132
1133 finalurl = protocol & basichostname
1134 If (port <> "") Then finalurl = finalurl & ":" & port
1135
1136 ' Remove any possible WLS-Response variables
1137 ' from query part of URL to avoid
1138 ' confusion and just in case we've
1139 ' called 'SendAuthenticationRequest'
1140 ' after WLS returned incorrect WLS-Response
1141 ' 'GET' - which could lead to multiple
1142 ' 'WLS-Response=&WLS-Response=...'
1143 ' coming back from WLS server.
1144
1145 url_query = Request.ServerVariables("REQUEST_URI")
1146 rgx.Pattern = "&WLS-Response=[^&]*"
1147 url_query = rgx.Replace(url_query, "")
1148 rgx.Pattern = "\?WLS-Response=[^&]*"
1149 url_query = rgx.Replace(url_query, "?")
1150 rgx.Pattern = "\?&"
1151 url_query = rgx.Replace(url_query, "?")
1152 rgx.Pattern = "\?$"
1153 url_query = rgx.Replace(url_query, "")
1154
1155 url = finalurl & url_query
1156
1157 End Function
1158
1159
1160 Public Function full_cookie_name()
1161
1162 ' Get name of cookie, typically 'Ucam-WebAuth-Session'
1163 ' If 'https', then distinguish the name of the cookie by suffixing '-S'
1164
1165 full_cookie_name = cookie_name
1166
1167 If (using_https()) Then full_cookie_name = cookie_name & "-S"
1168
1169 End Function
1170
1171
1172 Public Sub setcookie(ByVal name , ByVal value , ByVal expire , ByVal path , ByVal domain , ByVal secure , ByVal httponly )
1173
1174 ' Sets cookie.
1175 '
1176 ' name: Name of cookie.
1177 ' value: Value of cookie.
1178 ' expire: Expiry of cookie. Don't set it to make it a session cookie.
1179 ' path: Path of cookie.
1180 ' domain: Domain.
1181 ' secure: Only allow on secure domains.
1182 ' httponly: Only allow on http non-secure domains.
1183
1184 If (expire <> 0) Then
1185 If (expire = 1) Then
1186 expire_datetime = DateAdd("s", 1, UTCNow())
1187 Else
1188 timestamp_now = DateDiff("s", "01/01/1970 00:00:00", UTCNow())
1189
1190 If (expire < timestamp_now) Then
1191 write_log("setcookie: Setting expire to past so don't bother setting cookie.")
1192 Exit Sub
1193 End If
1194
1195 expire_datetime = DateAdd("s", expire, "01/01/1970 00:00:00")
1196 End If
1197 End If
1198
1199 Response.Cookies(name) = value
1200
1201 ' Whats correct value for Expires field when expire = 0?
1202
1203 If (expire_datetime <> 0) Then
1204 Response.Cookies(name).Expires = expire_datetime
1205 End If
1206
1207 ' Problems setting 'httponly' in VBScript as
1208 ' VBScript encodes everything to do with cookies.
1209 ' So if 'httponly' is required, a different
1210 ' way of setting cookies by directly setting
1211 ' the header with 'Set-Cookie' will be necessary.
1212
1213 ' If (httponly) Then
1214 ' Response.Cookies(name).Path = path & "; HttpOnly"
1215 ' Else
1216 ' Response.Cookies(name).Path = path
1217 ' End If
1218
1219 Response.Cookies(name).Path = path
1220
1221 If (domain <> "") Then Response.Cookies(name).Domain = domain
1222
1223 Response.Cookies(name).Secure = secure
1224
1225 ' Response.Cookies.Add(cookie)
1226 End Sub
1227
1228
1229 Public Function time2iso(t)
1230
1231 ' Convert a Unix timestamp into the format required by Raven.
1232 ' Format based on RFC 3339, but see Raven documentation for
1233 ' a full description.
1234
1235 unUDate = DateAdd("s", t, "01/01/1970 00:00:00")
1236
1237 time2iso = (lpad(Year(unUDate), 4) & lpad(Month(unUDate),2) & lpad(Day(unUDate),2) & "T" &_
1238 lpad(Hour(unUDate), 2) & lpad(Minute(unUDate),2) & lpad(Second(unUDate),2) & "Z")
1239
1240 End Function
1241
1242
1243 Public Function iso2time(t)
1244
1245 ' Convert a time in Raven format into a Unix timestamp
1246 ' ie. the number of seconds since epoch.
1247
1248 iso2time = 0
1249
1250 if (Len(t) <> 16) Then Exit Function
1251
1252 On Error Resume Next
1253
1254 sYear = Mid(t,1,4)
1255 sMonth = Mid(t, 5, 2)
1256 sDay = Mid(t, 7, 2)
1257 sHour = Mid(t, 10, 2)
1258 sMinute = Mid(t, 12, 2)
1259 sSecond = Mid(t, 14, 2)
1260
1261 var_origin = CDate("01/01/1970 00:00:00")
1262 var_date = CDate(sDay & "/" & sMonth & "/" & sYear & " " & sHour & ":" & sMinute & ":" & sSecond)
1263
1264 iso2time = DateDiff("s", var_origin, var_date)
1265
1266 On Error GoTo 0
1267
1268 End Function
1269
1270
1271 Public Function load_key(ByVal key_id )
1272
1273 ' Returns content of a certificate key as a string
1274
1275 key_filename = key_dir & "/" & key_id & ".crt"
1276
1277 Set fso = CreateObject("Scripting.FileSystemObject")
1278 Set file = fso.getFile(key_filename)
1279 If isNull(file) Then
1280 Exit Function
1281 End If
1282
1283 Set ts = file.OpenAsTextStream()
1284 s = Space(file.size)
1285 a = Split(s," ")
1286
1287 i = 0
1288 ' Do not replace the following block by readBinary = by ts.readAll(),
1289 ' it would result in broken output, because that method is not intended for binary data
1290 While Not ts.atEndOfStream
1291 a(i) = ts.read(1)
1292 i = i + 1
1293 Wend
1294 ts.close
1295
1296 load_key = Join(a,"")
1297
1298 End Function
1299
1300
1301 Public Function check_signature(ByVal data , ByVal sig , ByVal key_id )
1302
1303 ' Checks the 'signature' provided by the WLS when it signed 'data'
1304 ' is a valid signature for the data. This ensures the data has not been
1305 ' tampered with.
1306
1307 key_str = load_key(key_id)
1308
1309 ' We make a call to a some Javascript libraries to do the verification
1310
1311 check_signature = JavascriptCertVerify(data, wls_decode(sig), key_str)
1312
1313 End Function
1314
1315
1316 Public Function wls_encode(plainTextBytes)
1317
1318 ' Encode a byte array of data in a way that can be
1319 ' easily sent to the WLS
1320
1321 Set rgx = New RegExp
1322 rgx.Global = True
1323 rgx.IgnoreCase = False
1324 rgx.Pattern = "\+": plainTextBytes = rgx.Replace(plainTextBytes, "_")
1325 rgx.Pattern = "/": plainTextBytes = rgx.Replace(plainTextBytes, ".")
1326 rgx.Pattern = "=": plainTextBytes = rgx.Replace(plainTextBytes, "_")
1327
1328 wls_encode = plainTextBytes
1329
1330 End Function
1331
1332
1333 Public Function wls_decode(ByVal sig )
1334
1335 ' Decode a string of data received from the WLS
1336 ' into a string with data as hex doubledigits (as used by javascript security library)
1337
1338 Set rgx = New RegExp
1339 rgx.Global = True
1340 rgx.IgnoreCase = False
1341 rgx.Pattern = "-": sig = rgx.Replace(sig, "+")
1342 rgx.Pattern = "\.": sig = rgx.Replace(sig, "/")
1343 rgx.Pattern = "_": sig = rgx.Replace(sig, "=")
1344
1345 sig_base64 = base64_decode(sig)
1346
1347 sig_hex = ""
1348 For i = 1 To LenB(sig_base64)
1349 sig_hex = sig_hex & Right(String(2, "0") & LCase(Hex(AscB(MidB(sig_base64, i, 1)))), 2)
1350 Next
1351
1352 wls_decode = sig_hex
1353
1354 End Function
1355
1356
1357 Public Function hmac_sha1(ByVal key , ByVal data )
1358
1359 ' Create HMACSHA1 hash value for 'data' using public 'key'.
1360 '
1361 ' key: raw content of a certificate file as string.
1362 ' data: data to create hash value from as string.
1363
1364 hmac_sha1 = wls_encode(b64_hmac_sha1(key, data))
1365
1366 End Function
1367
1368
1369 Public Function hmac_sha1_verify(ByVal key , ByVal data , ByVal sig )
1370
1371 ' Verify the 'data' has been signed by public 'key'
1372 '
1373 ' Compute HMACSHA1 hash value for 'data' using public 'key'
1374 ' then compare the value to 'sig'(nature).
1375 '
1376 ' key: Full text for public key.
1377 ' data: Data to be verified.
1378 ' signature: Signature of signed data (ie. hash value generated by us earlier).
1379
1380 hmac_sha1_verify = (sig = hmac_sha1(key, data))
1381
1382 End Function
1383
1384
1385 ' ****************************************
1386 '
1387 ' ----------------------------------------
1388 ' ------- MISCELLANEOUS FUNCTIONS --------
1389 ' ------- SPECIFIC TO ASP VERSION --------
1390 ' ----------------------------------------
1391 '
1392 ' ****************************************
1393
1394 Function lpad(strInput, length)
1395
1396 ' Left pad string with zeros.
1397
1398 lpad = Right(String(length, "0") & strInput, length)
1399
1400 End Function
1401
1402
1403 Public Function url_decode(ByVal url_encoded)
1404
1405 ' Decodes URL-encoded string
1406
1407 url_decode = ""
1408
1409 If (url_encoded = "") Then Exit Function
1410
1411 Dim aSplit
1412 Dim sOutput
1413 Dim I
1414 If IsNull(url_encoded) Then
1415 Exit Function
1416 End If
1417
1418 ' convert all pluses to spaces
1419 sOutput = REPLACE(url_encoded, "+", " ")
1420
1421 ' next convert %hexdigits to the character
1422 aSplit = Split(sOutput, "%")
1423
1424 If IsArray(aSplit) Then
1425 sOutput = aSplit(0)
1426 For I = 0 to UBound(aSplit) - 1
1427 sOutput = sOutput & _
1428 Chr("&H" & Left(aSplit(i + 1), 2)) &_
1429 Right(aSplit(i + 1), Len(aSplit(i + 1)) - 2)
1430 Next
1431 End If
1432
1433 url_decode = sOutput
1434
1435 End Function
1436
1437
1438 Public Function UTCNow()
1439
1440 ' Gets the UTC current time
1441 ' The UTC time is the standard time the Raven server is operating with.
1442
1443 Set dateTime = CreateObject("WbemScripting.SWbemDateTime")
1444 dateTime.SetVarDate (now())
1445 UTCNow = dateTime.GetVarDate (false)
1446
1447 End Function
1448
1449
1450 Public Function base64_decode(strB64)
1451
1452 ' Base64 decodes a string
1453
1454 strXML = "<B64DECODE xmlns:dt=" & Chr(34) & _
1455 "urn:schemas-microsoft-com:datatypes" & Chr(34) & " " & _
1456 "dt:dt=" & Chr(34) & "bin.base64" & Chr(34) & ">" & _
1457 strB64 & "</B64DECODE>"
1458 Set oXMLDoc = CreateObject("MSXML2.DOMDocument.3.0")
1459 oXMLDoc.LoadXML(strXML)
1460
1461 base64_decode = oXMLDoc.selectsinglenode("B64DECODE").nodeTypedValue
1462
1463 set oXMLDoc = Nothing
1464
1465 End Function
1466
1467
1468 ' ****************************************
1469 '
1470 ' Special get/set methods to enable
1471 ' seamless access to cookies or query
1472 ' variables as if they were ordinary
1473 ' member variables.
1474 '
1475 ' ****************************************
1476
1477 ' Get/set authentication_cookie
1478 ' 'authentication_cookie' is used to get and set
1479 ' the value of the authentication cookie.
1480
1481 Public Property Get authentication_cookie
1482
1483 ' If the current value of the private variable
1484 ' m_authentication_cookie is null then we check
1485 ' to see what the value of the actual cookie is
1486 ' and use that to set the variable accordingly.
1487
1488 If (m_authentication_cookie = "") Then
1489 On Error Resume Next
1490 m_authentication_cookie = Request.Cookies(full_cookie_name())
1491 On Error GoTo 0
1492 End If
1493
1494 authentication_cookie = m_authentication_cookie
1495
1496 End Property
1497
1498 Public Property Let authentication_cookie(v_authentication_cookie)
1499
1500 ' Reset the private variable _authentication_cookie
1501 ' so the system is forced to query the cookie directly
1502 ' next time it wants to check the value through
1503 ' authentication_cookie.
1504
1505 m_authentication_cookie = ""
1506
1507 setcookie full_cookie_name(), v_authentication_cookie, 0, cookie_path, cookie_domain, using_https(), False
1508
1509 End Property
1510
1511
1512 Public Property Get authentication_response_string()
1513
1514 ' Get authentication_response_string
1515 '
1516 ' Checks to see if there is a 'WLS-Response'
1517 ' variable set and uses this as the value
1518 ' for authentication_response_string
1519
1520 ' If the value is already set, we just return it straightaway
1521
1522 If (m_authentication_response_string <> "") Then
1523 authentication_response_string = m_authentication_response_string
1524 Exit Property
1525 End If
1526
1527 ' If the value is not set, we look for the value of 'WLS-Response' in 'QUERY_STRING'
1528
1529 query_string = Request.ServerVariables("QUERY_STRING")
1530
1531 If (query_string = "") Then
1532 authentication_response_string = m_authentication_response_string
1533 Exit Property
1534 End If
1535
1536
1537 ' We're processing urls with possible name-value pairs
1538 ' in addition to WLS-Response=... so we need to extract all
1539 ' name-value pairs and check specifically for WLS-Response.
1540
1541 namevaluepairs = Split(query_string, "&")
1542
1543 For Each namevalue In namevaluepairs
1544
1545 pair = Split(namevalue, "=")
1546
1547 If (UBound(pair) = 1) Then
1548 If (pair(0) = "WLS-Response") Then
1549 authentication_response_string = url_decode(pair(1))
1550 Exit Property
1551 End If
1552 End If
1553 Next
1554
1555 authentication_response_string = ""
1556
1557 End Property
1558
1559
1560 ' ****************************************
1561 '
1562 ' Get/set methods to enable public access
1563 ' to private variables.
1564 '
1565 ' ****************************************
1566
1567 ' Get/set auth_service
1568
1569 Public Property Get auth_service
1570 auth_service = m_auth_service
1571 End Property
1572 Public Property Let auth_service(v_auth_service)
1573 m_auth_service = v_auth_service
1574 End Property
1575
1576 ' Get/set authrequest_desc
1577
1578 Public Property Get authrequest_desc
1579 authrequest_desc = m_description
1580 End Property
1581 Public Property Let authrequest_desc(v_authrequest_desc)
1582 m_authrequest_desc = v_authrequest_desc
1583 End Property
1584
1585 ' Get/set authrequest_skew
1586
1587 Public Property Get authrequest_skew
1588 authrequest_skew = m_authrequest_skew
1589 End Property
1590 Public Property Let authrequest_skew(v_authrequest_skew)
1591 m_authrequest_skew = v_authrequest_skew
1592 End Property
1593
1594 ' Get/set authrequest_fail
1595
1596 Public Property Get authrequest_fail
1597 authrequest_fail = m_authrequest_fail
1598 End Property
1599 Public Property Let authrequest_fail(v_authrequest_fail)
1600 m_authrequest_fail = v_authrequest_fail
1601 End Property
1602
1603 ' Get/set authrequest_iact
1604
1605 Public Property Get authrequest_iact
1606 authrequest_iact = m_authrequest_iact
1607 End Property
1608 Public Property Let authrequest_iact(v_authrequest_iact)
1609 m_authrequest_iact = v_authrequest_iact
1610 End Property
1611
1612 ' Get/set use_authrequest_iact
1613
1614 Public Property Get use_authrequest_iact
1615 use_authrequest_iact = m_use_authrequest_iact
1616 End Property
1617 Public Property Let use_authrequest_iact(v_use_authrequest_iact)
1618 m_use_authrequest_iact = v_use_authrequest_iact
1619 End Property
1620
1621 ' Get/set authrequest_aauth
1622
1623 Public Property Get authrequest_aauth
1624 authrequest_aauth = m_authrequest_aauth
1625 End Property
1626 Public Property Let authrequest_aauth(v_authrequest_aauth)
1627 m_authrequest_aauth = v_authrequest_aauth
1628 End Property
1629
1630 ' Get/set authrequest_params
1631
1632 Public Property Get authrequest_params
1633 authrequest_params = m_authrequest_params
1634 End Property
1635 Public Property Let authrequest_params(v_authrequest_params)
1636 m_authrequest_params = v_authrequest_params
1637 End Property
1638 ' Get/set cookie_key
1639
1640 Public Property Get cookie_key
1641 cookie_key = m_cookie_key
1642 End Property
1643 Public Property Let cookie_key(v_cookie_key)
1644 m_cookie_key = v_cookie_key
1645 End Property
1646
1647 ' Get/set redirected
1648 '
1649 ' 'redirected' is a flag that says
1650 ' whether a redirect has been sent
1651 ' which is useful to check before
1652 ' attempting to output any information
1653 ' to the browser.
1654
1655 Public Property Get redirected
1656 redirected = m_redirected
1657 End Property
1658 Public Property Let redirected(v_redirected)
1659 m_redirected = v_redirected
1660 End Property
1661
1662 ' Get/set cookie_path
1663
1664 Public Property Get cookie_path
1665 cookie_path = m_cookie_path
1666 End Property
1667 Public Property Let cookie_path(v_cookie_path)
1668 m_cookie_path = v_cookie_path
1669 End Property
1670
1671 ' Get/set response_timeout
1672
1673 Public Property Get response_timeout
1674 response_timeout = m_response_timeout
1675 End Property
1676 Public Property Let response_timeout(v_response_timeout)
1677 m_response_timeout = v_response_timeout
1678 End Property
1679
1680 ' Get/set hostname
1681
1682 Public Property Get hostname
1683 hostname = m_hostname
1684 End Property
1685 Public Property Let hostname(v_hostname)
1686 m_hostname = v_hostname
1687 End Property
1688
1689 ' Get/set key_dir
1690
1691 Public Property Get key_dir
1692 key_dir = m_key_dir
1693 End Property
1694 Public Property Let key_dir(v_key_dir)
1695 m_key_dir = v_key_dir
1696 End Property
1697
1698 ' Get/set max_session_life
1699
1700 Public Property Get max_session_life
1701 max_session_life = m_max_session_life
1702 End Property
1703 Public Property Let max_session_life(v_max_session_life)
1704 m_max_session_life = v_max_session_life
1705 End Property
1706
1707 ' Get/set timeout_message
1708
1709 Public Property Get timeout_message
1710 timeout_message = m_timeout_message
1711 End Property
1712 Public Property Let timeout_message(v_timeout_message)
1713 m_timeout_message = v_timeout_message
1714 End Property
1715
1716 ' Get/set cookie_name
1717
1718 Public Property Get cookie_name
1719 cookie_name = m_cookie_name
1720 End Property
1721 Public Property Let cookie_name(v_cookie_name)
1722 m_cookie_name = v_cookie_name
1723 End Property
1724
1725 ' Get/set cookie_domain
1726
1727 Public Property Get cookie_domain
1728 cookie_domain = m_cookie_domain
1729 End Property
1730 Public Property Let cookie_domain(v_cookie_domain)
1731 m_cookie_domain = v_cookie_domain
1732 End Property
1733
1734 ' Get/set log_file
1735
1736 Public Property Get log_file
1737 log_file = m_log_file
1738 End Property
1739 Public Property Let log_file(v_log_file)
1740 m_log_file = v_log_file
1741 End Property
1742
1743 ' ****************************************
1744 '
1745 ' Read-only methods to retrieve
1746 ' authentication response values.
1747 '
1748 ' See Raven documentation for a full
1749 ' explanation of each parameter.
1750 '
1751 ' ****************************************
1752
1753 Public Function status()
1754
1755 ' Read-only status
1756
1757 status = ""
1758
1759 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_STATUS) Then
1760 status = m_authentication_response(AUTHENTICATION_RESPONSE_STATUS)
1761 End If
1762
1763 End Function
1764
1765 Public Function success()
1766
1767 ' Read-only success
1768
1769 success = False
1770
1771 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_STATUS) Then
1772 success = (m_authentication_response(AUTHENTICATION_RESPONSE_STATUS) = "200")
1773 End If
1774
1775 End Function
1776
1777 Public Function msg()
1778
1779 ' Read-only msg
1780
1781 msg = ""
1782
1783 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_MSG) Then
1784 msg = m_authentication_response(AUTHENTICATION_RESPONSE_MSG)
1785 End If
1786
1787 End Function
1788
1789 Public Function issue()
1790
1791 ' Read-only issue
1792
1793 issue = ""
1794
1795 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_ISSUE) Then
1796 issue = m_authentication_response(AUTHENTICATION_RESPONSE_ISSUE)
1797 End If
1798
1799 End Function
1800
1801 Public Function expire()
1802
1803 ' Read-only expire
1804
1805 expire = ""
1806
1807 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_EXPIRE) Then
1808 expire = m_authentication_response(AUTHENTICATION_RESPONSE_EXPIRE)
1809 End If
1810
1811 End Function
1812
1813 Public Function id()
1814
1815 ' Read-only id
1816
1817 id = ""
1818
1819 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_ID) Then
1820 id = m_authentication_response(AUTHENTICATION_RESPONSE_ID)
1821 End If
1822
1823 End Function
1824
1825 Public Function principal()
1826
1827 ' Read-only principal
1828 ' ie. the user id of the authenticated user
1829
1830 principal = ""
1831
1832 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PRINCIPAL) Then
1833 principal = m_authentication_response(AUTHENTICATION_RESPONSE_PRINCIPAL)
1834 End If
1835
1836 End Function
1837
1838 Public Function auth()
1839
1840 ' Read-only auth
1841
1842 auth = ""
1843
1844 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_AUTH) Then
1845 auth = m_authentication_response(AUTHENTICATION_RESPONSE_AUTH)
1846 End If
1847
1848 End Function
1849
1850 Public Function sso()
1851
1852 ' Read-only sso
1853
1854 sso = ""
1855
1856 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_SSO) Then
1857 sso = m_authentication_response(AUTHENTICATION_RESPONSE_SSO)
1858 End If
1859
1860 End Function
1861
1862 Public Function webauth_params()
1863
1864 ' Read-only params
1865
1866 webauth_params = ""
1867
1868 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PARAMS) Then
1869 webauth_params = m_authentication_response(AUTHENTICATION_RESPONSE_PARAMS)
1870 End If
1871
1872 End Function
1873
1874 Public Function ptags()
1875
1876 ' Read-only ptags
1877 ' ie. whether user is 'current' staff/student
1878 ' or another type of user.
1879
1880 ptags = ""
1881
1882 If (UBound(m_authentication_response) >= AUTHENTICATION_RESPONSE_PTAGS) Then
1883 ptags = m_authentication_response(AUTHENTICATION_RESPONSE_PTAGS)
1884 End If
1885
1886 End Function
1887
1888 End Class
1889
1890 %>
1891
1892 <script runat="server" language="JavaScript" src="js/core.js"></script>
1893 <script runat="server" language="JavaScript" src="js/md5.js"></script>
1894 <script runat="server" language="JavaScript" src="js/sha1.js"></script>
1895 <script runat="server" language="JavaScript" src="js/sha256.js"></script>
1896 <script runat="server" language="JavaScript" src="js/ripemd160.js"></script>
1897 <script runat="server" language="JavaScript" src="js/x64-core.js"></script>
1898 <script runat="server" language="JavaScript" src="js/sha512.js"></script>
1899 <script runat="server" language="JavaScript" src="js/jsbn.js"></script>
1900 <script runat="server" language="JavaScript" src="js/jsbn2.js"></script>
1901 <script runat="server" language="JavaScript" src="js/rsa.js"></script>
1902 <script runat="server" language="JavaScript" src="js/rsa2.js"></script>
1903 <script runat="server" language="JavaScript" src="js/crypto-1.1.js"></script>
1904 <script runat="server" language="JavaScript" src="js/x509-1.1.js"></script>
1905 <script runat="server" language="JavaScript" src="js/base64.js"></script>
1906 <script runat="server" language="JavaScript" src="js/asn1hex-1.1.js"></script>
1907 <script runat="server" language="JavaScript" src="js/rsapem-1.1.js"></script>
1908 <script runat="server" language="JavaScript" src="js/rsasign-1.2.js"></script>
1909 <script runat="server" language="JavaScript" src="js/hex_sha1_js.js"></script>
1910 <script runat="server" language="JavaScript" >
1911
1912 // Javascript cryptographic libraries above adapted for ASP use from
1913 // client libraries at http://kjur.github.io/jsrsasign/index.html
1914 // These libraries are used in 'JavascriptCertVerify' (Javascript)
1915 // and 'hmac_sha1' functions.
1916
1917 function JavascriptCertVerify(sMsg, sSignature, sCertificateValue)
1918 {
1919 // Javascript function to verify message has
1920 // been signed with particular certificate.
1921
1922 var bytes = [];
1923 var x509 = new X509();
1924
1925 x509.readCertPEM(sCertificateValue);
1926
1927 return x509.subjectPublicKeyRSA.verifyString(sMsg, sSignature);
1928 }
1929
1930 </script>