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

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