source: trunk/third/mozilla/caps/src/nsScriptSecurityManager.cpp @ 20014

Revision 20014, 114.1 KB checked in by rbasch, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20013, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Netscape Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/NPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Norris Boyd
24 * Mitch Stoltz
25 * Steve Morse
26 *
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * either the GNU General Public License Version 2 or later (the "GPL"), or
30 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the NPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the NPL, the GPL or the LGPL.
39 *
40 * ***** END LICENSE BLOCK ***** */
41#include "nsScriptSecurityManager.h"
42#include "nsIServiceManager.h"
43#include "nsIScriptObjectPrincipal.h"
44#include "nsIScriptContext.h"
45#include "nsIURL.h"
46#include "nsIJARURI.h"
47#include "nspr.h"
48#include "nsJSPrincipals.h"
49#include "nsSystemPrincipal.h"
50#include "nsCodebasePrincipal.h"
51#include "nsCertificatePrincipal.h"
52#include "nsAggregatePrincipal.h"
53#include "nsXPIDLString.h"
54#include "nsCRT.h"
55#include "nsIJSContextStack.h"
56#include "nsDOMError.h"
57#include "nsDOMCID.h"
58#include "jsdbgapi.h"
59#include "nsIXPConnect.h"
60#include "nsIXPCSecurityManager.h"
61#include "nsTextFormatter.h"
62#include "nsIStringBundle.h"
63#include "nsNetUtil.h"
64#include "nsDirectoryService.h"
65#include "nsDirectoryServiceDefs.h"
66#include "nsIFile.h"
67#include "nsIZipReader.h"
68#include "nsIJAR.h"
69#include "nsIPluginInstance.h"
70#include "nsIXPConnect.h"
71#include "nsIScriptGlobalObject.h"
72#include "nsIDOMWindowInternal.h"
73#include "nsIDocShell.h"
74#include "nsIDocShellTreeItem.h"
75#include "nsIPrompt.h"
76#include "nsIWindowWatcher.h"
77#include "nsIConsoleService.h"
78#include "nsISecurityCheckedComponent.h"
79#include "nsIPrefBranchInternal.h"
80#include "nsIJSRuntimeService.h"
81#include "nsIObserverService.h"
82#include "nsIContent.h"
83
84static NS_DEFINE_IID(kIStringBundleServiceIID, NS_ISTRINGBUNDLESERVICE_IID);
85static NS_DEFINE_IID(kStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
86static NS_DEFINE_CID(kCScriptNameSetRegistryCID,
87                     NS_SCRIPT_NAMESET_REGISTRY_CID);
88static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
89static NS_DEFINE_IID(kObserverServiceIID, NS_IOBSERVERSERVICE_IID);
90
91nsIStringBundle *nsScriptSecurityManager::sStrBundle = nsnull;
92
93///////////////////////////
94// Convenience Functions //
95///////////////////////////
96// Result of this function should not be freed.
97static inline const PRUnichar *
98JSValIDToString(JSContext *cx, const jsval idval)
99{
100    JSString *str = JS_ValueToString(cx, idval);
101    if(!str)
102        return nsnull;
103    return NS_REINTERPRET_CAST(PRUnichar*, JS_GetStringChars(str));
104}
105
106already_AddRefed<nsIScriptContext>
107GetScriptContext(JSContext *cx)
108{
109    nsIScriptContext *scriptContext;
110    GetScriptContextFromJSContext(cx, &scriptContext);
111
112    return scriptContext;
113}
114
115// Helper class to get stuff from the ClassInfo and not waste extra time with
116// virtual method calls for things it has already gotten
117class ClassInfoData
118{
119public:
120    ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
121        : mClassInfo(aClassInfo), mDidGetFlags(PR_FALSE),
122          mName(NS_CONST_CAST(char *, aName)), mMustFreeName(PR_FALSE)
123    {
124    }
125
126    ~ClassInfoData()
127    {
128        if (mMustFreeName)
129            nsMemory::Free(mName);
130    }
131
132    PRUint32 GetFlags()
133    {
134        if (!mDidGetFlags) {
135            if (mClassInfo) {
136                nsresult rv = mClassInfo->GetFlags(&mFlags);
137                if (NS_FAILED(rv)) {
138                    mFlags = 0;
139                }
140            } else {
141                mFlags = 0;
142            }
143
144            mDidGetFlags = PR_TRUE;
145        }
146
147        return mFlags;
148    }
149
150    PRBool IsDOMClass()
151    {
152        return GetFlags() & nsIClassInfo::DOM_OBJECT;
153    }
154
155    PRBool IsContentNode()
156    {
157        return GetFlags() & nsIClassInfo::CONTENT_NODE;
158    }
159
160    const char* GetName()
161    {
162        if (!mName) {
163            if (mClassInfo) {
164                mClassInfo->GetClassDescription(&mName);
165            }
166
167            if (mName) {
168                mMustFreeName = PR_TRUE;
169            } else {
170                mName = NS_CONST_CAST(char *, "UnnamedClass");
171            }
172        }
173
174        return mName;
175    }
176
177private:
178    nsIClassInfo *mClassInfo; // WEAK
179    PRBool mDidGetFlags;
180    PRUint32 mFlags;
181    char *mName;
182    PRBool mMustFreeName;
183};
184 
185JSContext *
186nsScriptSecurityManager::GetCurrentJSContext()
187{
188    // Get JSContext from stack.
189    if (!mJSContextStack)
190    {
191        mJSContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
192        if (!mJSContextStack)
193            return nsnull;
194    }
195    JSContext *cx;
196    if (NS_FAILED(mJSContextStack->Peek(&cx)))
197        return nsnull;
198    return cx;
199}
200
201JSContext *
202nsScriptSecurityManager::GetSafeJSContext()
203{
204    // Get JSContext from stack.
205    if (!mJSContextStack) {
206        mJSContextStack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
207        if (!mJSContextStack)
208            return nsnull;
209    }
210
211    JSContext *cx;
212    if (NS_FAILED(mJSContextStack->GetSafeJSContext(&cx)))
213        return nsnull;
214    return cx;
215}
216
217NS_IMETHODIMP
218nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
219                                             nsIURI* aTargetURI,
220                                             PRBool* result)
221{
222    nsresult rv;
223    *result = PR_FALSE;
224
225    if (aSourceURI == aTargetURI)
226    {
227        *result = PR_TRUE;
228        return NS_OK;
229    }
230    if (aTargetURI == nsnull)
231    {
232        // return false
233        return NS_OK;
234    }
235
236    // If either uri is a jar URI, get the base URI
237    nsCOMPtr<nsIJARURI> jarURI;
238    nsCOMPtr<nsIURI> sourceBaseURI(aSourceURI);
239    while((jarURI = do_QueryInterface(sourceBaseURI)))
240    {
241        jarURI->GetJARFile(getter_AddRefs(sourceBaseURI));
242    }
243    nsCOMPtr<nsIURI> targetBaseURI(aTargetURI);
244    while((jarURI = do_QueryInterface(targetBaseURI)))
245    {
246        jarURI->GetJARFile(getter_AddRefs(targetBaseURI));
247    }
248
249    if (!sourceBaseURI || !targetBaseURI)
250        return NS_ERROR_FAILURE;
251
252    // Compare schemes
253    nsCAutoString targetScheme;
254    rv = targetBaseURI->GetScheme(targetScheme);
255    nsCAutoString sourceScheme;
256    if (NS_SUCCEEDED(rv))
257        rv = sourceBaseURI->GetScheme(sourceScheme);
258    if (NS_SUCCEEDED(rv) &&
259        targetScheme.Equals(sourceScheme, nsCaseInsensitiveCStringComparator()))
260    {
261        if (targetScheme.Equals(NS_LITERAL_CSTRING("file"),
262            nsCaseInsensitiveCStringComparator()))
263        {
264            // All file: urls are considered to have the same origin.
265            *result = PR_TRUE;
266        }
267        else if (targetScheme.Equals(NS_LITERAL_CSTRING("imap"),
268                                     nsCaseInsensitiveCStringComparator()) ||
269                 targetScheme.Equals(NS_LITERAL_CSTRING("mailbox"),
270                                     nsCaseInsensitiveCStringComparator()) ||
271                 targetScheme.Equals(NS_LITERAL_CSTRING("news"),
272                                     nsCaseInsensitiveCStringComparator()))
273        {
274            // Each message is a distinct trust domain; use the
275            // whole spec for comparison
276            nsCAutoString targetSpec;
277            if (NS_FAILED(targetBaseURI->GetSpec(targetSpec)))
278                return NS_ERROR_FAILURE;
279            nsCAutoString sourceSpec;
280            if (NS_FAILED(sourceBaseURI->GetSpec(sourceSpec)))
281                return NS_ERROR_FAILURE;
282            *result = targetSpec.Equals(sourceSpec);
283        }
284        else
285        {
286            // Compare hosts
287            nsCAutoString targetHost;
288            rv = targetBaseURI->GetHost(targetHost);
289            nsCAutoString sourceHost;
290            if (NS_SUCCEEDED(rv))
291                rv = sourceBaseURI->GetHost(sourceHost);
292            *result = NS_SUCCEEDED(rv) &&
293                      targetHost.Equals(sourceHost,
294                                        nsCaseInsensitiveCStringComparator());
295            if (*result)
296            {
297                // Compare ports
298                PRInt32 targetPort;
299                rv = targetBaseURI->GetPort(&targetPort);
300                PRInt32 sourcePort;
301                if (NS_SUCCEEDED(rv))
302                    rv = sourceBaseURI->GetPort(&sourcePort);
303                *result = NS_SUCCEEDED(rv) && targetPort == sourcePort;
304                // If the port comparison failed, see if either URL has a
305                // port of -1. If so, replace -1 with the default port
306                // for that scheme.
307                if (!*result && (sourcePort == -1 || targetPort == -1))
308                {
309                    PRInt32 defaultPort;
310                    //XXX had to hard-code the defualt port for http(s) here.
311                    //    remove this after darin fixes bug 113206
312                    if (sourceScheme.Equals(NS_LITERAL_CSTRING("http"),
313                                            nsCaseInsensitiveCStringComparator()))
314                        defaultPort = 80;
315                    else if (sourceScheme.Equals(NS_LITERAL_CSTRING("https"),
316                                                 nsCaseInsensitiveCStringComparator()))
317                        defaultPort = 443;
318                    else
319                    {
320                        nsCOMPtr<nsIIOService> ioService(
321                            do_GetService(NS_IOSERVICE_CONTRACTID));
322                        if (!ioService)
323                            return NS_ERROR_FAILURE;
324                        nsCOMPtr<nsIProtocolHandler> protocolHandler;
325                        rv = ioService->GetProtocolHandler(sourceScheme.get(),
326                                                           getter_AddRefs(protocolHandler));
327                        if (NS_FAILED(rv))
328                        {
329                            *result = PR_FALSE;
330                            return NS_OK;
331                        }
332                   
333                        rv = protocolHandler->GetDefaultPort(&defaultPort);
334                        if (NS_FAILED(rv) || defaultPort == -1)
335                            return NS_OK; // No default port for this scheme
336                    }
337                    if (sourcePort == -1)
338                        sourcePort = defaultPort;
339                    else if (targetPort == -1)
340                        targetPort = defaultPort;
341                    *result = targetPort == sourcePort;
342                }
343            }
344        }
345    }
346    return NS_OK;
347}
348
349////////////////////
350// Policy Storage //
351////////////////////
352
353// Table of security levels
354PR_STATIC_CALLBACK(PRBool)
355DeleteCapability(nsHashKey *aKey, void *aData, void* closure)
356{
357    nsMemory::Free(aData);
358    return PR_TRUE;
359}
360
361//-- Per-Domain Policy - applies to one or more protocols or hosts
362struct DomainEntry
363{
364    DomainEntry(const char* aOrigin,
365                DomainPolicy* aDomainPolicy) : mOrigin(aOrigin),
366                                               mDomainPolicy(aDomainPolicy),
367                                               mNext(nsnull)
368    {
369        mDomainPolicy->Hold();
370    }
371
372    ~DomainEntry()
373    {
374        mDomainPolicy->Drop();
375    }
376
377    PRBool Matches(const char *anOrigin)
378    {
379        int len = strlen(anOrigin);
380        int thisLen = mOrigin.Length();
381        if (len < thisLen)
382            return PR_FALSE;
383        if (mOrigin.RFindChar(':', thisLen-1, 1) != -1)
384        //-- Policy applies to all URLs of this scheme, compare scheme only
385            return mOrigin.EqualsWithConversion(anOrigin, PR_TRUE, thisLen);
386
387        //-- Policy applies to a particular host; compare domains
388        if (!mOrigin.Equals(anOrigin + (len - thisLen)))
389            return PR_FALSE;
390        if (len == thisLen)
391            return PR_TRUE;
392        char charBefore = anOrigin[len-thisLen-1];
393        return (charBefore == '.' || charBefore == ':' || charBefore == '/');
394    }
395
396    nsCString         mOrigin;
397    DomainPolicy*     mDomainPolicy;
398    DomainEntry*      mNext;
399#ifdef DEBUG
400    nsCString         mPolicyName_DEBUG;
401#endif
402};
403
404PR_STATIC_CALLBACK(PRBool)
405DeleteDomainEntry(nsHashKey *aKey, void *aData, void* closure)
406{
407    DomainEntry *entry = (DomainEntry*) aData;
408    do
409    {
410        DomainEntry *next = entry->mNext;
411        delete entry;
412        entry = next;
413    } while (entry);
414    return PR_TRUE;
415}
416
417/////////////////////////////
418// nsScriptSecurityManager //
419/////////////////////////////
420
421////////////////////////////////////
422// Methods implementing ISupports //
423////////////////////////////////////
424NS_IMPL_ISUPPORTS3(nsScriptSecurityManager,
425                   nsIScriptSecurityManager,
426                   nsIXPCSecurityManager,
427                   nsIObserver)
428
429///////////////////////////////////////////////////
430// Methods implementing nsIScriptSecurityManager //
431///////////////////////////////////////////////////
432
433///////////////// Security Checks /////////////////
434JSBool JS_DLL_CALLBACK
435nsScriptSecurityManager::CheckObjectAccess(JSContext *cx, JSObject *obj,
436                                           jsval id, JSAccessMode mode,
437                                           jsval *vp)
438{
439    // Get the security manager
440    nsScriptSecurityManager *ssm =
441        nsScriptSecurityManager::GetScriptSecurityManager();
442
443    NS_ASSERTION(ssm, "Failed to get security manager service");
444    if (!ssm)
445        return JS_FALSE;
446
447    // Get the object being accessed.  We protect these cases:
448    // 1. The Function.prototype.caller property's value, which might lead
449    //    an attacker up a call-stack to a function or another object from
450    //    a different trust domain.
451    // 2. A user-defined getter or setter function accessible on another
452    //    trust domain's window or document object.
453    // If *vp is not a primitive, some new JS engine call to this hook was
454    // added, but we can handle that case too -- if a primitive value in a
455    // property of obj is being accessed, we should use obj as the target
456    // object.
457    NS_ASSERTION(!JSVAL_IS_PRIMITIVE(*vp), "unexpected target property value");
458    JSObject* target = JSVAL_IS_PRIMITIVE(*vp) ? obj : JSVAL_TO_OBJECT(*vp);
459
460    // Do the same-origin check -- this sets a JS exception if the check fails.
461    // Pass the parent object's class name, as we have no class-info for it.
462    nsresult rv =
463        ssm->CheckPropertyAccess(cx, target, JS_GetClass(cx, obj)->name, id,
464                                 nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
465
466    if (NS_FAILED(rv))
467        return JS_FALSE; // Security check failed (XXX was an error reported?)
468
469    return JS_TRUE;
470}
471
472NS_IMETHODIMP
473nsScriptSecurityManager::CheckPropertyAccess(JSContext* cx,
474                                             JSObject* aJSObject,
475                                             const char* aClassName,
476                                             jsval aProperty,
477                                             PRUint32 aAction)
478{
479    return CheckPropertyAccessImpl(aAction, nsnull, cx, aJSObject,
480                                   nsnull, nsnull, nsnull,
481                                   aClassName, aProperty, nsnull);
482}
483
484NS_IMETHODIMP
485nsScriptSecurityManager::CheckConnect(JSContext* cx,
486                                      nsIURI* aTargetURI,
487                                      const char* aClassName,
488                                      const char* aPropertyName)
489{
490    // Get a context if necessary
491    if (!cx)
492    {
493        cx = GetCurrentJSContext();
494        if (!cx)
495            return NS_OK; // No JS context, so allow the load
496    }
497
498    nsresult rv = CheckLoadURIFromScript(cx, aTargetURI);
499    if (NS_FAILED(rv)) return rv;
500
501    JSString* propertyName = ::JS_InternString(cx, aPropertyName);
502    if (!propertyName)
503        return NS_ERROR_OUT_OF_MEMORY;
504
505    return CheckPropertyAccessImpl(nsIXPCSecurityManager::ACCESS_CALL_METHOD, nsnull,
506                                   cx, nsnull, nsnull, aTargetURI,
507                                   nsnull, aClassName, STRING_TO_JSVAL(propertyName), nsnull);
508}
509
510NS_IMETHODIMP
511nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
512                                         nsIURI* aTargetURI)
513{
514    nsresult rv;
515
516    // Get a context if necessary
517    if (!cx)
518    {
519        cx = GetCurrentJSContext();
520        if (!cx)
521            return NS_OK; // No JS context, so allow access
522    }
523
524    // Get a principal from the context
525    nsCOMPtr<nsIPrincipal> sourcePrincipal;
526    rv = GetSubjectPrincipal(cx, getter_AddRefs(sourcePrincipal));
527    if (NS_FAILED(rv))
528        return rv;
529
530    if (!sourcePrincipal)
531    {
532        NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
533        return NS_OK;
534    }
535
536    PRBool equals = PR_FALSE;
537    rv = sourcePrincipal->Equals(mSystemPrincipal, &equals);
538    if (NS_SUCCEEDED(rv) && equals)
539    {
540        // This is a system (chrome) script, so allow access
541        return NS_OK;
542    }
543
544    // Get the original URI from the source principal.
545    // This has the effect of ignoring any change to document.domain
546    // which must be done to avoid DNS spoofing (bug 154930)
547    nsCOMPtr<nsIAggregatePrincipal> sourceAgg(do_QueryInterface(sourcePrincipal, &rv));
548    NS_ENSURE_SUCCESS(rv, rv); // If it's not a system principal, it must be an aggregate
549    nsCOMPtr<nsIPrincipal> sourceOriginal;
550    rv = sourceAgg->GetOriginalCodebase(getter_AddRefs(sourceOriginal));
551    NS_ENSURE_SUCCESS(rv, rv);
552    nsCOMPtr<nsICodebasePrincipal> sourceCodebase(do_QueryInterface(sourcePrincipal, &rv));
553    NS_ENSURE_SUCCESS(rv, rv);
554    nsCOMPtr<nsIURI> sourceURI;
555    rv = sourceCodebase->GetURI(getter_AddRefs(sourceURI));
556    NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE);
557
558    // Compare origins
559    PRBool sameOrigin = PR_FALSE;
560    rv = SecurityCompareURIs(sourceURI, aTargetURI, &sameOrigin);
561    NS_ENSURE_SUCCESS(rv, rv);
562
563    if (!sameOrigin)
564    {
565         ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI);
566         return NS_ERROR_DOM_BAD_URI;
567    }
568    return NS_OK;
569}
570
571NS_IMETHODIMP
572nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
573                                            nsIURI* aTargetURI)
574{
575    nsresult rv;
576    PRBool sameOrigin = PR_FALSE;
577    rv = SecurityCompareURIs(aSourceURI, aTargetURI, &sameOrigin);
578    NS_ENSURE_SUCCESS(rv, rv);
579
580    if (!sameOrigin)
581    {
582         ReportError(nsnull, NS_LITERAL_STRING("CheckSameOriginError"),
583                     aSourceURI, aTargetURI);
584         return NS_ERROR_DOM_BAD_URI;
585    }
586    return NS_OK;
587}
588
589NS_IMETHODIMP
590nsScriptSecurityManager::CheckSameOriginPrincipal(nsIPrincipal* aSourcePrincipal,
591                                                  nsIPrincipal* aTargetPrincipal)
592{
593    return CheckSameOriginPrincipalInternal(aSourcePrincipal,
594                                            aTargetPrincipal,
595                                            PR_FALSE);
596}
597
598
599nsresult
600nsScriptSecurityManager::CheckPropertyAccessImpl(PRUint32 aAction,
601                                                 nsIXPCNativeCallContext* aCallContext,
602                                                 JSContext* cx, JSObject* aJSObject,
603                                                 nsISupports* aObj, nsIURI* aTargetURI,
604                                                 nsIClassInfo* aClassInfo,
605                                                 const char* aClassName, jsval aProperty,
606                                                 void** aCachedClassPolicy)
607{
608    nsCOMPtr<nsIPrincipal> subjectPrincipal;
609    if (NS_FAILED(GetSubjectPrincipal(cx, getter_AddRefs(subjectPrincipal))))
610        return NS_ERROR_FAILURE;
611
612    PRBool equals;
613    if (!subjectPrincipal ||
614        NS_SUCCEEDED(subjectPrincipal->Equals(mSystemPrincipal, &equals)) && equals)
615        // We have native code or the system principal: just allow access
616        return NS_OK;
617
618    nsresult rv;
619    // Hold the class info data here so we don't have to go back to virtual
620    // methods all the time
621    ClassInfoData classInfoData(aClassInfo, aClassName);
622#ifdef DEBUG_mstoltz
623    nsCAutoString propertyName;
624    propertyName.AssignWithConversion((PRUnichar*)JSValIDToString(cx, aProperty));
625    printf("### CanAccess(%s.%s, %i) ", classInfoData.GetName(),
626           propertyName.get(), aAction);
627#endif
628
629    //-- Look up the security policy for this class and subject domain
630    SecurityLevel securityLevel;
631    rv = LookupPolicy(subjectPrincipal, classInfoData.GetName(), aProperty, aAction,
632                      (ClassPolicy**)aCachedClassPolicy, &securityLevel);
633    if (NS_FAILED(rv))
634        return rv;
635
636    if (securityLevel.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
637    {   
638        // No policy found for this property so use the default of last resort.
639        // If we were called from somewhere other than XPConnect
640        // (no XPC call context), assume this is a DOM class. Otherwise,
641        // ask the ClassInfo.
642        if (!aCallContext || classInfoData.IsDOMClass())
643            securityLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
644        else
645            securityLevel.level = SCRIPT_SECURITY_NO_ACCESS;
646    }
647
648    if (SECURITY_ACCESS_LEVEL_FLAG(securityLevel))
649    // This flag means securityLevel is allAccess, noAccess, or sameOrigin
650    {
651        switch (securityLevel.level)
652        {
653        case SCRIPT_SECURITY_NO_ACCESS:
654#ifdef DEBUG_mstoltz
655            printf("noAccess ");
656#endif
657            rv = NS_ERROR_DOM_PROP_ACCESS_DENIED;
658            break;
659
660        case SCRIPT_SECURITY_ALL_ACCESS:
661#ifdef DEBUG_mstoltz
662            printf("allAccess ");
663#endif
664            rv = NS_OK;
665            break;
666
667        case SCRIPT_SECURITY_SAME_ORIGIN_ACCESS:
668            {
669#ifdef DEBUG_mstoltz
670                printf("sameOrigin ");
671#endif
672                nsCOMPtr<nsIPrincipal> objectPrincipal;
673                if(aJSObject)
674                {
675                    rv = doGetObjectPrincipal(cx,
676                                              NS_REINTERPRET_CAST(JSObject*,
677                                                                  aJSObject),
678                                              getter_AddRefs(objectPrincipal));
679                    if (NS_FAILED(rv))
680                        return NS_ERROR_FAILURE;
681                }
682                else if(aTargetURI)
683                {
684                    if (NS_FAILED(GetCodebasePrincipal(
685                          aTargetURI, getter_AddRefs(objectPrincipal))))
686                        return NS_ERROR_FAILURE;
687                }
688                else
689                {
690                    NS_ERROR("CheckPropertyAccessImpl called without a target object or URL");
691                    return NS_ERROR_FAILURE;
692                }
693                rv = CheckSameOriginDOMProp(subjectPrincipal, objectPrincipal,
694                                            aAction, (PRBool)aTargetURI);
695                break;
696            }
697        default:
698#ifdef DEBUG_mstoltz
699                printf("ERROR ");
700#endif
701            NS_ERROR("Bad Security Level Value");
702            return NS_ERROR_FAILURE;
703        }
704    }
705    else // if SECURITY_ACCESS_LEVEL_FLAG is false, securityLevel is a capability
706    {
707#ifdef DEBUG_mstoltz
708        printf("Cap:%s ", securityLevel.capability);
709#endif
710        PRBool capabilityEnabled = PR_FALSE;
711        rv = IsCapabilityEnabled(securityLevel.capability, &capabilityEnabled);
712        if (NS_FAILED(rv) || !capabilityEnabled)
713            rv = NS_ERROR_DOM_SECURITY_ERR;
714        else
715            rv = NS_OK;
716    }
717
718    if (NS_SUCCEEDED(rv) && classInfoData.IsContentNode())
719    {
720        // No access to anonymous content from the web!  (bug 164086)
721        nsCOMPtr<nsIContent> content(do_QueryInterface(aObj));
722        NS_ASSERTION(content, "classinfo had CONTENT_NODE set but node did not"
723                              "implement nsIContent!  Fasten your seat belt.");
724        if (content->IsNativeAnonymous()) {
725            rv = NS_ERROR_DOM_SECURITY_ERR;
726        }
727    }
728
729    if (NS_SUCCEEDED(rv))
730    {
731#ifdef DEBUG_mstoltz
732    printf(" GRANTED.\n");
733#endif
734        return rv;
735    }
736
737    //--See if the object advertises a non-default level of access
738    //  using nsISecurityCheckedComponent
739    nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
740        do_QueryInterface(aObj);
741
742    nsXPIDLCString objectSecurityLevel;
743    if (checkedComponent)
744    {
745        nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
746        nsCOMPtr<nsIInterfaceInfo> interfaceInfo;
747        const nsIID* objIID;
748        rv = aCallContext->GetCalleeWrapper(getter_AddRefs(wrapper));
749        if (NS_SUCCEEDED(rv))
750            rv = wrapper->FindInterfaceWithMember(aProperty, getter_AddRefs(interfaceInfo));
751        if (NS_SUCCEEDED(rv))
752            rv = interfaceInfo->GetIIDShared(&objIID);
753        if (NS_SUCCEEDED(rv))
754        {
755            switch (aAction)
756            {
757            case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
758                checkedComponent->CanGetProperty(objIID,
759                                                 JSValIDToString(cx, aProperty),
760                                                 getter_Copies(objectSecurityLevel));
761                break;
762            case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
763                checkedComponent->CanSetProperty(objIID,
764                                                 JSValIDToString(cx, aProperty),
765                                                 getter_Copies(objectSecurityLevel));
766                break;
767            case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
768                checkedComponent->CanCallMethod(objIID,
769                                                JSValIDToString(cx, aProperty),
770                                                getter_Copies(objectSecurityLevel));
771            }
772        }
773    }
774    rv = CheckXPCPermissions(aObj, objectSecurityLevel);
775#ifdef DEBUG_mstoltz
776    if(NS_SUCCEEDED(rv))
777        printf("CheckXPCPerms GRANTED.\n");
778    else
779        printf("CheckXPCPerms DENIED.\n");
780#endif
781
782    if (NS_FAILED(rv)) //-- Security tests failed, access is denied, report error
783    {
784        nsAutoString stringName;
785        switch(aAction)
786        {
787        case nsIXPCSecurityManager::ACCESS_GET_PROPERTY:
788            stringName.Assign(NS_LITERAL_STRING("GetPropertyDenied"));
789            break;
790        case nsIXPCSecurityManager::ACCESS_SET_PROPERTY:
791            stringName.Assign(NS_LITERAL_STRING("SetPropertyDenied"));
792            break;
793        case nsIXPCSecurityManager::ACCESS_CALL_METHOD:
794            stringName.Assign(NS_LITERAL_STRING("CallMethodDenied"));
795        }
796
797        NS_ConvertUTF8toUTF16 className(classInfoData.GetName());
798        const PRUnichar *formatStrings[] =
799        {
800            className.get(),
801            JSValIDToString(cx, aProperty)
802        };
803
804        nsXPIDLString errorMsg;
805        // We need to keep our existing failure rv and not override it
806        // with a likely success code from the following string bundle
807        // call in order to throw the correct security exception later.
808        nsresult rv2 = sStrBundle->FormatStringFromName(PromiseFlatString(stringName).get(),
809                                                        formatStrings,
810                                                        NS_ARRAY_LENGTH(formatStrings),
811                                                        getter_Copies(errorMsg));
812        NS_ENSURE_SUCCESS(rv2, rv2);
813 
814        JS_SetPendingException(cx,
815            STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx,
816                NS_REINTERPRET_CAST(const jschar*, errorMsg.get()))));
817
818        nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
819        if (xpc)
820        {
821            nsCOMPtr<nsIXPCNativeCallContext> xpcCallContext;
822            xpc->GetCurrentNativeCallContext(getter_AddRefs(xpcCallContext));
823            if (xpcCallContext)
824                xpcCallContext->SetExceptionWasThrown(PR_TRUE);
825        }
826    }
827
828    return rv;
829}
830
831nsresult
832nsScriptSecurityManager::CheckSameOriginPrincipalInternal(nsIPrincipal* aSubject,
833                                                          nsIPrincipal* aObject,
834                                                          PRBool aIsCheckConnect)
835{
836    /*
837    ** Get origin of subject and object and compare.
838    */
839    if (aSubject == aObject)
840        return NS_OK;
841
842    PRBool isSameOrigin = PR_FALSE;
843    nsresult rv = aSubject->Equals(aObject, &isSameOrigin);
844    NS_ENSURE_SUCCESS(rv, rv);
845
846    if (isSameOrigin)
847    {   // If either the subject or the object has changed its principal by
848        // explicitly setting document.domain then the other must also have
849        // done so in order to be considered the same origin. This prevents
850        // DNS spoofing based on document.domain (154930)
851
852        // But this restriction does not apply to CheckConnect calls, since
853        // that's called for data-only load checks like XMLHTTPRequest, where
854        // the target document has not yet loaded and can't have set its domain
855        // (bug 163950)
856        if (aIsCheckConnect)
857            return NS_OK;
858
859        nsCOMPtr<nsIAggregatePrincipal> subjectAgg(do_QueryInterface(aSubject, &rv));
860        NS_ENSURE_SUCCESS(rv, rv);
861        PRBool subjectDomainChanged = PR_FALSE;
862        subjectAgg->GetDomainChanged(&subjectDomainChanged);
863
864        nsCOMPtr<nsIAggregatePrincipal> objectAgg(do_QueryInterface(aObject, &rv));
865        NS_ENSURE_SUCCESS(rv, rv);
866        PRBool objectDomainChanged = PR_FALSE;
867        objectAgg->GetDomainChanged(&objectDomainChanged);
868
869        // If both or neither explicitly set their domain, allow the access
870        if (!(subjectDomainChanged || objectDomainChanged) ||
871            (subjectDomainChanged && objectDomainChanged))
872            return NS_OK;
873    }
874
875    // Allow access to about:blank
876    nsCOMPtr<nsICodebasePrincipal> objectCodebase(do_QueryInterface(aObject));
877    if (objectCodebase)
878    {
879        nsXPIDLCString origin;
880        rv = objectCodebase->GetOrigin(getter_Copies(origin));
881        NS_ENSURE_SUCCESS(rv, rv);
882        if (nsCRT::strcasecmp(origin, "about:blank") == 0)
883            return NS_OK;
884    }
885
886    /*
887    ** Access tests failed, so now report error.
888    */
889    return NS_ERROR_DOM_PROP_ACCESS_DENIED;
890}
891
892
893nsresult
894nsScriptSecurityManager::CheckSameOriginDOMProp(nsIPrincipal* aSubject,
895                                                nsIPrincipal* aObject,
896                                                PRUint32 aAction,
897                                                PRBool aIsCheckConnect)
898{
899    nsresult rv = CheckSameOriginPrincipalInternal(aSubject, aObject,
900                                                   aIsCheckConnect);
901   
902    if (NS_SUCCEEDED(rv))
903    {
904        return NS_OK;
905    }
906
907    /*
908    * If we failed the origin tests it still might be the case that we
909    * are a signed script and have permissions to do this operation.
910    * Check for that here.
911    */
912    PRBool capabilityEnabled = PR_FALSE;
913    const char* cap = aAction == nsIXPCSecurityManager::ACCESS_SET_PROPERTY ?
914                      "UniversalBrowserWrite" : "UniversalBrowserRead";
915    rv = IsCapabilityEnabled(cap, &capabilityEnabled);
916    NS_ENSURE_SUCCESS(rv, rv);
917    if (capabilityEnabled)
918        return NS_OK;
919
920    /*
921    ** Access tests failed, so now report error.
922    */
923    return NS_ERROR_DOM_PROP_ACCESS_DENIED;
924}
925
926nsresult
927nsScriptSecurityManager::LookupPolicy(nsIPrincipal* aPrincipal,
928                                     const char* aClassName, jsval aProperty,
929                                     PRUint32 aAction,
930                                     ClassPolicy** aCachedClassPolicy,
931                                     SecurityLevel* result)
932{
933    nsresult rv;
934    result->level = SCRIPT_SECURITY_UNDEFINED_ACCESS;
935
936    //-- Initialize policies if necessary
937    if (mPolicyPrefsChanged)
938    {
939        rv = InitPolicies();
940        if (NS_FAILED(rv))
941            return rv;
942    }
943
944    DomainPolicy* dpolicy = nsnull;
945    nsCOMPtr<nsIAggregatePrincipal> agg(do_QueryInterface(aPrincipal));
946    NS_ASSERTION(agg, "Subject principal not an aggregate - this shouldn't happen");
947    if (agg)
948        agg->GetCachedSecurityPolicy((void**)&dpolicy);
949
950    if (!dpolicy && mOriginToPolicyMap)
951    {
952        //-- Look up the relevant domain policy, if any
953#ifdef DEBUG_mstoltz
954        printf("DomainLookup ");
955#endif
956
957        nsCOMPtr<nsICodebasePrincipal> codebase(do_QueryInterface(aPrincipal));
958        if (!codebase)
959            return NS_ERROR_FAILURE;
960
961        nsXPIDLCString origin;
962        if (NS_FAILED(rv = codebase->GetOrigin(getter_Copies(origin))))
963            return rv;
964 
965        const char *start = origin;
966        const char *nextToLastDot = nsnull;
967        const char *lastDot = nsnull;
968        const char *colon = nsnull;
969        const char *p = start;
970        while (*p)
971        {
972            if (*p == '.')
973            {
974                nextToLastDot = lastDot;
975                lastDot = p;
976            }
977            if (!colon && *p == ':')
978                colon = p;
979            p++;
980        }
981
982        nsCStringKey key(nextToLastDot ? nextToLastDot+1 : start);
983        DomainEntry *de = (DomainEntry*) mOriginToPolicyMap->Get(&key);
984        if (!de)
985        {
986            nsCAutoString scheme(start, colon-start+1);
987            nsCStringKey schemeKey(scheme);
988            de = (DomainEntry*) mOriginToPolicyMap->Get(&schemeKey);
989        }
990
991        while (de)
992        {
993            if (de->Matches(start))
994            {
995                dpolicy = de->mDomainPolicy;
996                break;
997            }
998            de = de->mNext;
999        }
1000
1001        if (!dpolicy)
1002            dpolicy = mDefaultPolicy;
1003
1004        agg->SetCachedSecurityPolicy((void*)dpolicy);
1005    }
1006
1007    ClassPolicy* cpolicy = nsnull;
1008
1009    if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
1010    {
1011        // No per-domain policy for this principal (the more common case)
1012        // so look for a cached class policy from the object wrapper
1013        cpolicy = *aCachedClassPolicy;
1014    }
1015
1016    if (!cpolicy)
1017    { //-- No cached policy for this class, need to look it up
1018#ifdef DEBUG_mstoltz
1019        printf("ClassLookup ");
1020#endif
1021
1022        cpolicy = NS_REINTERPRET_CAST(ClassPolicy*,
1023                                      PL_DHashTableOperate(dpolicy,
1024                                                           aClassName,
1025                                                           PL_DHASH_LOOKUP));
1026
1027        if (PL_DHASH_ENTRY_IS_FREE(cpolicy))
1028            cpolicy = NO_POLICY_FOR_CLASS;
1029
1030        if ((dpolicy == mDefaultPolicy) && aCachedClassPolicy)
1031            *aCachedClassPolicy = cpolicy;
1032    }
1033    PropertyPolicy* ppolicy = nsnull;
1034    if (cpolicy != NO_POLICY_FOR_CLASS)
1035    {
1036        ppolicy = NS_REINTERPRET_CAST(PropertyPolicy*,
1037                                      PL_DHashTableOperate(cpolicy->mPolicy,
1038                                                           (void*)aProperty,
1039                                                           PL_DHASH_LOOKUP));
1040    }
1041    else
1042    {
1043        // If there's no per-domain policy and no default policy, we're done
1044        if (dpolicy == mDefaultPolicy)
1045            return NS_OK;
1046
1047        // This class is not present in the domain policy, check its wildcard policy
1048        if (dpolicy->mWildcardPolicy)
1049        {
1050            ppolicy =
1051              NS_REINTERPRET_CAST(PropertyPolicy*,
1052                                  PL_DHashTableOperate(dpolicy->mWildcardPolicy->mPolicy,
1053                                                       (void*)aProperty,
1054                                                       PL_DHASH_LOOKUP));
1055        }
1056
1057        // If there's no wildcard policy, check the default policy for this class
1058        if (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy))
1059        {
1060            cpolicy = NS_REINTERPRET_CAST(ClassPolicy*,
1061                                          PL_DHashTableOperate(mDefaultPolicy,
1062                                                               aClassName,
1063                                                               PL_DHASH_LOOKUP));
1064
1065            if (PL_DHASH_ENTRY_IS_BUSY(cpolicy))
1066            {
1067                ppolicy =
1068                  NS_REINTERPRET_CAST(PropertyPolicy*,
1069                                      PL_DHashTableOperate(cpolicy->mPolicy,
1070                                                           (void*)aProperty,
1071                                                           PL_DHASH_LOOKUP));
1072            }
1073        }
1074    }
1075
1076    if (!ppolicy || PL_DHASH_ENTRY_IS_FREE(ppolicy))
1077        return NS_OK;
1078
1079    // Get the correct security level from the property policy
1080    if (aAction == nsIXPCSecurityManager::ACCESS_SET_PROPERTY)
1081        *result = ppolicy->mSet;
1082    else
1083        *result = ppolicy->mGet;
1084
1085    return NS_OK;
1086}
1087
1088
1089NS_IMETHODIMP
1090nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
1091{
1092    // Get principal of currently executing script.
1093    nsCOMPtr<nsIPrincipal> principal;
1094    if (NS_FAILED(GetSubjectPrincipal(cx, getter_AddRefs(principal))))
1095        return NS_ERROR_FAILURE;
1096
1097    // Native code can load all URIs.
1098    if (!principal)
1099        return NS_OK;
1100
1101    // The system principal can load all URIs.
1102    PRBool equals = PR_FALSE;
1103    if (NS_FAILED(principal->Equals(mSystemPrincipal, &equals)))
1104        return NS_ERROR_FAILURE;
1105    if (equals)
1106        return NS_OK;
1107
1108    // Otherwise, principal should have a codebase that we can use to
1109    // do the remaining tests.
1110    nsCOMPtr<nsICodebasePrincipal> codebase(do_QueryInterface(principal));
1111    if (!codebase)
1112        return NS_ERROR_FAILURE;
1113    nsCOMPtr<nsIURI> uri;
1114    if (NS_FAILED(codebase->GetURI(getter_AddRefs(uri))))
1115        return NS_ERROR_FAILURE;
1116    if (NS_SUCCEEDED(CheckLoadURI(uri, aURI, nsIScriptSecurityManager::STANDARD )))
1117        return NS_OK;
1118
1119    // See if we're attempting to load a file: URI. If so, let a
1120    // UniversalFileRead capability trump the above check.
1121    PRBool isFile = PR_FALSE;
1122    PRBool isRes = PR_FALSE;
1123    if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
1124        NS_FAILED(aURI->SchemeIs("resource", &isRes)))
1125        return NS_ERROR_FAILURE;
1126    if (isFile || isRes)
1127    {
1128        PRBool enabled;
1129        if (NS_FAILED(IsCapabilityEnabled("UniversalFileRead", &enabled)))
1130            return NS_ERROR_FAILURE;
1131        if (enabled)
1132            return NS_OK;
1133    }
1134
1135    // Report error.
1136    nsCAutoString spec;
1137    if (NS_FAILED(aURI->GetAsciiSpec(spec)))
1138        return NS_ERROR_FAILURE;
1139    JS_ReportError(cx, "Access to '%s' from script denied", spec.get());
1140    return NS_ERROR_DOM_BAD_URI;
1141}
1142
1143nsresult
1144nsScriptSecurityManager::GetBaseURIScheme(nsIURI* aURI, char** aScheme)
1145{
1146    if (!aURI)
1147       return NS_ERROR_FAILURE;
1148
1149    nsresult rv;
1150    nsCOMPtr<nsIURI> uri(aURI);
1151
1152    //-- get the source scheme
1153    nsCAutoString scheme;
1154    rv = uri->GetScheme(scheme);
1155    if (NS_FAILED(rv)) return rv;
1156
1157    //-- If uri is a view-source URI, drill down to the base URI
1158    nsCAutoString path;
1159    while(PL_strcmp(scheme.get(), "view-source") == 0)
1160    {
1161        rv = uri->GetPath(path);
1162        if (NS_FAILED(rv)) return rv;
1163        rv = NS_NewURI(getter_AddRefs(uri), path, nsnull);
1164        if (NS_FAILED(rv)) return rv;
1165        rv = uri->GetScheme(scheme);
1166        if (NS_FAILED(rv)) return rv;
1167    }
1168
1169    //-- If uri is a jar URI, drill down again
1170    nsCOMPtr<nsIJARURI> jarURI;
1171    PRBool isJAR = PR_FALSE;
1172    while((jarURI = do_QueryInterface(uri)))
1173    {
1174        jarURI->GetJARFile(getter_AddRefs(uri));
1175        isJAR = PR_TRUE;
1176    }
1177    if (!uri) return NS_ERROR_FAILURE;
1178    if (isJAR)
1179    {
1180        rv = uri->GetScheme(scheme);
1181        if (NS_FAILED(rv)) return rv;
1182    }
1183
1184    //-- if uri is an about uri, distinguish 'safe' and 'unsafe' about URIs
1185    static const char aboutScheme[] = "about";
1186    if(nsCRT::strcasecmp(scheme.get(), aboutScheme) == 0)
1187    {
1188        nsCAutoString spec;
1189        if(NS_FAILED(uri->GetAsciiSpec(spec)))
1190            return NS_ERROR_FAILURE;
1191        const char* page = spec.get() + sizeof(aboutScheme);
1192        if ((strcmp(page, "blank") == 0)   ||
1193            (strcmp(page, "") == 0)        ||
1194            (strcmp(page, "mozilla") == 0) ||
1195            (strcmp(page, "credits") == 0))
1196        {
1197            *aScheme = nsCRT::strdup("about safe");
1198            return *aScheme ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1199        }
1200    }
1201
1202    *aScheme = nsCRT::strdup(scheme.get());
1203    return *aScheme ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1204}
1205
1206NS_IMETHODIMP
1207nsScriptSecurityManager::CheckLoadURI(nsIURI *aSourceURI, nsIURI *aTargetURI,
1208                                      PRUint32 aFlags)
1209{
1210    nsresult rv;
1211    //-- get the source scheme
1212    nsXPIDLCString sourceScheme;
1213    rv = GetBaseURIScheme(aSourceURI, getter_Copies(sourceScheme));
1214    if (NS_FAILED(rv)) return rv;
1215
1216    // Some loads are not allowed from mail/news messages
1217    if ((aFlags & nsIScriptSecurityManager::DISALLOW_FROM_MAIL) &&
1218        (nsCRT::strcasecmp(sourceScheme, "mailbox")  == 0 ||
1219         nsCRT::strcasecmp(sourceScheme, "imap")     == 0 ||
1220         nsCRT::strcasecmp(sourceScheme, "news")     == 0))
1221    {
1222        return NS_ERROR_DOM_BAD_URI;
1223    }
1224
1225    //-- get the target scheme
1226    nsXPIDLCString targetScheme;
1227    rv = GetBaseURIScheme(aTargetURI, getter_Copies(targetScheme));
1228    if (NS_FAILED(rv)) return rv;
1229
1230    if (nsCRT::strcasecmp(targetScheme, sourceScheme) == 0)
1231    {
1232        // every scheme can access another URI from the same scheme
1233        return NS_OK;
1234    }
1235
1236    //-- Some callers do not allow loading javascript: or data: URLs
1237    if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT_OR_DATA) &&
1238        (targetScheme.Equals("javascript") || targetScheme.Equals("data")))
1239    {
1240       return NS_ERROR_DOM_BAD_URI;
1241    }
1242
1243    //-- If the schemes don't match, the policy is specified in this table.
1244    enum Action { AllowProtocol, DenyProtocol, PrefControlled, ChromeProtocol};
1245    static const struct
1246    {
1247        const char *name;
1248        Action action;
1249    } protocolList[] =
1250    {
1251        //-- Keep the most commonly used protocols at the top of the list
1252        //   to increase performance
1253        { "http",            AllowProtocol  },
1254        { "chrome",          ChromeProtocol },
1255        { "file",            PrefControlled },
1256        { "https",           AllowProtocol  },
1257        { "mailbox",         DenyProtocol   },
1258        { "pop",             AllowProtocol  },
1259        { "imap",            DenyProtocol   },
1260        { "pop3",            DenyProtocol   },
1261        { "news",            AllowProtocol  },
1262        { "javascript",      AllowProtocol  },
1263        { "ftp",             AllowProtocol  },
1264        { "about safe",      AllowProtocol  },
1265        { "about",           DenyProtocol   },
1266        { "mailto",          AllowProtocol  },
1267        { "aim",             AllowProtocol  },
1268        { "data",            AllowProtocol  },
1269        { "keyword",         DenyProtocol   },
1270        { "resource",        ChromeProtocol },
1271        { "gopher",          AllowProtocol  },
1272        { "datetime",        DenyProtocol   },
1273        { "finger",          AllowProtocol  },
1274        { "res",             DenyProtocol   },
1275        { "x-jsd",           ChromeProtocol }
1276    };
1277
1278    NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
1279    for (unsigned i=0; i < sizeof(protocolList)/sizeof(protocolList[0]); i++)
1280    {
1281        if (nsCRT::strcasecmp(targetScheme, protocolList[i].name) == 0)
1282        {
1283            PRBool doCheck = PR_FALSE;
1284            switch (protocolList[i].action)
1285            {
1286            case AllowProtocol:
1287                // everyone can access these schemes.
1288                return NS_OK;
1289            case PrefControlled:
1290                // Allow access if pref is false
1291                {
1292                    mSecurityPref->SecurityGetBoolPref("security.checkloaduri", &doCheck);
1293                    if (doCheck)
1294                    {
1295                        // resource: and chrome: are equivalent, securitywise
1296                        if ((PL_strcmp(sourceScheme, "chrome") == 0) ||
1297                            (PL_strcmp(sourceScheme, "resource") == 0))
1298                            return NS_OK;
1299
1300                        ReportError(nsnull, errorTag, aSourceURI, aTargetURI);
1301                        return NS_ERROR_DOM_BAD_URI;
1302                    }
1303                    return NS_OK;
1304                }
1305            case ChromeProtocol:
1306                if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME)
1307                    return NS_OK;
1308                // resource: and chrome: are equivalent, securitywise
1309                if ((PL_strcmp(sourceScheme, "chrome") == 0) ||
1310                    (PL_strcmp(sourceScheme, "resource") == 0))
1311                    return NS_OK;
1312                ReportError(nsnull, errorTag, aSourceURI, aTargetURI);
1313                return NS_ERROR_DOM_BAD_URI;
1314            case DenyProtocol:
1315                // Deny access
1316                ReportError(nsnull, errorTag, aSourceURI, aTargetURI);
1317                return NS_ERROR_DOM_BAD_URI;
1318            }
1319        }
1320    }
1321
1322    // If we reach here, we have an unknown protocol. Warn, but allow.
1323    // This is risky from a security standpoint, but allows flexibility
1324    // in installing new protocol handlers after initial ship.
1325    NS_WARNING("unknown protocol in nsScriptSecurityManager::CheckLoadURI");
1326
1327    return NS_OK;
1328}
1329
1330nsresult
1331nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
1332                                     nsIURI* aSource, nsIURI* aTarget)
1333{
1334    nsresult rv;
1335    NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
1336
1337    // Get the source URL spec
1338    nsCAutoString sourceSpec;
1339    rv = aSource->GetAsciiSpec(sourceSpec);
1340    NS_ENSURE_SUCCESS(rv, rv);
1341
1342    // Get the target URL spec
1343    nsCAutoString targetSpec;
1344    rv = aTarget->GetAsciiSpec(targetSpec);
1345    NS_ENSURE_SUCCESS(rv, rv);
1346
1347    // Localize the error message
1348    nsXPIDLString message;
1349    NS_ConvertASCIItoUCS2 ucsSourceSpec(sourceSpec);
1350    NS_ConvertASCIItoUCS2 ucsTargetSpec(targetSpec);
1351    const PRUnichar *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
1352    rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
1353                                          formatStrings,
1354                                          NS_ARRAY_LENGTH(formatStrings),
1355                                          getter_Copies(message));
1356    NS_ENSURE_SUCCESS(rv, rv);
1357
1358    // If a JS context was passed in, set a JS exception.
1359    // Otherwise, print the error message directly to the JS console
1360    // and to standard output
1361    if (cx)
1362    {
1363        JS_SetPendingException(cx,
1364            STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx,
1365                NS_REINTERPRET_CAST(const jschar*, message.get()))));
1366        // Tell XPConnect that an exception was thrown, if appropriate
1367        nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
1368        if (xpc)
1369        {
1370            nsCOMPtr<nsIXPCNativeCallContext> xpcCallContext;
1371            xpc->GetCurrentNativeCallContext(getter_AddRefs(xpcCallContext));
1372             if (xpcCallContext)
1373                xpcCallContext->SetExceptionWasThrown(PR_TRUE);
1374        }
1375    }
1376    else // Print directly to the console
1377    {
1378        nsCOMPtr<nsIConsoleService> console(
1379            do_GetService("@mozilla.org/consoleservice;1"));
1380        NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1381
1382        console->LogStringMessage(message.get());
1383#ifdef DEBUG
1384        fprintf(stderr, "%s\n", NS_LossyConvertUCS2toASCII(message).get());
1385#endif
1386    }
1387    return NS_OK;
1388}
1389
1390NS_IMETHODIMP
1391nsScriptSecurityManager::CheckLoadURIStr(const char* aSourceURIStr, const char* aTargetURIStr,
1392                                         PRUint32 aFlags)
1393{
1394    nsCOMPtr<nsIURI> source;
1395    nsresult rv = NS_NewURI(getter_AddRefs(source), nsDependentCString(aSourceURIStr), nsnull);
1396    NS_ENSURE_SUCCESS(rv, rv);
1397    nsCOMPtr<nsIURI> target;
1398    rv = NS_NewURI(getter_AddRefs(target), nsDependentCString(aTargetURIStr), nsnull);
1399    NS_ENSURE_SUCCESS(rv, rv);
1400    return CheckLoadURI(source, target, aFlags);
1401}
1402
1403NS_IMETHODIMP
1404nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
1405                                             void *aTargetObj)
1406{
1407    //-- This check is called for event handlers
1408    nsCOMPtr<nsIPrincipal> subject;
1409    nsresult rv = GetFunctionObjectPrincipal(aCx, (JSObject *)aFunObj,
1410                                             getter_AddRefs(subject));
1411    //-- If subject is null, get a principal from the function object's scope.
1412    if (NS_SUCCEEDED(rv) && !subject)
1413    {
1414#ifdef DEBUG
1415        {
1416            JSFunction *fun =
1417                (JSFunction *)JS_GetPrivate(aCx, (JSObject *)aFunObj);
1418            JSScript *script = JS_GetFunctionScript(aCx, fun);
1419
1420            NS_ASSERTION(!script, "Null principal for non-native function!");
1421        }
1422#endif
1423
1424        rv = doGetObjectPrincipal(aCx, (JSObject*)aFunObj,
1425                                  getter_AddRefs(subject));
1426    }
1427
1428    if (NS_FAILED(rv)) return rv;
1429    if (!subject) return NS_ERROR_FAILURE;
1430
1431
1432    PRBool isSystem;
1433    if (NS_SUCCEEDED(subject->Equals(mSystemPrincipal, &isSystem)) && isSystem)
1434        // This is the system principal: just allow access
1435        return NS_OK;
1436
1437    // Check if the principal the function was compiled under is
1438    // allowed to execute scripts.
1439
1440    PRBool result;
1441    rv = CanExecuteScripts(aCx, subject, &result);
1442    if (NS_FAILED(rv))
1443      return rv;
1444
1445    if (!result)
1446      return NS_ERROR_DOM_SECURITY_ERR;
1447
1448    /*
1449    ** Get origin of subject and object and compare.
1450    */
1451    JSObject* obj = (JSObject*)aTargetObj;
1452    nsCOMPtr<nsIPrincipal> object;
1453    if (NS_FAILED(doGetObjectPrincipal(aCx, obj, getter_AddRefs(object))))
1454        return NS_ERROR_FAILURE;
1455    if (subject == object)
1456        return NS_OK;
1457
1458    PRBool isSameOrigin = PR_FALSE;
1459    if (NS_FAILED(subject->Equals(object, &isSameOrigin)))
1460        return NS_ERROR_FAILURE;
1461
1462    if (isSameOrigin)
1463        return NS_OK;
1464
1465    // Allow access to about:blank
1466    nsCOMPtr<nsICodebasePrincipal> objectCodebase(do_QueryInterface(object));
1467    if (objectCodebase)
1468    {
1469        nsXPIDLCString origin;
1470        if (NS_FAILED(objectCodebase->GetOrigin(getter_Copies(origin))))
1471            return NS_ERROR_FAILURE;
1472        if (nsCRT::strcasecmp(origin, "about:blank") == 0)
1473            return NS_OK;
1474    }
1475
1476    /*
1477    ** Access tests failed.  Fail silently without a JS exception.
1478    */
1479    return NS_ERROR_DOM_SECURITY_ERR;
1480}
1481
1482nsresult
1483nsScriptSecurityManager::GetRootDocShell(JSContext *cx, nsIDocShell **result)
1484{
1485    nsresult rv;
1486    *result = nsnull;
1487    nsCOMPtr<nsIDocShell> docshell;
1488    nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContext(cx);
1489    if (!scriptContext) return NS_ERROR_FAILURE;
1490    nsCOMPtr<nsIScriptGlobalObject> globalObject;
1491    scriptContext->GetGlobalObject(getter_AddRefs(globalObject));
1492    if (!globalObject)  return NS_ERROR_FAILURE;
1493    rv = globalObject->GetDocShell(getter_AddRefs(docshell));
1494    if (NS_FAILED(rv)) return rv;
1495    nsCOMPtr<nsIDocShellTreeItem> docshellTreeItem(do_QueryInterface(docshell, &rv));
1496    if (NS_FAILED(rv)) return rv;
1497    nsCOMPtr<nsIDocShellTreeItem> rootItem;
1498    rv = docshellTreeItem->GetRootTreeItem(getter_AddRefs(rootItem));
1499    if (NS_FAILED(rv)) return rv;
1500
1501    return rootItem->QueryInterface(NS_GET_IID(nsIDocShell), (void**)result);
1502}
1503
1504NS_IMETHODIMP
1505nsScriptSecurityManager::CanExecuteScripts(JSContext* cx,
1506                                           nsIPrincipal *aPrincipal,
1507                                           PRBool *result)
1508{
1509    *result = PR_FALSE;
1510
1511    if (aPrincipal == mSystemPrincipal)
1512    {
1513        // Even if JavaScript is disabled, we must still execute system scripts
1514        *result = PR_TRUE;
1515        return NS_OK;
1516    }
1517
1518    //-- Always allow chrome pages to run scripts
1519    //   This is for about: URLs, which are chrome but don't
1520    //   have the system principal
1521    nsresult rv;
1522    if (!mIsJavaScriptEnabled)
1523    {
1524        nsCOMPtr<nsICodebasePrincipal> codebase(do_QueryInterface(aPrincipal));
1525        if (codebase)
1526        {
1527            nsXPIDLCString origin;
1528            rv = codebase->GetOrigin(getter_Copies(origin));
1529            static const char chromePrefix[] = "chrome:";
1530            if (NS_SUCCEEDED(rv) &&
1531                (PL_strncmp(origin, chromePrefix, sizeof(chromePrefix)-1) == 0))
1532            {
1533                *result = PR_TRUE;
1534                return NS_OK;
1535            }
1536        }
1537    }
1538
1539    //-- See if the current window allows JS execution
1540    nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContext(cx);
1541    if (!scriptContext) return NS_ERROR_FAILURE;
1542    nsCOMPtr<nsIScriptGlobalObject> globalObject;
1543    scriptContext->GetGlobalObject(getter_AddRefs(globalObject));
1544    if (!globalObject) return NS_ERROR_FAILURE;
1545   
1546    nsCOMPtr<nsIDocShell> docshell;
1547    globalObject->GetDocShell(getter_AddRefs(docshell));
1548    nsCOMPtr<nsIDocShellTreeItem> treeItem;
1549    if (docshell)
1550    {
1551        treeItem = do_QueryInterface(docshell);
1552        nsCOMPtr<nsIDocShellTreeItem> parentItem;
1553        // Walk up the docshell tree to see if any containing docshell disallows scripts
1554        do
1555        {
1556            rv = docshell->GetAllowJavascript(result);
1557            if (NS_FAILED(rv)) return rv;
1558            if (!*result)
1559                return NS_OK; // Do not run scripts
1560            if (treeItem)
1561            {
1562                treeItem->GetParent(getter_AddRefs(parentItem));
1563                if (parentItem)
1564                {
1565                    treeItem = parentItem;
1566                    docshell = do_QueryInterface(treeItem, &rv);
1567                    NS_ASSERTION(docshell, "cannot get a docshell from a treeItem!");
1568                    if (NS_FAILED(rv)) break;
1569                }
1570            }
1571        } while (parentItem);
1572    }
1573
1574    //-- See if JS is disabled globally (via prefs)
1575    *result = mIsJavaScriptEnabled;
1576    if (mIsJavaScriptEnabled != mIsMailJavaScriptEnabled)
1577    {
1578        // Get docshell from the global window again.
1579        globalObject->GetDocShell(getter_AddRefs(docshell));
1580        treeItem = do_QueryInterface(docshell);
1581        if (treeItem)
1582        {
1583            nsCOMPtr<nsIDocShellTreeItem> rootItem;
1584            treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
1585            docshell = do_QueryInterface(rootItem);
1586            if (docshell)
1587            {
1588                // Is this script running from mail?
1589                PRUint32 appType;
1590                rv = docshell->GetAppType(&appType);
1591                if (NS_FAILED(rv)) return rv;
1592                if (appType == nsIDocShell::APP_TYPE_MAIL)
1593                {
1594                    *result = mIsMailJavaScriptEnabled;
1595                }
1596            }
1597        }
1598    }
1599   
1600    if (!*result)
1601        return NS_OK; // Do not run scripts
1602
1603    //-- Check for a per-site policy
1604    static const char jsPrefGroupName[] = "javascript";
1605
1606    SecurityLevel secLevel;
1607    rv = LookupPolicy(aPrincipal, (char*)jsPrefGroupName, sEnabledID,
1608                      nsIXPCSecurityManager::ACCESS_GET_PROPERTY,
1609                      nsnull, &secLevel);
1610    if (NS_FAILED(rv) || secLevel.level == SCRIPT_SECURITY_NO_ACCESS)
1611    {
1612        *result = PR_FALSE;
1613        return rv;
1614    }
1615
1616    //-- Nobody vetoed, so allow the JS to run.
1617    *result = PR_TRUE;
1618    return NS_OK;
1619}
1620
1621///////////////// Principals ///////////////////////
1622NS_IMETHODIMP
1623nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **result)
1624{
1625    JSContext *cx = GetCurrentJSContext();
1626    if (!cx)
1627    {
1628        *result = nsnull;
1629        return NS_OK;
1630    }
1631    return GetSubjectPrincipal(cx, result);
1632}
1633
1634NS_IMETHODIMP
1635nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
1636{
1637    if (!mSystemPrincipal)
1638    {
1639        mSystemPrincipal = new nsSystemPrincipal();
1640        if (!mSystemPrincipal)
1641            return NS_ERROR_OUT_OF_MEMORY;
1642        NS_ADDREF(mSystemPrincipal);
1643    }
1644    *result = mSystemPrincipal;
1645    NS_ADDREF(*result);
1646    return NS_OK;
1647}
1648
1649NS_IMETHODIMP
1650nsScriptSecurityManager::SubjectPrincipalIsSystem(PRBool* aIsSystem)
1651{
1652    NS_ENSURE_ARG_POINTER(aIsSystem);
1653    *aIsSystem = PR_FALSE;
1654
1655    if (!mSystemPrincipal)
1656        return NS_OK;
1657
1658    nsCOMPtr<nsIPrincipal> subject;
1659    nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
1660    if (NS_FAILED(rv))
1661        return rv;
1662
1663    if(!subject)
1664    {
1665        // No subject principal means no JS is running;
1666        // this is the equivalent of system principal code
1667        *aIsSystem = PR_TRUE;
1668        return NS_OK;
1669    }
1670
1671    return mSystemPrincipal->Equals(subject, aIsSystem);
1672}
1673
1674NS_IMETHODIMP
1675nsScriptSecurityManager::GetCertificatePrincipal(const char* aCertID,
1676                                                 nsIPrincipal **result)
1677{
1678    nsresult rv;
1679    //-- Create a certificate principal
1680    nsCertificatePrincipal *certificate = new nsCertificatePrincipal();
1681    if (!certificate)
1682        return NS_ERROR_OUT_OF_MEMORY;
1683    NS_ADDREF(certificate);
1684    if (NS_FAILED(certificate->Init(aCertID)))
1685    {
1686        NS_RELEASE(certificate);
1687        return NS_ERROR_FAILURE;
1688    }
1689    nsCOMPtr<nsIPrincipal> principal(do_QueryInterface((nsBasePrincipal*)certificate, &rv));
1690    NS_RELEASE(certificate);
1691    if (NS_FAILED(rv)) return rv;
1692
1693    if (mPrincipals)
1694    {
1695        // Check to see if we already have this principal.
1696        nsIPrincipalKey key(principal);
1697        nsCOMPtr<nsIPrincipal> fromTable = (nsIPrincipal *) mPrincipals->Get(&key);
1698        if (fromTable)
1699            principal = fromTable;
1700    }
1701
1702    //-- Bundle this certificate principal into an aggregate principal
1703    nsAggregatePrincipal* agg = new nsAggregatePrincipal();
1704    if (!agg) return NS_ERROR_OUT_OF_MEMORY;
1705    rv = agg->SetCertificate(principal);
1706    if (NS_FAILED(rv)) return rv;
1707    principal = do_QueryInterface((nsBasePrincipal*)agg, &rv);
1708    if (NS_FAILED(rv)) return rv;
1709
1710    *result = principal;
1711    NS_ADDREF(*result);
1712    return NS_OK;
1713}
1714
1715nsresult
1716nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, nsIPrincipal **result)
1717{
1718    nsresult rv = NS_OK;
1719    nsCodebasePrincipal *codebase = new nsCodebasePrincipal();
1720    if (!codebase)
1721        return NS_ERROR_OUT_OF_MEMORY;
1722    NS_ADDREF(codebase);
1723    if (NS_FAILED(codebase->Init(aURI)))
1724    {
1725        NS_RELEASE(codebase);
1726        return NS_ERROR_FAILURE;
1727    }
1728    rv = CallQueryInterface((nsBasePrincipal*)codebase, result);
1729    NS_RELEASE(codebase);
1730    return rv;
1731}
1732
1733NS_IMETHODIMP
1734nsScriptSecurityManager::GetCodebasePrincipal(nsIURI *aURI,
1735                                              nsIPrincipal **result)
1736{
1737    nsresult rv;
1738    nsCOMPtr<nsIPrincipal> principal;
1739    rv = CreateCodebasePrincipal(aURI, getter_AddRefs(principal));
1740    if (NS_FAILED(rv)) return rv;
1741
1742    if (mPrincipals)
1743    {
1744        //-- Check to see if we already have this principal.
1745        nsIPrincipalKey key(principal);
1746        nsCOMPtr<nsIPrincipal> fromTable = (nsIPrincipal *) mPrincipals->Get(&key);
1747        if (fromTable)
1748            principal = fromTable;
1749        else //-- Check to see if we have a more general principal
1750        {
1751            nsCOMPtr<nsICodebasePrincipal> codebasePrin(do_QueryInterface(principal));
1752            nsXPIDLCString originUrl;
1753            rv = codebasePrin->GetOrigin(getter_Copies(originUrl));
1754            if (NS_FAILED(rv)) return rv;
1755            nsCOMPtr<nsIURI> newURI;
1756            rv = NS_NewURI(getter_AddRefs(newURI), originUrl, nsnull);
1757            if (NS_FAILED(rv)) return rv;
1758            nsCOMPtr<nsIPrincipal> principal2;
1759            rv = CreateCodebasePrincipal(newURI, getter_AddRefs(principal2));
1760            if (NS_FAILED(rv)) return rv;
1761             nsIPrincipalKey key2(principal2);
1762                fromTable = (nsIPrincipal *) mPrincipals->Get(&key2);
1763            if (fromTable)
1764                principal = fromTable;
1765        }               
1766    }
1767
1768    //-- Bundle this codebase principal into an aggregate principal
1769    nsAggregatePrincipal* agg = new nsAggregatePrincipal();
1770    if (!agg) return NS_ERROR_OUT_OF_MEMORY;
1771    rv = agg->SetCodebase(principal);
1772    if (NS_FAILED(rv)) return rv;
1773    principal = do_QueryInterface((nsBasePrincipal*)agg, &rv);
1774    if (NS_FAILED(rv)) return rv;
1775
1776    *result = principal;
1777    NS_ADDREF(*result);
1778    return NS_OK;
1779}
1780
1781nsresult
1782nsScriptSecurityManager::GetPrincipalFromContext(JSContext *cx,
1783                                                 nsIPrincipal **result)
1784{
1785    *result = nsnull;
1786
1787    nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContext(cx);
1788
1789    if (!scriptContext)
1790    {
1791        return NS_ERROR_FAILURE;
1792    }
1793
1794    nsCOMPtr<nsIScriptGlobalObject> global;
1795    scriptContext->GetGlobalObject(getter_AddRefs(global));
1796    nsCOMPtr<nsIScriptObjectPrincipal> globalData(do_QueryInterface(global));
1797    if (globalData)
1798        globalData->GetPrincipal(result);
1799
1800    return NS_OK;
1801}
1802
1803nsresult
1804nsScriptSecurityManager::GetScriptPrincipal(JSContext *cx,
1805                                            JSScript *script,
1806                                            nsIPrincipal **result)
1807{
1808    if (!script)
1809    {
1810        *result = nsnull;
1811        return NS_OK;
1812    }
1813    JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
1814    if (!jsp) {
1815        // Script didn't have principals -- shouldn't happen.
1816        return NS_ERROR_FAILURE;
1817    }
1818    nsJSPrincipals *nsJSPrin = NS_STATIC_CAST(nsJSPrincipals *, jsp);
1819    *result = nsJSPrin->nsIPrincipalPtr;
1820    if (!*result)
1821        return NS_ERROR_FAILURE;
1822    NS_ADDREF(*result);
1823    return NS_OK;
1824
1825}
1826
1827nsresult
1828nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
1829                                                    JSObject *obj,
1830                                                    nsIPrincipal **result)
1831{
1832    JSFunction *fun = (JSFunction *) JS_GetPrivate(cx, obj);
1833    JSScript *script = JS_GetFunctionScript(cx, fun);
1834
1835    nsCOMPtr<nsIPrincipal> scriptPrincipal;
1836    if (script)
1837    {
1838        if (JS_GetFunctionObject(fun) != obj)
1839        {
1840            // Function is a clone, its prototype was precompiled from
1841            // brutally shared chrome. For this case only, get the
1842            // principals from the clone's scope since there's no
1843            // reliable principals compiled into the function.
1844            return doGetObjectPrincipal(cx, obj, result);
1845        }
1846
1847        if (NS_FAILED(GetScriptPrincipal(cx, script,
1848                                         getter_AddRefs(scriptPrincipal))))
1849            return NS_ERROR_FAILURE;
1850
1851    }
1852
1853    *result = scriptPrincipal.get();
1854    NS_IF_ADDREF(*result);
1855    return NS_OK;
1856}
1857
1858nsresult
1859nsScriptSecurityManager::GetFramePrincipal(JSContext *cx,
1860                                           JSStackFrame *fp,
1861                                           nsIPrincipal **result)
1862{
1863    JSObject *obj = JS_GetFrameFunctionObject(cx, fp);
1864    if (!obj)
1865    {
1866        // Must be in a top-level script. Get principal from the script.
1867        JSScript *script = JS_GetFrameScript(cx, fp);
1868        return GetScriptPrincipal(cx, script, result);
1869    }
1870
1871    nsresult rv = GetFunctionObjectPrincipal(cx, obj, result);
1872
1873#ifdef DEBUG
1874    if (NS_SUCCEEDED(rv) && !*result)
1875    {
1876        JSFunction *fun = (JSFunction *)JS_GetPrivate(cx, obj);
1877        JSScript *script = JS_GetFunctionScript(cx, fun);
1878
1879        NS_ASSERTION(!script, "Null principal for non-native function!");
1880    }
1881#endif
1882
1883    return rv;
1884}
1885
1886nsresult
1887nsScriptSecurityManager::GetPrincipalAndFrame(JSContext *cx,
1888                                              nsIPrincipal **result,
1889                                              JSStackFrame **frameResult)
1890{
1891    // Get principals from innermost frame of JavaScript or Java.
1892    JSStackFrame *fp = nsnull; // tell JS_FrameIterator to start at innermost
1893    for (fp = JS_FrameIterator(cx, &fp); fp; fp = JS_FrameIterator(cx, &fp))
1894    {
1895        if (NS_FAILED(GetFramePrincipal(cx, fp, result)))
1896            return NS_ERROR_FAILURE;
1897        if (*result)
1898        {
1899            *frameResult = fp;
1900            return NS_OK;
1901        }
1902    }
1903
1904    //-- If there's no principal on the stack, look at the global object
1905    //   and return the innermost frame for annotations.
1906    if (cx)
1907    {
1908        nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContext(cx);
1909        if (scriptContext)
1910        {
1911            nsCOMPtr<nsIScriptGlobalObject> global;
1912            scriptContext->GetGlobalObject(getter_AddRefs(global));
1913            NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
1914            nsCOMPtr<nsIScriptObjectPrincipal> globalData(do_QueryInterface(global));
1915            NS_ENSURE_TRUE(globalData, NS_ERROR_FAILURE);
1916            globalData->GetPrincipal(result);
1917            if (*result)
1918            {
1919                JSStackFrame *inner = nsnull;
1920                *frameResult = JS_FrameIterator(cx, &inner);
1921                return NS_OK;
1922            }
1923        }
1924    }
1925
1926    *result = nsnull;
1927    return NS_OK;
1928}
1929
1930nsresult
1931nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
1932                                             nsIPrincipal **result)
1933{
1934    JSStackFrame *fp;
1935    return GetPrincipalAndFrame(cx, result, &fp);
1936}
1937
1938NS_IMETHODIMP
1939nsScriptSecurityManager::GetObjectPrincipal(JSContext *aCx, JSObject *aObj,
1940                                            nsIPrincipal **result)
1941{
1942    return doGetObjectPrincipal(aCx, aObj, result);
1943}
1944
1945// static
1946nsresult
1947nsScriptSecurityManager::doGetObjectPrincipal(JSContext *aCx, JSObject *aObj,
1948                                              nsIPrincipal **result)
1949{
1950    NS_ASSERTION(aCx && aObj && result, "Bad call to doGetObjectPrincipal()!");
1951
1952    do
1953    {
1954        const JSClass *jsClass = JS_GetClass(aCx, aObj);
1955
1956        if (jsClass && !(~jsClass->flags & (JSCLASS_HAS_PRIVATE |
1957                                            JSCLASS_PRIVATE_IS_NSISUPPORTS)))
1958        {
1959            // No need to refcount |priv| here.
1960            nsISupports *priv = (nsISupports *)JS_GetPrivate(aCx, aObj);
1961            nsCOMPtr<nsIScriptObjectPrincipal> objPrin;
1962
1963            /*
1964             * If it's a wrapped native (as most
1965             * JSCLASS_PRIVATE_IS_NSISUPPORTS objects are in mozilla),
1966             * check the underlying native instead.
1967             */
1968            nsCOMPtr<nsIXPConnectWrappedNative> xpcWrapper =
1969                do_QueryInterface(priv);
1970
1971            if (xpcWrapper)
1972            {
1973                nsCOMPtr<nsISupports> supports;
1974                xpcWrapper->GetNative(getter_AddRefs(supports));
1975
1976                objPrin = do_QueryInterface(supports);
1977            }
1978            else
1979            {
1980                objPrin = do_QueryInterface(priv);
1981            }
1982
1983            if (objPrin && NS_SUCCEEDED(objPrin->GetPrincipal(result)))
1984            {
1985                return NS_OK;
1986            }
1987        }
1988
1989        aObj = JS_GetParent(aCx, aObj);
1990    } while (aObj);
1991
1992    // Couldn't find a principal for this object.
1993    return NS_ERROR_FAILURE;
1994}
1995
1996nsresult
1997nsScriptSecurityManager::SavePrincipal(nsIPrincipal* aToSave)
1998{
1999    nsresult rv;
2000    nsCOMPtr<nsIPrincipal> persistent = aToSave;
2001    nsCOMPtr<nsIAggregatePrincipal> aggregate(do_QueryInterface(aToSave, &rv));
2002    if (NS_SUCCEEDED(rv))
2003        if (NS_FAILED(aggregate->GetPrimaryChild(getter_AddRefs(persistent))))
2004            return NS_ERROR_FAILURE;
2005
2006    //-- Save to mPrincipals
2007    if (!mPrincipals)
2008    {
2009        mPrincipals = new nsSupportsHashtable(31);
2010        if (!mPrincipals)
2011            return NS_ERROR_OUT_OF_MEMORY;
2012    }
2013    nsIPrincipalKey key(persistent);
2014    mPrincipals->Put(&key, persistent);
2015
2016    //-- Save to prefs
2017    nsXPIDLCString idPrefName;
2018    nsXPIDLCString id;
2019    nsXPIDLCString grantedList;
2020    nsXPIDLCString deniedList;
2021    rv = persistent->GetPreferences(getter_Copies(idPrefName),
2022                                    getter_Copies(id),
2023                                    getter_Copies(grantedList),
2024                                    getter_Copies(deniedList));
2025    if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2026
2027    nsXPIDLCString grantedPrefName;
2028    nsXPIDLCString deniedPrefName;
2029    rv = PrincipalPrefNames( idPrefName,
2030                             getter_Copies(grantedPrefName),
2031                             getter_Copies(deniedPrefName)  );
2032    if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2033
2034    mIsWritingPrefs = PR_TRUE;
2035    if (grantedList)
2036        mSecurityPref->SecuritySetCharPref(grantedPrefName, grantedList);
2037    else
2038        mSecurityPref->SecurityClearUserPref(grantedPrefName);
2039
2040    if (deniedList)
2041        mSecurityPref->SecuritySetCharPref(deniedPrefName, deniedList);
2042    else
2043        mSecurityPref->SecurityClearUserPref(deniedPrefName);
2044
2045    if (grantedList || deniedList)
2046        mSecurityPref->SecuritySetCharPref(idPrefName, id);
2047    else
2048        mSecurityPref->SecurityClearUserPref(idPrefName);
2049
2050    mIsWritingPrefs = PR_FALSE;
2051
2052    nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
2053    NS_ENSURE_SUCCESS(rv, rv);
2054    return prefService->SavePrefFile(nsnull);
2055}
2056
2057///////////////// Capabilities API /////////////////////
2058NS_IMETHODIMP
2059nsScriptSecurityManager::IsCapabilityEnabled(const char *capability,
2060                                             PRBool *result)
2061{
2062    nsresult rv;
2063    JSStackFrame *fp = nsnull;
2064    JSContext *cx = GetCurrentJSContext();
2065    fp = cx ? JS_FrameIterator(cx, &fp) : nsnull;
2066    if (!fp)
2067    {
2068        // No script code on stack. Allow execution.
2069        *result = PR_TRUE;
2070        return NS_OK;
2071    }
2072    *result = PR_FALSE;
2073    nsCOMPtr<nsIPrincipal> previousPrincipal;
2074    do
2075    {
2076        nsCOMPtr<nsIPrincipal> principal;
2077        if (NS_FAILED(GetFramePrincipal(cx, fp, getter_AddRefs(principal))))
2078            return NS_ERROR_FAILURE;
2079        if (!principal)
2080            continue;
2081        // If caller has a different principal, stop looking up the stack.
2082        if(previousPrincipal)
2083        {
2084            PRBool isEqual = PR_FALSE;
2085            if(NS_FAILED(previousPrincipal->Equals(principal, &isEqual)) || !isEqual)
2086                break;
2087        }
2088        else
2089            previousPrincipal = principal;
2090
2091        // First check if the principal is even able to enable the
2092        // given capability. If not, don't look any further.
2093        PRInt16 canEnable;
2094        rv = principal->CanEnableCapability(capability, &canEnable);
2095        if (NS_FAILED(rv)) return rv;
2096        if (canEnable != nsIPrincipal::ENABLE_GRANTED &&
2097            canEnable != nsIPrincipal::ENABLE_WITH_USER_PERMISSION)
2098            return NS_OK;
2099
2100        // Now see if the capability is enabled.
2101        void *annotation = JS_GetFrameAnnotation(cx, fp);
2102        rv = principal->IsCapabilityEnabled(capability, annotation, result);
2103        if (NS_FAILED(rv)) return rv;
2104        if (*result)
2105            return NS_OK;
2106    } while ((fp = JS_FrameIterator(cx, &fp)) != nsnull);
2107
2108    if (!previousPrincipal)
2109    {
2110        // No principals on the stack, all native code.  Allow
2111        // execution if the subject principal is the system principal.
2112
2113        return SubjectPrincipalIsSystem(result);
2114    }
2115
2116    return NS_OK;
2117}
2118
2119PRBool
2120nsScriptSecurityManager::CheckConfirmDialog(JSContext* cx, nsIPrincipal* aPrincipal,
2121                                            const char* aCapability, PRBool *checkValue)
2122{
2123    nsresult rv;
2124    *checkValue = PR_FALSE;
2125
2126    //-- Get a prompter for the current window.
2127    nsCOMPtr<nsIPrompt> prompter;
2128    if (cx)
2129    {
2130        nsCOMPtr<nsIScriptContext> scriptContext = GetScriptContext(cx);
2131        if (scriptContext)
2132        {
2133            nsCOMPtr<nsIScriptGlobalObject> globalObject;
2134            scriptContext->GetGlobalObject(getter_AddRefs(globalObject));
2135            NS_ASSERTION(globalObject, "script context has no global object");
2136            nsCOMPtr<nsIDOMWindowInternal> domWin(do_QueryInterface(globalObject));
2137            if (domWin)
2138                domWin->GetPrompter(getter_AddRefs(prompter));
2139        }
2140    }
2141
2142    if (!prompter)
2143    {
2144        //-- Couldn't get prompter from the current window, so get the prompt service.
2145        nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
2146        if (wwatch)
2147          wwatch->GetNewPrompter(0, getter_AddRefs(prompter));
2148        if (!prompter)
2149            return PR_FALSE;
2150    }
2151
2152    //-- Localize the dialog text
2153    nsXPIDLString check;
2154    rv = sStrBundle->GetStringFromName(NS_LITERAL_STRING("CheckMessage").get(),
2155                                       getter_Copies(check));
2156    if (NS_FAILED(rv))
2157        return PR_FALSE;
2158
2159    nsXPIDLString title;
2160    rv = sStrBundle->GetStringFromName(NS_LITERAL_STRING("Titleline").get(),
2161                                       getter_Copies(title));
2162    if (NS_FAILED(rv))
2163        return PR_FALSE;
2164
2165    nsXPIDLCString source;
2166    rv = aPrincipal->ToUserVisibleString(getter_Copies(source));
2167
2168    if (NS_FAILED(rv))
2169        return PR_FALSE;
2170
2171    NS_ConvertUTF8toUTF16 location(source.get());
2172    NS_ConvertUTF8toUTF16 capability(aCapability);
2173    const PRUnichar *formatStrings[] = { location.get(), capability.get() };
2174
2175    nsXPIDLString message;
2176    rv = sStrBundle->FormatStringFromName(NS_LITERAL_STRING("EnableCapabilityQuery").get(),
2177                                          formatStrings,
2178                                          NS_ARRAY_LENGTH(formatStrings),
2179                                          getter_Copies(message));
2180    if (NS_FAILED(rv))
2181        return PR_FALSE;
2182
2183    PRInt32 buttonPressed = 1; // If the user exits by clicking the close box, assume No (button 1)
2184    rv = prompter->ConfirmEx(title.get(), message.get(),
2185                             (nsIPrompt::BUTTON_TITLE_YES * nsIPrompt::BUTTON_POS_0) +
2186                             (nsIPrompt::BUTTON_TITLE_NO * nsIPrompt::BUTTON_POS_1),
2187                             nsnull, nsnull, nsnull, check.get(), checkValue, &buttonPressed);
2188
2189    if (NS_FAILED(rv))
2190        *checkValue = PR_FALSE;
2191    return (buttonPressed == 0);
2192}
2193
2194NS_IMETHODIMP
2195nsScriptSecurityManager::RequestCapability(nsIPrincipal* aPrincipal,
2196                                           const char *capability, PRInt16* canEnable)
2197{
2198    if (NS_FAILED(aPrincipal->CanEnableCapability(capability, canEnable)))
2199        return NS_ERROR_FAILURE;
2200    if (*canEnable == nsIPrincipal::ENABLE_WITH_USER_PERMISSION)
2201    {
2202        // Prompt user for permission to enable capability.
2203        JSContext* cx = GetCurrentJSContext();
2204        PRBool remember;
2205        if (CheckConfirmDialog(cx, aPrincipal, capability, &remember))
2206            *canEnable = nsIPrincipal::ENABLE_GRANTED;
2207        else
2208            *canEnable = nsIPrincipal::ENABLE_DENIED;
2209        if (remember)
2210        {
2211            //-- Save principal to prefs and to mPrincipals
2212            if (NS_FAILED(aPrincipal->SetCanEnableCapability(capability, *canEnable)))
2213                return NS_ERROR_FAILURE;
2214            if (NS_FAILED(SavePrincipal(aPrincipal)))
2215                return NS_ERROR_FAILURE;
2216        }
2217    }
2218    return NS_OK;
2219}
2220
2221NS_IMETHODIMP
2222nsScriptSecurityManager::EnableCapability(const char *capability)
2223{
2224    JSContext *cx = GetCurrentJSContext();
2225    JSStackFrame *fp;
2226
2227    //-- Error checks for capability string length (200)
2228    if(PL_strlen(capability)>200)
2229    {
2230        static const char msg[] = "Capability name too long";
2231        JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, msg)));
2232        return NS_ERROR_FAILURE;
2233    }
2234
2235    nsCOMPtr<nsIPrincipal> principal;
2236    if (NS_FAILED(GetPrincipalAndFrame(cx, getter_AddRefs(principal), &fp)))
2237        return NS_ERROR_FAILURE;
2238    void *annotation = JS_GetFrameAnnotation(cx, fp);
2239    PRBool enabled;
2240    if (NS_FAILED(principal->IsCapabilityEnabled(capability, annotation,
2241                                                 &enabled)))
2242        return NS_ERROR_FAILURE;
2243    if (enabled)
2244        return NS_OK;
2245
2246    PRInt16 canEnable;
2247    if (NS_FAILED(RequestCapability(principal, capability, &canEnable)))
2248        return NS_ERROR_FAILURE;
2249
2250    if (canEnable != nsIPrincipal::ENABLE_GRANTED)
2251    {
2252        nsXPIDLCString val;
2253        nsresult rv = principal->ToUserVisibleString(getter_Copies(val));
2254
2255        if (NS_FAILED(rv))
2256            return rv;
2257
2258        NS_ConvertUTF8toUTF16 location(val.get());
2259        NS_ConvertUTF8toUTF16 cap(capability);
2260        const PRUnichar *formatStrings[] = { location.get(), cap.get() };
2261
2262        nsXPIDLString message;
2263        rv = sStrBundle->FormatStringFromName(NS_LITERAL_STRING("EnableCapabilityDenied").get(),
2264                                              formatStrings,
2265                                              NS_ARRAY_LENGTH(formatStrings),
2266                                              getter_Copies(message));
2267        if (NS_FAILED(rv))
2268            return rv;
2269
2270        JS_SetPendingException(cx,
2271            STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx,
2272                NS_REINTERPRET_CAST(const jschar*, message.get()))));
2273
2274        return NS_ERROR_FAILURE; // XXX better error code?
2275    }
2276    if (NS_FAILED(principal->EnableCapability(capability, &annotation)))
2277        return NS_ERROR_FAILURE;
2278    JS_SetFrameAnnotation(cx, fp, annotation);
2279    return NS_OK;
2280}
2281
2282NS_IMETHODIMP
2283nsScriptSecurityManager::RevertCapability(const char *capability)
2284{
2285    JSContext *cx = GetCurrentJSContext();
2286    JSStackFrame *fp;
2287    nsCOMPtr<nsIPrincipal> principal;
2288    if (NS_FAILED(GetPrincipalAndFrame(cx, getter_AddRefs(principal), &fp)))
2289        return NS_ERROR_FAILURE;
2290    void *annotation = JS_GetFrameAnnotation(cx, fp);
2291    principal->RevertCapability(capability, &annotation);
2292    JS_SetFrameAnnotation(cx, fp, annotation);
2293    return NS_OK;
2294}
2295
2296NS_IMETHODIMP
2297nsScriptSecurityManager::DisableCapability(const char *capability)
2298{
2299    JSContext *cx = GetCurrentJSContext();
2300    JSStackFrame *fp;
2301    nsCOMPtr<nsIPrincipal> principal;
2302    if (NS_FAILED(GetPrincipalAndFrame(cx, getter_AddRefs(principal), &fp)))
2303        return NS_ERROR_FAILURE;
2304    void *annotation = JS_GetFrameAnnotation(cx, fp);
2305    principal->DisableCapability(capability, &annotation);
2306    JS_SetFrameAnnotation(cx, fp, annotation);
2307    return NS_OK;
2308}
2309
2310//////////////// Master Certificate Functions ///////////////////////////////////////
2311NS_IMETHODIMP
2312nsScriptSecurityManager::SetCanEnableCapability(const char* certificateID,
2313                                                const char* capability,
2314                                                PRInt16 canEnable)
2315{
2316    nsresult rv;
2317    nsCOMPtr<nsIPrincipal> subjectPrincipal;
2318    rv = GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
2319    if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2320
2321    //-- Get the system certificate
2322    if (!mSystemCertificate)
2323    {
2324        nsCOMPtr<nsIFile> systemCertFile;
2325        nsCOMPtr<nsIProperties> directoryService =
2326                 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
2327        if (!directoryService) return NS_ERROR_FAILURE;
2328        rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
2329                              getter_AddRefs(systemCertFile));
2330        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2331#ifdef XP_MAC
2332        // On Mac, this file will be located in the Essential Files folder
2333        systemCertFile->AppendNative(NS_LITERAL_CSTRING("Essential Files"));
2334        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2335#endif
2336        systemCertFile->AppendNative(NS_LITERAL_CSTRING("systemSignature.jar"));
2337        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2338        nsCOMPtr<nsIZipReader> systemCertZip;
2339        rv = nsComponentManager::CreateInstance(kZipReaderCID, nsnull,
2340                                                NS_GET_IID(nsIZipReader),
2341                                                getter_AddRefs(systemCertZip));
2342        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2343        systemCertZip->Init(systemCertFile);
2344        rv = systemCertZip->Open();
2345        if (NS_SUCCEEDED(rv))
2346        {
2347            nsCOMPtr<nsIJAR> systemCertJar(do_QueryInterface(systemCertZip, &rv));
2348            if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2349            rv = systemCertJar->GetCertificatePrincipal(nsnull,
2350                                                        getter_AddRefs(mSystemCertificate));
2351            if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2352        }
2353    }
2354
2355    //-- Make sure the caller's principal is the system certificate
2356    PRBool isEqual = PR_FALSE;
2357    if (mSystemCertificate)
2358    {
2359        rv = mSystemCertificate->Equals(subjectPrincipal, &isEqual);
2360        if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2361    }
2362    if (!isEqual)
2363    {
2364        JSContext* cx = GetCurrentJSContext();
2365        if (!cx) return NS_ERROR_FAILURE;
2366        static const char msg1[] = "Only code signed by the system certificate may call SetCanEnableCapability or Invalidate";
2367        static const char msg2[] = "Attempt to call SetCanEnableCapability or Invalidate when no system certificate has been established";
2368            JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx,
2369                                   mSystemCertificate ? msg1 : msg2)));
2370        return NS_ERROR_FAILURE;
2371    }
2372
2373    //-- Get the target principal
2374    nsCOMPtr<nsIPrincipal> objectPrincipal;
2375    rv =  GetCertificatePrincipal(certificateID, getter_AddRefs(objectPrincipal));
2376    if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2377    rv = objectPrincipal->SetCanEnableCapability(capability, canEnable);
2378    if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
2379    return SavePrincipal(objectPrincipal);
2380}
2381
2382////////////////////////////////////////////////
2383// Methods implementing nsIXPCSecurityManager //
2384////////////////////////////////////////////////
2385
2386NS_IMETHODIMP
2387nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
2388                                          const nsIID &aIID,
2389                                          nsISupports *aObj,
2390                                          nsIClassInfo *aClassInfo,
2391                                          void **aPolicy)
2392{
2393#if 0
2394    char* iidStr = aIID.ToString();
2395    printf("### CanCreateWrapper(%s) ", iidStr);
2396    PR_FREEIF(iidStr);
2397#endif
2398// XXX Special case for nsIXPCException ?
2399    ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nsnull);
2400    if (objClassInfo.IsDOMClass())
2401    {
2402#if 0
2403        printf("DOM class - GRANTED.\n");
2404#endif
2405        return NS_OK;
2406    }
2407
2408    //--See if the object advertises a non-default level of access
2409    //  using nsISecurityCheckedComponent
2410    nsCOMPtr<nsISecurityCheckedComponent> checkedComponent =
2411        do_QueryInterface(aObj);
2412
2413    nsXPIDLCString objectSecurityLevel;
2414    if (checkedComponent)
2415        checkedComponent->CanCreateWrapper((nsIID *)&aIID, getter_Copies(objectSecurityLevel));
2416
2417    nsresult rv = CheckXPCPermissions(aObj, objectSecurityLevel);
2418    if (NS_FAILED(rv))
2419    {
2420        //-- Access denied, report an error
2421
2422        NS_NAMED_LITERAL_STRING(strName, "CreateWrapperDenied");
2423        NS_ConvertUTF8toUTF16 className(objClassInfo.GetName());
2424        const PRUnichar* formatStrings[] = { className.get() };
2425        nsXPIDLString errorMsg;
2426        // We need to keep our existing failure rv and not override it
2427        // with a likely success code from the following string bundle
2428        // call in order to throw the correct security exception later.
2429        nsresult rv2 =
2430            sStrBundle->FormatStringFromName(strName.get(),
2431                                             formatStrings,
2432                                             NS_ARRAY_LENGTH(formatStrings),
2433                                             getter_Copies(errorMsg));
2434        NS_ENSURE_SUCCESS(rv2, rv2);
2435
2436        JS_SetPendingException(cx,
2437            STRING_TO_JSVAL(JS_NewUCStringCopyZ(cx,
2438                NS_REINTERPRET_CAST(const jschar*, errorMsg.get()))));
2439    }
2440    return rv;
2441}
2442
2443#ifdef XPC_IDISPATCH_SUPPORT
2444nsresult
2445nsScriptSecurityManager::CheckComponentPermissions(JSContext *cx,
2446                                                   const nsCID &aCID)
2447{
2448    nsresult rv;
2449    nsCOMPtr<nsIPrincipal> subjectPrincipal;
2450    if (NS_FAILED(GetSubjectPrincipal(cx, getter_AddRefs(subjectPrincipal))))
2451        return NS_ERROR_FAILURE;
2452
2453    // Reformat the CID string so it's suitable for prefs
2454    nsXPIDLCString cidTemp;
2455    cidTemp.Adopt(aCID.ToString());
2456    nsCAutoString cid(NS_LITERAL_CSTRING("CID") +
2457                      Substring(cidTemp, 1, cidTemp.Length() - 2));
2458    ToUpperCase(cid);
2459
2460#ifdef DEBUG_mstoltz
2461    printf("### CheckComponentPermissions(ClassID.%s) ",cid.get());
2462#endif
2463
2464    // Look up the policy for this class.
2465    // while this isn't a property we'll treat it as such, using ACCESS_CALL_METHOD
2466    jsval cidVal = STRING_TO_JSVAL(::JS_InternString(cx, cid.get()));
2467
2468    SecurityLevel securityLevel;
2469    rv = LookupPolicy(subjectPrincipal, "ClassID", cidVal,
2470                      nsIXPCSecurityManager::ACCESS_CALL_METHOD,
2471                      nsnull, &securityLevel);
2472    if (NS_FAILED(rv))
2473        return rv;
2474
2475    // If there's no policy stored, use the "security.classID.allowByDefault" pref
2476    if (securityLevel.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
2477        securityLevel.level = mXPCDefaultGrantAll ? SCRIPT_SECURITY_ALL_ACCESS :
2478                                                    SCRIPT_SECURITY_NO_ACCESS;
2479
2480    if (securityLevel.level == SCRIPT_SECURITY_ALL_ACCESS)
2481    {
2482#ifdef DEBUG_mstoltz
2483        printf(" GRANTED.\n");
2484#endif
2485        return NS_OK;
2486    }
2487
2488#ifdef DEBUG_mstoltz
2489    printf(" DENIED.\n");
2490#endif
2491    return NS_ERROR_DOM_PROP_ACCESS_DENIED;
2492}
2493#endif
2494
2495NS_IMETHODIMP
2496nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
2497                                           const nsCID &aCID)
2498{
2499    nsresult rv = CheckXPCPermissions(nsnull, nsnull);
2500    if (NS_FAILED(rv))
2501#ifdef XPC_IDISPATCH_SUPPORT
2502    {
2503        rv = CheckComponentPermissions(cx, aCID);
2504    }
2505    if (NS_FAILED(rv))
2506#endif
2507    {
2508        //-- Access denied, report an error
2509        nsCAutoString errorMsg("Permission denied to create instance of class. CID=");
2510        nsXPIDLCString cidStr;
2511        cidStr += aCID.ToString();
2512        errorMsg.Append(cidStr);
2513        JS_SetPendingException(cx,
2514                               STRING_TO_JSVAL(JS_NewStringCopyZ(cx, errorMsg.get())));
2515    }
2516    return rv;
2517}
2518
2519NS_IMETHODIMP
2520nsScriptSecurityManager::CanGetService(JSContext *cx,
2521                                       const nsCID &aCID)
2522{
2523#if 0
2524    char* cidStr = aCID.ToString();
2525    printf("### CanGetService(%s) ", cidStr);
2526    PR_FREEIF(cidStr);
2527#endif
2528
2529    nsresult rv = CheckXPCPermissions(nsnull, nsnull);
2530    if (NS_FAILED(rv))
2531    {
2532        //-- Access denied, report an error
2533        nsCAutoString errorMsg("Permission denied to get service. CID=");
2534        nsXPIDLCString cidStr;
2535        cidStr += aCID.ToString();
2536        errorMsg.Append(cidStr);
2537        JS_SetPendingException(cx,
2538                               STRING_TO_JSVAL(JS_NewStringCopyZ(cx, errorMsg.get())));
2539    }
2540    return rv;
2541}
2542
2543
2544NS_IMETHODIMP
2545nsScriptSecurityManager::CanAccess(PRUint32 aAction,
2546                                   nsIXPCNativeCallContext* aCallContext,
2547                                   JSContext* cx,
2548                                   JSObject* aJSObject,
2549                                   nsISupports* aObj,
2550                                   nsIClassInfo* aClassInfo,
2551                                   jsval aPropertyName,
2552                                   void** aPolicy)
2553{
2554    return CheckPropertyAccessImpl(aAction, aCallContext, cx,
2555                                   aJSObject, aObj, nsnull, aClassInfo,
2556                                   nsnull, aPropertyName, aPolicy);
2557}
2558
2559nsresult
2560nsScriptSecurityManager::CheckXPCPermissions(nsISupports* aObj,
2561                                             const char* aObjectSecurityLevel)
2562{
2563    //-- Check for the all-powerful UniversalXPConnect privilege
2564    PRBool ok = PR_FALSE;
2565    if (NS_SUCCEEDED(IsCapabilityEnabled("UniversalXPConnect", &ok)) && ok)
2566        return NS_OK;
2567
2568    //-- If the object implements nsISecurityCheckedComponent, it has a non-default policy.
2569    if (aObjectSecurityLevel)
2570    {
2571        if (PL_strcasecmp(aObjectSecurityLevel, "AllAccess") == 0)
2572            return NS_OK;
2573        else if (PL_strcasecmp(aObjectSecurityLevel, "NoAccess") != 0)
2574        {
2575            PRBool canAccess = PR_FALSE;
2576            if (NS_SUCCEEDED(IsCapabilityEnabled(aObjectSecurityLevel, &canAccess)) &&
2577                canAccess)
2578                return NS_OK;
2579        }
2580    }
2581
2582    //-- If user allows scripting of plugins by untrusted scripts,
2583    //   and the target object is a plugin, allow the access.
2584    if(aObj)
2585    {
2586        nsresult rv;
2587        nsCOMPtr<nsIPluginInstance> plugin(do_QueryInterface(aObj, &rv));
2588        if (NS_SUCCEEDED(rv))
2589        {
2590            static PRBool prefSet = PR_FALSE;
2591            static PRBool allowPluginAccess = PR_FALSE;
2592            if (!prefSet)
2593            {
2594                rv = mSecurityPref->SecurityGetBoolPref("security.xpconnect.plugin.unrestricted",
2595                                                       &allowPluginAccess);
2596                prefSet = PR_TRUE;
2597            }
2598            if (allowPluginAccess)
2599                return NS_OK;
2600        }
2601    }
2602
2603    //-- Access tests failed
2604    return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
2605}
2606
2607/////////////////////////////////////
2608// Method implementing nsIObserver //
2609/////////////////////////////////////
2610static const char sPrincipalPrefix[] = "capability.principal";
2611static NS_NAMED_LITERAL_CSTRING(sPolicyPrefix, "capability.policy.");
2612
2613NS_IMETHODIMP
2614nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
2615                                 const PRUnichar* aMessage)
2616{
2617    nsresult rv = NS_OK;
2618    NS_ConvertUCS2toUTF8 messageStr(aMessage);
2619    const char *message = messageStr.get();
2620
2621    static const char jsPrefix[] = "javascript.";
2622    if((PL_strncmp(message, jsPrefix, sizeof(jsPrefix)-1) == 0)
2623#ifdef XPC_IDISPATCH_SUPPORT
2624        || (PL_strcmp(message, sXPCDefaultGrantAllName) == 0)
2625#endif
2626        )
2627        JSEnabledPrefChanged(mSecurityPref);
2628    if(PL_strncmp(message, sPolicyPrefix.get(), sPolicyPrefix.Length()) == 0)
2629        mPolicyPrefsChanged = PR_TRUE; // This will force re-initialization of the pref table
2630    else if((PL_strncmp(message, sPrincipalPrefix, sizeof(sPrincipalPrefix)-1) == 0) &&
2631            !mIsWritingPrefs)
2632    {
2633        static const char id[] = "id";
2634        char* lastDot = PL_strrchr(message, '.');
2635        //-- This check makes sure the string copy below doesn't overwrite its bounds
2636        if(PL_strlen(lastDot) >= sizeof(id))
2637        {
2638            PL_strcpy(lastDot + 1, id);
2639            const char** idPrefArray = (const char**)&message;
2640            rv = InitPrincipals(1, idPrefArray, mSecurityPref);
2641        }
2642    }
2643    return rv;
2644}
2645
2646/////////////////////////////////////////////
2647// Constructor, Destructor, Initialization //
2648/////////////////////////////////////////////
2649nsScriptSecurityManager::nsScriptSecurityManager(void)
2650    : mOriginToPolicyMap(nsnull),
2651      mDefaultPolicy(nsnull),
2652      mCapabilities(nsnull),
2653      mSystemPrincipal(nsnull), mPrincipals(nsnull),
2654      mIsJavaScriptEnabled(PR_FALSE),
2655      mIsMailJavaScriptEnabled(PR_FALSE),
2656      mIsWritingPrefs(PR_FALSE),
2657      mNameSetRegistered(PR_FALSE),
2658      mPolicyPrefsChanged(PR_TRUE)
2659#ifdef XPC_IDISPATCH_SUPPORT
2660      ,mXPCDefaultGrantAll(PR_FALSE)
2661#endif
2662{
2663    NS_ASSERTION(sizeof(long) == sizeof(void*), "long and void* have different lengths on this platform. This may cause a security failure.");
2664}
2665
2666
2667nsresult nsScriptSecurityManager::Init()
2668{
2669    JSContext* cx = GetSafeJSContext();
2670    if (!cx) return NS_ERROR_FAILURE;   // this can happen of xpt loading fails
2671   
2672    ::JS_BeginRequest(cx);
2673    if (sEnabledID == JSVAL_VOID)
2674        sEnabledID = STRING_TO_JSVAL(::JS_InternString(cx, "enabled"));
2675    ::JS_EndRequest(cx);
2676
2677    nsresult rv = InitPrefs();
2678    NS_ENSURE_SUCCESS(rv, rv);
2679
2680    nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
2681    NS_ENSURE_SUCCESS(rv, rv);
2682
2683    rv = bundleService->CreateBundle("chrome://communicator/locale/security/caps.properties", &sStrBundle);
2684    NS_ENSURE_SUCCESS(rv, rv);
2685
2686    //-- Register security check callback in the JS engine
2687    //   Currently this is used to control access to function.caller
2688    nsCOMPtr<nsIJSRuntimeService> runtimeService =
2689        do_GetService("@mozilla.org/js/xpc/RuntimeService;1", &rv);
2690    NS_ENSURE_SUCCESS(rv, rv);
2691
2692    JSRuntime *rt;
2693    rv = runtimeService->GetRuntime(&rt);
2694    NS_ENSURE_SUCCESS(rv, rv);
2695
2696#ifdef DEBUG
2697    JSCheckAccessOp oldCallback =
2698#endif
2699        JS_SetCheckObjectAccessCallback(rt, CheckObjectAccess);
2700
2701    // For now, assert that no callback was set previously
2702    NS_ASSERTION(!oldCallback, "Someone already set a JS CheckObjectAccess callback");
2703    return NS_OK;
2704}
2705
2706static nsScriptSecurityManager *gScriptSecMan = nsnull;
2707
2708jsval nsScriptSecurityManager::sEnabledID   = JSVAL_VOID;
2709
2710nsScriptSecurityManager::~nsScriptSecurityManager(void)
2711{
2712    delete mOriginToPolicyMap;
2713    delete mDefaultPolicy;
2714    NS_IF_RELEASE(mSystemPrincipal);
2715    delete mPrincipals;
2716    delete mCapabilities;
2717    gScriptSecMan = nsnull;
2718}
2719
2720void
2721nsScriptSecurityManager::Shutdown()
2722{
2723    sEnabledID = JSVAL_VOID;
2724
2725    NS_IF_RELEASE(sStrBundle);
2726}
2727
2728nsScriptSecurityManager *
2729nsScriptSecurityManager::GetScriptSecurityManager()
2730{
2731    if (!gScriptSecMan)
2732    {
2733        nsScriptSecurityManager* ssManager = new nsScriptSecurityManager();
2734        if (!ssManager)
2735            return nsnull;
2736        nsresult rv;
2737        rv = ssManager->Init();
2738        NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
2739        if (NS_FAILED(rv)) {
2740            delete ssManager;
2741            return nsnull;
2742        }
2743 
2744        rv = nsJSPrincipals::Startup();
2745        if (NS_FAILED(rv)) {
2746            NS_WARNING("can't initialize JS engine security protocol glue!");
2747            delete ssManager;
2748            return nsnull;
2749        }
2750 
2751        nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
2752        if (NS_FAILED(rv) || !xpc) {
2753            NS_WARNING("Failed to get the XPConnect service");
2754            delete ssManager;
2755            return nsnull;
2756        }
2757 
2758            rv = xpc->SetDefaultSecurityManager(
2759                        NS_STATIC_CAST(nsIXPCSecurityManager*, ssManager),
2760                            nsIXPCSecurityManager::HOOK_ALL);
2761        if (NS_FAILED(rv)) {
2762            NS_WARNING("Failed to install xpconnect security manager!");
2763            delete ssManager;
2764            return nsnull;
2765        }
2766
2767        gScriptSecMan = ssManager;
2768    }
2769    return gScriptSecMan;
2770}
2771
2772// Currently this nsGenericFactory constructor is used only from FastLoad
2773// (XPCOM object deserialization) code, when "creating" the system principal
2774// singleton.
2775nsSystemPrincipal *
2776nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
2777{
2778    nsIPrincipal *sysprin = nsnull;
2779    if (gScriptSecMan)
2780        gScriptSecMan->GetSystemPrincipal(&sysprin);
2781    return NS_STATIC_CAST(nsSystemPrincipal*, sysprin);
2782}
2783
2784nsresult
2785nsScriptSecurityManager::InitPolicies()
2786{
2787    nsresult rv;
2788
2789    // Reset the "dirty" flag
2790    mPolicyPrefsChanged = PR_FALSE;
2791
2792    // Clear any policies cached on XPConnect wrappers
2793    nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
2794    if (NS_FAILED(rv)) return rv;
2795    rv = xpc->ClearAllWrappedNativeSecurityPolicies();
2796    if (NS_FAILED(rv)) return rv;
2797
2798    //-- Reset mOriginToPolicyMap
2799    delete mOriginToPolicyMap;
2800    mOriginToPolicyMap =
2801      new nsObjectHashtable(nsnull, nsnull, DeleteDomainEntry, nsnull);
2802
2803    //-- Reset and initialize the default policy
2804    delete mDefaultPolicy;
2805    mDefaultPolicy =
2806      new DomainPolicy();
2807    if (!mOriginToPolicyMap || !mDefaultPolicy)
2808        return NS_ERROR_OUT_OF_MEMORY;
2809
2810    //-- Initialize the table of security levels
2811    if (!mCapabilities)
2812    {
2813        mCapabilities =
2814          new nsObjectHashtable(nsnull, nsnull, DeleteCapability, nsnull);
2815        if (!mCapabilities)
2816            return NS_ERROR_OUT_OF_MEMORY;
2817    }
2818
2819    // Get a JS context - we need it to create internalized strings later.
2820    JSContext* cx = GetSafeJSContext();
2821    NS_ASSERTION(cx, "failed to get JS context");
2822    rv = InitDomainPolicy(cx, "default", mDefaultPolicy);
2823    NS_ENSURE_SUCCESS(rv, rv);
2824
2825    nsXPIDLCString policyNames;
2826    rv = mSecurityPref->SecurityGetCharPref("capability.policy.policynames",
2827                                            getter_Copies(policyNames));
2828
2829    nsXPIDLCString defaultPolicyNames;
2830    rv = mSecurityPref->SecurityGetCharPref("capability.policy.default_policynames",
2831                                            getter_Copies(defaultPolicyNames));
2832    policyNames += NS_LITERAL_CSTRING(" ") + defaultPolicyNames;
2833
2834    //-- Initialize domain policies
2835    char* policyCurrent = (char*)policyNames.get();
2836    PRBool morePolicies = PR_TRUE;
2837    while (morePolicies)
2838    {
2839        while(*policyCurrent == ' ' || *policyCurrent == ',')
2840            policyCurrent++;
2841        if (*policyCurrent == '\0')
2842            break;
2843        char* nameBegin = policyCurrent;
2844
2845        while(*policyCurrent != '\0' && *policyCurrent != ' ' && *policyCurrent != ',')
2846            policyCurrent++;
2847
2848        morePolicies = (*policyCurrent != '\0');
2849        *policyCurrent = '\0';
2850        policyCurrent++;
2851
2852        nsCAutoString sitesPrefName(sPolicyPrefix +
2853                                    nsDependentCString(nameBegin) +
2854                                    NS_LITERAL_CSTRING(".sites"));
2855        nsXPIDLCString domainList;
2856        rv = mSecurityPref->SecurityGetCharPref(sitesPrefName.get(),
2857                                                getter_Copies(domainList));
2858        if (NS_FAILED(rv))
2859            continue;
2860
2861        DomainPolicy* domainPolicy = new DomainPolicy();
2862        if (!domainPolicy)
2863            return NS_ERROR_OUT_OF_MEMORY;
2864
2865        //-- Parse list of sites and create an entry in mOriginToPolicyMap for each
2866        char* domainStart = (char*)domainList.get();
2867        char* domainCurrent = domainStart;
2868        char* lastDot = nsnull;
2869        char* nextToLastDot = nsnull;
2870        PRBool moreDomains = PR_TRUE;
2871        while (moreDomains)
2872        {
2873            if (*domainCurrent == ' ' || *domainCurrent == '\0')
2874            {
2875                moreDomains = (*domainCurrent != '\0');
2876                *domainCurrent = '\0';
2877                nsCStringKey key(nextToLastDot ? nextToLastDot+1 : domainStart);
2878                DomainEntry *newEntry = new DomainEntry(domainStart, domainPolicy);
2879                if (!newEntry)
2880                    return NS_ERROR_OUT_OF_MEMORY;
2881#ifdef DEBUG
2882                newEntry->mPolicyName_DEBUG = nameBegin;
2883#endif
2884                DomainEntry *existingEntry = (DomainEntry *)
2885                    mOriginToPolicyMap->Get(&key);
2886                if (!existingEntry)
2887                    mOriginToPolicyMap->Put(&key, newEntry);
2888                else
2889                {
2890                    if (existingEntry->Matches(domainStart))
2891                    {
2892                        newEntry->mNext = existingEntry;
2893                        mOriginToPolicyMap->Put(&key, newEntry);
2894                    }
2895                    else
2896                    {
2897                        while (existingEntry->mNext)
2898                        {
2899                            if (existingEntry->mNext->Matches(domainStart))
2900                            {
2901                                newEntry->mNext = existingEntry->mNext;
2902                                existingEntry->mNext = newEntry;
2903                                break;
2904                            }
2905                            existingEntry = existingEntry->mNext;
2906                        }
2907                        if (!existingEntry->mNext)
2908                            existingEntry->mNext = newEntry;
2909                    }
2910                }
2911                domainStart = domainCurrent + 1;
2912                lastDot = nextToLastDot = nsnull;
2913            }
2914            else if (*domainCurrent == '.')
2915            {
2916                nextToLastDot = lastDot;
2917                lastDot = domainCurrent;
2918            }
2919            domainCurrent++;
2920        }
2921
2922        rv = InitDomainPolicy(cx, nameBegin, domainPolicy);
2923        NS_ENSURE_SUCCESS(rv, rv);
2924    }
2925
2926#ifdef DEBUG_mstoltz
2927    PrintPolicyDB();
2928#endif
2929    return NS_OK;
2930}
2931
2932
2933nsresult
2934nsScriptSecurityManager::InitDomainPolicy(JSContext* cx,
2935                                          const char* aPolicyName,
2936                                          DomainPolicy* aDomainPolicy)
2937{
2938    nsresult rv;
2939    nsCAutoString policyPrefix(sPolicyPrefix +
2940                               nsDependentCString(aPolicyName) +
2941                               NS_LITERAL_CSTRING("."));
2942    PRUint32 prefixLength = policyPrefix.Length() - 1; // subtract the '.'
2943
2944    // XXX fix string use here.
2945    PRUint32 prefCount;
2946    char** prefNames;
2947    rv = mPrefBranch->GetChildList(policyPrefix.get(),
2948                                   &prefCount, &prefNames);
2949    if (NS_FAILED(rv)) return rv;
2950    if (prefCount == 0)
2951        return NS_OK;
2952
2953    //-- Populate the policy
2954    PRUint32 currentPref = 0;
2955    for (; currentPref < prefCount; currentPref++)
2956    {
2957        // Get the class name
2958        const char* start = prefNames[currentPref] + prefixLength +1;
2959        char* end = PL_strchr(start, '.');
2960        if (!end) // malformed pref, bail on this one
2961            continue;
2962        static const char sitesStr[] = "sites";
2963
2964        // We dealt with "sites" in InitPolicies(), so no need to do
2965        // that again...
2966        if (PL_strncmp(start, sitesStr, sizeof(sitesStr)-1) == 0)
2967            continue;
2968
2969        // Get the pref value
2970        nsXPIDLCString prefValue;
2971        rv = mSecurityPref->SecurityGetCharPref(prefNames[currentPref],
2972                                                getter_Copies(prefValue));
2973        if (NS_FAILED(rv) || !prefValue)
2974            continue;
2975
2976        SecurityLevel secLevel;
2977        if (PL_strcasecmp(prefValue, "noAccess") == 0)
2978            secLevel.level = SCRIPT_SECURITY_NO_ACCESS;
2979        else if (PL_strcasecmp(prefValue, "allAccess") == 0)
2980            secLevel.level = SCRIPT_SECURITY_ALL_ACCESS;
2981        else if (PL_strcasecmp(prefValue, "sameOrigin") == 0)
2982            secLevel.level = SCRIPT_SECURITY_SAME_ORIGIN_ACCESS;
2983        else
2984        {  //-- pref value is the name of a capability
2985            nsCStringKey secLevelKey(prefValue);
2986            secLevel.capability =
2987                NS_REINTERPRET_CAST(char*, mCapabilities->Get(&secLevelKey));
2988            if (!secLevel.capability)
2989            {
2990                secLevel.capability = nsCRT::strdup(prefValue);
2991                if (!secLevel.capability)
2992                    break;
2993                mCapabilities->Put(&secLevelKey,
2994                                   secLevel.capability);
2995            }
2996        }
2997
2998        *end = '\0';
2999        // Find or store this class in the classes table
3000        ClassPolicy* cpolicy =
3001          NS_REINTERPRET_CAST(ClassPolicy*,
3002                              PL_DHashTableOperate(aDomainPolicy, start,
3003                                                   PL_DHASH_ADD));
3004        if (!cpolicy)
3005            break;
3006
3007        // If this is the wildcard class (class '*'), save it in mWildcardPolicy
3008        // (we leave it stored in the hashtable too to take care of the cleanup)
3009        if ((*start == '*') && (end == start + 1))
3010            aDomainPolicy->mWildcardPolicy = cpolicy;
3011
3012        // Get the property name
3013        start = end + 1;
3014        end = PL_strchr(start, '.');
3015        if (end)
3016            *end = '\0';
3017
3018        JSString* propertyKey = ::JS_InternString(cx, start);
3019        if (!propertyKey)
3020            return NS_ERROR_OUT_OF_MEMORY;
3021
3022        // Store this property in the class policy
3023        const void* ppkey =
3024          NS_REINTERPRET_CAST(const void*, STRING_TO_JSVAL(propertyKey));
3025        PropertyPolicy* ppolicy =
3026          NS_REINTERPRET_CAST(PropertyPolicy*,
3027                              PL_DHashTableOperate(cpolicy->mPolicy, ppkey,
3028                                                   PL_DHASH_ADD));
3029        if (!ppolicy)
3030            break;
3031
3032        if (end) // The pref specifies an access mode
3033        {
3034            start = end + 1;
3035            if (PL_strcasecmp(start, "set") == 0)
3036                ppolicy->mSet = secLevel;
3037            else
3038                ppolicy->mGet = secLevel;
3039        }
3040        else
3041        {
3042            if (ppolicy->mGet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
3043                ppolicy->mGet = secLevel;
3044            if (ppolicy->mSet.level == SCRIPT_SECURITY_UNDEFINED_ACCESS)
3045                ppolicy->mSet = secLevel;
3046        }
3047    }
3048
3049    NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
3050    if (currentPref < prefCount) // Loop exited early because of out-of-memory error
3051        return NS_ERROR_OUT_OF_MEMORY;
3052    return NS_OK;
3053}
3054
3055
3056nsresult
3057nsScriptSecurityManager::PrincipalPrefNames(const char* pref,
3058                                            char** grantedPref, char** deniedPref)
3059{
3060    char* lastDot = PL_strrchr(pref, '.');
3061    if (!lastDot) return NS_ERROR_FAILURE;
3062    PRInt32 prefLen = lastDot - pref + 1;
3063
3064    *grantedPref = nsnull;
3065    *deniedPref = nsnull;
3066
3067    static const char granted[] = "granted";
3068    *grantedPref = (char*)PR_MALLOC(prefLen + sizeof(granted));
3069    if (!grantedPref) return NS_ERROR_OUT_OF_MEMORY;
3070    PL_strncpy(*grantedPref, pref, prefLen);
3071    PL_strcpy(*grantedPref + prefLen, granted);
3072
3073    static const char denied[] = "denied";
3074    *deniedPref = (char*)PR_MALLOC(prefLen + sizeof(denied));
3075    if (!deniedPref)
3076    {
3077        PR_FREEIF(*grantedPref);
3078        return NS_ERROR_OUT_OF_MEMORY;
3079    }
3080    PL_strncpy(*deniedPref, pref, prefLen);
3081    PL_strcpy(*deniedPref + prefLen, denied);
3082
3083    return NS_OK;
3084}
3085
3086nsresult
3087nsScriptSecurityManager::InitPrincipals(PRUint32 aPrefCount, const char** aPrefNames,
3088                                        nsISecurityPref* aSecurityPref)
3089{
3090    /* This is the principal preference syntax:
3091     * capability.principal.[codebase|codebaseTrusted|certificate].<name>.[id|granted|denied]
3092     * For example:
3093     * user_pref("capability.principal.certificate.p1.id","12:34:AB:CD");
3094     * user_pref("capability.principal.certificate.p1.granted","Capability1 Capability2");
3095     * user_pref("capability.principal.certificate.p1.denied","Capability3");
3096     */
3097
3098    /* codebaseTrusted means a codebase principal that can enable capabilities even if
3099     * codebase principals are disabled. Don't use trustedCodebase except with unspoofable
3100     * URLs such as HTTPS URLs.
3101     */
3102
3103    static const char idSuffix[] = ".id";
3104    for (PRUint32 c = 0; c < aPrefCount; c++)
3105    {
3106        PRInt32 prefNameLen = PL_strlen(aPrefNames[c]) - (sizeof(idSuffix)-1);
3107        if (PL_strcasecmp(aPrefNames[c] + prefNameLen, idSuffix) != 0)
3108            continue;
3109
3110        nsXPIDLCString id;
3111        if (NS_FAILED(mSecurityPref->SecurityGetCharPref(aPrefNames[c], getter_Copies(id))))
3112            return NS_ERROR_FAILURE;
3113
3114        nsXPIDLCString grantedPrefName;
3115        nsXPIDLCString deniedPrefName;
3116        nsresult rv = PrincipalPrefNames(aPrefNames[c],
3117                                         getter_Copies(grantedPrefName),
3118                                         getter_Copies(deniedPrefName));
3119        if (rv == NS_ERROR_OUT_OF_MEMORY)
3120            return rv;
3121        else if (NS_FAILED(rv))
3122            continue;
3123
3124        char* grantedList = nsnull;
3125        mSecurityPref->SecurityGetCharPref(grantedPrefName, &grantedList);
3126        char* deniedList = nsnull;
3127        mSecurityPref->SecurityGetCharPref(deniedPrefName, &deniedList);
3128
3129        //-- Delete prefs if their value is the empty string
3130        if ((!id || id[0] == '\0') ||
3131            ((!grantedList || grantedList[0] == '\0') && (!deniedList || deniedList[0] == '\0')))
3132        {
3133            mSecurityPref->SecurityClearUserPref(aPrefNames[c]);
3134            mSecurityPref->SecurityClearUserPref(grantedPrefName);
3135            mSecurityPref->SecurityClearUserPref(deniedPrefName);
3136            PR_FREEIF(grantedList);
3137            PR_FREEIF(deniedList);
3138            continue;
3139        }
3140
3141        //-- Create a principal based on the prefs
3142        static const char certificateName[] = "capability.principal.certificate";
3143        static const char codebaseName[] = "capability.principal.codebase";
3144        static const char codebaseTrustedName[] = "capability.principal.codebaseTrusted";
3145        nsCOMPtr<nsIPrincipal> principal;
3146        if (PL_strncmp(aPrefNames[c], certificateName,
3147                       sizeof(certificateName)-1) == 0)
3148        {
3149            nsCertificatePrincipal *certificate = new nsCertificatePrincipal();
3150            if (certificate) {
3151                NS_ADDREF(certificate);
3152                if (NS_SUCCEEDED(certificate->InitFromPersistent(aPrefNames[c], id,
3153                                                                 grantedList, deniedList)))
3154                    principal = do_QueryInterface((nsBasePrincipal*)certificate);
3155                NS_RELEASE(certificate);
3156            }
3157        } else if(PL_strncmp(aPrefNames[c], codebaseName,
3158                             sizeof(codebaseName)-1) == 0)
3159        {
3160            nsCodebasePrincipal *codebase = new nsCodebasePrincipal();
3161            if (codebase) {
3162                NS_ADDREF(codebase);
3163                PRBool trusted = (PL_strncmp(aPrefNames[c], codebaseTrustedName,
3164                                             sizeof(codebaseTrustedName)-1) == 0);
3165                if (NS_SUCCEEDED(codebase->InitFromPersistent(aPrefNames[c], id,
3166                                                              grantedList, deniedList,
3167                                                              trusted)))
3168                    principal = do_QueryInterface((nsBasePrincipal*)codebase);
3169                NS_RELEASE(codebase);
3170            }
3171        }
3172        PR_FREEIF(grantedList);
3173        PR_FREEIF(deniedList);
3174
3175        if (principal)
3176        {
3177            if (!mPrincipals)
3178            {
3179                mPrincipals = new nsSupportsHashtable(31);
3180                if (!mPrincipals)
3181                    return NS_ERROR_OUT_OF_MEMORY;
3182            }
3183            nsIPrincipalKey key(principal);
3184            mPrincipals->Put(&key, principal);
3185        }
3186    }
3187    return NS_OK;
3188}
3189
3190const char* nsScriptSecurityManager::sJSEnabledPrefName = "javascript.enabled";
3191const char* nsScriptSecurityManager::sJSMailEnabledPrefName = "javascript.allow.mailnews";
3192#ifdef XPC_IDISPATCH_SUPPORT
3193const char* nsScriptSecurityManager::sXPCDefaultGrantAllName =
3194                "security.classID.allowByDefault";
3195#endif
3196inline void
3197nsScriptSecurityManager::JSEnabledPrefChanged(nsISecurityPref* aSecurityPref)
3198{
3199    if (NS_FAILED(mSecurityPref->SecurityGetBoolPref(sJSEnabledPrefName,
3200                                                     &mIsJavaScriptEnabled)))
3201        // Default to enabled.
3202        mIsJavaScriptEnabled = PR_TRUE;
3203
3204    if (NS_FAILED(mSecurityPref->SecurityGetBoolPref(sJSMailEnabledPrefName,
3205                                                     &mIsMailJavaScriptEnabled)))
3206        // Default to enabled.
3207        mIsMailJavaScriptEnabled = PR_TRUE;
3208#ifdef XPC_IDISPATCH_SUPPORT
3209    if (NS_FAILED(mSecurityPref->SecurityGetBoolPref(sXPCDefaultGrantAllName,
3210                                                     &mXPCDefaultGrantAll)))
3211        // Default to disabled.
3212        mXPCDefaultGrantAll = PR_FALSE;
3213#endif
3214}
3215
3216nsresult
3217nsScriptSecurityManager::InitPrefs()
3218{
3219    nsresult rv;
3220    nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
3221    NS_ENSURE_SUCCESS(rv, rv);
3222    rv = prefService->GetBranch(nsnull, getter_AddRefs(mPrefBranch));
3223    NS_ENSURE_SUCCESS(rv, rv);
3224    nsCOMPtr<nsIPrefBranchInternal> prefBranchInternal(do_QueryInterface(mPrefBranch, &rv));
3225    NS_ENSURE_SUCCESS(rv, rv);
3226    mSecurityPref = do_QueryInterface(mPrefBranch, &rv);
3227    NS_ENSURE_SUCCESS(rv, rv);
3228
3229    // Set the initial value of the "javascript.enabled" prefs
3230    JSEnabledPrefChanged(mSecurityPref);
3231    // set observer callbacks in case the value of the prefs change
3232    prefBranchInternal->AddObserver(sJSEnabledPrefName, this, PR_FALSE);
3233    prefBranchInternal->AddObserver(sJSMailEnabledPrefName, this, PR_FALSE);
3234#ifdef XPC_IDISPATCH_SUPPORT
3235    prefBranchInternal->AddObserver(sXPCDefaultGrantAllName, this, PR_FALSE);
3236#endif
3237    PRUint32 prefCount;
3238    char** prefNames;
3239
3240    // Set a callback for policy pref changes
3241    prefBranchInternal->AddObserver(sPolicyPrefix.get(), this, PR_FALSE);
3242
3243    //-- Initialize the principals database from prefs
3244    rv = mPrefBranch->GetChildList(sPrincipalPrefix, &prefCount, &prefNames);
3245    if (NS_SUCCEEDED(rv) && prefCount > 0)
3246    {
3247        rv = InitPrincipals(prefCount, (const char**)prefNames, mSecurityPref);
3248        NS_ENSURE_SUCCESS(rv, rv);
3249        NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(prefCount, prefNames);
3250    }
3251    //-- Set a callback for principal changes
3252    prefBranchInternal->AddObserver(sPrincipalPrefix, this, PR_FALSE);
3253
3254    return NS_OK;
3255}
3256
3257///////////////////////////////////////////////////////////////////////////////
3258// The following code prints the contents of the policy DB to the console.
3259#ifdef DEBUG_mstoltz
3260
3261//typedef PLDHashOperator
3262//(* PR_CALLBACK PLDHashEnumerator)(PLDHashTable *table, PLDHashEntryHdr *hdr,
3263//                                      PRUint32 number, void *arg);
3264PR_STATIC_CALLBACK(PLDHashOperator)
3265PrintPropertyPolicy(PLDHashTable *table, PLDHashEntryHdr *entry,
3266                    PRUint32 number, void *arg)
3267{
3268    PropertyPolicy* pp = (PropertyPolicy*)entry;
3269    nsCAutoString prop("        ");
3270    JSContext* cx = (JSContext*)arg;
3271    prop.AppendInt((PRUint32)pp->key);
3272    prop += ' ';
3273    prop.AppendWithConversion((PRUnichar*)JSValIDToString(cx, pp->key));
3274    prop += ": Get=";
3275    if (SECURITY_ACCESS_LEVEL_FLAG(pp->mGet))
3276        prop.AppendInt(pp->mGet.level);
3277    else
3278        prop += pp->mGet.capability;
3279
3280    prop += " Set=";
3281    if (SECURITY_ACCESS_LEVEL_FLAG(pp->mSet))
3282        prop.AppendInt(pp->mSet.level);
3283    else
3284        prop += pp->mSet.capability;
3285       
3286    printf("%s.\n", prop.get());
3287    return PL_DHASH_NEXT;
3288}
3289
3290PR_STATIC_CALLBACK(PLDHashOperator)
3291PrintClassPolicy(PLDHashTable *table, PLDHashEntryHdr *entry,
3292                 PRUint32 number, void *arg)
3293{
3294    ClassPolicy* cp = (ClassPolicy*)entry;
3295    printf("    %s\n", cp->key);
3296
3297    PL_DHashTableEnumerate(cp->mPolicy, PrintPropertyPolicy, arg);
3298    return PL_DHASH_NEXT;
3299}
3300
3301// typedef PRBool
3302// (*PR_CALLBACK nsHashtableEnumFunc)(nsHashKey *aKey, void *aData, void* aClosure);
3303PR_STATIC_CALLBACK(PRBool)
3304PrintDomainPolicy(nsHashKey *aKey, void *aData, void* aClosure)
3305{
3306    DomainEntry* de = (DomainEntry*)aData;
3307    printf("----------------------------\n");
3308    printf("Domain: %s Policy Name: %s.\n", de->mOrigin.get(),
3309           de->mPolicyName_DEBUG.get());
3310    PL_DHashTableEnumerate(de->mDomainPolicy, PrintClassPolicy, aClosure);
3311    return PR_TRUE;
3312}
3313
3314PR_STATIC_CALLBACK(PRBool)
3315PrintCapability(nsHashKey *aKey, void *aData, void* aClosure)
3316{
3317    char* cap = (char*)aData;
3318    printf("    %s.\n", cap);
3319    return PR_TRUE;
3320}
3321
3322void
3323nsScriptSecurityManager::PrintPolicyDB()
3324{
3325    printf("############## Security Policies ###############\n");
3326    if(mOriginToPolicyMap)
3327    {
3328        JSContext* cx = GetCurrentJSContext();
3329        printf("----------------------------\n");
3330        printf("Domain: Default.\n");
3331        PL_DHashTableEnumerate(mDefaultPolicy, PrintClassPolicy, (void*)cx);
3332        mOriginToPolicyMap->Enumerate(PrintDomainPolicy, (void*)cx);
3333    }
3334    printf("############ End Security Policies #############\n\n");
3335    printf("############## Capabilities ###############\n");
3336    mCapabilities->Enumerate(PrintCapability);
3337    printf("############## End Capabilities ###############\n");
3338}
3339#endif
3340
Note: See TracBrowser for help on using the repository browser.