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