Commit 8488f254 authored by Tom Lane's avatar Tom Lane

Upgrade parsing code for ACLs to be less hokey and more cognizant of

the actual logical structure and quoting rules being used.  Fixes bug
reported by Chris K-L on 7/8/03.
parent 1c6eba44
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.5 2003/07/24 15:52:53 petere Exp $ * $Header: /cvsroot/pgsql/src/bin/pg_dump/dumputils.c,v 1.6 2003/07/31 17:21:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,12 +19,15 @@ ...@@ -19,12 +19,15 @@
#include "parser/keywords.h" #include "parser/keywords.h"
#define supports_grant_options(version) ((version) >= 70400)
static bool parseAclArray(const char *acls, char ***itemarray, int *nitems);
static bool parseAclItem(const char *item, const char *type, const char *name, static bool parseAclItem(const char *item, const char *type, const char *name,
int remoteVersion, int remoteVersion,
PQExpBuffer grantee, PQExpBuffer grantor, PQExpBuffer grantee, PQExpBuffer grantor,
PQExpBuffer privs, PQExpBuffer privswgo); PQExpBuffer privs, PQExpBuffer privswgo);
static char *copyAclUserName(PQExpBuffer output, char *input);
static void AddAcl(PQExpBuffer aclbuf, const char *keyword); static void AddAcl(PQExpBuffer aclbuf, const char *keyword);
#define supports_grant_options(version) ((version) >= 70400)
/* /*
...@@ -185,8 +188,9 @@ buildACLCommands(const char *name, const char *type, ...@@ -185,8 +188,9 @@ buildACLCommands(const char *name, const char *type,
int remoteVersion, int remoteVersion,
PQExpBuffer sql) PQExpBuffer sql)
{ {
char *aclbuf, char **aclitems;
*tok; int naclitems;
int i;
PQExpBuffer grantee, grantor, privs, privswgo; PQExpBuffer grantee, grantor, privs, privswgo;
PQExpBuffer firstsql, secondsql; PQExpBuffer firstsql, secondsql;
bool found_owner_privs = false; bool found_owner_privs = false;
...@@ -194,6 +198,13 @@ buildACLCommands(const char *name, const char *type, ...@@ -194,6 +198,13 @@ buildACLCommands(const char *name, const char *type,
if (strlen(acls) == 0) if (strlen(acls) == 0)
return true; /* object has default permissions */ return true; /* object has default permissions */
if (!parseAclArray(acls, &aclitems, &naclitems))
{
if (aclitems)
free(aclitems);
return false;
}
grantee = createPQExpBuffer(); grantee = createPQExpBuffer();
grantor = createPQExpBuffer(); grantor = createPQExpBuffer();
privs = createPQExpBuffer(); privs = createPQExpBuffer();
...@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type, ...@@ -217,27 +228,10 @@ buildACLCommands(const char *name, const char *type,
appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM PUBLIC;\n", appendPQExpBuffer(firstsql, "REVOKE ALL ON %s %s FROM PUBLIC;\n",
type, name); type, name);
/* Make a working copy of acls so we can use strtok */ /* Scan individual ACL items */
aclbuf = strdup(acls); for (i = 0; i < naclitems; i++)
/* Scan comma-separated ACL items */
for (tok = strtok(aclbuf, ","); tok != NULL; tok = strtok(NULL, ","))
{ {
size_t toklen; if (!parseAclItem(aclitems[i], type, name, remoteVersion,
/*
* Token may start with '{' and/or '"'. Actually only the start
* of the string should have '{', but we don't verify that.
*/
if (*tok == '{')
tok++;
if (*tok == '"')
tok++;
toklen = strlen(tok);
while (toklen >=0 && (tok[toklen-1] == '"' || tok[toklen-1] == '}'))
tok[toklen-- - 1] = '\0';
if (!parseAclItem(tok, type, name, remoteVersion,
grantee, grantor, privs, privswgo)) grantee, grantor, privs, privswgo))
return false; return false;
...@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type, ...@@ -327,7 +321,6 @@ buildACLCommands(const char *name, const char *type,
type, name, fmtId(owner)); type, name, fmtId(owner));
} }
free(aclbuf);
destroyPQExpBuffer(grantee); destroyPQExpBuffer(grantee);
destroyPQExpBuffer(grantor); destroyPQExpBuffer(grantor);
destroyPQExpBuffer(privs); destroyPQExpBuffer(privs);
...@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type, ...@@ -337,15 +330,105 @@ buildACLCommands(const char *name, const char *type,
destroyPQExpBuffer(firstsql); destroyPQExpBuffer(firstsql);
destroyPQExpBuffer(secondsql); destroyPQExpBuffer(secondsql);
free(aclitems);
return true; return true;
} }
/*
* Deconstruct an ACL array (or actually any 1-dimensional Postgres array)
* into individual items.
*
* On success, returns true and sets *itemarray and *nitems to describe
* an array of individual strings. On parse failure, returns false;
* *itemarray may exist or be NULL.
*
* NOTE: free'ing itemarray is sufficient to deallocate the working storage.
*/
static bool
parseAclArray(const char *acls, char ***itemarray, int *nitems)
{
int inputlen;
char **items;
char *strings;
int curitem;
/*
* We expect input in the form of "{item,item,item}" where any item
* is either raw data, or surrounded by double quotes (in which case
* embedded characters including backslashes and quotes are backslashed).
*
* We build the result as an array of pointers followed by the actual
* string data, all in one malloc block for convenience of deallocation.
* The worst-case storage need is not more than one pointer and one
* character for each input character (consider "{,,,,,,,,,,}").
*/
*itemarray = NULL;
*nitems = 0;
inputlen = strlen(acls);
if (inputlen < 2 || acls[0] != '{' || acls[inputlen-1] != '}')
return false; /* bad input */
items = (char **) malloc(inputlen * (sizeof(char *) + sizeof(char)));
if (items == NULL)
return false; /* out of memory */
*itemarray = items;
strings = (char *) (items + inputlen);
acls++; /* advance over initial '{' */
curitem = 0;
while (*acls != '}')
{
if (*acls == '\0')
return false; /* premature end of string */
items[curitem] = strings;
while (*acls != '}' && *acls != ',')
{
if (*acls == '\0')
return false; /* premature end of string */
if (*acls != '"')
*strings++ = *acls++; /* copy unquoted data */
else
{
/* process quoted substring */
acls++;
while (*acls != '"')
{
if (*acls == '\0')
return false; /* premature end of string */
if (*acls == '\\')
{
acls++;
if (*acls == '\0')
return false; /* premature end of string */
}
*strings++ = *acls++; /* copy quoted data */
}
acls++;
}
}
*strings++ = '\0';
if (*acls == ',')
acls++;
curitem++;
}
if (acls[1] != '\0')
return false; /* bogus syntax (embedded '}') */
*nitems = curitem;
return true;
}
/* /*
* This will take an aclitem string of privilege code letters and * This will parse an aclitem string, having the general form
* parse it into grantee, grantor, and privilege information. The * username=privilegecodes/grantor
* privilege information is split between privileges with grant option * or
* (privswgo) and without (privs). * group groupname=privilegecodes/grantor
* (the /grantor part will not be present if pre-7.4 database).
*
* The returned grantee string will be the dequoted username or groupname
* (preceded with "group " in the latter case). The returned grantor is
* the dequoted grantor name or empty. Privilege characters are decoded
* and split between privileges with grant option (privswgo) and without
* (privs).
* *
* Note: for cross-version compatibility, it's important to use ALL when * Note: for cross-version compatibility, it's important to use ALL when
* appropriate. * appropriate.
...@@ -365,19 +448,19 @@ parseAclItem(const char *item, const char *type, const char *name, ...@@ -365,19 +448,19 @@ parseAclItem(const char *item, const char *type, const char *name,
buf = strdup(item); buf = strdup(item);
/* user name is string up to = */ /* user or group name is string up to = */
eqpos = strchr(buf, '='); eqpos = copyAclUserName(grantee, buf);
if (!eqpos) if (*eqpos != '=')
return false; return false;
*eqpos = '\0';
printfPQExpBuffer(grantee, "%s", buf);
/* grantor may be listed after / */ /* grantor may be listed after / */
slpos = strchr(eqpos + 1, '/'); slpos = strchr(eqpos + 1, '/');
if (slpos) if (slpos)
{ {
*slpos = '\0'; *slpos++ = '\0';
printfPQExpBuffer(grantor, "%s", slpos + 1); slpos = copyAclUserName(grantor, slpos);
if (*slpos != '\0')
return false;
} }
else else
resetPQExpBuffer(grantor); resetPQExpBuffer(grantor);
...@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name, ...@@ -457,6 +540,38 @@ parseAclItem(const char *item, const char *type, const char *name,
return true; return true;
} }
/*
* Transfer a user or group name starting at *input into the output buffer,
* dequoting if needed. Returns a pointer to just past the input name.
* The name is taken to end at an unquoted '=' or end of string.
*/
static char *
copyAclUserName(PQExpBuffer output, char *input)
{
resetPQExpBuffer(output);
while (*input && *input != '=')
{
if (*input != '"')
appendPQExpBufferChar(output, *input++);
else
{
input++;
while (*input != '"')
{
if (*input == '\0')
return input; /* really a syntax error... */
/*
* There is no quoting convention here, thus we can't cope
* with usernames containing double quotes. Keep this code
* in sync with putid() in backend's acl.c.
*/
appendPQExpBufferChar(output, *input++);
}
input++;
}
}
return input;
}
/* /*
* Append a privilege keyword to a keyword list, inserting comma if needed. * Append a privilege keyword to a keyword list, inserting comma if needed.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment