postgresql/src/backend/utils/init/usercontext.c

93 lines
2.9 KiB
C

/*-------------------------------------------------------------------------
*
* usercontext.c
* Convenience functions for running code as a different database user.
*
* Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/init/usercontext.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/guc.h"
#include "utils/usercontext.h"
/*
* Temporarily switch to a new user ID.
*
* If the current user doesn't have permission to SET ROLE to the new user,
* an ERROR occurs.
*
* If the new user doesn't have permission to SET ROLE to the current user,
* SECURITY_RESTRICTED_OPERATION is imposed and a new GUC nest level is
* created so that any settings changes can be rolled back.
*/
void
SwitchToUntrustedUser(Oid userid, UserContext *context)
{
/* Get the current user ID and security context. */
GetUserIdAndSecContext(&context->save_userid,
&context->save_sec_context);
/* Check that we have sufficient privileges to assume the target role. */
if (!member_can_set_role(context->save_userid, userid))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("role \"%s\" cannot SET ROLE to \"%s\"",
GetUserNameFromId(context->save_userid, false),
GetUserNameFromId(userid, false))));
/*
* Try to prevent the user to which we're switching from assuming the
* privileges of the current user, unless they can SET ROLE to that user
* anyway.
*/
if (member_can_set_role(userid, context->save_userid))
{
/*
* Each user can SET ROLE to the other, so there's no point in
* imposing any security restrictions. Just let the user do whatever
* they want.
*/
SetUserIdAndSecContext(userid, context->save_sec_context);
context->save_nestlevel = -1;
}
else
{
int sec_context = context->save_sec_context;
/*
* This user can SET ROLE to the target user, but not the other way
* around, so protect ourselves against the target user by setting
* SECURITY_RESTRICTED_OPERATION to prevent certain changes to the
* session state. Also set up a new GUC nest level, so that we can
* roll back any GUC changes that may be made by code running as the
* target user, inasmuch as they could be malicious.
*/
sec_context |= SECURITY_RESTRICTED_OPERATION;
SetUserIdAndSecContext(userid, sec_context);
context->save_nestlevel = NewGUCNestLevel();
}
}
/*
* Switch back to the original user ID.
*
* If we created a new GUC nest level, also roll back any changes that were
* made within it.
*/
void
RestoreUserContext(UserContext *context)
{
if (context->save_nestlevel != -1)
AtEOXact_GUC(false, context->save_nestlevel);
SetUserIdAndSecContext(context->save_userid, context->save_sec_context);
}