blob: 567d9eeef2b2c9bc166c425b75c59b0c4f5bfec4 [file] [log] [blame]
//*****************************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
//*****************************************************************************
// ==========================================================================
// Contains non inline methods in the following classes
// ComSecurityKey
// Contains helper function: qiCheckForInvalidKeys
// ==========================================================================
#include "ComSecurityKey.h"
#include <bitset>
#include <string>
#include <vector>
#include "exp_function.h"
#include "ComDistribution.h"
#include "ComUser.h"
#include "PrivMgrDefs.h"
// ****************************************************************************
// function: qiSubjectMatchesRole
//
// This function compares the subjectKey with the list of roles the current
// user has been granted. If it matches one of the roles, return TRUE,
// otherwise it returns FALSE.
// ****************************************************************************
NABoolean qiSubjectMatchesRole(uint32_t subjectKey)
{
NAList <Int32> roleIDs(NULL);
ComUser::getCurrentUserRoles(roleIDs);
for (int i = 0; i < roleIDs.entries(); i++)
{
if (subjectKey = ComSecurityKey::generateHash(roleIDs[i]))
return TRUE;
}
return FALSE;
}
// ****************************************************************************
// function: qiCheckForInvalidObject
//
// This function compares the list of query invalidate keys that changed to
// the list of invalidation keys associated with the object.
//
// If the changed invalidation key matches one of the keys for the object,
// return TRUE. TRUE indicates that the object should be reloaded to pick up
// DDL or privilege changes.
// ****************************************************************************
NABoolean qiCheckForInvalidObject (const Int32 numInvalidationKeys,
const SQL_QIKEY* invalidationKeys,
const Int64 objectUID,
const ComSecurityKeySet & objectKeys)
{
NABoolean found = FALSE;
ComQIActionType invalidationKeyType = COM_QI_INVALID_ACTIONTYPE;
// check each invalidation key against objects in NATableDB or NARoutineDB
// cache
for ( Int32 i = 0; i < numInvalidationKeys && !found; i++ )
{
invalidationKeyType = ComQIActionTypeLiteralToEnum( invalidationKeys[i].operation );
Int32 numObjectKeys = objectKeys.entries();
switch (invalidationKeyType)
{
// Indicates that the DDL of the object has changed.
case COM_QI_OBJECT_REDEF:
// Indicates that the histogram statistics of the object has changed.
case COM_QI_STATS_UPDATED:
{
if (invalidationKeys[i].ddlObjectUID == objectUID)
found = TRUE;
break;
}
// Scan the passed-in object keys to find any that match the subject,
// object, and key type. That is, the subject has the privilege
// (invalidation key type) on the object or a column of the object.
case COM_QI_OBJECT_SELECT:
case COM_QI_OBJECT_INSERT:
case COM_QI_OBJECT_DELETE:
case COM_QI_OBJECT_UPDATE:
case COM_QI_OBJECT_USAGE:
case COM_QI_OBJECT_REFERENCES:
case COM_QI_OBJECT_EXECUTE:
for (Int32 j = 0; j < numObjectKeys && !found; j++ )
{
ComSecurityKey keyValue = objectKeys[j];
if ( ( invalidationKeys[i].revokeKey.object ==
keyValue.getObjectHashValue() ) &&
( invalidationKeyType ==
keyValue.getSecurityKeyType() ) )
{
if ( invalidationKeys[i].revokeKey.subject ==
keyValue.getSubjectHashValue() ||
qiSubjectMatchesRole(invalidationKeys[i].revokeKey.subject) )
found = TRUE;
}
}
break;
case COM_QI_USER_GRANT_SPECIAL_ROLE:
case COM_QI_USER_GRANT_ROLE:
{
for (Int32 j = 0; j < numObjectKeys && !found; j++ )
{
ComSecurityKey keyValue = objectKeys[j];
if ( ( invalidationKeys[i].revokeKey.subject ==
keyValue.getSubjectHashValue() ) &&
( invalidationKeys[i].revokeKey.object ==
keyValue.getObjectHashValue() ) &&
( invalidationKeyType ==
keyValue.getSecurityKeyType() ) )
found = TRUE;
}
break;
}
default:
found = TRUE;
break;
}
}
return found;
}
// ****************************************************************************
// Function that builds query invalidation keys for privileges. A separate
// invalidation key is added for each granted DML privilege.
//
// Types of keys available for privs:
// OBJECT_IS_SCHEMA - not supported until we support schema level privs
// OBJECT_IS_OBJECT - supported for granting privs to user
// OBJECT_IS_COLUMN - not supported at this time
// OBJECT_IS_SPECIAL_ROLE - key for PUBLIC authorization ID
// SUBJECT_IS_USER - support for granting roles to user
// SUBJECT_IS_ROLE - not supported until we grant roles to roles
//
// returns false is unable to build keys
// ****************************************************************************
bool buildSecurityKeys( const int32_t userID,
const int32_t granteeID,
const int64_t objectUID,
const PrivMgrCoreDesc &privs,
ComSecurityKeySet &secKeySet )
{
if (privs.isNull())
return true;
NABoolean doDebug = (getenv("DBUSER_DEBUG") ? TRUE : FALSE);
std::string msg ("Method: buildSecurityKeys: ");
if (doDebug)
{
printf("[DBUSER:%d] %s\n", (int) getpid(), msg.c_str());
fflush(stdout);
}
// If public is the grantee, generate special security key
// A user cannot be revoked from public
if (ComUser::isPublicUserID(granteeID))
{
ComSecurityKey key(granteeID, ComSecurityKey::OBJECT_IS_SPECIAL_ROLE);
if (doDebug)
{
NAString msg (key.print(granteeID, objectUID));
printf("[DBUSER:%d] (public) %s\n", (int) getpid(), msg.data());
fflush(stdout);
}
if (key.isValid())
secKeySet.insert(key);
else
return false;
}
// If the grantee is a role, generate a special security key
// If the role is revoked from the user, this key takes affect
if (PrivMgr::isRoleID(granteeID))
{
ComSecurityKey key (userID, granteeID, ComSecurityKey::SUBJECT_IS_USER);
if (doDebug)
{
NAString msg = key.print(userID, granteeID);
printf("[DBUSER:%d] (role) %s\n",
(int) getpid(), msg.data());
fflush(stdout);
}
if (key.isValid())
secKeySet.insert(key);
else
return false;
}
// Generate object invalidation keys
// Only need to generate keys for DML privileges
for ( size_t i = FIRST_DML_PRIV; i <= LAST_DML_PRIV; i++ )
{
if ( privs.getPriv(PrivType(i)))
{
ComSecurityKey key (granteeID, objectUID, PrivType(i),
ComSecurityKey::OBJECT_IS_OBJECT);
if (doDebug)
{
NAString msg = key.print(granteeID, objectUID);
printf("[DBUSER:%d] (DML)%s\n",
(int) getpid(), msg.data());
fflush(stdout);
}
if (key.isValid())
secKeySet.insert(key);
else
return false;
}
}
return true;
}
// ****************************************************************************
// Function that returns the types of invalidation to perform
// For DDL invalidation keys, always need to update caches
// For security invalidation keys
// update caches if key is for an object revoke from the current user or
// current users roles
// reset list of roles if key is a revoke role from the current user
//
// returns:
// resetRoleList -- need to reset the list of roles for the current user
// updateCaches -- need to update cache entries related for the keys
// ****************************************************************************
void qiInvalidationType (const Int32 numInvalidationKeys,
const SQL_QIKEY* invalidationKeys,
const Int32 userID,
bool &resetRoleList,
bool &updateCaches)
{
NABoolean doDebug = (getenv("DBUSER_DEBUG") ? TRUE : FALSE);
char buf[100];
resetRoleList = false;
updateCaches = false;
ComQIActionType invalidationKeyType = COM_QI_INVALID_ACTIONTYPE;
// Have the ComSecurityKey constructor compute the hash value for the the User's ID.
// Note: The following code doesn't care about the object's hash value or the resulting
// ComSecurityKey's ActionType....we just need the hash value for the User's ID.
// Perhaps a new constructor would be good (also done in RelRoot::checkPrivileges)
uint32_t userHashValue = ComSecurityKey::generateHash(userID);
if (doDebug)
{
sprintf(buf, ": num keys(%d)", numInvalidationKeys);
printf("[DBUSER:%d] Method: qiInvalidationType%s\n",
(int) getpid(), buf);
fflush(stdout);
sprintf(buf, "Not applicable");
}
for ( Int32 i = 0; i < numInvalidationKeys; i++ )
{
invalidationKeyType = ComQIActionTypeLiteralToEnum( invalidationKeys[i].operation );
switch (invalidationKeyType)
{
// Object changed, need to update caches
case COM_QI_OBJECT_REDEF:
case COM_QI_STATS_UPDATED:
if (doDebug)
sprintf(buf, "object/stats, operation: %c%c, objectUID: %ld",
invalidationKeys[i].operation[0],
invalidationKeys[i].operation[1],
invalidationKeys[i].ddlObjectUID);
updateCaches = true;
break;
// Privilege changed on an object, need to update caches if
// any QI keys are associated with the current user
case COM_QI_OBJECT_SELECT:
case COM_QI_OBJECT_INSERT:
case COM_QI_OBJECT_DELETE:
case COM_QI_OBJECT_UPDATE:
case COM_QI_OBJECT_USAGE:
case COM_QI_OBJECT_REFERENCES:
case COM_QI_OBJECT_EXECUTE:
// If the current user matches the revoke subject, update
if (invalidationKeys[i].revokeKey.subject == userHashValue)
{
if (doDebug)
sprintf(buf, "user: %d, operation: %c%c, subject: %u, object: %u", userID,
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
updateCaches = true;
}
// If one of the users roles matches the revokes subject, update
else if (qiSubjectMatchesRole(invalidationKeys[i].revokeKey.subject))
{
if (doDebug)
sprintf(buf, "role: %d, operation: %c%c, subject: %u, object: %u", userID,
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
updateCaches = true;
}
break;
// For public user (SPECIAL_ROLE), the subject is a special hash
case COM_QI_USER_GRANT_SPECIAL_ROLE:
if (invalidationKeys[i].revokeKey.subject == ComSecurityKey::SPECIAL_SUBJECT_HASH)
{
if (doDebug)
sprintf(buf, "user: %d, operation: %c%c, subject: %u, object: %u", userID,
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
updateCaches = true;
}
break;
// A revoke role from a user was performed. Need to reset role list
// if QI key associated with the current user and remove any plans
// that include the role key
case COM_QI_USER_GRANT_ROLE:
if (invalidationKeys[i].revokeKey.subject == userHashValue)
{
if (doDebug)
sprintf(buf, "user: %d, operation: %c%c, subject: %u, object: %u", userID,
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
resetRoleList = true;
updateCaches = true;
}
break;
// If a role was granted, refresh the active role llist
case COM_QI_GRANT_ROLE:
if (doDebug)
sprintf(buf, "operation: %c%c, subject: %u, object: %u",
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
resetRoleList = true;
break;
// unknown key type, search and update cache (should not happen)
default:
if (doDebug)
sprintf(buf, "user: %d, operation: %c%c, subject: %u, object: %u", userID,
invalidationKeys[i].operation[0], invalidationKeys[i].operation[1],
invalidationKeys[i].revokeKey.subject, invalidationKeys[i].revokeKey.object);
resetRoleList = true;
updateCaches = true;
break;
}
if (doDebug)
{
printf("[DBUSER:%d] %s\n",
(int) getpid(), buf);
fflush(stdout);
}
}
}
// *****************************************************************************
// ComSecurityKey methods
// *****************************************************************************
// Constructor for privilege grant on a SQL object to an authID
ComSecurityKey::ComSecurityKey(const int32_t subjectUserID,
const int64_t objectUID,
const PrivType which,
const QIType typeOfObject)
:
subjectHash_(0),
objectHash_(0),
actionType_(COM_QI_INVALID_ACTIONTYPE)
{
// Set the type.
actionType_ = convertBitmapToQIActionType(which, typeOfObject);
if (actionType_ != COM_QI_INVALID_ACTIONTYPE)
{
// This is a valid key. Set the subject and object values.
// Authorization ID of the grantee
// Hash value of the authorization ID of the subject
subjectHash_ = generateHash(subjectUserID);
// Hash value of the UID of the object
objectHash_ = generateHash(objectUID);
}
}
ComSecurityKey::ComSecurityKey(
const int32_t subjectUserID,
const int64_t objectUserID,
const QIType typeOfSubject)
:
subjectHash_(0),
objectHash_(0),
actionType_(COM_QI_INVALID_ACTIONTYPE)
{
if (typeOfSubject == SUBJECT_IS_USER)
actionType_ = COM_QI_USER_GRANT_ROLE; // revoke role <object> from <user subject>
else if (typeOfSubject == SUBJECT_IS_GRANT_ROLE)
actionType_ = COM_QI_GRANT_ROLE;
else
actionType_ = COM_QI_ROLE_GRANT_ROLE;
if (actionType_ != COM_QI_INVALID_ACTIONTYPE)
{
// This is a valid key. Set the subject and object values.
// Authorization ID of the grantee
// Hash value of the authorization ID of the subject
subjectHash_ = generateHash(subjectUserID);
// Hash value of the authorization ID of the object
objectHash_ = generateHash(objectUserID);
}
}
// Constructor for a special role grant to an authID.
ComSecurityKey::ComSecurityKey(
const int32_t subjectUserID,
const QIType typeOfObject )
:
subjectHash_(0),
objectHash_(0),
actionType_(COM_QI_INVALID_ACTIONTYPE)
{
// Set the values
if (typeOfObject == OBJECT_IS_SPECIAL_ROLE)
{
actionType_ = COM_QI_USER_GRANT_SPECIAL_ROLE;
subjectHash_ = SPECIAL_SUBJECT_HASH;
objectHash_ = SPECIAL_OBJECT_HASH;
}
}
ComSecurityKey::ComSecurityKey() :
subjectHash_(0),
objectHash_(0),
actionType_(COM_QI_INVALID_ACTIONTYPE)
{};
bool ComSecurityKey::operator == (const ComSecurityKey &other) const
{
if((subjectHash_ == other.subjectHash_)&&
(objectHash_ == other.objectHash_) &&
(actionType_ == other.actionType_))
return true;
return false;
}
ComQIActionType ComSecurityKey::convertBitmapToQIActionType (
const PrivType which,
const QIType inputType) const
{
// Convert from a PrivType value into a security key action type
// Note that this does not cover grant or revoke role
// Only converts values that are applicable to query invalidation. Returns an
// invalid value for all other privileges.
// Need to differentiate between schema, object, and column
ComQIActionType result = COM_QI_INVALID_ACTIONTYPE;
switch(which)
{
case SELECT_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_SELECT;
//else
// result = COM_QI_COLUMN_SELECT;
break;
case INSERT_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_INSERT;
//else
// result = COM_QI_COLUMN_INSERT;
break;
case DELETE_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_DELETE;
break;
case UPDATE_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_UPDATE;
//else
// result = COM_QI_COLUMN_UPDATE;
break;
case USAGE_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_USAGE;
break;
case REFERENCES_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_REFERENCES;
break;
case EXECUTE_PRIV:
if (inputType == OBJECT_IS_OBJECT)
result = COM_QI_OBJECT_EXECUTE;
else if (inputType == OBJECT_IS_SCHEMA)
result = COM_QI_SCHEMA_EXECUTE;
break;
default:
result = COM_QI_INVALID_ACTIONTYPE;
break;
};
return (result);
}
// Basic method to generate hash values
uint32_t ComSecurityKey::generateHash(int64_t hashInput)
{
uint32_t hashResult = ExHDPHash::hash8((char*)&hashInput, ExHDPHash::NO_FLAGS);
return hashResult;
}
// Generate hash value based on authorization ID
uint32_t ComSecurityKey::generateHash(int32_t hashID)
{
int64_t hVal = (int64_t) hashID;
uint32_t hashResult = generateHash(hVal);
return hashResult;
}
void ComSecurityKey::getSecurityKeyTypeAsLit (std::string &actionString) const
{
switch(actionType_)
{
case COM_QI_GRANT_ROLE:
actionString = COM_QI_GRANT_ROLE_LIT;
break;
case COM_QI_USER_GRANT_ROLE:
actionString = COM_QI_USER_GRANT_ROLE_LIT;
break;
case COM_QI_ROLE_GRANT_ROLE:
actionString = COM_QI_ROLE_GRANT_ROLE_LIT;
break;
case COM_QI_OBJECT_SELECT:
actionString = COM_QI_OBJECT_SELECT_LIT;
break;
case COM_QI_OBJECT_INSERT:
actionString = COM_QI_OBJECT_INSERT_LIT;
break;
case COM_QI_OBJECT_DELETE:
actionString = COM_QI_OBJECT_DELETE_LIT;
break;
case COM_QI_OBJECT_UPDATE:
actionString = COM_QI_OBJECT_UPDATE_LIT;
break;
case COM_QI_OBJECT_USAGE:
actionString = COM_QI_OBJECT_USAGE_LIT;
break;
case COM_QI_OBJECT_REFERENCES:
actionString = COM_QI_OBJECT_REFERENCES_LIT;
break;
case COM_QI_SCHEMA_SELECT:
actionString = COM_QI_SCHEMA_SELECT_LIT;
break;
case COM_QI_SCHEMA_INSERT:
actionString = COM_QI_SCHEMA_INSERT_LIT;
break;
case COM_QI_SCHEMA_DELETE:
actionString = COM_QI_SCHEMA_DELETE_LIT;
break;
case COM_QI_SCHEMA_UPDATE:
actionString = COM_QI_SCHEMA_UPDATE_LIT;
break;
case COM_QI_OBJECT_EXECUTE:
actionString = COM_QI_OBJECT_EXECUTE_LIT;
break;
case COM_QI_SCHEMA_EXECUTE:
actionString = COM_QI_SCHEMA_EXECUTE_LIT;
break;
case COM_QI_USER_GRANT_SPECIAL_ROLE:
actionString = COM_QI_USER_GRANT_SPECIAL_ROLE_LIT;
break;
default:
actionString = COM_QI_INVALID_ACTIONTYPE_LIT;
}
}
NAString ComSecurityKey::print(Int32 subjectID, Int64 objectID)
{
std::string typeString;
switch(actionType_)
{
case COM_QI_GRANT_ROLE:
typeString = "GRANT_ROLE";
break;
case COM_QI_USER_GRANT_ROLE:
typeString = "USER_GRANT_ROLE";
break;
case COM_QI_ROLE_GRANT_ROLE:
typeString = "ROLE_GRANT_ROLE";
break;
case COM_QI_SCHEMA_SELECT:
typeString = "SCHEMA_SELECT";
break;
case COM_QI_SCHEMA_INSERT:
typeString = "SCHEMA_INSERT";
break;
case COM_QI_SCHEMA_DELETE:
typeString = "SCHEMA_DELETE";
break;
case COM_QI_SCHEMA_UPDATE:
typeString = "SCHEMA_UPDATE";
break;
case COM_QI_OBJECT_SELECT:
typeString = "OBJECT_SELECT";
break;
case COM_QI_OBJECT_INSERT:
typeString = "OBJECT_INSERT";
break;
case COM_QI_OBJECT_DELETE:
typeString = "OBJECT_DELETE";
break;
case COM_QI_OBJECT_UPDATE:
typeString = "OBJECT_UPDATE";
break;
case COM_QI_OBJECT_USAGE:
typeString = "OBJECT_USAGE";
break;
case COM_QI_OBJECT_REFERENCES:
typeString = "OBJECT_REFERENCES";
break;
default:
typeString = "INVALID_ACTIONTYPE";
break;
};
char buf[200];
sprintf (buf, " - subjectHash: %u (%d), objectHash: %u (%ld), type: %s",
subjectHash_, subjectID,
objectHash_, objectID,
typeString.data());
NAString keyDetails = buf;
return keyDetails;
}