/* -*- 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, "