/* -*- 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.
*/
#include "nsPrivilegeManager.h"
#include "nsPrivilege.h"
#include "prmem.h"
#include "prmon.h"
#include "prlog.h"
#include "prprf.h"
#include "plbase64.h"
#include "jpermission.h"
#include "rdf.h"
#include "jsec2rdf.h"
static nsPrivilegeManager * thePrivilegeManager = NULL;
static nsPrincipal *theSystemPrincipal = NULL;
static nsPrincipal *theUnsignedPrincipal;
static nsPrincipalArray *theUnsignedPrincipalArray;
static nsPrincipal *theUnknownPrincipal;
static nsPrincipalArray *theUnknownPrincipalArray;
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 *gListOfPrincipals;
char *gForever;
char *gSession;
char *gDenied;
nsPrivilegeTable *gPrivilegeTable;
static PRBool getPrincipalString(nsHashKey *aKey, void *aData);
static nsPrincipal *RDF_getPrincipal(JSec_Principal jsec_pr);
static PRBool RDF_RemovePrincipal(nsPrincipal *prin);
static PRBool RDF_RemovePrincipalsPrivilege(nsPrincipal *prin, nsTarget *target);
PR_BEGIN_EXTERN_C
#include "xp.h"
#include "prefapi.h"
PRBool CMGetBoolPref(char * pref_name)
{
XP_Bool pref;
if (PREF_GetBoolPref(pref_name, &pref) >=0) {
return pref;
}
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);
}
}
//
// PUBLIC METHODS
//
nsPrivilegeManager::nsPrivilegeManager(void)
{
nsCaps_lock();
itsPrinToPrivTable = new nsHashtable();
itsPrinToMacroTargetPrivTable = new nsHashtable();
itsPrinNameToPrincipalTable = new nsHashtable();
nsCaps_unlock();
}
nsPrivilegeManager::~nsPrivilegeManager(void)
{
nsCaps_lock();
if (itsPrinToPrivTable)
delete itsPrinToPrivTable;
if (itsPrinToMacroTargetPrivTable)
delete itsPrinToMacroTargetPrivTable;
if (itsPrinNameToPrincipalTable)
delete itsPrinNameToPrincipalTable;
nsCaps_unlock();
}
void nsPrivilegeManager::addToPrinNameToPrincipalTable(nsPrincipal *prin)
{
char *prinName = prin->toString();
if (prinName == NULL) {
return;
}
StringKey prinNameKey(prinName);
nsCaps_lock();
if (NULL == itsPrinNameToPrincipalTable->Get(&prinNameKey)) {
itsPrinNameToPrincipalTable->Put(&prinNameKey, prin);
}
nsCaps_unlock();
}
void nsPrivilegeManager::registerSystemPrincipal(nsPrincipal *prin)
{
PrincipalKey prinKey(prin);
nsCaps_lock();
if (NULL == itsPrinToPrivTable->Get(&prinKey)) {
itsPrinToPrivTable->Put(&prinKey, new nsSystemPrivilegeTable());
}
if (NULL == itsPrinToMacroTargetPrivTable->Get(&prinKey)) {
itsPrinToMacroTargetPrivTable->Put(&prinKey, new nsSystemPrivilegeTable());
}
theSystemPrincipal = prin;
CreateSystemTargets(prin);
// Load the signed applet's ACL from the persistence store
load();
nsCaps_unlock();
}
void nsPrivilegeManager::registerPrincipal(nsPrincipal *prin)
{
//
// the new PrivilegeTable will have all privileges "blank forever"
// until changed by calls to enablePrincipalPrivilegeHelper
//
PrincipalKey prinKey(prin);
nsCaps_lock();
if (NULL == itsPrinToPrivTable->Get(&prinKey)) {
itsPrinToPrivTable->Put(&prinKey, new nsPrivilegeTable());
}
if (NULL == itsPrinToMacroTargetPrivTable->Get(&prinKey)) {
itsPrinToMacroTargetPrivTable->Put(&prinKey, new nsPrivilegeTable());
}
addToPrinNameToPrincipalTable(prin);
nsCaps_unlock();
}
PRBool nsPrivilegeManager::unregisterPrincipal(nsPrincipal *prin)
{
if (prin->equals(getSystemPrincipal())) {
return PR_FALSE;
}
PrincipalKey prinKey(prin);
nsCaps_lock();
/* Get the privilegetables and free them up */
nsPrivilegeTable *pt =
(nsPrivilegeTable *)itsPrinToPrivTable->Get(&prinKey);
if (pt != NULL) {
delete pt;
}
nsPrivilegeTable *mpt =
(nsPrivilegeTable *)itsPrinToMacroTargetPrivTable->Get(&prinKey);
if (mpt != NULL) {
delete mpt;
}
/* Remove the principal */
void *old_prin = itsPrinToPrivTable->Remove(&prinKey);
void *old_prin1 = itsPrinToMacroTargetPrivTable->Remove(&prinKey);
/* remove principal from PrinNameToPrincipalTable */
char *prinName = prin->toString();
StringKey prinNameKey(prinName);
itsPrinNameToPrincipalTable->Remove(&prinNameKey);
/* remove the principal from RDF also */
RDF_RemovePrincipal(prin);
nsCaps_unlock();
if ((old_prin == NULL) && (old_prin1 == NULL)) {
return PR_FALSE;
}
return PR_TRUE;
}
PRBool nsPrivilegeManager::isPrivilegeEnabled(nsTarget *target, PRInt32 callerDepth)
{
nsTargetArray * targetArray = new nsTargetArray();
nsTarget *targ = nsTarget::findTarget(target);
if (targ != target) {
return PR_FALSE;
}
targetArray->Add(targ);
return (checkPrivilegeEnabled(targetArray, callerDepth, NULL) != NULL) ? PR_FALSE : PR_TRUE;
}
PRBool nsPrivilegeManager::enablePrivilege(nsTarget *target, PRInt32 callerDepth)
{
return enablePrivilegePrivate(target, NULL, callerDepth);
}
PRBool nsPrivilegeManager::enablePrivilege(nsTarget *target,
nsPrincipal *preferredPrincipal,
PRInt32 callerDepth)
{
return enablePrivilegePrivate(target, preferredPrincipal, callerDepth);
}
PRBool nsPrivilegeManager::revertPrivilege(nsTarget *target,
PRInt32 callerDepth)
{
nsTarget *targ = nsTarget::findTarget(target);
if (targ != target) {
//throw new ForbiddenTargetException(target + " is not a registered target");
return PR_FALSE;
}
nsPrivilegeTable *privTable = getPrivilegeTableFromStack(callerDepth,
PR_TRUE);
nsCaps_lock();
privTable->put(target, nsPrivilege::findPrivilege(nsPermissionState_Blank,
nsDurationState_Scope));
nsCaps_unlock();
return PR_TRUE;
}
PRBool nsPrivilegeManager::disablePrivilege(nsTarget *target,
PRInt32 callerDepth)
{
nsTarget *targ = nsTarget::findTarget(target);
if (targ != target) {
//throw new ForbiddenTargetException(target + " is not a registered target");
return PR_FALSE;
}
nsPrivilegeTable *privTable = getPrivilegeTableFromStack(callerDepth,
PR_TRUE);
nsCaps_lock();
privTable->put(target, nsPrivilege::findPrivilege(nsPermissionState_Forbidden,
nsDurationState_Scope));
nsCaps_unlock();
return PR_TRUE;
}
PRBool nsPrivilegeManager::enablePrincipalPrivilegeHelper(nsTarget *target,
PRInt32 callerDepth,
nsPrincipal *preferredPrin,
void * data,
nsTarget *impersonator)
{
nsPrincipalArray* callerPrinArray;
nsPrincipal *useThisPrin = NULL;
char *err;
/* Get the registered target */
nsTarget *targ = nsTarget::findTarget(target);
if (targ != target) {
return PR_FALSE;
/* throw new ForbiddenTargetException(target + " is not a registered target"); */
}
callerPrinArray = getClassPrincipalsFromStack(callerDepth);
if (preferredPrin != NULL) {
nsPrincipal *callerPrin;
for (PRUint32 i = callerPrinArray->GetSize(); i-- > 0;) {
callerPrin = (nsPrincipal *)callerPrinArray->Get(i);
if ((callerPrin->equals(preferredPrin)) &&
((callerPrin->isCert() ||
callerPrin->isCertFingerprint()))) {
useThisPrin = callerPrin;
break;
}
}
if ((useThisPrin == NULL) && (impersonator)){
nsTarget *t1;
t1 = impersonator;
if (PR_FALSE == isPrivilegeEnabled(t1, 0))
return PR_FALSE;
useThisPrin = preferredPrin;
callerPrinArray = new nsPrincipalArray();
callerPrinArray->Add(preferredPrin);
}
}
if (isPermissionGranted(target, callerPrinArray, data))
return PR_TRUE;
// Do a user dialog
nsPrivilege *newPrivilege;
//
// 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) {
if (callerPrinArray->GetSize() == 0)
// throw new ForbiddenTargetException("request's caller has no principal!");
return PR_FALSE;
useThisPrin = (nsPrincipal *)callerPrinArray->Get(0);
}
/* Get the Lock to display the dialog */
nsCaps_lock();
PRBool ret_val=PR_FALSE;
if (PR_TRUE == isPermissionGranted(target, callerPrinArray, data)) {
ret_val = PR_TRUE;
goto done;
}
newPrivilege = target->enablePrivilege(useThisPrin, data);
// 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
//
if ((!newPrivilege->isAllowed()) &&
(newPrivilege->getDuration() == nsDurationState_Session)) {
// "User didn't grant the " + target->getName() + " privilege.";
ret_val = PR_FALSE;
goto done;
}
registerPrincipalAndSetPrivileges(useThisPrin, target, newPrivilege);
//System.out.println("Privilege table modified for: " +
// useThisPrin.toVerboseString() + " for target " +
// target + " Privilege " + newPrivilege);
// Save the signed applet's ACL to the persistence store
err = useThisPrin->savePrincipalPermanently();
if ((err == NULL) &&
(newPrivilege->getDuration() == nsDurationState_Forever)) {
//XXX: How do we save permanent access for unsigned principals
///
if (!useThisPrin->equals(theUnsignedPrincipal)) {
save(useThisPrin, target, newPrivilege);
}
}
// if newPrivilege is FORBIDDEN then throw an exception
if (newPrivilege->isForbidden()) {
// "User didn't grant the " + target->getName() + " privilege.";
ret_val = PR_FALSE;
goto done;
}
ret_val = PR_TRUE;
done:
nsCaps_unlock();
return PR_TRUE;
}
nsPrivilegeTable *
nsPrivilegeManager::enableScopePrivilegeHelper(nsTarget *target,
PRInt32 callerDepth,
void *data,
PRBool helpingSetScopePrivilege,
nsPrincipal *prefPrin)
{
nsPrivilegeTable *privTable;
nsPrivilege *allowedScope;
PRBool res;
nsTarget *targ = nsTarget::findTarget(target);
if (targ != target) {
//throw new ForbiddenTargetException(target + " is not a registered target");
return NULL;
}
if (prefPrin != NULL) {
res = checkPrivilegeGranted(target, prefPrin, data);
} else {
res = checkPrivilegeGranted(target, callerDepth, data);
}
if (res == PR_FALSE)
return NULL;
privTable = getPrivilegeTableFromStack(callerDepth,
(helpingSetScopePrivilege ? PR_FALSE : PR_TRUE));
if (helpingSetScopePrivilege) {
if (privTable == NULL) {
privTable = new nsPrivilegeTable();
}
}
allowedScope = nsPrivilege::findPrivilege(nsPermissionState_Allowed,
nsDurationState_Scope);
updatePrivilegeTable(target, privTable, allowedScope);
return privTable;
}
void
nsPrivilegeManager::registerPrincipalAndSetPrivileges(nsPrincipal *prin,
nsTarget *target,
nsPrivilege *newPrivilege)
{
nsPrivilegeTable *privTable;
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);
updatePrivilegeTable(target, privTable, newPrivilege);
}
void nsPrivilegeManager::updatePrivilegeTable(nsTarget *target,
nsPrivilegeTable *privTable,
nsPrivilege *newPrivilege)
{
nsTargetArray* primitiveTargets = target->getFlattenedTargetArray();
nsPrivilege *oldPrivilege;
nsPrivilege *privilege;
nsTarget *primTarget;
nsCaps_lock();
for (int i = primitiveTargets->GetSize(); i-- > 0;) {
primTarget = (nsTarget *)primitiveTargets->Get(i);
oldPrivilege = privTable->get(primTarget);
if (oldPrivilege != NULL) {
privilege = nsPrivilege::add(oldPrivilege, newPrivilege);
} else {
privilege = newPrivilege;
}
privTable->put(primTarget, privilege);
}
nsCaps_unlock();
}
PRBool nsPrivilegeManager::checkPrivilegeGranted(nsTarget *target,
PRInt32 callerDepth)
{
return checkPrivilegeGranted(target, callerDepth, NULL);
}
PRBool nsPrivilegeManager::checkPrivilegeGranted(nsTarget *target,
nsPrincipal *prin,
void *data)
{
nsPrivilege * privilege = getPrincipalPrivilege(target, prin, data);
if (!privilege->isAllowed()) {
//throw new ForbiddenTargetException("access to target denied");
return PR_FALSE;
}
return PR_TRUE;
}
PRBool nsPrivilegeManager::checkPrivilegeGranted(nsTarget *target,
PRInt32 callerDepth,
void *data)
{
nsPrincipalArray* callerPrinArray =
getClassPrincipalsFromStack(callerDepth);
nsPermissionState privilege =
getPrincipalPrivilege(target, callerPrinArray, data);
if (privilege != nsPermissionState_Allowed) {
//throw new ForbiddenTargetException("access to target denied");
return PR_FALSE;
}
return PR_TRUE;
}
nsPrivilegeManager * nsPrivilegeManager::getPrivilegeManager(void)
{
return thePrivilegeManager;
}
nsPrincipalArray* nsPrivilegeManager::getMyPrincipals(PRInt32 callerDepth)
{
if (thePrivilegeManager == NULL)
return NULL;
return thePrivilegeManager->getClassPrincipalsFromStack(callerDepth);
}
nsPrincipal* nsPrivilegeManager::getSystemPrincipal(void)
{
return theSystemPrincipal;
}
PRBool nsPrivilegeManager::hasSystemPrincipal(nsPrincipalArray *prinArray)
{
nsPrincipal *sysPrin = getSystemPrincipal();
nsPrincipal *prin;
if (sysPrin == NULL)
return PR_FALSE;
for (int i = prinArray->GetSize(); i-- > 0;) {
prin = (nsPrincipal *)prinArray->Get(i);
if (sysPrin->equals(prin))
return PR_TRUE;
}
return PR_FALSE;
}
nsPrincipal* nsPrivilegeManager::getUnsignedPrincipal(void)
{
return theUnsignedPrincipal;
}
nsPrincipal* nsPrivilegeManager::getUnknownPrincipal(void)
{
return theUnknownPrincipal;
}
nsSetComparisonType
nsPrivilegeManager::comparePrincipalArray(nsPrincipalArray* p1,
nsPrincipalArray* p2)
{
nsHashtable *p2Hashtable = new nsHashtable();
PRBool value;
nsPrincipal *prin;
PRUint32 i;
for (i = p2->GetSize(); i-- > 0;) {
prin = (nsPrincipal *)p2->Get(i);
PrincipalKey prinKey(prin);
p2Hashtable->Put(&prinKey, (void *)PR_TRUE);
}
for (i = p1->GetSize(); i-- > 0;) {
prin = (nsPrincipal *)p1->Get(i);
PrincipalKey prinKey(prin);
value = (PRBool)p2Hashtable->Get(&prinKey);
if (!value) {
// We found an extra element in p1
return nsSetComparisonType_NoSubset;
}
if (value == PR_TRUE) {
p2Hashtable->Put(&prinKey, (void *)PR_FALSE);
}
}
for (i = p2->GetSize(); i-- > 0;) {
prin = (nsPrincipal *)p2->Get(i);
PrincipalKey prinKey(prin);
value = (PRBool)p2Hashtable->Get(&prinKey);
if (value == PR_TRUE) {
// We found an extra element in p2
return nsSetComparisonType_ProperSubset;
}
}
return nsSetComparisonType_Equal;
}
nsPrincipalArray*
nsPrivilegeManager::intersectPrincipalArray(nsPrincipalArray* p1,
nsPrincipalArray* p2)
{
int p1_length = p1->GetSize();
int p2_length = p2->GetSize();
nsPrincipalArray *in = new nsPrincipalArray();
int count = 0;
nsPrincipal *prin1;
nsPrincipal *prin2;
int i;
in->SetSize(p1_length, 1);
int in_length = in->GetSize();
for (i=0; i < p1_length; i++) {
for (int j=0; j < p2_length; j++) {
prin1 = (nsPrincipal *)p1->Get(i);
prin2 = (nsPrincipal *)p2->Get(j);
if (prin1->equals(prin2)) {
in->Set(i, (void *)PR_TRUE);
count++;
break;
} else {
in->Set(i, (void *)PR_FALSE);
}
}
}
nsPrincipalArray *result = new nsPrincipalArray();
result->SetSize(count, 1);
PRBool doesIntersect;
int j=0;
PR_ASSERT(in_length == (int)(p1->GetSize()));
PR_ASSERT(in_length == (int)(in->GetSize()));
for (i=0; i < in_length; i++) {
doesIntersect = (PRBool)in->Get(i);
if (doesIntersect) {
PR_ASSERT(j < count);
result->Set(j, p1->Get(i));
j++;
}
}
return result;
}
PRBool nsPrivilegeManager::canExtendTrust(nsPrincipalArray* from,
nsPrincipalArray* to)
{
if ((from == NULL) || (to == NULL))
return PR_FALSE;
nsPrincipalArray * intersect = intersectPrincipalArray(from, to);
if (intersect->GetSize() == from->GetSize())
return PR_TRUE;
if (intersect->GetSize() == 0 ||
(intersect->GetSize() != (from->GetSize() - 1)))
return PR_FALSE;
int intersect_size = intersect->GetSize();
nsPrincipal *prin;
int i;
for (i=0; i < intersect_size; i++) {
prin = (nsPrincipal *)intersect->Get(i);
if (prin->isCodebase())
return PR_FALSE;
}
int codebaseCount = 0;
int from_size = from->GetSize();
for (i=0; i < from_size; i++) {
prin = (nsPrincipal *)from->Get(i);
if (prin->isCodebase())
codebaseCount++;
}
return (codebaseCount == 1) ? PR_TRUE : PR_FALSE;
}
PRBool nsPrivilegeManager::checkMatchPrincipal(nsPrincipal *prin,
PRInt32 callerDepth)
{
nsPrincipalArray *prinArray = new nsPrincipalArray();
prinArray->Add(prin);
nsPrincipalArray *classPrinArray = getClassPrincipalsFromStack(callerDepth);
return (comparePrincipalArray(prinArray, classPrinArray) != nsSetComparisonType_NoSubset) ? PR_TRUE : PR_FALSE;
}
static PRBool getPrincipalString(nsHashKey *aKey, void *aData)
{
/* Admin UI */
/* XXX: Ignore empty strings */
const char *string = ((StringKey *) aKey)->itsString;
if (gListOfPrincipals == NULL) {
gListOfPrincipals = PR_sprintf_append(gListOfPrincipals, "\"%s\"", string);
} else {
gListOfPrincipals = PR_sprintf_append(gListOfPrincipals, ",\"%s\"", string);
}
return PR_TRUE;
}
const char * nsPrivilegeManager::getAllPrincipalsString(void)
{
/* Admin UI */
char *principalStrings=NULL;
if (itsPrinNameToPrincipalTable == NULL) {
return NULL;
}
nsCaps_lock();
gListOfPrincipals = NULL;
itsPrinNameToPrincipalTable->Enumerate(getPrincipalString);
if (gListOfPrincipals) {
/* Make a copy of the principals and return it */
principalStrings = XP_AppendStr(principalStrings, gListOfPrincipals);
PR_Free(gListOfPrincipals);
}
gListOfPrincipals = NULL;
nsCaps_unlock();
return principalStrings;
}
nsPrincipal * nsPrivilegeManager::getPrincipalFromString(char *prinName)
{
/* Admin UI */
StringKey prinNameKey(prinName);
nsCaps_lock();
nsPrincipal *prin =
(nsPrincipal *)itsPrinNameToPrincipalTable->Get(&prinNameKey);
nsCaps_unlock();
return prin;
}
static PRBool getPermissionsString(nsHashKey *aKey, void *aData)
{
/* Admin UI */
TargetKey *targetKey = (TargetKey *) aKey;
nsTarget *target = targetKey->itsTarget;
nsPrivilege *priv = (nsPrivilege *)aData;
char *desc = target->getDescription();
if (priv->isAllowedForever()) {
gForever = PR_sprintf_append(gForever, "