/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* handles operations dealing with enabling and disabling privileges */ #include "nsPrivilegeManager.h" #include "nsPrivilege.h" #include "nsPrincipalArray.h" #include "prmem.h" #include "prmon.h" #include "prlog.h" #include "prprf.h" #include "plbase64.h" #include "plstr.h" #include "jpermission.h" #ifdef ENABLE_RDF #include "rdf.h" #include "jsec2rdf.h" #endif /* ENABLE_RDF */ static nsIPrivilege * thePrivilegeCache[nsIPrivilege::PrivilegeState_NumberOfPrivileges][nsIPrivilege::PrivilegeDuration_NumberOfDurations]; static PRMonitor * caps_lock = NULL; /* We could avoid the following globals if nsHashTable's Enumerate accepted * a void * as argument and it passed that argument as a parameter to the * callback function. */ char * gForever, * gSession, * gDenied; nsPrivilegeTable * gPrivilegeTable; static PRBool RDF_RemovePrincipalsPrivilege(nsIPrincipal * prin, nsITarget * target); #ifdef ENABLE_RDF static nsIPrincipal * RDF_getPrincipal(JSec_Principal jsec_pr); static JSec_Principal RDF_CreatePrincipal(nsPrincipal *prin); #endif /* ENABLE_RDF */ PR_BEGIN_EXTERN_C #include "prefapi.h" PRBool CMGetBoolPref(char * pref_name) { #ifdef MOZ_SECURITY XP_Bool pref; if (PREF_GetBoolPref(pref_name, &pref) >=0) return pref; #endif /* MOZ_SECURITY */ return FALSE; } PR_END_EXTERN_C PRBool nsCaps_lock(void) { if(caps_lock == NULL) { caps_lock = PR_NewMonitor(); if(caps_lock == NULL) return PR_FALSE; } PR_EnterMonitor(caps_lock); return PR_TRUE; } void nsCaps_unlock(void) { PR_ASSERT(caps_lock != NULL); if(caps_lock != NULL) PR_ExitMonitor(caps_lock); } static NS_DEFINE_IID(kIPrivilegeManagerIID, NS_IPRIVILEGEMANAGER_IID); NS_IMPL_ISUPPORTS(nsPrivilegeManager, kIPrivilegeManagerIID); nsPrivilegeManager::nsPrivilegeManager(void) { NS_INIT_REFCNT(); NS_ADDREF(this); nsCaps_lock(); itsPrinToPrivTable = new nsHashtable(); itsPrinToMacroTargetPrivTable = new nsHashtable(); PRInt16 privState = 0, durationState = 0; for (PRUint16 i = 0; i < nsIPrivilege::PrivilegeState_NumberOfPrivileges; i++) { for(PRUint16 j = 0; j < nsIPrivilege::PrivilegeDuration_NumberOfDurations; j++) { privState = i; durationState = j; thePrivilegeCache[i][j] = new nsPrivilege(privState, durationState); } } #ifdef ENABLE_RDF RDFJSec_InitPrivilegeDB(); #endif /* ENABLE_RDF */ nsCaps_unlock(); } nsPrivilegeManager::~nsPrivilegeManager(void) { nsCaps_lock(); if(itsPrinToPrivTable) delete itsPrinToPrivTable; if(itsPrinToMacroTargetPrivTable) delete itsPrinToMacroTargetPrivTable; nsCaps_unlock(); } nsPrivilegeManager * nsPrivilegeManager::GetPrivilegeManager() { static nsPrivilegeManager * privMan = NULL; if(!privMan) privMan = new nsPrivilegeManager(); return privMan; } nsIPrivilege * nsPrivilegeManager::FindPrivilege(PRInt16 privState, PRInt16 privDuration) { return thePrivilegeCache[privState][privDuration]; } nsIPrivilege * nsPrivilegeManager::FindPrivilege(nsIPrivilege * priv) { PRInt16 privState; PRInt16 privDuration; priv->GetState(& privState); priv->GetDuration(& privDuration); return nsPrivilegeManager::FindPrivilege(privState, privDuration); } nsIPrivilege * nsPrivilegeManager::FindPrivilege(char * privStr) { PRInt16 privState; PRInt16 privDuration; if (PL_strcmp(privStr, "allowed in scope") == 0) { privState = nsIPrivilege::PrivilegeState_Allowed; privDuration = nsIPrivilege::PrivilegeDuration_Scope; } else if (PL_strcmp(privStr, "allowed in session") == 0) { privState = nsIPrivilege::PrivilegeState_Allowed; privDuration = nsIPrivilege::PrivilegeDuration_Session; } else if (PL_strcmp(privStr, "allowed forever") == 0) { privState = nsIPrivilege::PrivilegeState_Allowed; privDuration = nsIPrivilege::PrivilegeDuration_Forever; } else if (PL_strcmp(privStr, "forbidden forever") == 0) { privState = nsIPrivilege::PrivilegeState_Forbidden; privDuration = nsIPrivilege::PrivilegeDuration_Forever; } else if (PL_strcmp(privStr, "forbidden in session") == 0) { privState = nsIPrivilege::PrivilegeState_Forbidden; privDuration = nsIPrivilege::PrivilegeDuration_Session; } else if (PL_strcmp(privStr, "forbidden in scope") == 0) { privState = nsIPrivilege::PrivilegeState_Forbidden; privDuration = nsIPrivilege::PrivilegeDuration_Scope; } else if (PL_strcmp(privStr, "blank forever") == 0) { privState = nsIPrivilege::PrivilegeState_Blank; privDuration = nsIPrivilege::PrivilegeDuration_Forever; } else if (PL_strcmp(privStr, "blank in session") == 0) { privState = nsIPrivilege::PrivilegeState_Blank; privDuration = nsIPrivilege::PrivilegeDuration_Session; } else if (PL_strcmp(privStr, "blank in scope") == 0) { privState = nsIPrivilege::PrivilegeState_Blank; privDuration = nsIPrivilege::PrivilegeDuration_Scope; } else { privState = nsIPrivilege::PrivilegeState_Blank; privDuration = nsIPrivilege::PrivilegeDuration_Scope; } return nsPrivilegeManager::FindPrivilege(privState, privDuration); } nsIPrivilege * nsPrivilegeManager::Add(nsIPrivilege * priv1, nsIPrivilege * priv2) { nsresult rv; PRInt16 * p1state = NULL, * p2state = NULL; rv = priv1->GetState(p1state); rv = priv2->GetState(p2state); return (p1state < p2state) ? priv1 : priv2; } PRBool nsPrivilegeManager::IsPrivilegeEnabled(nsITarget * target, PRInt32 callerDepth) { PRBool result; IsPrivilegeEnabled(NULL, target, callerDepth,& result); return result; } NS_IMETHODIMP nsPrivilegeManager::IsPrivilegeEnabled(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth, PRBool * result) { nsTargetArray * targetArray = new nsTargetArray(); nsITarget * targ = nsTarget::FindTarget(target); if (targ != target) { * result = PR_FALSE; return NS_OK; } targetArray->Add(targ); * result = (this->CheckPrivilegeEnabled(context, targetArray, callerDepth, NULL) != NULL) ? PR_FALSE : PR_TRUE; return NS_OK; } PRBool nsPrivilegeManager::EnablePrivilege(nsITarget * target, PRInt32 callerDepth) { return this->EnablePrivilegePrivate(NULL, target, NULL, callerDepth); } PRBool nsPrivilegeManager::EnablePrivilege(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth) { return this->EnablePrivilegePrivate(context, target, NULL, callerDepth); } PRBool nsPrivilegeManager::EnablePrivilege(nsITarget * target, nsIPrincipal * preferredPrincipal, PRInt32 callerDepth) { return this->EnablePrivilegePrivate(NULL, target, preferredPrincipal, callerDepth); } NS_IMETHODIMP nsPrivilegeManager::EnablePrivilege(nsIScriptContext * context, nsITarget * target, nsIPrincipal * preferredPrincipal, PRInt32 callerDepth, PRBool * result) { * result = this->EnablePrivilegePrivate(context, target, preferredPrincipal, callerDepth); return NS_OK; } PRBool nsPrivilegeManager::RevertPrivilege(nsITarget * target, PRInt32 callerDepth) { PRBool result; this->RevertPrivilege(NULL, target, callerDepth,& result); return result; } NS_IMETHODIMP nsPrivilegeManager::RevertPrivilege(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth, PRBool * result) { nsITarget * targ = nsTarget::FindTarget(target); if (targ != target) { * result = PR_FALSE; return NS_OK; } nsPrivilegeTable *privTable = this->GetPrivilegeTableFromStack(context, callerDepth, PR_TRUE); nsCaps_lock(); privTable->Put(target, nsPrivilegeManager::FindPrivilege(nsIPrivilege::PrivilegeState_Blank, nsIPrivilege::PrivilegeDuration_Scope)); nsCaps_unlock(); * result = PR_TRUE; return NS_OK; } PRBool nsPrivilegeManager::DisablePrivilege(nsITarget * target, PRInt32 callerDepth) { PRBool result; result = this->DisablePrivilege(NULL, target, callerDepth,& result); return result; } NS_IMETHODIMP nsPrivilegeManager::DisablePrivilege(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth, PRBool * result) { nsITarget * targ = nsTarget::FindTarget(target); if (targ != target) { * result = PR_FALSE; return NS_OK; } nsPrivilegeTable *privTable = this->GetPrivilegeTableFromStack(context, callerDepth, PR_TRUE); nsCaps_lock(); privTable->Put(target, nsPrivilegeManager::FindPrivilege(nsIPrivilege::PrivilegeState_Forbidden, nsIPrivilege::PrivilegeDuration_Scope)); nsCaps_unlock(); * result = PR_TRUE; return NS_OK; } PRBool nsPrivilegeManager::EnablePrincipalPrivilegeHelper(nsITarget *target, PRInt32 callerDepth, nsIPrincipal * preferredPrin, void * data, nsITarget *impersonator) { return this->EnablePrincipalPrivilegeHelper(NULL, target, callerDepth, preferredPrin, data, impersonator); } PRBool nsPrivilegeManager::EnablePrincipalPrivilegeHelper(nsIScriptContext * context, nsITarget *target, PRInt32 callerDepth, nsIPrincipal * preferredPrin, void * data, nsITarget *impersonator) { #if 1 return PR_FALSE; #else nsIPrincipalArray* callerPrinArray; nsIPrincipal * useThisPrin = NULL; /* Get the registered target */ nsITarget *targ = nsTarget::FindTarget(target); if (targ != target) return PR_FALSE; nsPrincipalManager * prinMan; nsPrincipalManager::GetPrincipalManager(& prinMan); callerPrinArray = prinMan->GetClassPrincipalsFromStack((nsIScriptContext *)context, callerDepth); if (preferredPrin != NULL) { nsIPrincipal * callerPrin; PRUint32 i; callerPrinArray->GetPrincipalArraySize(& i); while (i-- > 0) { callerPrinArray->GetPrincipalArrayElement(i, & callerPrin); PRBool prinEq; PRInt16 prinType; callerPrin->Equals(preferredPrin, & prinEq); callerPrin->GetType(& prinType); if (prinEq && ((prinType == nsIPrincipal::PrincipalType_Certificate) || (prinType == nsIPrincipal::PrincipalType_CertificateFingerPrint) || (prinType == nsIPrincipal::PrincipalType_CertificateKey) || (prinType == nsIPrincipal::PrincipalType_CertificateChain)) ) { useThisPrin = callerPrin; break; } } if ((useThisPrin == NULL) && (impersonator)){ nsITarget *t1; t1 = impersonator; PRBool penabled; this->IsPrivilegeEnabled(context, t1, 0, & penabled); if (!penabled) return PR_FALSE; useThisPrin = preferredPrin; callerPrinArray = new nsPrincipalArray(); callerPrinArray->AddPrincipalArrayElement(preferredPrin); } } if (this->IsPermissionGranted(target, callerPrinArray, data)) return PR_TRUE; // // before we do the user dialog, we need to figure out which principal // gets the user's blessing. The applet is allowed to bias this // by offering a preferred principal. We only honor this if the // principal is *registered* (stored in itsPrinToPrivTable) and // is based on cryptographic credentials, rather than a codebase. // // if no preferredPrin is specified, or we don't like preferredPrin, // we'll use the first principal on the calling class. We know that // cryptographic credentials go first in the list, so this should // get us something reasonable. // if (useThisPrin == NULL) { PRUint32 size; callerPrinArray->GetPrincipalArraySize(& size); if (size == 0) return PR_FALSE; // throw new ForbiddenTargetException("request's caller has no principal!"); callerPrinArray->GetPrincipalArrayElement(0,& useThisPrin); } // Do a user dialog PRBool result; AskPermission(useThisPrin, target, data,& result); return result; #endif } nsPrivilegeTable * nsPrivilegeManager::EnableScopePrivilegeHelper(nsITarget * target, PRInt32 callerDepth, void * data, PRBool helpingSetScopePrivilege, nsIPrincipal * prefPrin) { return this->EnableScopePrivilegeHelper(NULL, target, callerDepth, data, helpingSetScopePrivilege, prefPrin); } nsPrivilegeTable * nsPrivilegeManager::EnableScopePrivilegeHelper(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth, void * data, PRBool helpingSetScopePrivilege, nsIPrincipal * prefPrin) { nsPrivilegeTable * privTable; nsIPrivilege * allowedScope; PRBool res; nsITarget * targ = nsTarget::FindTarget(target); if (targ != target) return NULL; //throw new ForbiddenTargetException(target + " is not a registered target"); (prefPrin != NULL) ? res = this->CheckPrivilegeGranted(target, prefPrin, data) : this->CheckPrivilegeGranted(context, target, callerDepth, data,& res); if (res == PR_FALSE) return NULL; privTable = this->GetPrivilegeTableFromStack(context, callerDepth, (helpingSetScopePrivilege ? PR_FALSE : PR_TRUE)); if (helpingSetScopePrivilege && privTable == NULL) privTable = new nsPrivilegeTable(); allowedScope = nsPrivilegeManager::FindPrivilege(nsIPrivilege::PrivilegeState_Allowed, nsIPrivilege::PrivilegeDuration_Scope); this->UpdatePrivilegeTable(target, privTable, allowedScope); return privTable; } NS_IMETHODIMP nsPrivilegeManager::AskPermission(nsIPrincipal * useThisPrin, nsITarget * target, void * data, PRBool * result) { * result = PR_FALSE; PRBool privAllowed = PR_FALSE, privForbidden = PR_FALSE; PRInt16 privDuration; nsIPrivilege * newPrivilege = NULL; // Get the Lock to display the dialog nsCaps_lock(); nsIPrincipalArray * callerPrinArray = (nsIPrincipalArray *)new nsPrincipalArray(); callerPrinArray->AddPrincipalArrayElement(useThisPrin); if (this->IsPermissionGranted(target, callerPrinArray, data)) { * result = PR_TRUE; goto done; } // Do a user dialog target->EnablePrivilege(useThisPrin, data,& newPrivilege); // Forbidden for session is equivelent to decide later. // If the privilege is DECIDE_LATER then throw exception. // That is user should be prompted again when this applet // performs the same privileged operation newPrivilege->IsAllowed(& privAllowed); newPrivilege->GetDuration(& privDuration); if (!privAllowed && (privDuration == nsIPrivilege::PrivilegeDuration_Session)) { // "User didn't grant the " + target->getName() + " privilege."; * result = PR_FALSE; goto done; } this->SetPermission(useThisPrin, target, newPrivilege); // if newPrivilege is FORBIDDEN then throw an exception newPrivilege->IsForbidden(& privForbidden); if (privForbidden) { // "User didn't grant the " + target->getName() + " privilege."; * result = PR_FALSE; goto done; } * result = PR_TRUE; done: delete callerPrinArray; nsCaps_unlock(); return NS_OK; } NS_IMETHODIMP nsPrivilegeManager::SetPermission(nsIPrincipal * useThisPrin, nsITarget * target, nsIPrivilege * newPrivilege) { // RegisterPrincipalAndSetPrivileges(useThisPrin, target, newPrivilege); //XXX ARIEL - THIS LOOKS SO WRONG, FIX IT!!!!!!!!!!!!!!! //System.out.println("Privilege table modified for: " + useThisPrin.toVerboseString() + " for target " + //target + " Privilege " + newPrivilege); // Save the signed applet's ACL to the persistence store // char * err = useThisPrin->savePrincipalPermanently(); // if((err == NULL) && (newPrivilege->GetDuration() == nsIPrivilege::PrivilegeDuration_Forever)) { // PRBool result; // useThisPrin->Equals(theUnsignedPrincipal,& result); // if (!result) Save(useThisPrin, target, newPrivilege); // } return NS_OK; } void nsPrivilegeManager::RegisterPrincipalAndSetPrivileges(nsIPrincipal * prin, nsITarget * target, nsIPrivilege * newPrivilege) { nsPrivilegeTable *privTable; nsPrincipalManager * prinMan; nsPrincipalManager::GetPrincipalManager(& prinMan); prinMan->RegisterPrincipal(prin); //Store the list of targets for which the user has given privilege PrincipalKey prinKey(prin); nsCaps_lock(); privTable = (nsPrivilegeTable *)itsPrinToMacroTargetPrivTable->Get(&prinKey); privTable->Put(target, newPrivilege); nsCaps_unlock(); privTable = (nsPrivilegeTable *)itsPrinToPrivTable->Get(&prinKey); this->UpdatePrivilegeTable(target, privTable, newPrivilege); } void nsPrivilegeManager::UpdatePrivilegeTable(nsITarget * target, nsPrivilegeTable * privTable, nsIPrivilege * newPrivilege) { nsTargetArray * primitiveTargets; target->GetFlattenedTargetArray(& primitiveTargets); nsIPrivilege * oldPrivilege, * privilege; nsITarget * primTarget; nsCaps_lock(); for (int i = primitiveTargets->GetSize(); i-- > 0;) { primTarget = (nsITarget *)primitiveTargets->Get(i); oldPrivilege = privTable->Get(primTarget); privilege = (oldPrivilege != NULL) ? nsPrivilegeManager::Add(oldPrivilege, newPrivilege) : newPrivilege; privTable->Put(primTarget, privilege); } nsCaps_unlock(); } PRBool nsPrivilegeManager::CheckPrivilegeGranted(nsITarget *target, PRInt32 callerDepth) { PRBool result; this->CheckPrivilegeGranted(NULL, target, callerDepth, NULL,& result); return result; } PRBool nsPrivilegeManager::CheckPrivilegeGranted(nsIScriptContext * context, nsITarget *target, PRInt32 callerDepth) { PRBool result; CheckPrivilegeGranted(context, target, callerDepth, NULL,& result); return result; } PRBool nsPrivilegeManager::CheckPrivilegeGranted(nsITarget *target, nsIPrincipal *prin, void *data) { nsIPrivilege * privilege; this->GetPrincipalPrivilege(target, prin, data,& privilege); PRBool allowed; privilege->IsAllowed(& allowed); return (allowed) ? PR_TRUE : PR_FALSE; } PRBool nsPrivilegeManager::CheckPrivilegeGranted(nsITarget *target, PRInt32 callerDepth, void *data) { PRBool result; CheckPrivilegeGranted(NULL, target, callerDepth, data,& result); return result; } NS_IMETHODIMP nsPrivilegeManager::CheckPrivilegeGranted(nsIScriptContext * context, nsITarget * target, PRInt32 callerDepth, void * data, PRBool * result) { nsPrincipalManager * prinMan; nsPrincipalManager::GetPrincipalManager(& prinMan); nsIPrincipalArray * callerPrinArray = prinMan->GetClassPrincipalsFromStack((nsIScriptContext *)context, callerDepth); PRInt16 privilegeState = this->GetPrincipalPrivilege(target, callerPrinArray, data); * result = (privilegeState == nsIPrivilege::PrivilegeState_Allowed) ? PR_TRUE : PR_FALSE; return NS_OK; } static PRBool GetPermissionsString(nsHashKey * aKey, void * aData, void * closure) { /* TargetKey *targetKey = (TargetKey *) aKey; nsTarget *target = targetKey->itsTarget; nsIPrivilege * priv = (nsIPrivilege *)aData; char * desc = target->GetDescription(); if (priv->isAllowedForever()) gForever = PR_sprintf_append(gForever, "