1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et tw=80: */
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
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) 2003
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Christopher A. Aillon <christopher@aillon.com>
25 : * Giorgio Maone <g.maone@informaction.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nscore.h"
42 : #include "nsScriptSecurityManager.h"
43 : #include "nsString.h"
44 : #include "nsReadableUtils.h"
45 : #include "plstr.h"
46 : #include "nsCRT.h"
47 : #include "nsIURI.h"
48 : #include "nsIFileURL.h"
49 : #include "nsIProtocolHandler.h"
50 : #include "nsNetUtil.h"
51 : #include "nsJSPrincipals.h"
52 : #include "nsVoidArray.h"
53 : #include "nsHashtable.h"
54 : #include "nsIObjectInputStream.h"
55 : #include "nsIObjectOutputStream.h"
56 : #include "nsIClassInfoImpl.h"
57 : #include "nsDOMError.h"
58 : #include "nsIContentSecurityPolicy.h"
59 :
60 : #include "nsPrincipal.h"
61 :
62 : #include "mozilla/Preferences.h"
63 : #include "mozilla/HashFunctions.h"
64 :
65 : using namespace mozilla;
66 :
67 : static bool gCodeBasePrincipalSupport = false;
68 : static bool gIsObservingCodeBasePrincipalSupport = false;
69 :
70 207 : static bool URIIsImmutable(nsIURI* aURI)
71 : {
72 414 : nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
73 : bool isMutable;
74 : return
75 : mutableObj &&
76 414 : NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
77 414 : !isMutable;
78 : }
79 :
80 : // Static member variables
81 : PRInt32 nsPrincipal::sCapabilitiesOrdinal = 0;
82 : const char nsPrincipal::sInvalid[] = "Invalid";
83 :
84 :
85 : NS_IMPL_CLASSINFO(nsPrincipal, NULL, nsIClassInfo::MAIN_THREAD_ONLY,
86 : NS_PRINCIPAL_CID)
87 1151 : NS_IMPL_QUERY_INTERFACE2_CI(nsPrincipal,
88 : nsIPrincipal,
89 42 : nsISerializable)
90 12 : NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal,
91 : nsIPrincipal,
92 12 : nsISerializable)
93 :
94 : NS_IMETHODIMP_(nsrefcnt)
95 1833 : nsPrincipal::AddRef()
96 : {
97 1833 : NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
98 : // XXXcaa does this need to be threadsafe? See bug 143559.
99 1833 : nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
100 1833 : NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
101 1833 : return count;
102 : }
103 :
104 : NS_IMETHODIMP_(nsrefcnt)
105 1833 : nsPrincipal::Release()
106 : {
107 1833 : NS_PRECONDITION(0 != refcount, "dup release");
108 1833 : nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
109 1833 : NS_LOG_RELEASE(this, count, "nsPrincipal");
110 1833 : if (count == 0) {
111 207 : delete this;
112 : }
113 :
114 1833 : return count;
115 : }
116 :
117 207 : nsPrincipal::nsPrincipal()
118 : : mCapabilities(nsnull),
119 : mSecurityPolicy(nsnull),
120 : mTrusted(false),
121 : mInitialized(false),
122 : mCodebaseImmutable(false),
123 207 : mDomainImmutable(false)
124 : {
125 207 : if (!gIsObservingCodeBasePrincipalSupport) {
126 : nsresult rv =
127 : Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
128 : "signed.applets.codebase_principal_support",
129 51 : false);
130 51 : gIsObservingCodeBasePrincipalSupport = NS_SUCCEEDED(rv);
131 51 : NS_WARN_IF_FALSE(gIsObservingCodeBasePrincipalSupport,
132 : "Installing gCodeBasePrincipalSupport failed!");
133 : }
134 207 : }
135 :
136 : nsresult
137 207 : nsPrincipal::Init(const nsACString& aCertFingerprint,
138 : const nsACString& aSubjectName,
139 : const nsACString& aPrettyName,
140 : nsISupports* aCert,
141 : nsIURI *aCodebase)
142 : {
143 207 : NS_ENSURE_STATE(!mInitialized);
144 207 : NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() || aCodebase); // better have one of these.
145 :
146 207 : mInitialized = true;
147 :
148 207 : mCodebase = NS_TryToMakeImmutable(aCodebase);
149 207 : mCodebaseImmutable = URIIsImmutable(mCodebase);
150 :
151 207 : if (aCertFingerprint.IsEmpty())
152 207 : return NS_OK;
153 :
154 0 : return SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
155 : }
156 :
157 621 : nsPrincipal::~nsPrincipal(void)
158 : {
159 207 : SetSecurityPolicy(nsnull);
160 207 : delete mCapabilities;
161 828 : }
162 :
163 : void
164 0 : nsPrincipal::GetScriptLocation(nsACString &aStr)
165 : {
166 0 : if (mCert) {
167 0 : aStr.Assign(mCert->fingerprint);
168 : } else {
169 0 : mCodebase->GetSpec(aStr);
170 : }
171 0 : }
172 :
173 : #ifdef DEBUG
174 0 : void nsPrincipal::dumpImpl()
175 : {
176 0 : nsCAutoString str;
177 0 : GetScriptLocation(str);
178 0 : fprintf(stderr, "nsPrincipal (%p) = %s\n", this, str.get());
179 0 : }
180 : #endif
181 :
182 : NS_IMETHODIMP
183 0 : nsPrincipal::GetOrigin(char **aOrigin)
184 : {
185 0 : *aOrigin = nsnull;
186 :
187 0 : nsCOMPtr<nsIURI> origin;
188 0 : if (mCodebase) {
189 0 : origin = NS_GetInnermostURI(mCodebase);
190 : }
191 :
192 0 : if (!origin) {
193 0 : NS_ASSERTION(mCert, "No Domain or Codebase for a non-cert principal");
194 0 : return NS_ERROR_FAILURE;
195 : }
196 :
197 0 : nsCAutoString hostPort;
198 :
199 : // chrome: URLs don't have a meaningful origin, so make
200 : // sure we just get the full spec for them.
201 : // XXX this should be removed in favor of the solution in
202 : // bug 160042.
203 : bool isChrome;
204 0 : nsresult rv = origin->SchemeIs("chrome", &isChrome);
205 0 : if (NS_SUCCEEDED(rv) && !isChrome) {
206 0 : rv = origin->GetAsciiHost(hostPort);
207 : // Some implementations return an empty string, treat it as no support
208 : // for asciiHost by that implementation.
209 0 : if (hostPort.IsEmpty())
210 0 : rv = NS_ERROR_FAILURE;
211 : }
212 :
213 : PRInt32 port;
214 0 : if (NS_SUCCEEDED(rv) && !isChrome) {
215 0 : rv = origin->GetPort(&port);
216 : }
217 :
218 0 : if (NS_SUCCEEDED(rv) && !isChrome) {
219 0 : if (port != -1) {
220 0 : hostPort.AppendLiteral(":");
221 0 : hostPort.AppendInt(port, 10);
222 : }
223 :
224 0 : nsCAutoString scheme;
225 0 : rv = origin->GetScheme(scheme);
226 0 : NS_ENSURE_SUCCESS(rv, rv);
227 0 : *aOrigin = ToNewCString(scheme + NS_LITERAL_CSTRING("://") + hostPort);
228 : }
229 : else {
230 : // Some URIs (e.g., nsSimpleURI) don't support asciiHost. Just
231 : // get the full spec.
232 0 : nsCAutoString spec;
233 : // XXX nsMozIconURI and nsJARURI don't implement this correctly, they
234 : // both fall back to GetSpec. That needs to be fixed.
235 0 : rv = origin->GetAsciiSpec(spec);
236 0 : NS_ENSURE_SUCCESS(rv, rv);
237 0 : *aOrigin = ToNewCString(spec);
238 : }
239 :
240 0 : return *aOrigin ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
241 : }
242 :
243 : NS_IMETHODIMP
244 0 : nsPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
245 : {
246 0 : if (mSecurityPolicy && mSecurityPolicy->IsInvalid())
247 0 : SetSecurityPolicy(nsnull);
248 :
249 0 : *aSecurityPolicy = (void *) mSecurityPolicy;
250 0 : return NS_OK;
251 : }
252 :
253 : NS_IMETHODIMP
254 208 : nsPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
255 : {
256 208 : DomainPolicy *newPolicy = reinterpret_cast<DomainPolicy *>(aSecurityPolicy);
257 208 : if (newPolicy)
258 1 : newPolicy->Hold();
259 :
260 208 : if (mSecurityPolicy)
261 1 : mSecurityPolicy->Drop();
262 :
263 208 : mSecurityPolicy = newPolicy;
264 208 : return NS_OK;
265 : }
266 :
267 : bool
268 0 : nsPrincipal::CertificateEquals(nsIPrincipal *aOther)
269 : {
270 : bool otherHasCert;
271 0 : aOther->GetHasCertificate(&otherHasCert);
272 0 : if (otherHasCert != (mCert != nsnull)) {
273 : // One has a cert while the other doesn't. Not equal.
274 0 : return false;
275 : }
276 :
277 0 : if (!mCert)
278 0 : return true;
279 :
280 0 : nsCAutoString str;
281 0 : aOther->GetFingerprint(str);
282 0 : if (!str.Equals(mCert->fingerprint))
283 0 : return false;
284 :
285 : // If either subject name is empty, just let the result stand (so that
286 : // nsScriptSecurityManager::SetCanEnableCapability works), but if they're
287 : // both non-empty, only claim equality if they're equal.
288 0 : if (!mCert->subjectName.IsEmpty()) {
289 : // Check the other principal's subject name
290 0 : aOther->GetSubjectName(str);
291 0 : return str.Equals(mCert->subjectName) || str.IsEmpty();
292 : }
293 :
294 0 : return true;
295 : }
296 :
297 : NS_IMETHODIMP
298 0 : nsPrincipal::Equals(nsIPrincipal *aOther, bool *aResult)
299 : {
300 0 : if (!aOther) {
301 0 : NS_WARNING("Need a principal to compare this to!");
302 0 : *aResult = false;
303 0 : return NS_OK;
304 : }
305 :
306 0 : if (this != aOther) {
307 0 : if (!CertificateEquals(aOther)) {
308 0 : *aResult = false;
309 0 : return NS_OK;
310 : }
311 :
312 0 : if (mCert) {
313 : // If either principal has no URI, it's the saved principal from
314 : // preferences; in that case, test true. Do NOT test true if the two
315 : // principals have URIs with different codebases.
316 0 : nsCOMPtr<nsIURI> otherURI;
317 0 : nsresult rv = aOther->GetURI(getter_AddRefs(otherURI));
318 0 : if (NS_FAILED(rv)) {
319 0 : *aResult = false;
320 0 : return rv;
321 : }
322 :
323 0 : if (!otherURI || !mCodebase) {
324 0 : *aResult = true;
325 0 : return NS_OK;
326 : }
327 :
328 : // Fall through to the codebase comparison.
329 : }
330 :
331 : // Codebases are equal if they have the same origin.
332 : *aResult =
333 0 : NS_SUCCEEDED(nsScriptSecurityManager::CheckSameOriginPrincipal(this,
334 0 : aOther));
335 0 : return NS_OK;
336 : }
337 :
338 0 : *aResult = true;
339 0 : return NS_OK;
340 : }
341 :
342 : NS_IMETHODIMP
343 0 : nsPrincipal::EqualsIgnoringDomain(nsIPrincipal *aOther, bool *aResult)
344 : {
345 0 : if (this == aOther) {
346 0 : *aResult = true;
347 0 : return NS_OK;
348 : }
349 :
350 0 : *aResult = false;
351 0 : if (!CertificateEquals(aOther)) {
352 0 : return NS_OK;
353 : }
354 :
355 0 : nsCOMPtr<nsIURI> otherURI;
356 0 : nsresult rv = aOther->GetURI(getter_AddRefs(otherURI));
357 0 : if (NS_FAILED(rv)) {
358 0 : return rv;
359 : }
360 :
361 0 : NS_ASSERTION(mCodebase,
362 : "shouldn't be calling this on principals from preferences");
363 :
364 : // Compare codebases.
365 : *aResult = nsScriptSecurityManager::SecurityCompareURIs(mCodebase,
366 0 : otherURI);
367 0 : return NS_OK;
368 : }
369 :
370 : NS_IMETHODIMP
371 0 : nsPrincipal::Subsumes(nsIPrincipal *aOther, bool *aResult)
372 : {
373 0 : return Equals(aOther, aResult);
374 : }
375 :
376 : NS_IMETHODIMP
377 0 : nsPrincipal::SubsumesIgnoringDomain(nsIPrincipal *aOther, bool *aResult)
378 : {
379 0 : return EqualsIgnoringDomain(aOther, aResult);
380 : }
381 :
382 : static bool
383 1 : URIIsLocalFile(nsIURI *aURI)
384 : {
385 : bool isFile;
386 2 : nsCOMPtr<nsINetUtil> util = do_GetNetUtil();
387 :
388 2 : return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
389 : nsIProtocolHandler::URI_IS_LOCAL_FILE,
390 : &isFile)) &&
391 2 : isFile;
392 : }
393 :
394 : NS_IMETHODIMP
395 2 : nsPrincipal::CheckMayLoad(nsIURI* aURI, bool aReport)
396 : {
397 2 : if (!nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
398 2 : if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
399 1 : URIIsLocalFile(aURI)) {
400 0 : nsCOMPtr<nsIFileURL> fileURL(do_QueryInterface(aURI));
401 :
402 0 : if (!URIIsLocalFile(mCodebase)) {
403 : // If the codebase is not also a file: uri then forget it
404 : // (don't want resource: principals in a file: doc)
405 : //
406 : // note: we're not de-nesting jar: uris here, we want to
407 : // keep archive content bottled up in its own little island
408 :
409 0 : if (aReport) {
410 : nsScriptSecurityManager::ReportError(
411 0 : nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
412 : }
413 :
414 0 : return NS_ERROR_DOM_BAD_URI;
415 : }
416 :
417 : //
418 : // pull out the internal files
419 : //
420 0 : nsCOMPtr<nsIFileURL> codebaseFileURL(do_QueryInterface(mCodebase));
421 0 : nsCOMPtr<nsIFile> targetFile;
422 0 : nsCOMPtr<nsIFile> codebaseFile;
423 : bool targetIsDir;
424 :
425 : // Make sure targetFile is not a directory (bug 209234)
426 : // and that it exists w/out unescaping (bug 395343)
427 :
428 0 : if (!codebaseFileURL || !fileURL ||
429 0 : NS_FAILED(fileURL->GetFile(getter_AddRefs(targetFile))) ||
430 0 : NS_FAILED(codebaseFileURL->GetFile(getter_AddRefs(codebaseFile))) ||
431 0 : !targetFile || !codebaseFile ||
432 0 : NS_FAILED(targetFile->Normalize()) ||
433 0 : NS_FAILED(codebaseFile->Normalize()) ||
434 0 : NS_FAILED(targetFile->IsDirectory(&targetIsDir)) ||
435 : targetIsDir) {
436 0 : if (aReport) {
437 : nsScriptSecurityManager::ReportError(
438 0 : nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
439 : }
440 :
441 0 : return NS_ERROR_DOM_BAD_URI;
442 : }
443 :
444 : //
445 : // If the file to be loaded is in a subdirectory of the codebase
446 : // (or same-dir if codebase is not a directory) then it will
447 : // inherit its codebase principal and be scriptable by that codebase.
448 : //
449 : bool codebaseIsDir;
450 0 : bool contained = false;
451 0 : nsresult rv = codebaseFile->IsDirectory(&codebaseIsDir);
452 0 : if (NS_SUCCEEDED(rv) && codebaseIsDir) {
453 0 : rv = codebaseFile->Contains(targetFile, true, &contained);
454 : }
455 : else {
456 0 : nsCOMPtr<nsIFile> codebaseParent;
457 0 : rv = codebaseFile->GetParent(getter_AddRefs(codebaseParent));
458 0 : if (NS_SUCCEEDED(rv) && codebaseParent) {
459 0 : rv = codebaseParent->Contains(targetFile, true, &contained);
460 : }
461 : }
462 :
463 0 : if (NS_SUCCEEDED(rv) && contained) {
464 0 : return NS_OK;
465 : }
466 : }
467 :
468 1 : if (aReport) {
469 : nsScriptSecurityManager::ReportError(
470 0 : nsnull, NS_LITERAL_STRING("CheckSameOriginError"), mCodebase, aURI);
471 : }
472 :
473 1 : return NS_ERROR_DOM_BAD_URI;
474 : }
475 :
476 1 : return NS_OK;
477 : }
478 :
479 : NS_IMETHODIMP
480 0 : nsPrincipal::CanEnableCapability(const char *capability, PRInt16 *result)
481 : {
482 : // If this principal is marked invalid, can't enable any capabilities
483 0 : if (mCapabilities) {
484 0 : nsCStringKey invalidKey(sInvalid);
485 0 : if (mCapabilities->Exists(&invalidKey)) {
486 0 : *result = nsIPrincipal::ENABLE_DENIED;
487 :
488 0 : return NS_OK;
489 : }
490 : }
491 :
492 0 : if (!mCert && !mTrusted) {
493 0 : NS_ASSERTION(mInitialized, "Trying to enable a capability on an "
494 : "uninitialized principal");
495 :
496 : // If we are a non-trusted codebase principal, capabilities can not
497 : // be enabled if the user has not set the pref allowing scripts to
498 : // request enhanced capabilities; however, the file: and resource:
499 : // schemes are special and may be able to get extra capabilities
500 : // even with the pref disabled.
501 :
502 0 : if (!gCodeBasePrincipalSupport) {
503 0 : bool mightEnable = false;
504 0 : nsresult rv = mCodebase->SchemeIs("file", &mightEnable);
505 0 : if (NS_FAILED(rv) || !mightEnable) {
506 0 : rv = mCodebase->SchemeIs("resource", &mightEnable);
507 0 : if (NS_FAILED(rv) || !mightEnable) {
508 0 : *result = nsIPrincipal::ENABLE_DENIED;
509 :
510 0 : return NS_OK;
511 : }
512 : }
513 : }
514 : }
515 :
516 0 : const char *start = capability;
517 0 : *result = nsIPrincipal::ENABLE_GRANTED;
518 0 : for(;;) {
519 0 : const char *space = PL_strchr(start, ' ');
520 0 : PRInt32 len = space ? space - start : strlen(start);
521 0 : nsCAutoString capString(start, len);
522 0 : nsCStringKey key(capString);
523 : PRInt16 value =
524 0 : mCapabilities ? (PRInt16)NS_PTR_TO_INT32(mCapabilities->Get(&key)) : 0;
525 0 : if (value == 0 || value == nsIPrincipal::ENABLE_UNKNOWN) {
526 : // We don't know whether we can enable this capability,
527 : // so we should ask the user.
528 0 : value = nsIPrincipal::ENABLE_WITH_USER_PERMISSION;
529 : }
530 :
531 0 : if (value < *result) {
532 0 : *result = value;
533 : }
534 :
535 0 : if (!space) {
536 : break;
537 : }
538 :
539 0 : start = space + 1;
540 : }
541 :
542 0 : return NS_OK;
543 : }
544 :
545 : NS_IMETHODIMP
546 0 : nsPrincipal::SetCanEnableCapability(const char *capability,
547 : PRInt16 canEnable)
548 : {
549 : // If this principal is marked invalid, can't enable any capabilities
550 0 : if (!mCapabilities) {
551 0 : mCapabilities = new nsHashtable(7); // XXXbz gets bumped up to 16 anyway
552 0 : NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
553 : }
554 :
555 0 : nsCStringKey invalidKey(sInvalid);
556 0 : if (mCapabilities->Exists(&invalidKey)) {
557 0 : return NS_OK;
558 : }
559 :
560 0 : if (PL_strcmp(capability, sInvalid) == 0) {
561 0 : mCapabilities->Reset();
562 : }
563 :
564 0 : const char *start = capability;
565 0 : for(;;) {
566 0 : const char *space = PL_strchr(start, ' ');
567 0 : int len = space ? space - start : strlen(start);
568 0 : nsCAutoString capString(start, len);
569 0 : nsCStringKey key(capString);
570 0 : mCapabilities->Put(&key, NS_INT32_TO_PTR(canEnable));
571 0 : if (!space) {
572 : break;
573 : }
574 :
575 0 : start = space + 1;
576 : }
577 :
578 0 : return NS_OK;
579 : }
580 :
581 : NS_IMETHODIMP
582 0 : nsPrincipal::IsCapabilityEnabled(const char *capability, void *annotation,
583 : bool *result)
584 : {
585 0 : *result = false;
586 0 : nsHashtable *ht = (nsHashtable *) annotation;
587 0 : if (!ht) {
588 0 : return NS_OK;
589 : }
590 0 : const char *start = capability;
591 0 : for(;;) {
592 0 : const char *space = PL_strchr(start, ' ');
593 0 : int len = space ? space - start : strlen(start);
594 0 : nsCAutoString capString(start, len);
595 0 : nsCStringKey key(capString);
596 0 : *result = (ht->Get(&key) == (void *) AnnotationEnabled);
597 0 : if (!*result) {
598 : // If any single capability is not enabled, then return false.
599 0 : return NS_OK;
600 : }
601 :
602 0 : if (!space) {
603 0 : return NS_OK;
604 : }
605 :
606 0 : start = space + 1;
607 : }
608 :
609 : return NS_OK;
610 : }
611 :
612 : NS_IMETHODIMP
613 0 : nsPrincipal::EnableCapability(const char *capability, void **annotation)
614 : {
615 0 : return SetCapability(capability, annotation, AnnotationEnabled);
616 : }
617 :
618 : NS_IMETHODIMP
619 0 : nsPrincipal::DisableCapability(const char *capability, void **annotation)
620 : {
621 0 : return SetCapability(capability, annotation, AnnotationDisabled);
622 : }
623 :
624 : NS_IMETHODIMP
625 0 : nsPrincipal::RevertCapability(const char *capability, void **annotation)
626 : {
627 0 : if (*annotation) {
628 0 : nsHashtable *ht = (nsHashtable *) *annotation;
629 0 : const char *start = capability;
630 0 : for(;;) {
631 0 : const char *space = PL_strchr(start, ' ');
632 0 : int len = space ? space - start : strlen(start);
633 0 : nsCAutoString capString(start, len);
634 0 : nsCStringKey key(capString);
635 0 : ht->Remove(&key);
636 0 : if (!space) {
637 0 : return NS_OK;
638 : }
639 :
640 0 : start = space + 1;
641 : }
642 : }
643 0 : return NS_OK;
644 : }
645 :
646 : nsresult
647 0 : nsPrincipal::SetCapability(const char *capability, void **annotation,
648 : AnnotationValue value)
649 : {
650 0 : if (*annotation == nsnull) {
651 0 : nsHashtable* ht = new nsHashtable(5);
652 :
653 0 : if (!ht) {
654 0 : return NS_ERROR_OUT_OF_MEMORY;
655 : }
656 :
657 : // This object owns its annotations. Save them so we can release
658 : // them when we destroy this object.
659 0 : if (!mAnnotations.AppendElement(ht)) {
660 0 : delete ht;
661 0 : return NS_ERROR_OUT_OF_MEMORY;
662 : }
663 :
664 0 : *annotation = ht;
665 : }
666 :
667 0 : const char *start = capability;
668 0 : for(;;) {
669 0 : const char *space = PL_strchr(start, ' ');
670 0 : int len = space ? space - start : strlen(start);
671 0 : nsCAutoString capString(start, len);
672 0 : nsCStringKey key(capString);
673 0 : nsHashtable *ht = static_cast<nsHashtable *>(*annotation);
674 0 : ht->Put(&key, (void *) value);
675 0 : if (!space) {
676 : break;
677 : }
678 :
679 0 : start = space + 1;
680 : }
681 :
682 0 : return NS_OK;
683 : }
684 :
685 : NS_IMETHODIMP
686 0 : nsPrincipal::GetHasCertificate(bool* aResult)
687 : {
688 0 : *aResult = (mCert != nsnull);
689 :
690 0 : return NS_OK;
691 : }
692 :
693 : NS_IMETHODIMP
694 226 : nsPrincipal::GetURI(nsIURI** aURI)
695 : {
696 226 : if (mCodebaseImmutable) {
697 226 : NS_ADDREF(*aURI = mCodebase);
698 226 : return NS_OK;
699 : }
700 :
701 0 : if (!mCodebase) {
702 0 : *aURI = nsnull;
703 0 : return NS_OK;
704 : }
705 :
706 0 : return NS_EnsureSafeToReturn(mCodebase, aURI);
707 : }
708 :
709 : void
710 0 : nsPrincipal::SetURI(nsIURI* aURI)
711 : {
712 0 : mCodebase = NS_TryToMakeImmutable(aURI);
713 0 : mCodebaseImmutable = URIIsImmutable(mCodebase);
714 0 : }
715 :
716 :
717 : nsresult
718 0 : nsPrincipal::SetCertificate(const nsACString& aFingerprint,
719 : const nsACString& aSubjectName,
720 : const nsACString& aPrettyName,
721 : nsISupports* aCert)
722 : {
723 0 : NS_ENSURE_STATE(!mCert);
724 :
725 0 : if (aFingerprint.IsEmpty()) {
726 0 : return NS_ERROR_INVALID_ARG;
727 : }
728 :
729 0 : mCert = new Certificate(aFingerprint, aSubjectName, aPrettyName, aCert);
730 0 : if (!mCert) {
731 0 : return NS_ERROR_OUT_OF_MEMORY;
732 : }
733 :
734 0 : return NS_OK;
735 : }
736 :
737 : NS_IMETHODIMP
738 0 : nsPrincipal::GetFingerprint(nsACString& aFingerprint)
739 : {
740 0 : NS_ENSURE_STATE(mCert);
741 :
742 0 : aFingerprint = mCert->fingerprint;
743 :
744 0 : return NS_OK;
745 : }
746 :
747 : NS_IMETHODIMP
748 0 : nsPrincipal::GetPrettyName(nsACString& aName)
749 : {
750 0 : NS_ENSURE_STATE(mCert);
751 :
752 0 : aName = mCert->prettyName;
753 :
754 0 : return NS_OK;
755 : }
756 :
757 : NS_IMETHODIMP
758 0 : nsPrincipal::GetSubjectName(nsACString& aName)
759 : {
760 0 : NS_ENSURE_STATE(mCert);
761 :
762 0 : aName = mCert->subjectName;
763 :
764 0 : return NS_OK;
765 : }
766 :
767 : NS_IMETHODIMP
768 0 : nsPrincipal::GetCertificate(nsISupports** aCertificate)
769 : {
770 0 : if (mCert) {
771 0 : NS_IF_ADDREF(*aCertificate = mCert->cert);
772 : }
773 : else {
774 0 : *aCertificate = nsnull;
775 : }
776 0 : return NS_OK;
777 : }
778 :
779 : NS_IMETHODIMP
780 0 : nsPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp)
781 : {
782 0 : NS_IF_ADDREF(*aCsp = mCSP);
783 0 : return NS_OK;
784 : }
785 :
786 : NS_IMETHODIMP
787 0 : nsPrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
788 : {
789 : // If CSP was already set, it should not be destroyed! Instead, it should
790 : // get set anew when a new principal is created.
791 0 : if (mCSP)
792 0 : return NS_ERROR_ALREADY_INITIALIZED;
793 :
794 0 : mCSP = aCsp;
795 0 : return NS_OK;
796 : }
797 :
798 : NS_IMETHODIMP
799 0 : nsPrincipal::GetHashValue(PRUint32* aValue)
800 : {
801 0 : NS_PRECONDITION(mCert || mCodebase, "Need a cert or codebase");
802 :
803 : // If there is a certificate, it takes precendence over the codebase.
804 0 : if (mCert) {
805 0 : *aValue = HashString(mCert->fingerprint);
806 : }
807 : else {
808 0 : *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this);
809 : }
810 :
811 0 : return NS_OK;
812 : }
813 :
814 : NS_IMETHODIMP
815 1 : nsPrincipal::GetDomain(nsIURI** aDomain)
816 : {
817 1 : if (!mDomain) {
818 1 : *aDomain = nsnull;
819 1 : return NS_OK;
820 : }
821 :
822 0 : if (mDomainImmutable) {
823 0 : NS_ADDREF(*aDomain = mDomain);
824 0 : return NS_OK;
825 : }
826 :
827 0 : return NS_EnsureSafeToReturn(mDomain, aDomain);
828 : }
829 :
830 : NS_IMETHODIMP
831 0 : nsPrincipal::SetDomain(nsIURI* aDomain)
832 : {
833 0 : mDomain = NS_TryToMakeImmutable(aDomain);
834 0 : mDomainImmutable = URIIsImmutable(mDomain);
835 :
836 : // Domain has changed, forget cached security policy
837 0 : SetSecurityPolicy(nsnull);
838 :
839 0 : return NS_OK;
840 : }
841 :
842 : nsresult
843 0 : nsPrincipal::InitFromPersistent(const char* aPrefName,
844 : const nsCString& aToken,
845 : const nsCString& aSubjectName,
846 : const nsACString& aPrettyName,
847 : const char* aGrantedList,
848 : const char* aDeniedList,
849 : nsISupports* aCert,
850 : bool aIsCert,
851 : bool aTrusted)
852 : {
853 0 : NS_PRECONDITION(!mCapabilities || mCapabilities->Count() == 0,
854 : "mCapabilities was already initialized?");
855 0 : NS_PRECONDITION(mAnnotations.Length() == 0,
856 : "mAnnotations was already initialized?");
857 0 : NS_PRECONDITION(!mInitialized, "We were already initialized?");
858 :
859 0 : mInitialized = true;
860 :
861 : nsresult rv;
862 0 : if (aIsCert) {
863 0 : rv = SetCertificate(aToken, aSubjectName, aPrettyName, aCert);
864 :
865 0 : if (NS_FAILED(rv)) {
866 0 : return rv;
867 : }
868 : }
869 : else {
870 0 : rv = NS_NewURI(getter_AddRefs(mCodebase), aToken, nsnull);
871 0 : if (NS_FAILED(rv)) {
872 0 : NS_ERROR("Malformed URI in capability.principal preference.");
873 0 : return rv;
874 : }
875 :
876 0 : NS_TryToSetImmutable(mCodebase);
877 0 : mCodebaseImmutable = URIIsImmutable(mCodebase);
878 :
879 0 : mTrusted = aTrusted;
880 : }
881 :
882 : //-- Save the preference name
883 0 : mPrefName = aPrefName;
884 :
885 0 : const char* ordinalBegin = PL_strpbrk(aPrefName, "1234567890");
886 0 : if (ordinalBegin) {
887 0 : PRIntn n = atoi(ordinalBegin);
888 0 : if (sCapabilitiesOrdinal <= n) {
889 0 : sCapabilitiesOrdinal = n + 1;
890 : }
891 : }
892 :
893 : //-- Store the capabilities
894 0 : rv = NS_OK;
895 0 : if (aGrantedList) {
896 0 : rv = SetCanEnableCapability(aGrantedList, nsIPrincipal::ENABLE_GRANTED);
897 : }
898 :
899 0 : if (NS_SUCCEEDED(rv) && aDeniedList) {
900 0 : rv = SetCanEnableCapability(aDeniedList, nsIPrincipal::ENABLE_DENIED);
901 : }
902 :
903 0 : return rv;
904 : }
905 :
906 : nsresult
907 0 : nsPrincipal::EnsureCertData(const nsACString& aSubjectName,
908 : const nsACString& aPrettyName,
909 : nsISupports* aCert)
910 : {
911 0 : NS_ENSURE_STATE(mCert);
912 :
913 0 : if (!mCert->subjectName.IsEmpty() &&
914 0 : !mCert->subjectName.Equals(aSubjectName)) {
915 0 : return NS_ERROR_INVALID_ARG;
916 : }
917 :
918 0 : mCert->subjectName = aSubjectName;
919 0 : mCert->prettyName = aPrettyName;
920 0 : mCert->cert = aCert;
921 0 : return NS_OK;
922 : }
923 :
924 : struct CapabilityList
925 : {
926 : nsCString* granted;
927 : nsCString* denied;
928 : };
929 :
930 : static bool
931 0 : AppendCapability(nsHashKey *aKey, void *aData, void *capListPtr)
932 : {
933 0 : CapabilityList* capList = (CapabilityList*)capListPtr;
934 0 : PRInt16 value = (PRInt16)NS_PTR_TO_INT32(aData);
935 0 : nsCStringKey* key = (nsCStringKey *)aKey;
936 0 : if (value == nsIPrincipal::ENABLE_GRANTED) {
937 0 : capList->granted->Append(key->GetString(), key->GetStringLength());
938 0 : capList->granted->Append(' ');
939 : }
940 0 : else if (value == nsIPrincipal::ENABLE_DENIED) {
941 0 : capList->denied->Append(key->GetString(), key->GetStringLength());
942 0 : capList->denied->Append(' ');
943 : }
944 :
945 0 : return true;
946 : }
947 :
948 : NS_IMETHODIMP
949 0 : nsPrincipal::GetPreferences(char** aPrefName, char** aID,
950 : char** aSubjectName,
951 : char** aGrantedList, char** aDeniedList,
952 : bool* aIsTrusted)
953 : {
954 0 : if (mPrefName.IsEmpty()) {
955 0 : if (mCert) {
956 0 : mPrefName.Assign("capability.principal.certificate.p");
957 : }
958 : else {
959 0 : mPrefName.Assign("capability.principal.codebase.p");
960 : }
961 :
962 0 : mPrefName.AppendInt(sCapabilitiesOrdinal++);
963 0 : mPrefName.Append(".id");
964 : }
965 :
966 0 : *aPrefName = nsnull;
967 0 : *aID = nsnull;
968 0 : *aSubjectName = nsnull;
969 0 : *aGrantedList = nsnull;
970 0 : *aDeniedList = nsnull;
971 0 : *aIsTrusted = mTrusted;
972 :
973 0 : char *prefName = nsnull;
974 0 : char *id = nsnull;
975 0 : char *subjectName = nsnull;
976 0 : char *granted = nsnull;
977 0 : char *denied = nsnull;
978 :
979 : //-- Preference name
980 0 : prefName = ToNewCString(mPrefName);
981 0 : if (!prefName) {
982 0 : return NS_ERROR_OUT_OF_MEMORY;
983 : }
984 :
985 : //-- ID
986 0 : nsresult rv = NS_OK;
987 0 : if (mCert) {
988 0 : id = ToNewCString(mCert->fingerprint);
989 0 : if (!id) {
990 0 : rv = NS_ERROR_OUT_OF_MEMORY;
991 : }
992 : }
993 : else {
994 0 : rv = GetOrigin(&id);
995 : }
996 :
997 0 : if (NS_FAILED(rv)) {
998 0 : nsMemory::Free(prefName);
999 0 : return rv;
1000 : }
1001 :
1002 0 : if (mCert) {
1003 0 : subjectName = ToNewCString(mCert->subjectName);
1004 : } else {
1005 0 : subjectName = ToNewCString(EmptyCString());
1006 : }
1007 :
1008 0 : if (!subjectName) {
1009 0 : nsMemory::Free(prefName);
1010 0 : nsMemory::Free(id);
1011 0 : return NS_ERROR_OUT_OF_MEMORY;
1012 : }
1013 :
1014 : //-- Capabilities
1015 0 : nsCAutoString grantedListStr, deniedListStr;
1016 0 : if (mCapabilities) {
1017 0 : CapabilityList capList = CapabilityList();
1018 0 : capList.granted = &grantedListStr;
1019 0 : capList.denied = &deniedListStr;
1020 0 : mCapabilities->Enumerate(AppendCapability, (void*)&capList);
1021 : }
1022 :
1023 0 : if (!grantedListStr.IsEmpty()) {
1024 0 : grantedListStr.Truncate(grantedListStr.Length() - 1);
1025 0 : granted = ToNewCString(grantedListStr);
1026 0 : if (!granted) {
1027 0 : nsMemory::Free(prefName);
1028 0 : nsMemory::Free(id);
1029 0 : nsMemory::Free(subjectName);
1030 0 : return NS_ERROR_OUT_OF_MEMORY;
1031 : }
1032 : }
1033 :
1034 0 : if (!deniedListStr.IsEmpty()) {
1035 0 : deniedListStr.Truncate(deniedListStr.Length() - 1);
1036 0 : denied = ToNewCString(deniedListStr);
1037 0 : if (!denied) {
1038 0 : nsMemory::Free(prefName);
1039 0 : nsMemory::Free(id);
1040 0 : nsMemory::Free(subjectName);
1041 0 : if (granted) {
1042 0 : nsMemory::Free(granted);
1043 : }
1044 0 : return NS_ERROR_OUT_OF_MEMORY;
1045 : }
1046 : }
1047 :
1048 0 : *aPrefName = prefName;
1049 0 : *aID = id;
1050 0 : *aSubjectName = subjectName;
1051 0 : *aGrantedList = granted;
1052 0 : *aDeniedList = denied;
1053 :
1054 0 : return NS_OK;
1055 : }
1056 :
1057 : static nsresult
1058 0 : ReadAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey** aKey,
1059 : void** aData)
1060 : {
1061 : nsresult rv;
1062 0 : nsCStringKey* key = new nsCStringKey(aStream, &rv);
1063 0 : if (!key)
1064 0 : return NS_ERROR_OUT_OF_MEMORY;
1065 :
1066 0 : if (NS_FAILED(rv)) {
1067 0 : delete key;
1068 0 : return rv;
1069 : }
1070 :
1071 : PRUint32 value;
1072 0 : rv = aStream->Read32(&value);
1073 0 : if (NS_FAILED(rv)) {
1074 0 : delete key;
1075 0 : return rv;
1076 : }
1077 :
1078 0 : *aKey = key;
1079 0 : *aData = (void*) value;
1080 0 : return NS_OK;
1081 : }
1082 :
1083 : static void
1084 0 : FreeAnnotationEntry(nsIObjectInputStream* aStream, nsHashKey* aKey,
1085 : void* aData)
1086 : {
1087 0 : delete aKey;
1088 0 : }
1089 :
1090 : NS_IMETHODIMP
1091 0 : nsPrincipal::Read(nsIObjectInputStream* aStream)
1092 : {
1093 : bool hasCapabilities;
1094 0 : nsresult rv = aStream->ReadBoolean(&hasCapabilities);
1095 0 : if (NS_SUCCEEDED(rv) && hasCapabilities) {
1096 : mCapabilities = new nsHashtable(aStream, ReadAnnotationEntry,
1097 0 : FreeAnnotationEntry, &rv);
1098 0 : NS_ENSURE_TRUE(mCapabilities, NS_ERROR_OUT_OF_MEMORY);
1099 : }
1100 :
1101 0 : if (NS_FAILED(rv)) {
1102 0 : return rv;
1103 : }
1104 :
1105 0 : rv = NS_ReadOptionalCString(aStream, mPrefName);
1106 0 : if (NS_FAILED(rv)) {
1107 0 : return rv;
1108 : }
1109 :
1110 0 : const char* ordinalBegin = PL_strpbrk(mPrefName.get(), "1234567890");
1111 0 : if (ordinalBegin) {
1112 0 : PRIntn n = atoi(ordinalBegin);
1113 0 : if (sCapabilitiesOrdinal <= n) {
1114 0 : sCapabilitiesOrdinal = n + 1;
1115 : }
1116 : }
1117 :
1118 : bool haveCert;
1119 0 : rv = aStream->ReadBoolean(&haveCert);
1120 0 : if (NS_FAILED(rv)) {
1121 0 : return rv;
1122 : }
1123 :
1124 0 : nsCString fingerprint;
1125 0 : nsCString subjectName;
1126 0 : nsCString prettyName;
1127 0 : nsCOMPtr<nsISupports> cert;
1128 0 : if (haveCert) {
1129 0 : rv = NS_ReadOptionalCString(aStream, fingerprint);
1130 0 : if (NS_FAILED(rv)) {
1131 0 : return rv;
1132 : }
1133 :
1134 0 : rv = NS_ReadOptionalCString(aStream, subjectName);
1135 0 : if (NS_FAILED(rv)) {
1136 0 : return rv;
1137 : }
1138 :
1139 0 : rv = NS_ReadOptionalCString(aStream, prettyName);
1140 0 : if (NS_FAILED(rv)) {
1141 0 : return rv;
1142 : }
1143 :
1144 0 : rv = aStream->ReadObject(true, getter_AddRefs(cert));
1145 0 : if (NS_FAILED(rv)) {
1146 0 : return rv;
1147 : }
1148 : }
1149 :
1150 0 : nsCOMPtr<nsIURI> codebase;
1151 0 : rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(codebase));
1152 0 : if (NS_FAILED(rv)) {
1153 0 : return rv;
1154 : }
1155 :
1156 0 : rv = Init(fingerprint, subjectName, prettyName, cert, codebase);
1157 0 : NS_ENSURE_SUCCESS(rv, rv);
1158 :
1159 0 : nsCOMPtr<nsIURI> domain;
1160 0 : rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(domain));
1161 0 : if (NS_FAILED(rv)) {
1162 0 : return rv;
1163 : }
1164 :
1165 0 : SetDomain(domain);
1166 :
1167 0 : rv = aStream->ReadBoolean(&mTrusted);
1168 0 : if (NS_FAILED(rv)) {
1169 0 : return rv;
1170 : }
1171 :
1172 0 : return NS_OK;
1173 : }
1174 :
1175 : static nsresult
1176 0 : WriteScalarValue(nsIObjectOutputStream* aStream, void* aData)
1177 : {
1178 0 : PRUint32 value = NS_PTR_TO_INT32(aData);
1179 :
1180 0 : return aStream->Write32(value);
1181 : }
1182 :
1183 : NS_IMETHODIMP
1184 0 : nsPrincipal::Write(nsIObjectOutputStream* aStream)
1185 : {
1186 0 : NS_ENSURE_STATE(mCert || mCodebase);
1187 :
1188 : // mAnnotations is transient data associated to specific JS stack frames. We
1189 : // don't want to serialize that.
1190 :
1191 0 : bool hasCapabilities = (mCapabilities && mCapabilities->Count() > 0);
1192 0 : nsresult rv = aStream->WriteBoolean(hasCapabilities);
1193 0 : if (NS_SUCCEEDED(rv) && hasCapabilities) {
1194 0 : rv = mCapabilities->Write(aStream, WriteScalarValue);
1195 : }
1196 :
1197 0 : if (NS_FAILED(rv)) {
1198 0 : return rv;
1199 : }
1200 :
1201 0 : rv = NS_WriteOptionalStringZ(aStream, mPrefName.get());
1202 0 : if (NS_FAILED(rv)) {
1203 0 : return rv;
1204 : }
1205 :
1206 0 : rv = aStream->WriteBoolean(mCert != nsnull);
1207 0 : if (NS_FAILED(rv)) {
1208 0 : return rv;
1209 : }
1210 :
1211 0 : if (mCert) {
1212 0 : NS_ENSURE_STATE(mCert->cert);
1213 :
1214 0 : rv = NS_WriteOptionalStringZ(aStream, mCert->fingerprint.get());
1215 0 : if (NS_FAILED(rv)) {
1216 0 : return rv;
1217 : }
1218 :
1219 0 : rv = NS_WriteOptionalStringZ(aStream, mCert->subjectName.get());
1220 0 : if (NS_FAILED(rv)) {
1221 0 : return rv;
1222 : }
1223 :
1224 0 : rv = NS_WriteOptionalStringZ(aStream, mCert->prettyName.get());
1225 0 : if (NS_FAILED(rv)) {
1226 0 : return rv;
1227 : }
1228 :
1229 0 : rv = aStream->WriteCompoundObject(mCert->cert, NS_GET_IID(nsISupports),
1230 0 : true);
1231 0 : if (NS_FAILED(rv)) {
1232 0 : return rv;
1233 : }
1234 : }
1235 :
1236 : // mSecurityPolicy is an optimization; it'll get looked up again as needed.
1237 : // Don't bother saving and restoring it, esp. since it might change if
1238 : // preferences change.
1239 :
1240 : rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
1241 0 : true);
1242 0 : if (NS_FAILED(rv)) {
1243 0 : return rv;
1244 : }
1245 :
1246 : rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
1247 0 : true);
1248 0 : if (NS_FAILED(rv)) {
1249 0 : return rv;
1250 : }
1251 :
1252 0 : rv = aStream->Write8(mTrusted);
1253 0 : if (NS_FAILED(rv)) {
1254 0 : return rv;
1255 : }
1256 :
1257 : // mCodebaseImmutable and mDomainImmutable will be recomputed based
1258 : // on the deserialized URIs in Read().
1259 :
1260 0 : return NS_OK;
1261 : }
|