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