1 /*-------------------------------------------------------------------------
2  *
3  * Utility routines for SQL dumping
4  *
5  * Basically this is stuff that is useful in both pg_dump and pg_dumpall.
6  *
7  *
8  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  * src/bin/pg_dump/dumputils.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres_fe.h"
16 
17 #include <ctype.h>
18 
19 #include "dumputils.h"
20 #include "fe_utils/string_utils.h"
21 
22 
23 static bool parseAclItem(const char *item, const char *type,
24 			 const char *name, const char *subname, int remoteVersion,
25 			 PQExpBuffer grantee, PQExpBuffer grantor,
26 			 PQExpBuffer privs, PQExpBuffer privswgo);
27 static char *copyAclUserName(PQExpBuffer output, char *input);
28 static void AddAcl(PQExpBuffer aclbuf, const char *keyword,
29 	   const char *subname);
30 
31 
32 /*
33  * Build GRANT/REVOKE command(s) for an object.
34  *
35  *	name: the object name, in the form to use in the commands (already quoted)
36  *	subname: the sub-object name, if any (already quoted); NULL if none
37  *	nspname: the namespace the object is in (NULL if none); not pre-quoted
38  *	type: the object type (as seen in GRANT command: must be one of
39  *		TABLE, SEQUENCE, FUNCTION, LANGUAGE, SCHEMA, DATABASE, TABLESPACE,
40  *		FOREIGN DATA WRAPPER, SERVER, or LARGE OBJECT)
41  *	acls: the ACL string fetched from the database
42  *	racls: the ACL string of any initial-but-now-revoked privileges
43  *	owner: username of object owner (will be passed through fmtId); can be
44  *		NULL or empty string to indicate "no owner known"
45  *	prefix: string to prefix to each generated command; typically empty
46  *	remoteVersion: version of database
47  *
48  * Returns TRUE if okay, FALSE if could not parse the acl string.
49  * The resulting commands (if any) are appended to the contents of 'sql'.
50  *
51  * Note: when processing a default ACL, prefix is "ALTER DEFAULT PRIVILEGES "
52  * or something similar, and name is an empty string.
53  *
54  * Note: beware of passing a fmtId() result directly as 'name' or 'subname',
55  * since this routine uses fmtId() internally.
56  */
57 bool
buildACLCommands(const char * name,const char * subname,const char * nspname,const char * type,const char * acls,const char * racls,const char * owner,const char * prefix,int remoteVersion,PQExpBuffer sql)58 buildACLCommands(const char *name, const char *subname, const char *nspname,
59 				 const char *type, const char *acls, const char *racls,
60 				 const char *owner, const char *prefix, int remoteVersion,
61 				 PQExpBuffer sql)
62 {
63 	bool		ok = true;
64 	char	  **aclitems = NULL;
65 	char	  **raclitems = NULL;
66 	int			naclitems = 0;
67 	int			nraclitems = 0;
68 	int			i;
69 	PQExpBuffer grantee,
70 				grantor,
71 				privs,
72 				privswgo;
73 	PQExpBuffer firstsql,
74 				secondsql;
75 	bool		found_owner_privs = false;
76 
77 	if (strlen(acls) == 0 && strlen(racls) == 0)
78 		return true;			/* object has default permissions */
79 
80 	/* treat empty-string owner same as NULL */
81 	if (owner && *owner == '\0')
82 		owner = NULL;
83 
84 	if (strlen(acls) != 0)
85 	{
86 		if (!parsePGArray(acls, &aclitems, &naclitems))
87 		{
88 			if (aclitems)
89 				free(aclitems);
90 			return false;
91 		}
92 	}
93 
94 	if (strlen(racls) != 0)
95 	{
96 		if (!parsePGArray(racls, &raclitems, &nraclitems))
97 		{
98 			if (raclitems)
99 				free(raclitems);
100 			return false;
101 		}
102 	}
103 
104 	grantee = createPQExpBuffer();
105 	grantor = createPQExpBuffer();
106 	privs = createPQExpBuffer();
107 	privswgo = createPQExpBuffer();
108 
109 	/*
110 	 * At the end, these two will be pasted together to form the result.
111 	 *
112 	 * For older systems we use these to ensure that the owner privileges go
113 	 * before the other ones, as a GRANT could create the default entry for
114 	 * the object, which generally includes all rights for the owner. In more
115 	 * recent versions we normally handle this because the owner rights come
116 	 * first in the ACLs, but older versions might have them after the PUBLIC
117 	 * privileges.
118 	 *
119 	 * For 9.6 and later systems, much of this changes.  With 9.6, we check
120 	 * the default privileges for the objects at dump time and create two sets
121 	 * of ACLs- "racls" which are the ACLs to REVOKE from the object (as the
122 	 * object may have initial privileges on it, along with any default ACLs
123 	 * which are not part of the current set of privileges), and regular
124 	 * "acls", which are the ACLs to GRANT to the object.  We handle the
125 	 * REVOKEs first, followed by the GRANTs.
126 	 */
127 	firstsql = createPQExpBuffer();
128 	secondsql = createPQExpBuffer();
129 
130 	/*
131 	 * For pre-9.6 systems, we always start with REVOKE ALL FROM PUBLIC, as we
132 	 * don't wish to make any assumptions about what the default ACLs are, and
133 	 * we do not collect them during the dump phase (and racls will always be
134 	 * the empty set, see above).
135 	 *
136 	 * For 9.6 and later, if any revoke ACLs have been provided, then include
137 	 * them in 'firstsql'.
138 	 *
139 	 * Revoke ACLs happen when an object starts out life with a set of
140 	 * privileges (eg: GRANT SELECT ON pg_class TO PUBLIC;) and the user has
141 	 * decided to revoke those rights.  Since those objects come into being
142 	 * with those default privileges, we have to revoke them to match what the
143 	 * current state of affairs is.  Note that we only started explicitly
144 	 * tracking such initial rights in 9.6, and prior to that all initial
145 	 * rights are actually handled by the simple 'REVOKE ALL .. FROM PUBLIC'
146 	 * case, for initdb-created objects.  Prior to 9.6, we didn't handle
147 	 * extensions correctly, but we do now by tracking their initial
148 	 * privileges, in the same way we track initdb initial privileges, see
149 	 * pg_init_privs.
150 	 */
151 	if (remoteVersion < 90600)
152 	{
153 		Assert(nraclitems == 0);
154 
155 		appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
156 		if (subname)
157 			appendPQExpBuffer(firstsql, "(%s)", subname);
158 		appendPQExpBuffer(firstsql, " ON %s ", type);
159 		if (nspname && *nspname)
160 			appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
161 		appendPQExpBuffer(firstsql, "%s FROM PUBLIC;\n", name);
162 	}
163 	else
164 	{
165 		/* Scan individual REVOKE ACL items */
166 		for (i = 0; i < nraclitems; i++)
167 		{
168 			if (!parseAclItem(raclitems[i], type, name, subname, remoteVersion,
169 							  grantee, grantor, privs, NULL))
170 			{
171 				ok = false;
172 				break;
173 			}
174 
175 			if (privs->len > 0)
176 			{
177 				appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s ",
178 								  prefix, privs->data, type);
179 				if (nspname && *nspname)
180 					appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
181 				appendPQExpBuffer(firstsql, "%s FROM ", name);
182 				if (grantee->len == 0)
183 					appendPQExpBufferStr(firstsql, "PUBLIC;\n");
184 				else if (strncmp(grantee->data, "group ",
185 								 strlen("group ")) == 0)
186 					appendPQExpBuffer(firstsql, "GROUP %s;\n",
187 									  fmtId(grantee->data + strlen("group ")));
188 				else
189 					appendPQExpBuffer(firstsql, "%s;\n",
190 									  fmtId(grantee->data));
191 			}
192 		}
193 	}
194 
195 	/*
196 	 * We still need some hacking though to cover the case where new default
197 	 * public privileges are added in new versions: the REVOKE ALL will revoke
198 	 * them, leading to behavior different from what the old version had,
199 	 * which is generally not what's wanted.  So add back default privs if the
200 	 * source database is too old to have had that particular priv.
201 	 */
202 	if (remoteVersion < 80200 && strcmp(type, "DATABASE") == 0)
203 	{
204 		/* database CONNECT priv didn't exist before 8.2 */
205 		appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n",
206 						  prefix, type, name);
207 	}
208 
209 	/* Scan individual ACL items */
210 	for (i = 0; i < naclitems; i++)
211 	{
212 		if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion,
213 						  grantee, grantor, privs, privswgo))
214 		{
215 			ok = false;
216 			break;
217 		}
218 
219 		if (grantor->len == 0 && owner)
220 			printfPQExpBuffer(grantor, "%s", owner);
221 
222 		if (privs->len > 0 || privswgo->len > 0)
223 		{
224 			/*
225 			 * Prior to 9.6, we had to handle owner privileges in a special
226 			 * manner by first REVOKE'ing the rights and then GRANT'ing them
227 			 * after.  With 9.6 and above, what we need to REVOKE and what we
228 			 * need to GRANT is figured out when we dump and stashed into
229 			 * "racls" and "acls", respectively.  See above.
230 			 */
231 			if (remoteVersion < 90600 && owner
232 				&& strcmp(grantee->data, owner) == 0
233 				&& strcmp(grantor->data, owner) == 0)
234 			{
235 				found_owner_privs = true;
236 
237 				/*
238 				 * For the owner, the default privilege level is ALL WITH
239 				 * GRANT OPTION.
240 				 */
241 				if (strcmp(privswgo->data, "ALL") != 0)
242 				{
243 					appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
244 					if (subname)
245 						appendPQExpBuffer(firstsql, "(%s)", subname);
246 					appendPQExpBuffer(firstsql, " ON %s ", type);
247 					if (nspname && *nspname)
248 						appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
249 					appendPQExpBuffer(firstsql, "%s FROM %s;\n",
250 									  name, fmtId(grantee->data));
251 					if (privs->len > 0)
252 					{
253 						appendPQExpBuffer(firstsql,
254 										  "%sGRANT %s ON %s ",
255 										  prefix, privs->data, type);
256 						if (nspname && *nspname)
257 							appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
258 						appendPQExpBuffer(firstsql,
259 										  "%s TO %s;\n",
260 										  name, fmtId(grantee->data));
261 					}
262 					if (privswgo->len > 0)
263 					{
264 						appendPQExpBuffer(firstsql,
265 										  "%sGRANT %s ON %s ",
266 										  prefix, privswgo->data, type);
267 						if (nspname && *nspname)
268 							appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
269 						appendPQExpBuffer(firstsql,
270 										  "%s TO %s WITH GRANT OPTION;\n",
271 										  name, fmtId(grantee->data));
272 					}
273 				}
274 			}
275 			else
276 			{
277 				/*
278 				 * For systems prior to 9.6, we can assume we are starting
279 				 * from no privs at this point.
280 				 *
281 				 * For 9.6 and above, at this point we have issued REVOKE
282 				 * statements for all initial and default privileges which are
283 				 * no longer present on the object (as they were passed in as
284 				 * 'racls') and we can simply GRANT the rights which are in
285 				 * 'acls'.
286 				 */
287 				if (grantor->len > 0
288 					&& (!owner || strcmp(owner, grantor->data) != 0))
289 					appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n",
290 									  fmtId(grantor->data));
291 
292 				if (privs->len > 0)
293 				{
294 					appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
295 									  prefix, privs->data, type);
296 					if (nspname && *nspname)
297 						appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
298 					appendPQExpBuffer(secondsql, "%s TO ", name);
299 					if (grantee->len == 0)
300 						appendPQExpBufferStr(secondsql, "PUBLIC;\n");
301 					else if (strncmp(grantee->data, "group ",
302 									 strlen("group ")) == 0)
303 						appendPQExpBuffer(secondsql, "GROUP %s;\n",
304 										  fmtId(grantee->data + strlen("group ")));
305 					else
306 						appendPQExpBuffer(secondsql, "%s;\n", fmtId(grantee->data));
307 				}
308 				if (privswgo->len > 0)
309 				{
310 					appendPQExpBuffer(secondsql, "%sGRANT %s ON %s ",
311 									  prefix, privswgo->data, type);
312 					if (nspname && *nspname)
313 						appendPQExpBuffer(secondsql, "%s.", fmtId(nspname));
314 					appendPQExpBuffer(secondsql, "%s TO ", name);
315 					if (grantee->len == 0)
316 						appendPQExpBufferStr(secondsql, "PUBLIC");
317 					else if (strncmp(grantee->data, "group ",
318 									 strlen("group ")) == 0)
319 						appendPQExpBuffer(secondsql, "GROUP %s",
320 										  fmtId(grantee->data + strlen("group ")));
321 					else
322 						appendPQExpBufferStr(secondsql, fmtId(grantee->data));
323 					appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n");
324 				}
325 
326 				if (grantor->len > 0
327 					&& (!owner || strcmp(owner, grantor->data) != 0))
328 					appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n");
329 			}
330 		}
331 	}
332 
333 	/*
334 	 * For systems prior to 9.6, if we didn't find any owner privs, the owner
335 	 * must have revoked 'em all.
336 	 *
337 	 * For 9.6 and above, we handle this through the 'racls'.  See above.
338 	 */
339 	if (remoteVersion < 90600 && !found_owner_privs && owner)
340 	{
341 		appendPQExpBuffer(firstsql, "%sREVOKE ALL", prefix);
342 		if (subname)
343 			appendPQExpBuffer(firstsql, "(%s)", subname);
344 		appendPQExpBuffer(firstsql, " ON %s ", type);
345 		if (nspname && *nspname)
346 			appendPQExpBuffer(firstsql, "%s.", fmtId(nspname));
347 		appendPQExpBuffer(firstsql, "%s FROM %s;\n",
348 						  name, fmtId(owner));
349 	}
350 
351 	destroyPQExpBuffer(grantee);
352 	destroyPQExpBuffer(grantor);
353 	destroyPQExpBuffer(privs);
354 	destroyPQExpBuffer(privswgo);
355 
356 	appendPQExpBuffer(sql, "%s%s", firstsql->data, secondsql->data);
357 	destroyPQExpBuffer(firstsql);
358 	destroyPQExpBuffer(secondsql);
359 
360 	if (aclitems)
361 		free(aclitems);
362 
363 	if (raclitems)
364 		free(raclitems);
365 
366 	return ok;
367 }
368 
369 /*
370  * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry.
371  *
372  *	type: the object type (TABLES, FUNCTIONS, etc)
373  *	nspname: schema name, or NULL for global default privileges
374  *	acls: the ACL string fetched from the database
375  *	owner: username of privileges owner (will be passed through fmtId)
376  *	remoteVersion: version of database
377  *
378  * Returns TRUE if okay, FALSE if could not parse the acl string.
379  * The resulting commands (if any) are appended to the contents of 'sql'.
380  */
381 bool
buildDefaultACLCommands(const char * type,const char * nspname,const char * acls,const char * racls,const char * initacls,const char * initracls,const char * owner,int remoteVersion,PQExpBuffer sql)382 buildDefaultACLCommands(const char *type, const char *nspname,
383 						const char *acls, const char *racls,
384 						const char *initacls, const char *initracls,
385 						const char *owner,
386 						int remoteVersion,
387 						PQExpBuffer sql)
388 {
389 	PQExpBuffer prefix;
390 
391 	prefix = createPQExpBuffer();
392 
393 	/*
394 	 * We incorporate the target role directly into the command, rather than
395 	 * playing around with SET ROLE or anything like that.  This is so that a
396 	 * permissions error leads to nothing happening, rather than changing
397 	 * default privileges for the wrong user.
398 	 */
399 	appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s ",
400 					  fmtId(owner));
401 	if (nspname)
402 		appendPQExpBuffer(prefix, "IN SCHEMA %s ", fmtId(nspname));
403 
404 	if (strlen(initacls) != 0 || strlen(initracls) != 0)
405 	{
406 		appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n");
407 		if (!buildACLCommands("", NULL, NULL, type,
408 							  initacls, initracls, owner,
409 							  prefix->data, remoteVersion, sql))
410 		{
411 			destroyPQExpBuffer(prefix);
412 			return false;
413 		}
414 		appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n");
415 	}
416 
417 	if (!buildACLCommands("", NULL, NULL, type,
418 						  acls, racls, owner,
419 						  prefix->data, remoteVersion, sql))
420 	{
421 		destroyPQExpBuffer(prefix);
422 		return false;
423 	}
424 
425 	destroyPQExpBuffer(prefix);
426 
427 	return true;
428 }
429 
430 /*
431  * This will parse an aclitem string, having the general form
432  *		username=privilegecodes/grantor
433  * or
434  *		group groupname=privilegecodes/grantor
435  * (the "group" case occurs only with servers before 8.1).
436  *
437  * Returns true on success, false on parse error.  On success, the components
438  * of the string are returned in the PQExpBuffer parameters.
439  *
440  * The returned grantee string will be the dequoted username or groupname
441  * (preceded with "group " in the latter case).  Note that a grant to PUBLIC
442  * is represented by an empty grantee string.  The returned grantor is the
443  * dequoted grantor name.  Privilege characters are translated to GRANT/REVOKE
444  * comma-separated privileges lists.  If "privswgo" is non-NULL, the result is
445  * separate lists for privileges with grant option ("privswgo") and without
446  * ("privs").  Otherwise, "privs" bears every relevant privilege, ignoring the
447  * grant option distinction.
448  *
449  * Note: for cross-version compatibility, it's important to use ALL to
450  * represent the privilege sets whenever appropriate.
451  */
452 static bool
parseAclItem(const char * item,const char * type,const char * name,const char * subname,int remoteVersion,PQExpBuffer grantee,PQExpBuffer grantor,PQExpBuffer privs,PQExpBuffer privswgo)453 parseAclItem(const char *item, const char *type,
454 			 const char *name, const char *subname, int remoteVersion,
455 			 PQExpBuffer grantee, PQExpBuffer grantor,
456 			 PQExpBuffer privs, PQExpBuffer privswgo)
457 {
458 	char	   *buf;
459 	bool		all_with_go = true;
460 	bool		all_without_go = true;
461 	char	   *eqpos;
462 	char	   *slpos;
463 	char	   *pos;
464 
465 	buf = strdup(item);
466 	if (!buf)
467 		return false;
468 
469 	/* user or group name is string up to = */
470 	eqpos = copyAclUserName(grantee, buf);
471 	if (*eqpos != '=')
472 	{
473 		free(buf);
474 		return false;
475 	}
476 
477 	/* grantor should appear after / */
478 	slpos = strchr(eqpos + 1, '/');
479 	if (slpos)
480 	{
481 		*slpos++ = '\0';
482 		slpos = copyAclUserName(grantor, slpos);
483 		if (*slpos != '\0')
484 		{
485 			free(buf);
486 			return false;
487 		}
488 	}
489 	else
490 	{
491 		free(buf);
492 		return false;
493 	}
494 
495 	/* privilege codes */
496 #define CONVERT_PRIV(code, keywd) \
497 do { \
498 	if ((pos = strchr(eqpos + 1, code))) \
499 	{ \
500 		if (*(pos + 1) == '*' && privswgo != NULL) \
501 		{ \
502 			AddAcl(privswgo, keywd, subname); \
503 			all_without_go = false; \
504 		} \
505 		else \
506 		{ \
507 			AddAcl(privs, keywd, subname); \
508 			all_with_go = false; \
509 		} \
510 	} \
511 	else \
512 		all_with_go = all_without_go = false; \
513 } while (0)
514 
515 	resetPQExpBuffer(privs);
516 	resetPQExpBuffer(privswgo);
517 
518 	if (strcmp(type, "TABLE") == 0 || strcmp(type, "SEQUENCE") == 0 ||
519 		strcmp(type, "TABLES") == 0 || strcmp(type, "SEQUENCES") == 0)
520 	{
521 		CONVERT_PRIV('r', "SELECT");
522 
523 		if (strcmp(type, "SEQUENCE") == 0 ||
524 			strcmp(type, "SEQUENCES") == 0)
525 			/* sequence only */
526 			CONVERT_PRIV('U', "USAGE");
527 		else
528 		{
529 			/* table only */
530 			CONVERT_PRIV('a', "INSERT");
531 			CONVERT_PRIV('x', "REFERENCES");
532 			/* rest are not applicable to columns */
533 			if (subname == NULL)
534 			{
535 				CONVERT_PRIV('d', "DELETE");
536 				CONVERT_PRIV('t', "TRIGGER");
537 				if (remoteVersion >= 80400)
538 					CONVERT_PRIV('D', "TRUNCATE");
539 			}
540 		}
541 
542 		/* UPDATE */
543 		CONVERT_PRIV('w', "UPDATE");
544 	}
545 	else if (strcmp(type, "FUNCTION") == 0 ||
546 			 strcmp(type, "FUNCTIONS") == 0)
547 		CONVERT_PRIV('X', "EXECUTE");
548 	else if (strcmp(type, "LANGUAGE") == 0)
549 		CONVERT_PRIV('U', "USAGE");
550 	else if (strcmp(type, "SCHEMA") == 0 ||
551 			 strcmp(type, "SCHEMAS") == 0)
552 	{
553 		CONVERT_PRIV('C', "CREATE");
554 		CONVERT_PRIV('U', "USAGE");
555 	}
556 	else if (strcmp(type, "DATABASE") == 0)
557 	{
558 		CONVERT_PRIV('C', "CREATE");
559 		CONVERT_PRIV('c', "CONNECT");
560 		CONVERT_PRIV('T', "TEMPORARY");
561 	}
562 	else if (strcmp(type, "TABLESPACE") == 0)
563 		CONVERT_PRIV('C', "CREATE");
564 	else if (strcmp(type, "TYPE") == 0 ||
565 			 strcmp(type, "TYPES") == 0)
566 		CONVERT_PRIV('U', "USAGE");
567 	else if (strcmp(type, "FOREIGN DATA WRAPPER") == 0)
568 		CONVERT_PRIV('U', "USAGE");
569 	else if (strcmp(type, "FOREIGN SERVER") == 0)
570 		CONVERT_PRIV('U', "USAGE");
571 	else if (strcmp(type, "FOREIGN TABLE") == 0)
572 		CONVERT_PRIV('r', "SELECT");
573 	else if (strcmp(type, "LARGE OBJECT") == 0)
574 	{
575 		CONVERT_PRIV('r', "SELECT");
576 		CONVERT_PRIV('w', "UPDATE");
577 	}
578 	else
579 		abort();
580 
581 #undef CONVERT_PRIV
582 
583 	if (all_with_go)
584 	{
585 		resetPQExpBuffer(privs);
586 		printfPQExpBuffer(privswgo, "ALL");
587 		if (subname)
588 			appendPQExpBuffer(privswgo, "(%s)", subname);
589 	}
590 	else if (all_without_go)
591 	{
592 		resetPQExpBuffer(privswgo);
593 		printfPQExpBuffer(privs, "ALL");
594 		if (subname)
595 			appendPQExpBuffer(privs, "(%s)", subname);
596 	}
597 
598 	free(buf);
599 
600 	return true;
601 }
602 
603 /*
604  * Transfer a user or group name starting at *input into the output buffer,
605  * dequoting if needed.  Returns a pointer to just past the input name.
606  * The name is taken to end at an unquoted '=' or end of string.
607  */
608 static char *
copyAclUserName(PQExpBuffer output,char * input)609 copyAclUserName(PQExpBuffer output, char *input)
610 {
611 	resetPQExpBuffer(output);
612 
613 	while (*input && *input != '=')
614 	{
615 		/*
616 		 * If user name isn't quoted, then just add it to the output buffer
617 		 */
618 		if (*input != '"')
619 			appendPQExpBufferChar(output, *input++);
620 		else
621 		{
622 			/* Otherwise, it's a quoted username */
623 			input++;
624 			/* Loop until we come across an unescaped quote */
625 			while (!(*input == '"' && *(input + 1) != '"'))
626 			{
627 				if (*input == '\0')
628 					return input;	/* really a syntax error... */
629 
630 				/*
631 				 * Quoting convention is to escape " as "".  Keep this code in
632 				 * sync with putid() in backend's acl.c.
633 				 */
634 				if (*input == '"' && *(input + 1) == '"')
635 					input++;
636 				appendPQExpBufferChar(output, *input++);
637 			}
638 			input++;
639 		}
640 	}
641 	return input;
642 }
643 
644 /*
645  * Append a privilege keyword to a keyword list, inserting comma if needed.
646  */
647 static void
AddAcl(PQExpBuffer aclbuf,const char * keyword,const char * subname)648 AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname)
649 {
650 	if (aclbuf->len > 0)
651 		appendPQExpBufferChar(aclbuf, ',');
652 	appendPQExpBufferStr(aclbuf, keyword);
653 	if (subname)
654 		appendPQExpBuffer(aclbuf, "(%s)", subname);
655 }
656 
657 
658 /*
659  * buildShSecLabelQuery
660  *
661  * Build a query to retrieve security labels for a shared object.
662  * The object is identified by its OID plus the name of the catalog
663  * it can be found in (e.g., "pg_database" for database names).
664  * The query is appended to "sql".  (We don't execute it here so as to
665  * keep this file free of assumptions about how to deal with SQL errors.)
666  */
667 void
buildShSecLabelQuery(PGconn * conn,const char * catalog_name,Oid objectId,PQExpBuffer sql)668 buildShSecLabelQuery(PGconn *conn, const char *catalog_name, Oid objectId,
669 					 PQExpBuffer sql)
670 {
671 	appendPQExpBuffer(sql,
672 					  "SELECT provider, label FROM pg_catalog.pg_shseclabel "
673 					  "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass "
674 					  "AND objoid = '%u'", catalog_name, objectId);
675 }
676 
677 /*
678  * emitShSecLabels
679  *
680  * Construct SECURITY LABEL commands using the data retrieved by the query
681  * generated by buildShSecLabelQuery, and append them to "buffer".
682  * Here, the target object is identified by its type name (e.g. "DATABASE")
683  * and its name (not pre-quoted).
684  */
685 void
emitShSecLabels(PGconn * conn,PGresult * res,PQExpBuffer buffer,const char * objtype,const char * objname)686 emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer,
687 				const char *objtype, const char *objname)
688 {
689 	int			i;
690 
691 	for (i = 0; i < PQntuples(res); i++)
692 	{
693 		char	   *provider = PQgetvalue(res, i, 0);
694 		char	   *label = PQgetvalue(res, i, 1);
695 
696 		/* must use fmtId result before calling it again */
697 		appendPQExpBuffer(buffer,
698 						  "SECURITY LABEL FOR %s ON %s",
699 						  fmtId(provider), objtype);
700 		appendPQExpBuffer(buffer,
701 						  " %s IS ",
702 						  fmtId(objname));
703 		appendStringLiteralConn(buffer, label, conn);
704 		appendPQExpBufferStr(buffer, ";\n");
705 	}
706 }
707 
708 /*
709  * buildACLQueries
710  *
711  * Build the subqueries to extract out the correct set of ACLs to be
712  * GRANT'd and REVOKE'd for the specific kind of object, accounting for any
713  * initial privileges (from pg_init_privs) and based on if we are in binary
714  * upgrade mode or not.
715  *
716  * Also builds subqueries to extract out the set of ACLs to go from the object
717  * default privileges to the privileges in pg_init_privs, if we are in binary
718  * upgrade mode, so that those privileges can be set up and recorded in the new
719  * cluster before the regular privileges are added on top of those.
720  */
721 void
buildACLQueries(PQExpBuffer acl_subquery,PQExpBuffer racl_subquery,PQExpBuffer init_acl_subquery,PQExpBuffer init_racl_subquery,const char * acl_column,const char * acl_owner,const char * obj_kind,bool binary_upgrade)722 buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery,
723 				PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery,
724 				const char *acl_column, const char *acl_owner,
725 				const char *obj_kind, bool binary_upgrade)
726 {
727 	/*
728 	 * To get the delta from what the permissions were at creation time
729 	 * (either initdb or CREATE EXTENSION) vs. what they are now, we have to
730 	 * look at two things:
731 	 *
732 	 * What privileges have been added, which we calculate by extracting all
733 	 * the current privileges (using the set of default privileges for the
734 	 * object type if current privileges are NULL) and then removing those
735 	 * which existed at creation time (again, using the set of default
736 	 * privileges for the object type if there were no creation time
737 	 * privileges).
738 	 *
739 	 * What privileges have been removed, which we calculate by extracting the
740 	 * privileges as they were at creation time (or the default privileges, as
741 	 * above), and then removing the current privileges (or the default
742 	 * privileges, if current privileges are NULL).
743 	 *
744 	 * As a good cross-check, both directions of these checks should result in
745 	 * the empty set if both the current ACL and the initial privs are NULL
746 	 * (meaning, in practice, that the default ACLs were there at init time
747 	 * and is what the current privileges are).
748 	 *
749 	 * We always perform this delta on all ACLs and expect that by the time
750 	 * these are run the initial privileges will be in place, even in a binary
751 	 * upgrade situation (see below).
752 	 *
753 	 * Finally, the order in which privileges are in the ACL string (the order
754 	 * they been GRANT'd in, which the backend maintains) must be preserved to
755 	 * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on
756 	 * those are dumped in the correct order.
757 	 */
758 	printfPQExpBuffer(acl_subquery,
759 					  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
760 					  "(SELECT acl, row_n FROM "
761 					  "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
762 					  "WITH ORDINALITY AS perm(acl,row_n) "
763 					  "WHERE NOT EXISTS ( "
764 					  "SELECT 1 FROM "
765 					  "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) "
766 					  "AS init(init_acl) WHERE acl = init_acl)) as foo)",
767 					  acl_column,
768 					  obj_kind,
769 					  acl_owner,
770 					  obj_kind,
771 					  acl_owner);
772 
773 	printfPQExpBuffer(racl_subquery,
774 					  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
775 					  "(SELECT acl, row_n FROM "
776 					  "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) "
777 					  "WITH ORDINALITY AS initp(acl,row_n) "
778 					  "WHERE NOT EXISTS ( "
779 					  "SELECT 1 FROM "
780 					  "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) "
781 					  "AS permp(orig_acl) WHERE acl = orig_acl)) as foo)",
782 					  obj_kind,
783 					  acl_owner,
784 					  acl_column,
785 					  obj_kind,
786 					  acl_owner);
787 
788 	/*
789 	 * In binary upgrade mode we don't run the extension script but instead
790 	 * dump out the objects independently and then recreate them.  To preserve
791 	 * the initial privileges which were set on extension objects, we need to
792 	 * grab the set of GRANT and REVOKE commands necessary to get from the
793 	 * default privileges of an object to the initial privileges as recorded
794 	 * in pg_init_privs.
795 	 *
796 	 * These will then be run ahead of the regular ACL commands, which were
797 	 * calculated using the queries above, inside of a block which sets a flag
798 	 * to indicate that the backend should record the results of these GRANT
799 	 * and REVOKE statements into pg_init_privs.  This is how we preserve the
800 	 * contents of that catalog across binary upgrades.
801 	 */
802 	if (binary_upgrade)
803 	{
804 		printfPQExpBuffer(init_acl_subquery,
805 						  "CASE WHEN privtype = 'e' THEN "
806 						  "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM "
807 						  "(SELECT acl, row_n FROM pg_catalog.unnest(pip.initprivs) "
808 						  "WITH ORDINALITY AS initp(acl,row_n) "
809 						  "WHERE NOT EXISTS ( "
810 						  "SELECT 1 FROM "
811 						  "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
812 						  "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END",
813 						  obj_kind,
814 						  acl_owner);
815 
816 		printfPQExpBuffer(init_racl_subquery,
817 						  "CASE WHEN privtype = 'e' THEN "
818 						  "(SELECT pg_catalog.array_agg(acl) FROM "
819 						  "(SELECT acl, row_n FROM "
820 						  "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) "
821 						  "WITH ORDINALITY AS privp(acl,row_n) "
822 						  "WHERE NOT EXISTS ( "
823 						  "SELECT 1 FROM pg_catalog.unnest(pip.initprivs) "
824 						  "AS initp(init_acl) WHERE acl = init_acl)) as foo) END",
825 						  obj_kind,
826 						  acl_owner);
827 	}
828 	else
829 	{
830 		printfPQExpBuffer(init_acl_subquery, "NULL");
831 		printfPQExpBuffer(init_racl_subquery, "NULL");
832 	}
833 }
834 
835 /*
836  * Detect whether the given GUC variable is of GUC_LIST_QUOTE type.
837  *
838  * It'd be better if we could inquire this directly from the backend; but even
839  * if there were a function for that, it could only tell us about variables
840  * currently known to guc.c, so that it'd be unsafe for extensions to declare
841  * GUC_LIST_QUOTE variables anyway.  Lacking a solution for that, it doesn't
842  * seem worth the work to do more than have this list, which must be kept in
843  * sync with the variables actually marked GUC_LIST_QUOTE in guc.c.
844  */
845 bool
variable_is_guc_list_quote(const char * name)846 variable_is_guc_list_quote(const char *name)
847 {
848 	if (pg_strcasecmp(name, "temp_tablespaces") == 0 ||
849 		pg_strcasecmp(name, "session_preload_libraries") == 0 ||
850 		pg_strcasecmp(name, "shared_preload_libraries") == 0 ||
851 		pg_strcasecmp(name, "local_preload_libraries") == 0 ||
852 		pg_strcasecmp(name, "search_path") == 0)
853 		return true;
854 	else
855 		return false;
856 }
857 
858 /*
859  * SplitGUCList --- parse a string containing identifiers or file names
860  *
861  * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without
862  * presuming whether the elements will be taken as identifiers or file names.
863  * See comparable code in src/backend/utils/adt/varlena.c.
864  *
865  * Inputs:
866  *	rawstring: the input string; must be overwritable!	On return, it's
867  *			   been modified to contain the separated identifiers.
868  *	separator: the separator punctuation expected between identifiers
869  *			   (typically '.' or ',').  Whitespace may also appear around
870  *			   identifiers.
871  * Outputs:
872  *	namelist: receives a malloc'd, null-terminated array of pointers to
873  *			  identifiers within rawstring.  Caller should free this
874  *			  even on error return.
875  *
876  * Returns true if okay, false if there is a syntax error in the string.
877  */
878 bool
SplitGUCList(char * rawstring,char separator,char *** namelist)879 SplitGUCList(char *rawstring, char separator,
880 			 char ***namelist)
881 {
882 	char	   *nextp = rawstring;
883 	bool		done = false;
884 	char	  **nextptr;
885 
886 	/*
887 	 * Since we disallow empty identifiers, this is a conservative
888 	 * overestimate of the number of pointers we could need.  Allow one for
889 	 * list terminator.
890 	 */
891 	*namelist = nextptr = (char **)
892 		pg_malloc((strlen(rawstring) / 2 + 2) * sizeof(char *));
893 	*nextptr = NULL;
894 
895 	while (isspace((unsigned char) *nextp))
896 		nextp++;				/* skip leading whitespace */
897 
898 	if (*nextp == '\0')
899 		return true;			/* allow empty string */
900 
901 	/* At the top of the loop, we are at start of a new identifier. */
902 	do
903 	{
904 		char	   *curname;
905 		char	   *endp;
906 
907 		if (*nextp == '"')
908 		{
909 			/* Quoted name --- collapse quote-quote pairs */
910 			curname = nextp + 1;
911 			for (;;)
912 			{
913 				endp = strchr(nextp + 1, '"');
914 				if (endp == NULL)
915 					return false;	/* mismatched quotes */
916 				if (endp[1] != '"')
917 					break;		/* found end of quoted name */
918 				/* Collapse adjacent quotes into one quote, and look again */
919 				memmove(endp, endp + 1, strlen(endp));
920 				nextp = endp;
921 			}
922 			/* endp now points at the terminating quote */
923 			nextp = endp + 1;
924 		}
925 		else
926 		{
927 			/* Unquoted name --- extends to separator or whitespace */
928 			curname = nextp;
929 			while (*nextp && *nextp != separator &&
930 				   !isspace((unsigned char) *nextp))
931 				nextp++;
932 			endp = nextp;
933 			if (curname == nextp)
934 				return false;	/* empty unquoted name not allowed */
935 		}
936 
937 		while (isspace((unsigned char) *nextp))
938 			nextp++;			/* skip trailing whitespace */
939 
940 		if (*nextp == separator)
941 		{
942 			nextp++;
943 			while (isspace((unsigned char) *nextp))
944 				nextp++;		/* skip leading whitespace for next */
945 			/* we expect another name, so done remains false */
946 		}
947 		else if (*nextp == '\0')
948 			done = true;
949 		else
950 			return false;		/* invalid syntax */
951 
952 		/* Now safe to overwrite separator with a null */
953 		*endp = '\0';
954 
955 		/*
956 		 * Finished isolating current name --- add it to output array
957 		 */
958 		*nextptr++ = curname;
959 
960 		/* Loop back if we didn't reach end of string */
961 	} while (!done);
962 
963 	*nextptr = NULL;
964 	return true;
965 }
966