1 /* -------------------------------------------------------------------------
2  *
3  * contrib/sepgsql/schema.c
4  *
5  * Routines corresponding to schema objects
6  *
7  * Copyright (c) 2010-2016, PostgreSQL Global Development Group
8  *
9  * -------------------------------------------------------------------------
10  */
11 #include "postgres.h"
12 
13 #include "access/genam.h"
14 #include "access/heapam.h"
15 #include "access/htup_details.h"
16 #include "access/sysattr.h"
17 #include "catalog/dependency.h"
18 #include "catalog/indexing.h"
19 #include "catalog/pg_database.h"
20 #include "catalog/pg_namespace.h"
21 #include "commands/seclabel.h"
22 #include "lib/stringinfo.h"
23 #include "miscadmin.h"
24 #include "utils/builtins.h"
25 #include "utils/fmgroids.h"
26 #include "utils/lsyscache.h"
27 #include "utils/tqual.h"
28 
29 #include "sepgsql.h"
30 
31 /*
32  * sepgsql_schema_post_create
33  *
34  * This routine assigns a default security label on a newly defined
35  * schema.
36  */
37 void
sepgsql_schema_post_create(Oid namespaceId)38 sepgsql_schema_post_create(Oid namespaceId)
39 {
40 	Relation	rel;
41 	ScanKeyData skey;
42 	SysScanDesc sscan;
43 	HeapTuple	tuple;
44 	char	   *tcontext;
45 	char	   *ncontext;
46 	const char *nsp_name;
47 	ObjectAddress object;
48 	Form_pg_namespace nspForm;
49 	StringInfoData audit_name;
50 
51 	/*
52 	 * Compute a default security label when we create a new schema object
53 	 * under the working database.
54 	 *
55 	 * XXX - uncoming version of libselinux supports to take object name to
56 	 * handle special treatment on default security label; such as special
57 	 * label on "pg_temp" schema.
58 	 */
59 	rel = heap_open(NamespaceRelationId, AccessShareLock);
60 
61 	ScanKeyInit(&skey,
62 				ObjectIdAttributeNumber,
63 				BTEqualStrategyNumber, F_OIDEQ,
64 				ObjectIdGetDatum(namespaceId));
65 
66 	sscan = systable_beginscan(rel, NamespaceOidIndexId, true,
67 							   SnapshotSelf, 1, &skey);
68 	tuple = systable_getnext(sscan);
69 	if (!HeapTupleIsValid(tuple))
70 		elog(ERROR, "catalog lookup failed for namespace %u", namespaceId);
71 
72 	nspForm = (Form_pg_namespace) GETSTRUCT(tuple);
73 	nsp_name = NameStr(nspForm->nspname);
74 	if (strncmp(nsp_name, "pg_temp_", 8) == 0)
75 		nsp_name = "pg_temp";
76 	else if (strncmp(nsp_name, "pg_toast_temp_", 14) == 0)
77 		nsp_name = "pg_toast_temp";
78 
79 	tcontext = sepgsql_get_label(DatabaseRelationId, MyDatabaseId, 0);
80 	ncontext = sepgsql_compute_create(sepgsql_get_client_label(),
81 									  tcontext,
82 									  SEPG_CLASS_DB_SCHEMA,
83 									  nsp_name);
84 
85 	/*
86 	 * check db_schema:{create}
87 	 */
88 	initStringInfo(&audit_name);
89 	appendStringInfo(&audit_name, "%s", quote_identifier(nsp_name));
90 	sepgsql_avc_check_perms_label(ncontext,
91 								  SEPG_CLASS_DB_SCHEMA,
92 								  SEPG_DB_SCHEMA__CREATE,
93 								  audit_name.data,
94 								  true);
95 	systable_endscan(sscan);
96 	heap_close(rel, AccessShareLock);
97 
98 	/*
99 	 * Assign the default security label on a new procedure
100 	 */
101 	object.classId = NamespaceRelationId;
102 	object.objectId = namespaceId;
103 	object.objectSubId = 0;
104 	SetSecurityLabel(&object, SEPGSQL_LABEL_TAG, ncontext);
105 
106 	pfree(ncontext);
107 	pfree(tcontext);
108 }
109 
110 /*
111  * sepgsql_schema_drop
112  *
113  * It checks privileges to drop the supplied schema object.
114  */
115 void
sepgsql_schema_drop(Oid namespaceId)116 sepgsql_schema_drop(Oid namespaceId)
117 {
118 	ObjectAddress object;
119 	char	   *audit_name;
120 
121 	/*
122 	 * check db_schema:{drop} permission
123 	 */
124 	object.classId = NamespaceRelationId;
125 	object.objectId = namespaceId;
126 	object.objectSubId = 0;
127 	audit_name = getObjectIdentity(&object);
128 
129 	sepgsql_avc_check_perms(&object,
130 							SEPG_CLASS_DB_SCHEMA,
131 							SEPG_DB_SCHEMA__DROP,
132 							audit_name,
133 							true);
134 	pfree(audit_name);
135 }
136 
137 /*
138  * sepgsql_schema_relabel
139  *
140  * It checks privileges to relabel the supplied schema
141  * by the `seclabel'.
142  */
143 void
sepgsql_schema_relabel(Oid namespaceId,const char * seclabel)144 sepgsql_schema_relabel(Oid namespaceId, const char *seclabel)
145 {
146 	ObjectAddress object;
147 	char	   *audit_name;
148 
149 	object.classId = NamespaceRelationId;
150 	object.objectId = namespaceId;
151 	object.objectSubId = 0;
152 	audit_name = getObjectIdentity(&object);
153 
154 	/*
155 	 * check db_schema:{setattr relabelfrom} permission
156 	 */
157 	sepgsql_avc_check_perms(&object,
158 							SEPG_CLASS_DB_SCHEMA,
159 							SEPG_DB_SCHEMA__SETATTR |
160 							SEPG_DB_SCHEMA__RELABELFROM,
161 							audit_name,
162 							true);
163 
164 	/*
165 	 * check db_schema:{relabelto} permission
166 	 */
167 	sepgsql_avc_check_perms_label(seclabel,
168 								  SEPG_CLASS_DB_SCHEMA,
169 								  SEPG_DB_SCHEMA__RELABELTO,
170 								  audit_name,
171 								  true);
172 	pfree(audit_name);
173 }
174 
175 /*
176  * sepgsql_schema_check_perms
177  *
178  * utility routine to check db_schema:{xxx} permissions
179  */
180 static bool
check_schema_perms(Oid namespaceId,uint32 required,bool abort_on_violation)181 check_schema_perms(Oid namespaceId, uint32 required, bool abort_on_violation)
182 {
183 	ObjectAddress object;
184 	char	   *audit_name;
185 	bool		result;
186 
187 	object.classId = NamespaceRelationId;
188 	object.objectId = namespaceId;
189 	object.objectSubId = 0;
190 	audit_name = getObjectIdentity(&object);
191 
192 	result = sepgsql_avc_check_perms(&object,
193 									 SEPG_CLASS_DB_SCHEMA,
194 									 required,
195 									 audit_name,
196 									 abort_on_violation);
197 	pfree(audit_name);
198 
199 	return result;
200 }
201 
202 /* db_schema:{setattr} permission */
203 void
sepgsql_schema_setattr(Oid namespaceId)204 sepgsql_schema_setattr(Oid namespaceId)
205 {
206 	check_schema_perms(namespaceId, SEPG_DB_SCHEMA__SETATTR, true);
207 }
208 
209 /* db_schema:{search} permission */
210 bool
sepgsql_schema_search(Oid namespaceId,bool abort_on_violation)211 sepgsql_schema_search(Oid namespaceId, bool abort_on_violation)
212 {
213 	return check_schema_perms(namespaceId,
214 							  SEPG_DB_SCHEMA__SEARCH,
215 							  abort_on_violation);
216 }
217 
218 void
sepgsql_schema_add_name(Oid namespaceId)219 sepgsql_schema_add_name(Oid namespaceId)
220 {
221 	check_schema_perms(namespaceId, SEPG_DB_SCHEMA__ADD_NAME, true);
222 }
223 
224 void
sepgsql_schema_remove_name(Oid namespaceId)225 sepgsql_schema_remove_name(Oid namespaceId)
226 {
227 	check_schema_perms(namespaceId, SEPG_DB_SCHEMA__REMOVE_NAME, true);
228 }
229 
230 void
sepgsql_schema_rename(Oid namespaceId)231 sepgsql_schema_rename(Oid namespaceId)
232 {
233 	check_schema_perms(namespaceId,
234 					   SEPG_DB_SCHEMA__ADD_NAME |
235 					   SEPG_DB_SCHEMA__REMOVE_NAME,
236 					   true);
237 }
238