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-2019, 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, PROCEDURE, 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 |
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 (aclitems) |
99 | free(aclitems); |
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, privswgo)) |
172 | { |
173 | ok = false; |
174 | break; |
175 | } |
176 | |
177 | if (privs->len > 0 || privswgo->len > 0) |
178 | { |
179 | if (privs->len > 0) |
180 | { |
181 | appendPQExpBuffer(firstsql, "%sREVOKE %s ON %s " , |
182 | prefix, privs->data, type); |
183 | if (nspname && *nspname) |
184 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
185 | appendPQExpBuffer(firstsql, "%s FROM " , name); |
186 | if (grantee->len == 0) |
187 | appendPQExpBufferStr(firstsql, "PUBLIC;\n" ); |
188 | else if (strncmp(grantee->data, "group " , |
189 | strlen("group " )) == 0) |
190 | appendPQExpBuffer(firstsql, "GROUP %s;\n" , |
191 | fmtId(grantee->data + strlen("group " ))); |
192 | else |
193 | appendPQExpBuffer(firstsql, "%s;\n" , |
194 | fmtId(grantee->data)); |
195 | } |
196 | if (privswgo->len > 0) |
197 | { |
198 | appendPQExpBuffer(firstsql, |
199 | "%sREVOKE GRANT OPTION FOR %s ON %s " , |
200 | prefix, privswgo->data, type); |
201 | if (nspname && *nspname) |
202 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
203 | appendPQExpBuffer(firstsql, "%s FROM " , name); |
204 | if (grantee->len == 0) |
205 | appendPQExpBufferStr(firstsql, "PUBLIC" ); |
206 | else if (strncmp(grantee->data, "group " , |
207 | strlen("group " )) == 0) |
208 | appendPQExpBuffer(firstsql, "GROUP %s" , |
209 | fmtId(grantee->data + strlen("group " ))); |
210 | else |
211 | appendPQExpBufferStr(firstsql, fmtId(grantee->data)); |
212 | } |
213 | } |
214 | } |
215 | } |
216 | |
217 | /* |
218 | * We still need some hacking though to cover the case where new default |
219 | * public privileges are added in new versions: the REVOKE ALL will revoke |
220 | * them, leading to behavior different from what the old version had, |
221 | * which is generally not what's wanted. So add back default privs if the |
222 | * source database is too old to have had that particular priv. |
223 | */ |
224 | if (remoteVersion < 80200 && strcmp(type, "DATABASE" ) == 0) |
225 | { |
226 | /* database CONNECT priv didn't exist before 8.2 */ |
227 | appendPQExpBuffer(firstsql, "%sGRANT CONNECT ON %s %s TO PUBLIC;\n" , |
228 | prefix, type, name); |
229 | } |
230 | |
231 | /* Scan individual ACL items */ |
232 | for (i = 0; i < naclitems; i++) |
233 | { |
234 | if (!parseAclItem(aclitems[i], type, name, subname, remoteVersion, |
235 | grantee, grantor, privs, privswgo)) |
236 | { |
237 | ok = false; |
238 | break; |
239 | } |
240 | |
241 | if (grantor->len == 0 && owner) |
242 | printfPQExpBuffer(grantor, "%s" , owner); |
243 | |
244 | if (privs->len > 0 || privswgo->len > 0) |
245 | { |
246 | /* |
247 | * Prior to 9.6, we had to handle owner privileges in a special |
248 | * manner by first REVOKE'ing the rights and then GRANT'ing them |
249 | * after. With 9.6 and above, what we need to REVOKE and what we |
250 | * need to GRANT is figured out when we dump and stashed into |
251 | * "racls" and "acls", respectively. See above. |
252 | */ |
253 | if (remoteVersion < 90600 && owner |
254 | && strcmp(grantee->data, owner) == 0 |
255 | && strcmp(grantor->data, owner) == 0) |
256 | { |
257 | found_owner_privs = true; |
258 | |
259 | /* |
260 | * For the owner, the default privilege level is ALL WITH |
261 | * GRANT OPTION. |
262 | */ |
263 | if (strcmp(privswgo->data, "ALL" ) != 0) |
264 | { |
265 | appendPQExpBuffer(firstsql, "%sREVOKE ALL" , prefix); |
266 | if (subname) |
267 | appendPQExpBuffer(firstsql, "(%s)" , subname); |
268 | appendPQExpBuffer(firstsql, " ON %s " , type); |
269 | if (nspname && *nspname) |
270 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
271 | appendPQExpBuffer(firstsql, "%s FROM %s;\n" , |
272 | name, fmtId(grantee->data)); |
273 | if (privs->len > 0) |
274 | { |
275 | appendPQExpBuffer(firstsql, |
276 | "%sGRANT %s ON %s " , |
277 | prefix, privs->data, type); |
278 | if (nspname && *nspname) |
279 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
280 | appendPQExpBuffer(firstsql, |
281 | "%s TO %s;\n" , |
282 | name, fmtId(grantee->data)); |
283 | } |
284 | if (privswgo->len > 0) |
285 | { |
286 | appendPQExpBuffer(firstsql, |
287 | "%sGRANT %s ON %s " , |
288 | prefix, privswgo->data, type); |
289 | if (nspname && *nspname) |
290 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
291 | appendPQExpBuffer(firstsql, |
292 | "%s TO %s WITH GRANT OPTION;\n" , |
293 | name, fmtId(grantee->data)); |
294 | } |
295 | } |
296 | } |
297 | else |
298 | { |
299 | /* |
300 | * For systems prior to 9.6, we can assume we are starting |
301 | * from no privs at this point. |
302 | * |
303 | * For 9.6 and above, at this point we have issued REVOKE |
304 | * statements for all initial and default privileges which are |
305 | * no longer present on the object (as they were passed in as |
306 | * 'racls') and we can simply GRANT the rights which are in |
307 | * 'acls'. |
308 | */ |
309 | if (grantor->len > 0 |
310 | && (!owner || strcmp(owner, grantor->data) != 0)) |
311 | appendPQExpBuffer(secondsql, "SET SESSION AUTHORIZATION %s;\n" , |
312 | fmtId(grantor->data)); |
313 | |
314 | if (privs->len > 0) |
315 | { |
316 | appendPQExpBuffer(secondsql, "%sGRANT %s ON %s " , |
317 | prefix, privs->data, type); |
318 | if (nspname && *nspname) |
319 | appendPQExpBuffer(secondsql, "%s." , fmtId(nspname)); |
320 | appendPQExpBuffer(secondsql, "%s TO " , name); |
321 | if (grantee->len == 0) |
322 | appendPQExpBufferStr(secondsql, "PUBLIC;\n" ); |
323 | else if (strncmp(grantee->data, "group " , |
324 | strlen("group " )) == 0) |
325 | appendPQExpBuffer(secondsql, "GROUP %s;\n" , |
326 | fmtId(grantee->data + strlen("group " ))); |
327 | else |
328 | appendPQExpBuffer(secondsql, "%s;\n" , fmtId(grantee->data)); |
329 | } |
330 | if (privswgo->len > 0) |
331 | { |
332 | appendPQExpBuffer(secondsql, "%sGRANT %s ON %s " , |
333 | prefix, privswgo->data, type); |
334 | if (nspname && *nspname) |
335 | appendPQExpBuffer(secondsql, "%s." , fmtId(nspname)); |
336 | appendPQExpBuffer(secondsql, "%s TO " , name); |
337 | if (grantee->len == 0) |
338 | appendPQExpBufferStr(secondsql, "PUBLIC" ); |
339 | else if (strncmp(grantee->data, "group " , |
340 | strlen("group " )) == 0) |
341 | appendPQExpBuffer(secondsql, "GROUP %s" , |
342 | fmtId(grantee->data + strlen("group " ))); |
343 | else |
344 | appendPQExpBufferStr(secondsql, fmtId(grantee->data)); |
345 | appendPQExpBufferStr(secondsql, " WITH GRANT OPTION;\n" ); |
346 | } |
347 | |
348 | if (grantor->len > 0 |
349 | && (!owner || strcmp(owner, grantor->data) != 0)) |
350 | appendPQExpBufferStr(secondsql, "RESET SESSION AUTHORIZATION;\n" ); |
351 | } |
352 | } |
353 | } |
354 | |
355 | /* |
356 | * For systems prior to 9.6, if we didn't find any owner privs, the owner |
357 | * must have revoked 'em all. |
358 | * |
359 | * For 9.6 and above, we handle this through the 'racls'. See above. |
360 | */ |
361 | if (remoteVersion < 90600 && !found_owner_privs && owner) |
362 | { |
363 | appendPQExpBuffer(firstsql, "%sREVOKE ALL" , prefix); |
364 | if (subname) |
365 | appendPQExpBuffer(firstsql, "(%s)" , subname); |
366 | appendPQExpBuffer(firstsql, " ON %s " , type); |
367 | if (nspname && *nspname) |
368 | appendPQExpBuffer(firstsql, "%s." , fmtId(nspname)); |
369 | appendPQExpBuffer(firstsql, "%s FROM %s;\n" , |
370 | name, fmtId(owner)); |
371 | } |
372 | |
373 | destroyPQExpBuffer(grantee); |
374 | destroyPQExpBuffer(grantor); |
375 | destroyPQExpBuffer(privs); |
376 | destroyPQExpBuffer(privswgo); |
377 | |
378 | appendPQExpBuffer(sql, "%s%s" , firstsql->data, secondsql->data); |
379 | destroyPQExpBuffer(firstsql); |
380 | destroyPQExpBuffer(secondsql); |
381 | |
382 | if (aclitems) |
383 | free(aclitems); |
384 | |
385 | if (raclitems) |
386 | free(raclitems); |
387 | |
388 | return ok; |
389 | } |
390 | |
391 | /* |
392 | * Build ALTER DEFAULT PRIVILEGES command(s) for single pg_default_acl entry. |
393 | * |
394 | * type: the object type (TABLES, FUNCTIONS, etc) |
395 | * nspname: schema name, or NULL for global default privileges |
396 | * acls: the ACL string fetched from the database |
397 | * owner: username of privileges owner (will be passed through fmtId) |
398 | * remoteVersion: version of database |
399 | * |
400 | * Returns true if okay, false if could not parse the acl string. |
401 | * The resulting commands (if any) are appended to the contents of 'sql'. |
402 | */ |
403 | bool |
404 | buildDefaultACLCommands(const char *type, const char *nspname, |
405 | const char *acls, const char *racls, |
406 | const char *initacls, const char *initracls, |
407 | const char *owner, |
408 | int remoteVersion, |
409 | PQExpBuffer sql) |
410 | { |
411 | PQExpBuffer prefix; |
412 | |
413 | prefix = createPQExpBuffer(); |
414 | |
415 | /* |
416 | * We incorporate the target role directly into the command, rather than |
417 | * playing around with SET ROLE or anything like that. This is so that a |
418 | * permissions error leads to nothing happening, rather than changing |
419 | * default privileges for the wrong user. |
420 | */ |
421 | appendPQExpBuffer(prefix, "ALTER DEFAULT PRIVILEGES FOR ROLE %s " , |
422 | fmtId(owner)); |
423 | if (nspname) |
424 | appendPQExpBuffer(prefix, "IN SCHEMA %s " , fmtId(nspname)); |
425 | |
426 | if (strlen(initacls) != 0 || strlen(initracls) != 0) |
427 | { |
428 | appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(true);\n" ); |
429 | if (!buildACLCommands("" , NULL, NULL, type, |
430 | initacls, initracls, owner, |
431 | prefix->data, remoteVersion, sql)) |
432 | { |
433 | destroyPQExpBuffer(prefix); |
434 | return false; |
435 | } |
436 | appendPQExpBuffer(sql, "SELECT pg_catalog.binary_upgrade_set_record_init_privs(false);\n" ); |
437 | } |
438 | |
439 | if (!buildACLCommands("" , NULL, NULL, type, |
440 | acls, racls, owner, |
441 | prefix->data, remoteVersion, sql)) |
442 | { |
443 | destroyPQExpBuffer(prefix); |
444 | return false; |
445 | } |
446 | |
447 | destroyPQExpBuffer(prefix); |
448 | |
449 | return true; |
450 | } |
451 | |
452 | /* |
453 | * This will parse an aclitem string, having the general form |
454 | * username=privilegecodes/grantor |
455 | * or |
456 | * group groupname=privilegecodes/grantor |
457 | * (the "group" case occurs only with servers before 8.1). |
458 | * |
459 | * Returns true on success, false on parse error. On success, the components |
460 | * of the string are returned in the PQExpBuffer parameters. |
461 | * |
462 | * The returned grantee string will be the dequoted username or groupname |
463 | * (preceded with "group " in the latter case). Note that a grant to PUBLIC |
464 | * is represented by an empty grantee string. The returned grantor is the |
465 | * dequoted grantor name. Privilege characters are decoded and split between |
466 | * privileges with grant option (privswgo) and without (privs). |
467 | * |
468 | * Note: for cross-version compatibility, it's important to use ALL to |
469 | * represent the privilege sets whenever appropriate. |
470 | */ |
471 | static bool |
472 | parseAclItem(const char *item, const char *type, |
473 | const char *name, const char *subname, int remoteVersion, |
474 | PQExpBuffer grantee, PQExpBuffer grantor, |
475 | PQExpBuffer privs, PQExpBuffer privswgo) |
476 | { |
477 | char *buf; |
478 | bool all_with_go = true; |
479 | bool all_without_go = true; |
480 | char *eqpos; |
481 | char *slpos; |
482 | char *pos; |
483 | |
484 | buf = pg_strdup(item); |
485 | |
486 | /* user or group name is string up to = */ |
487 | eqpos = copyAclUserName(grantee, buf); |
488 | if (*eqpos != '=') |
489 | { |
490 | pg_free(buf); |
491 | return false; |
492 | } |
493 | |
494 | /* grantor should appear after / */ |
495 | slpos = strchr(eqpos + 1, '/'); |
496 | if (slpos) |
497 | { |
498 | *slpos++ = '\0'; |
499 | slpos = copyAclUserName(grantor, slpos); |
500 | if (*slpos != '\0') |
501 | { |
502 | pg_free(buf); |
503 | return false; |
504 | } |
505 | } |
506 | else |
507 | { |
508 | pg_free(buf); |
509 | return false; |
510 | } |
511 | |
512 | /* privilege codes */ |
513 | #define CONVERT_PRIV(code, keywd) \ |
514 | do { \ |
515 | if ((pos = strchr(eqpos + 1, code))) \ |
516 | { \ |
517 | if (*(pos + 1) == '*') \ |
518 | { \ |
519 | AddAcl(privswgo, keywd, subname); \ |
520 | all_without_go = false; \ |
521 | } \ |
522 | else \ |
523 | { \ |
524 | AddAcl(privs, keywd, subname); \ |
525 | all_with_go = false; \ |
526 | } \ |
527 | } \ |
528 | else \ |
529 | all_with_go = all_without_go = false; \ |
530 | } while (0) |
531 | |
532 | resetPQExpBuffer(privs); |
533 | resetPQExpBuffer(privswgo); |
534 | |
535 | if (strcmp(type, "TABLE" ) == 0 || strcmp(type, "SEQUENCE" ) == 0 || |
536 | strcmp(type, "TABLES" ) == 0 || strcmp(type, "SEQUENCES" ) == 0) |
537 | { |
538 | CONVERT_PRIV('r', "SELECT" ); |
539 | |
540 | if (strcmp(type, "SEQUENCE" ) == 0 || |
541 | strcmp(type, "SEQUENCES" ) == 0) |
542 | /* sequence only */ |
543 | CONVERT_PRIV('U', "USAGE" ); |
544 | else |
545 | { |
546 | /* table only */ |
547 | CONVERT_PRIV('a', "INSERT" ); |
548 | CONVERT_PRIV('x', "REFERENCES" ); |
549 | /* rest are not applicable to columns */ |
550 | if (subname == NULL) |
551 | { |
552 | CONVERT_PRIV('d', "DELETE" ); |
553 | CONVERT_PRIV('t', "TRIGGER" ); |
554 | if (remoteVersion >= 80400) |
555 | CONVERT_PRIV('D', "TRUNCATE" ); |
556 | } |
557 | } |
558 | |
559 | /* UPDATE */ |
560 | CONVERT_PRIV('w', "UPDATE" ); |
561 | } |
562 | else if (strcmp(type, "FUNCTION" ) == 0 || |
563 | strcmp(type, "FUNCTIONS" ) == 0) |
564 | CONVERT_PRIV('X', "EXECUTE" ); |
565 | else if (strcmp(type, "PROCEDURE" ) == 0 || |
566 | strcmp(type, "PROCEDURES" ) == 0) |
567 | CONVERT_PRIV('X', "EXECUTE" ); |
568 | else if (strcmp(type, "LANGUAGE" ) == 0) |
569 | CONVERT_PRIV('U', "USAGE" ); |
570 | else if (strcmp(type, "SCHEMA" ) == 0 || |
571 | strcmp(type, "SCHEMAS" ) == 0) |
572 | { |
573 | CONVERT_PRIV('C', "CREATE" ); |
574 | CONVERT_PRIV('U', "USAGE" ); |
575 | } |
576 | else if (strcmp(type, "DATABASE" ) == 0) |
577 | { |
578 | CONVERT_PRIV('C', "CREATE" ); |
579 | CONVERT_PRIV('c', "CONNECT" ); |
580 | CONVERT_PRIV('T', "TEMPORARY" ); |
581 | } |
582 | else if (strcmp(type, "TABLESPACE" ) == 0) |
583 | CONVERT_PRIV('C', "CREATE" ); |
584 | else if (strcmp(type, "TYPE" ) == 0 || |
585 | strcmp(type, "TYPES" ) == 0) |
586 | CONVERT_PRIV('U', "USAGE" ); |
587 | else if (strcmp(type, "FOREIGN DATA WRAPPER" ) == 0) |
588 | CONVERT_PRIV('U', "USAGE" ); |
589 | else if (strcmp(type, "FOREIGN SERVER" ) == 0) |
590 | CONVERT_PRIV('U', "USAGE" ); |
591 | else if (strcmp(type, "FOREIGN TABLE" ) == 0) |
592 | CONVERT_PRIV('r', "SELECT" ); |
593 | else if (strcmp(type, "LARGE OBJECT" ) == 0) |
594 | { |
595 | CONVERT_PRIV('r', "SELECT" ); |
596 | CONVERT_PRIV('w', "UPDATE" ); |
597 | } |
598 | else |
599 | abort(); |
600 | |
601 | #undef CONVERT_PRIV |
602 | |
603 | if (all_with_go) |
604 | { |
605 | resetPQExpBuffer(privs); |
606 | printfPQExpBuffer(privswgo, "ALL" ); |
607 | if (subname) |
608 | appendPQExpBuffer(privswgo, "(%s)" , subname); |
609 | } |
610 | else if (all_without_go) |
611 | { |
612 | resetPQExpBuffer(privswgo); |
613 | printfPQExpBuffer(privs, "ALL" ); |
614 | if (subname) |
615 | appendPQExpBuffer(privs, "(%s)" , subname); |
616 | } |
617 | |
618 | pg_free(buf); |
619 | |
620 | return true; |
621 | } |
622 | |
623 | /* |
624 | * Transfer a user or group name starting at *input into the output buffer, |
625 | * dequoting if needed. Returns a pointer to just past the input name. |
626 | * The name is taken to end at an unquoted '=' or end of string. |
627 | */ |
628 | static char * |
629 | copyAclUserName(PQExpBuffer output, char *input) |
630 | { |
631 | resetPQExpBuffer(output); |
632 | |
633 | while (*input && *input != '=') |
634 | { |
635 | /* |
636 | * If user name isn't quoted, then just add it to the output buffer |
637 | */ |
638 | if (*input != '"') |
639 | appendPQExpBufferChar(output, *input++); |
640 | else |
641 | { |
642 | /* Otherwise, it's a quoted username */ |
643 | input++; |
644 | /* Loop until we come across an unescaped quote */ |
645 | while (!(*input == '"' && *(input + 1) != '"')) |
646 | { |
647 | if (*input == '\0') |
648 | return input; /* really a syntax error... */ |
649 | |
650 | /* |
651 | * Quoting convention is to escape " as "". Keep this code in |
652 | * sync with putid() in backend's acl.c. |
653 | */ |
654 | if (*input == '"' && *(input + 1) == '"') |
655 | input++; |
656 | appendPQExpBufferChar(output, *input++); |
657 | } |
658 | input++; |
659 | } |
660 | } |
661 | return input; |
662 | } |
663 | |
664 | /* |
665 | * Append a privilege keyword to a keyword list, inserting comma if needed. |
666 | */ |
667 | static void |
668 | AddAcl(PQExpBuffer aclbuf, const char *keyword, const char *subname) |
669 | { |
670 | if (aclbuf->len > 0) |
671 | appendPQExpBufferChar(aclbuf, ','); |
672 | appendPQExpBufferStr(aclbuf, keyword); |
673 | if (subname) |
674 | appendPQExpBuffer(aclbuf, "(%s)" , subname); |
675 | } |
676 | |
677 | |
678 | /* |
679 | * buildShSecLabelQuery |
680 | * |
681 | * Build a query to retrieve security labels for a shared object. |
682 | * The object is identified by its OID plus the name of the catalog |
683 | * it can be found in (e.g., "pg_database" for database names). |
684 | * The query is appended to "sql". (We don't execute it here so as to |
685 | * keep this file free of assumptions about how to deal with SQL errors.) |
686 | */ |
687 | void |
688 | buildShSecLabelQuery(PGconn *conn, const char *catalog_name, Oid objectId, |
689 | PQExpBuffer sql) |
690 | { |
691 | appendPQExpBuffer(sql, |
692 | "SELECT provider, label FROM pg_catalog.pg_shseclabel " |
693 | "WHERE classoid = 'pg_catalog.%s'::pg_catalog.regclass " |
694 | "AND objoid = '%u'" , catalog_name, objectId); |
695 | } |
696 | |
697 | /* |
698 | * emitShSecLabels |
699 | * |
700 | * Construct SECURITY LABEL commands using the data retrieved by the query |
701 | * generated by buildShSecLabelQuery, and append them to "buffer". |
702 | * Here, the target object is identified by its type name (e.g. "DATABASE") |
703 | * and its name (not pre-quoted). |
704 | */ |
705 | void |
706 | emitShSecLabels(PGconn *conn, PGresult *res, PQExpBuffer buffer, |
707 | const char *objtype, const char *objname) |
708 | { |
709 | int i; |
710 | |
711 | for (i = 0; i < PQntuples(res); i++) |
712 | { |
713 | char *provider = PQgetvalue(res, i, 0); |
714 | char *label = PQgetvalue(res, i, 1); |
715 | |
716 | /* must use fmtId result before calling it again */ |
717 | appendPQExpBuffer(buffer, |
718 | "SECURITY LABEL FOR %s ON %s" , |
719 | fmtId(provider), objtype); |
720 | appendPQExpBuffer(buffer, |
721 | " %s IS " , |
722 | fmtId(objname)); |
723 | appendStringLiteralConn(buffer, label, conn); |
724 | appendPQExpBufferStr(buffer, ";\n" ); |
725 | } |
726 | } |
727 | |
728 | /* |
729 | * buildACLQueries |
730 | * |
731 | * Build the subqueries to extract out the correct set of ACLs to be |
732 | * GRANT'd and REVOKE'd for the specific kind of object, accounting for any |
733 | * initial privileges (from pg_init_privs) and based on if we are in binary |
734 | * upgrade mode or not. |
735 | * |
736 | * Also builds subqueries to extract out the set of ACLs to go from the object |
737 | * default privileges to the privileges in pg_init_privs, if we are in binary |
738 | * upgrade mode, so that those privileges can be set up and recorded in the new |
739 | * cluster before the regular privileges are added on top of those. |
740 | */ |
741 | void |
742 | buildACLQueries(PQExpBuffer acl_subquery, PQExpBuffer racl_subquery, |
743 | PQExpBuffer init_acl_subquery, PQExpBuffer init_racl_subquery, |
744 | const char *acl_column, const char *acl_owner, |
745 | const char *obj_kind, bool binary_upgrade) |
746 | { |
747 | /* |
748 | * To get the delta from what the permissions were at creation time |
749 | * (either initdb or CREATE EXTENSION) vs. what they are now, we have to |
750 | * look at two things: |
751 | * |
752 | * What privileges have been added, which we calculate by extracting all |
753 | * the current privileges (using the set of default privileges for the |
754 | * object type if current privileges are NULL) and then removing those |
755 | * which existed at creation time (again, using the set of default |
756 | * privileges for the object type if there were no creation time |
757 | * privileges). |
758 | * |
759 | * What privileges have been removed, which we calculate by extracting the |
760 | * privileges as they were at creation time (or the default privileges, as |
761 | * above), and then removing the current privileges (or the default |
762 | * privileges, if current privileges are NULL). |
763 | * |
764 | * As a good cross-check, both directions of these checks should result in |
765 | * the empty set if both the current ACL and the initial privs are NULL |
766 | * (meaning, in practice, that the default ACLs were there at init time |
767 | * and is what the current privileges are). |
768 | * |
769 | * We always perform this delta on all ACLs and expect that by the time |
770 | * these are run the initial privileges will be in place, even in a binary |
771 | * upgrade situation (see below). |
772 | * |
773 | * Finally, the order in which privileges are in the ACL string (the order |
774 | * they been GRANT'd in, which the backend maintains) must be preserved to |
775 | * ensure that GRANTs WITH GRANT OPTION and subsequent GRANTs based on |
776 | * those are dumped in the correct order. |
777 | */ |
778 | printfPQExpBuffer(acl_subquery, |
779 | "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM " |
780 | "(SELECT acl, row_n FROM " |
781 | "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) " |
782 | "WITH ORDINALITY AS perm(acl,row_n) " |
783 | "WHERE NOT EXISTS ( " |
784 | "SELECT 1 FROM " |
785 | "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) " |
786 | "AS init(init_acl) WHERE acl = init_acl)) as foo)" , |
787 | acl_column, |
788 | obj_kind, |
789 | acl_owner, |
790 | obj_kind, |
791 | acl_owner); |
792 | |
793 | printfPQExpBuffer(racl_subquery, |
794 | "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM " |
795 | "(SELECT acl, row_n FROM " |
796 | "pg_catalog.unnest(coalesce(pip.initprivs,pg_catalog.acldefault(%s,%s))) " |
797 | "WITH ORDINALITY AS initp(acl,row_n) " |
798 | "WHERE NOT EXISTS ( " |
799 | "SELECT 1 FROM " |
800 | "pg_catalog.unnest(coalesce(%s,pg_catalog.acldefault(%s,%s))) " |
801 | "AS permp(orig_acl) WHERE acl = orig_acl)) as foo)" , |
802 | obj_kind, |
803 | acl_owner, |
804 | acl_column, |
805 | obj_kind, |
806 | acl_owner); |
807 | |
808 | /* |
809 | * In binary upgrade mode we don't run the extension script but instead |
810 | * dump out the objects independently and then recreate them. To preserve |
811 | * the initial privileges which were set on extension objects, we need to |
812 | * grab the set of GRANT and REVOKE commands necessary to get from the |
813 | * default privileges of an object to the initial privileges as recorded |
814 | * in pg_init_privs. |
815 | * |
816 | * These will then be run ahead of the regular ACL commands, which were |
817 | * calculated using the queries above, inside of a block which sets a flag |
818 | * to indicate that the backend should record the results of these GRANT |
819 | * and REVOKE statements into pg_init_privs. This is how we preserve the |
820 | * contents of that catalog across binary upgrades. |
821 | */ |
822 | if (binary_upgrade) |
823 | { |
824 | printfPQExpBuffer(init_acl_subquery, |
825 | "CASE WHEN privtype = 'e' THEN " |
826 | "(SELECT pg_catalog.array_agg(acl ORDER BY row_n) FROM " |
827 | "(SELECT acl, row_n FROM pg_catalog.unnest(pip.initprivs) " |
828 | "WITH ORDINALITY AS initp(acl,row_n) " |
829 | "WHERE NOT EXISTS ( " |
830 | "SELECT 1 FROM " |
831 | "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) " |
832 | "AS privm(orig_acl) WHERE acl = orig_acl)) as foo) END" , |
833 | obj_kind, |
834 | acl_owner); |
835 | |
836 | printfPQExpBuffer(init_racl_subquery, |
837 | "CASE WHEN privtype = 'e' THEN " |
838 | "(SELECT pg_catalog.array_agg(acl) FROM " |
839 | "(SELECT acl, row_n FROM " |
840 | "pg_catalog.unnest(pg_catalog.acldefault(%s,%s)) " |
841 | "WITH ORDINALITY AS privp(acl,row_n) " |
842 | "WHERE NOT EXISTS ( " |
843 | "SELECT 1 FROM pg_catalog.unnest(pip.initprivs) " |
844 | "AS initp(init_acl) WHERE acl = init_acl)) as foo) END" , |
845 | obj_kind, |
846 | acl_owner); |
847 | } |
848 | else |
849 | { |
850 | printfPQExpBuffer(init_acl_subquery, "NULL" ); |
851 | printfPQExpBuffer(init_racl_subquery, "NULL" ); |
852 | } |
853 | } |
854 | |
855 | /* |
856 | * Detect whether the given GUC variable is of GUC_LIST_QUOTE type. |
857 | * |
858 | * It'd be better if we could inquire this directly from the backend; but even |
859 | * if there were a function for that, it could only tell us about variables |
860 | * currently known to guc.c, so that it'd be unsafe for extensions to declare |
861 | * GUC_LIST_QUOTE variables anyway. Lacking a solution for that, it doesn't |
862 | * seem worth the work to do more than have this list, which must be kept in |
863 | * sync with the variables actually marked GUC_LIST_QUOTE in guc.c. |
864 | */ |
865 | bool |
866 | variable_is_guc_list_quote(const char *name) |
867 | { |
868 | if (pg_strcasecmp(name, "temp_tablespaces" ) == 0 || |
869 | pg_strcasecmp(name, "session_preload_libraries" ) == 0 || |
870 | pg_strcasecmp(name, "shared_preload_libraries" ) == 0 || |
871 | pg_strcasecmp(name, "local_preload_libraries" ) == 0 || |
872 | pg_strcasecmp(name, "search_path" ) == 0) |
873 | return true; |
874 | else |
875 | return false; |
876 | } |
877 | |
878 | /* |
879 | * SplitGUCList --- parse a string containing identifiers or file names |
880 | * |
881 | * This is used to split the value of a GUC_LIST_QUOTE GUC variable, without |
882 | * presuming whether the elements will be taken as identifiers or file names. |
883 | * See comparable code in src/backend/utils/adt/varlena.c. |
884 | * |
885 | * Inputs: |
886 | * rawstring: the input string; must be overwritable! On return, it's |
887 | * been modified to contain the separated identifiers. |
888 | * separator: the separator punctuation expected between identifiers |
889 | * (typically '.' or ','). Whitespace may also appear around |
890 | * identifiers. |
891 | * Outputs: |
892 | * namelist: receives a malloc'd, null-terminated array of pointers to |
893 | * identifiers within rawstring. Caller should free this |
894 | * even on error return. |
895 | * |
896 | * Returns true if okay, false if there is a syntax error in the string. |
897 | */ |
898 | bool |
899 | SplitGUCList(char *rawstring, char separator, |
900 | char ***namelist) |
901 | { |
902 | char *nextp = rawstring; |
903 | bool done = false; |
904 | char **nextptr; |
905 | |
906 | /* |
907 | * Since we disallow empty identifiers, this is a conservative |
908 | * overestimate of the number of pointers we could need. Allow one for |
909 | * list terminator. |
910 | */ |
911 | *namelist = nextptr = (char **) |
912 | pg_malloc((strlen(rawstring) / 2 + 2) * sizeof(char *)); |
913 | *nextptr = NULL; |
914 | |
915 | while (isspace((unsigned char) *nextp)) |
916 | nextp++; /* skip leading whitespace */ |
917 | |
918 | if (*nextp == '\0') |
919 | return true; /* allow empty string */ |
920 | |
921 | /* At the top of the loop, we are at start of a new identifier. */ |
922 | do |
923 | { |
924 | char *curname; |
925 | char *endp; |
926 | |
927 | if (*nextp == '"') |
928 | { |
929 | /* Quoted name --- collapse quote-quote pairs */ |
930 | curname = nextp + 1; |
931 | for (;;) |
932 | { |
933 | endp = strchr(nextp + 1, '"'); |
934 | if (endp == NULL) |
935 | return false; /* mismatched quotes */ |
936 | if (endp[1] != '"') |
937 | break; /* found end of quoted name */ |
938 | /* Collapse adjacent quotes into one quote, and look again */ |
939 | memmove(endp, endp + 1, strlen(endp)); |
940 | nextp = endp; |
941 | } |
942 | /* endp now points at the terminating quote */ |
943 | nextp = endp + 1; |
944 | } |
945 | else |
946 | { |
947 | /* Unquoted name --- extends to separator or whitespace */ |
948 | curname = nextp; |
949 | while (*nextp && *nextp != separator && |
950 | !isspace((unsigned char) *nextp)) |
951 | nextp++; |
952 | endp = nextp; |
953 | if (curname == nextp) |
954 | return false; /* empty unquoted name not allowed */ |
955 | } |
956 | |
957 | while (isspace((unsigned char) *nextp)) |
958 | nextp++; /* skip trailing whitespace */ |
959 | |
960 | if (*nextp == separator) |
961 | { |
962 | nextp++; |
963 | while (isspace((unsigned char) *nextp)) |
964 | nextp++; /* skip leading whitespace for next */ |
965 | /* we expect another name, so done remains false */ |
966 | } |
967 | else if (*nextp == '\0') |
968 | done = true; |
969 | else |
970 | return false; /* invalid syntax */ |
971 | |
972 | /* Now safe to overwrite separator with a null */ |
973 | *endp = '\0'; |
974 | |
975 | /* |
976 | * Finished isolating current name --- add it to output array |
977 | */ |
978 | *nextptr++ = curname; |
979 | |
980 | /* Loop back if we didn't reach end of string */ |
981 | } while (!done); |
982 | |
983 | *nextptr = NULL; |
984 | return true; |
985 | } |
986 | |
987 | /* |
988 | * Helper function for dumping "ALTER DATABASE/ROLE SET ..." commands. |
989 | * |
990 | * Parse the contents of configitem (a "name=value" string), wrap it in |
991 | * a complete ALTER command, and append it to buf. |
992 | * |
993 | * type is DATABASE or ROLE, and name is the name of the database or role. |
994 | * If we need an "IN" clause, type2 and name2 similarly define what to put |
995 | * there; otherwise they should be NULL. |
996 | * conn is used only to determine string-literal quoting conventions. |
997 | */ |
998 | void |
999 | makeAlterConfigCommand(PGconn *conn, const char *configitem, |
1000 | const char *type, const char *name, |
1001 | const char *type2, const char *name2, |
1002 | PQExpBuffer buf) |
1003 | { |
1004 | char *mine; |
1005 | char *pos; |
1006 | |
1007 | /* Parse the configitem. If we can't find an "=", silently do nothing. */ |
1008 | mine = pg_strdup(configitem); |
1009 | pos = strchr(mine, '='); |
1010 | if (pos == NULL) |
1011 | { |
1012 | pg_free(mine); |
1013 | return; |
1014 | } |
1015 | *pos++ = '\0'; |
1016 | |
1017 | /* Build the command, with suitable quoting for everything. */ |
1018 | appendPQExpBuffer(buf, "ALTER %s %s " , type, fmtId(name)); |
1019 | if (type2 != NULL && name2 != NULL) |
1020 | appendPQExpBuffer(buf, "IN %s %s " , type2, fmtId(name2)); |
1021 | appendPQExpBuffer(buf, "SET %s TO " , fmtId(mine)); |
1022 | |
1023 | /* |
1024 | * Variables that are marked GUC_LIST_QUOTE were already fully quoted by |
1025 | * flatten_set_variable_args() before they were put into the setconfig |
1026 | * array. However, because the quoting rules used there aren't exactly |
1027 | * like SQL's, we have to break the list value apart and then quote the |
1028 | * elements as string literals. (The elements may be double-quoted as-is, |
1029 | * but we can't just feed them to the SQL parser; it would do the wrong |
1030 | * thing with elements that are zero-length or longer than NAMEDATALEN.) |
1031 | * |
1032 | * Variables that are not so marked should just be emitted as simple |
1033 | * string literals. If the variable is not known to |
1034 | * variable_is_guc_list_quote(), we'll do that; this makes it unsafe to |
1035 | * use GUC_LIST_QUOTE for extension variables. |
1036 | */ |
1037 | if (variable_is_guc_list_quote(mine)) |
1038 | { |
1039 | char **namelist; |
1040 | char **nameptr; |
1041 | |
1042 | /* Parse string into list of identifiers */ |
1043 | /* this shouldn't fail really */ |
1044 | if (SplitGUCList(pos, ',', &namelist)) |
1045 | { |
1046 | for (nameptr = namelist; *nameptr; nameptr++) |
1047 | { |
1048 | if (nameptr != namelist) |
1049 | appendPQExpBufferStr(buf, ", " ); |
1050 | appendStringLiteralConn(buf, *nameptr, conn); |
1051 | } |
1052 | } |
1053 | pg_free(namelist); |
1054 | } |
1055 | else |
1056 | appendStringLiteralConn(buf, pos, conn); |
1057 | |
1058 | appendPQExpBufferStr(buf, ";\n" ); |
1059 | |
1060 | pg_free(mine); |
1061 | } |
1062 | |