/src/postgres/src/backend/utils/init/usercontext.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * usercontext.c |
4 | | * Convenience functions for running code as a different database user. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/utils/init/usercontext.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | #include "postgres.h" |
16 | | |
17 | | #include "miscadmin.h" |
18 | | #include "utils/acl.h" |
19 | | #include "utils/guc.h" |
20 | | #include "utils/usercontext.h" |
21 | | |
22 | | /* |
23 | | * Temporarily switch to a new user ID. |
24 | | * |
25 | | * If the current user doesn't have permission to SET ROLE to the new user, |
26 | | * an ERROR occurs. |
27 | | * |
28 | | * If the new user doesn't have permission to SET ROLE to the current user, |
29 | | * SECURITY_RESTRICTED_OPERATION is imposed and a new GUC nest level is |
30 | | * created so that any settings changes can be rolled back. |
31 | | */ |
32 | | void |
33 | | SwitchToUntrustedUser(Oid userid, UserContext *context) |
34 | 0 | { |
35 | | /* Get the current user ID and security context. */ |
36 | 0 | GetUserIdAndSecContext(&context->save_userid, |
37 | 0 | &context->save_sec_context); |
38 | | |
39 | | /* Check that we have sufficient privileges to assume the target role. */ |
40 | 0 | if (!member_can_set_role(context->save_userid, userid)) |
41 | 0 | ereport(ERROR, |
42 | 0 | (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), |
43 | 0 | errmsg("role \"%s\" cannot SET ROLE to \"%s\"", |
44 | 0 | GetUserNameFromId(context->save_userid, false), |
45 | 0 | GetUserNameFromId(userid, false)))); |
46 | | |
47 | | /* |
48 | | * Try to prevent the user to which we're switching from assuming the |
49 | | * privileges of the current user, unless they can SET ROLE to that user |
50 | | * anyway. |
51 | | */ |
52 | 0 | if (member_can_set_role(userid, context->save_userid)) |
53 | 0 | { |
54 | | /* |
55 | | * Each user can SET ROLE to the other, so there's no point in |
56 | | * imposing any security restrictions. Just let the user do whatever |
57 | | * they want. |
58 | | */ |
59 | 0 | SetUserIdAndSecContext(userid, context->save_sec_context); |
60 | 0 | context->save_nestlevel = -1; |
61 | 0 | } |
62 | 0 | else |
63 | 0 | { |
64 | 0 | int sec_context = context->save_sec_context; |
65 | | |
66 | | /* |
67 | | * This user can SET ROLE to the target user, but not the other way |
68 | | * around, so protect ourselves against the target user by setting |
69 | | * SECURITY_RESTRICTED_OPERATION to prevent certain changes to the |
70 | | * session state. Also set up a new GUC nest level, so that we can |
71 | | * roll back any GUC changes that may be made by code running as the |
72 | | * target user, inasmuch as they could be malicious. |
73 | | */ |
74 | 0 | sec_context |= SECURITY_RESTRICTED_OPERATION; |
75 | 0 | SetUserIdAndSecContext(userid, sec_context); |
76 | 0 | context->save_nestlevel = NewGUCNestLevel(); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | /* |
81 | | * Switch back to the original user ID. |
82 | | * |
83 | | * If we created a new GUC nest level, also roll back any changes that were |
84 | | * made within it. |
85 | | */ |
86 | | void |
87 | | RestoreUserContext(UserContext *context) |
88 | 0 | { |
89 | 0 | if (context->save_nestlevel != -1) |
90 | 0 | AtEOXact_GUC(false, context->save_nestlevel); |
91 | 0 | SetUserIdAndSecContext(context->save_userid, context->save_sec_context); |
92 | 0 | } |