summaryrefslogtreecommitdiffstats
path: root/src/backend/utils/init/usercontext.c
blob: dd9a0dd6a83f3e12bb20a2e3acc07fb63e3515be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*-------------------------------------------------------------------------
 *
 * 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);
}