Final fixes for getting GSSWeb to work again.
[gssweb.git] / browsers / common / contentscript.js
1 /*
2  * Copyright (c) 2015, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. Neither the name of JANET(UK) nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
25  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31  * OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  */
34 function addScript(url) {
35   var elt = document.createElement("script");
36   elt.setAttribute("src", 
37                    url   );
38   document.head.appendChild(elt);
39 }
40
41 navigator.generateNonce = function() {
42   // TODO: Make sure that we don't have a collision!
43   // Random integer in the range [0..(2^32-1)]
44   return Math.floor(Math.random() * ( 4294967295 )) ;
45 }
46
47
48 var port;
49 var browser;
50 var gssHostNames = {};
51 var pendingGssHostNames = {};
52
53 if ("undefined" != typeof(chrome) &&
54     "undefined" != typeof(chrome.extension) &&
55     "undefined" != typeof(chrome.extension.getURL))
56 {
57   // Running in chrome
58   browser = "Chrome"; 
59   gss_script_name = chrome.extension.getURL('navigator.gss.js');
60   port = chrome.runtime.connect({name: "com.painlesssecurity.gssweb"});
61 } else {
62   // Firefox
63   browser = "Firefox";
64   gss_script_name = 'chrome://gssweb/content/navigator.gss.js';
65 }
66
67 addScript( gss_script_name );
68
69 sendReplyToWebpage = function(gssReplyJSON) {
70   var appTag = gssReplyJSON.cookies.app_tag;
71   var csTag = gssReplyJSON.cookies.cs_tag;
72
73   gssReplyJSON.cookies.cs_tag = undefined;
74
75   /* Save off the hostnames of any reply to GSSImportName if:
76    * + There is a csTag
77    * + The invoked method was 'gss_import_name'
78    * + The method completed successfully
79    * + There is a hostname wating in the pendingGssHostNames hash
80    *   indexed by csTag
81   */
82   if ( typeof(csTag) != 'undefined' &&
83        gssReplyJSON.method == 'gss_import_name' &&
84        typeof(gssReplyJSON.return_values) != 'undefined' &&
85        gssReplyJSON.return_values.major_status == '0' &&
86        typeof(pendingGssHostNames[csTag]) != 'undefined' )
87   {
88     gssHostNames[gssReplyJSON.return_values.gss_name] =
89       pendingGssHostNames[csTag];
90     delete pendingGssHostNames[csTag];
91   }
92
93   console.log("[" + appTag +
94               "] Extension port listener received message: [" + 
95               JSON.stringify(gssReplyJSON) + "]"
96              );
97   window.postMessage(gssReplyJSON, "*");
98 }
99
100
101 /* When we get a message back from the extension 
102  * background script
103  */
104 if ("Chrome" == browser)
105 {
106   port.onMessage.addListener( sendReplyToWebpage );
107 }
108 else 
109 {
110   self.port.on('gss_response', sendReplyToWebpage );
111 }
112
113 window.addEventListener("message", function(event) {
114     // Check to see if this message's data is data we care about
115     if ( typeof(event.data.method) == 'undefined' ||
116          typeof(event.data.arguments) == 'undefined' ||
117          typeof(event.data.return_values) != 'undefined' )
118         return;
119     
120     if ( typeof(event.data.cookies) == 'undefined' )
121     {
122       event.data.cookies = {};
123     }
124     var appTag = event.data.cookies.app_tag;
125     
126     console.log("[" + appTag +
127                 "] Window message listener received message: [" +
128                 JSON.stringify(event.data) + "]"
129                );
130
131     /*
132      * Deny calls to init_sec_context where we don't know that the
133      * target has the same hostname as the origin.
134      */
135     if(event.data.method == "gss_init_sec_context" &&
136        typeof(event.data.arguments) != 'undefined' &&
137        gssHostNames[event.data.arguments.target_name] !=
138           document.location.hostname)
139     {
140       console.log("[" + appTag + "] Window message listener received " +
141                   "gss_init_sec_context, but the hostname in the " +
142                   "target_name could not be found to match the document " +
143                   "location hostname.");
144       sendReplyToWebpage({
145         'method':'gss_init_sec_context',
146         'return_values': {
147           'major_status': -2,
148           'minor_status': -1,
149           'major_status_message': 'The GSS call cannot be completed',
150           'minor_status_message': 'init_sec_context requires a target ' +
151                                   'that matches your page origin.'
152         },
153         'cookies': event.data.cookies
154       });
155       return;
156     }
157
158     /* Add a content script tag, csTag */
159     var csTag = navigator.generateNonce();
160     event.data.cookies.cs_tag = csTag;
161
162     /* Save out the hostname from calls to import_name with an
163      * NT hostbased name
164      */
165     if(event.data.method == 'gss_import_name')
166     {
167       if( typeof(event.data.arguments) != 'undefined' &&
168           ( event.data.arguments.input_name_type ==
169               "{1 2 840 113554 1 2 1 4 }" ||
170             event.data.arguments.input_name_type ==
171               "1.2.840.113554.1.2.1.4" ) )
172       {
173         var hostname = /[^@]*$/.exec(event.data.arguments.input_name)[0];
174         pendingGssHostNames[csTag] = hostname;
175       }
176     }
177
178     if ("Chrome" == browser)
179     {
180       port.postMessage(event.data);
181     } else 
182     {
183       self.port.emit("gss_request", event.data);
184     }
185 }, false);
186