summaryrefslogtreecommitdiff
path: root/tools/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/parse.c')
-rw-r--r--tools/parse.c594
1 files changed, 594 insertions, 0 deletions
diff --git a/tools/parse.c b/tools/parse.c
new file mode 100644
index 0000000..e7e6add
--- /dev/null
+++ b/tools/parse.c
@@ -0,0 +1,594 @@
+/*
+ File: parse.c
+ (Linux Access Control List Management)
+
+ Copyright (C) 1999, 2000
+ Andreas Gruenbacher, <a.gruenbacher@bestbits.at>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include "sys/acl.h"
+
+#include "sequence.h"
+#include "parse.h"
+#include "misc.h"
+
+#define SKIP_WS(x) ({ \
+ while (*(x)==' ' || *(x)=='\t' || *(x)=='\n' || *(x)=='\r') \
+ (x)++; \
+ })
+
+
+static int
+skip_tag_name(
+ const char **text_p,
+ const char *token)
+{
+ size_t len = strlen(token);
+ const char *text = *text_p;
+
+ SKIP_WS(text);
+ if (strncmp(text, token, len) == 0) {
+ text += len;
+ goto delimiter;
+ }
+ if (*text == *token) {
+ text++;
+ goto delimiter;
+ }
+ return 0;
+
+delimiter:
+ SKIP_WS(text);
+ if (*text == ':') {
+ *text_p = text+1;
+ return 1;
+ }
+ if (*text == ',' || *text == '\0') {
+ *text_p = text;
+ return 1;
+ }
+ return 0;
+}
+
+
+static char *
+get_token(
+ const char **text_p)
+{
+ char *token = NULL, *t;
+ const char *bp, *ep;
+
+ bp = *text_p;
+ SKIP_WS(bp);
+ ep = bp;
+
+ while (*ep!='\0' && *ep!='\r' && *ep!='\n' && *ep!=':' && *ep!=',')
+ ep++;
+ if (ep == bp)
+ goto after_token;
+ token = (char*)malloc(ep - bp + 1);
+ if (token == NULL)
+ goto after_token;
+ memcpy(token, bp, ep - bp);
+
+ /* Trim trailing whitespace */
+ t = token + (ep - bp - 1);
+ while (t >= token &&
+ (*t==' ' || *t=='\t' || *t=='\n' || *t=='\r'))
+ t--;
+ *(t+1) = '\0';
+
+after_token:
+ if (*ep == ':')
+ ep++;
+ *text_p = ep;
+ return token;
+}
+
+
+static int
+get_id(
+ const char *token,
+ id_t *id_p)
+{
+ char *ep;
+ long l;
+ l = strtol(token, &ep, 0);
+ if (*ep != '\0')
+ return -1;
+ if (l < 0) {
+ /*
+ Negative values are interpreted as 16-bit numbers,
+ so that id -2 maps to 65534 (nobody/nogroup), etc.
+ */
+ l &= 0xFFFF;
+ }
+ *id_p = l;
+ return 0;
+}
+
+
+static int
+get_uid(
+ const char *token,
+ uid_t *uid_p)
+{
+ struct passwd *passwd;
+
+ if (get_id(token, (id_t *)uid_p) == 0)
+ goto accept;
+ passwd = getpwnam(token);
+ if (passwd) {
+ *uid_p = passwd->pw_uid;
+ goto accept;
+ }
+ return -1;
+
+accept:
+ return 0;
+}
+
+
+static int
+get_gid(
+ const char *token,
+ gid_t *gid_p)
+{
+ struct group *group;
+
+ if (get_id(token, (id_t *)gid_p) == 0)
+ goto accept;
+ group = getgrnam(token);
+ if (group) {
+ *gid_p = group->gr_gid;
+ goto accept;
+ }
+ return -1;
+
+accept:
+ return 0;
+}
+
+
+/*
+ Parses the next acl entry in text_p.
+
+ Returns:
+ -1 on error, 0 on success.
+*/
+
+cmd_t
+parse_acl_cmd(
+ const char **text_p,
+ int seq_cmd,
+ int parse_mode)
+{
+ cmd_t cmd = cmd_init();
+ char *str;
+ const char *backup;
+ int error, perm_chars;
+ if (!cmd)
+ return NULL;
+
+ cmd->c_cmd = seq_cmd;
+ if (parse_mode & SEQ_PROMOTE_ACL)
+ cmd->c_type = ACL_TYPE_DEFAULT;
+ else
+ cmd->c_type = ACL_TYPE_ACCESS;
+ cmd->c_id = ACL_UNDEFINED_ID;
+ cmd->c_perm = 0;
+
+ if (parse_mode & SEQ_PARSE_DEFAULT) {
+ /* check for default acl entry */
+ backup = *text_p;
+ if (skip_tag_name(text_p, "default")) {
+ if (parse_mode & SEQ_PROMOTE_ACL) {
+ /* if promoting from acl to default acl and
+ a default acl entry is found, fail. */
+ *text_p = backup;
+ goto fail;
+ }
+ cmd->c_type = ACL_TYPE_DEFAULT;
+ }
+ }
+
+ /* parse acl entry type */
+ switch (**text_p) {
+ case 'u': /* user */
+ skip_tag_name(text_p, "user");
+
+user_entry:
+ backup = *text_p;
+ str = get_token(text_p);
+ if (str) {
+ cmd->c_tag = ACL_USER;
+ error = get_uid(unquote(str), &cmd->c_id);
+ free(str);
+ if (error) {
+ *text_p = backup;
+ goto fail;
+ }
+ } else {
+ cmd->c_tag = ACL_USER_OBJ;
+ }
+ break;
+
+ case 'g': /* group */
+ if (!skip_tag_name(text_p, "group"))
+ goto user_entry;
+
+ backup = *text_p;
+ str = get_token(text_p);
+ if (str) {
+ cmd->c_tag = ACL_GROUP;
+ error = get_gid(unquote(str), &cmd->c_id);
+ free(str);
+ if (error) {
+ *text_p = backup;
+ goto fail;
+ }
+ } else {
+ cmd->c_tag = ACL_GROUP_OBJ;
+ }
+ break;
+
+ case 'o': /* other */
+ if (!skip_tag_name(text_p, "other"))
+ goto user_entry;
+ /* skip empty entry qualifier field (this field may
+ be missing for compatibility with Solaris.) */
+ SKIP_WS(*text_p);
+ if (**text_p == ':')
+ (*text_p)++;
+ cmd->c_tag = ACL_OTHER;
+ break;
+
+ case 'm': /* mask */
+ if (!skip_tag_name(text_p, "mask"))
+ goto user_entry;
+ /* skip empty entry qualifier field (this field may
+ be missing for compatibility with Solaris.) */
+ SKIP_WS(*text_p);
+ if (**text_p == ':')
+ (*text_p)++;
+ cmd->c_tag = ACL_MASK;
+ break;
+
+ default: /* assume "user:" */
+ goto user_entry;
+ }
+
+ SKIP_WS(*text_p);
+ if (**text_p == ',' || **text_p == '\0') {
+ if (parse_mode & SEQ_PARSE_NO_PERM)
+ return cmd;
+ else
+ goto fail;
+ }
+ if (!(parse_mode & SEQ_PARSE_WITH_PERM))
+ return cmd;
+
+ /* parse permissions */
+ SKIP_WS(*text_p);
+ if (**text_p >= '0' && **text_p <= '7') {
+ cmd->c_perm = 0;
+ while (**text_p == '0')
+ (*text_p)++;
+ if (**text_p >= '1' && **text_p <= '7') {
+ cmd->c_perm = (*(*text_p)++ - '0');
+ }
+
+ return cmd;
+ }
+
+ for (perm_chars=0; perm_chars<3; perm_chars++, (*text_p)++) {
+ switch(**text_p) {
+ case 'r': /* read */
+ if (cmd->c_perm & CMD_PERM_READ)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_READ;
+ break;
+
+ case 'w': /* write */
+ if (cmd->c_perm & CMD_PERM_WRITE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_WRITE;
+ break;
+
+ case 'x': /* execute */
+ if (cmd->c_perm & CMD_PERM_EXECUTE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_EXECUTE;
+ break;
+
+ case 'X': /* execute only if directory or some
+ entries already have execute permissions
+ set */
+ if (cmd->c_perm & CMD_PERM_COND_EXECUTE)
+ goto fail;
+ cmd->c_perm |= CMD_PERM_COND_EXECUTE;
+ break;
+
+ case '-':
+ /* ignore */
+ break;
+
+ default:
+ if (perm_chars == 0)
+ goto fail;
+ return cmd;
+ }
+ }
+ if (perm_chars != 3)
+ goto fail;
+ return cmd;
+
+fail:
+ cmd_free(cmd);
+ return NULL;
+}
+
+
+/*
+ Parse a comma-separated list of acl entries.
+
+ which is set to the index of the first character that was not parsed,
+ or -1 in case of success.
+*/
+int
+parse_acl_seq(
+ seq_t seq,
+ const char *text_p,
+ int *which,
+ int seq_cmd,
+ int parse_mode)
+{
+ const char *initial_text_p = text_p;
+ cmd_t cmd;
+
+ if (which)
+ *which = -1;
+
+ while (*text_p != '\0') {
+ cmd = parse_acl_cmd(&text_p, seq_cmd, parse_mode);
+ if (cmd == NULL) {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (seq_append(seq, cmd) != 0) {
+ cmd_free(cmd);
+ goto fail;
+ }
+ SKIP_WS(text_p);
+ if (*text_p != ',')
+ break;
+ text_p++;
+ }
+
+ if (*text_p != '\0') {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (which)
+ *which = (text_p - initial_text_p);
+ return -1;
+}
+
+
+
+int
+read_acl_comments(
+ FILE *file,
+ int *line,
+ char **path_p,
+ uid_t *uid_p,
+ gid_t *gid_p,
+ mode_t *flags)
+{
+ int c;
+ /*
+ Max PATH_MAX bytes even for UTF-8 path names and additional 9
+ bytes for "# file: ". Not a good solution but for now it is the
+ best I can do without too much impact on the code. [tw]
+ */
+ char linebuf[(4*PATH_MAX)+9];
+ char *cp;
+ char *p;
+ int comments_read = 0;
+
+ if (path_p)
+ *path_p = NULL;
+ if (uid_p)
+ *uid_p = ACL_UNDEFINED_ID;
+ if (gid_p)
+ *gid_p = ACL_UNDEFINED_ID;
+ if (flags)
+ *flags = 0;
+
+ for(;;) {
+ c = fgetc(file);
+ if (c == EOF)
+ break;
+ if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
+ if (c=='\n')
+ (*line)++;
+ continue;
+ }
+ if (c != '#') {
+ ungetc(c, file);
+ break;
+ }
+ if (line)
+ (*line)++;
+
+ if (fgets(linebuf, sizeof(linebuf), file) == NULL)
+ break;
+
+ comments_read = 1;
+
+ p = strrchr(linebuf, '\0');
+ while (p > linebuf &&
+ (*(p-1)=='\r' || *(p-1)=='\n')) {
+ p--;
+ *p = '\0';
+ }
+
+ cp = linebuf;
+ SKIP_WS(cp);
+ if (strncmp(cp, "file:", 5) == 0) {
+ cp += 5;
+ SKIP_WS(cp);
+ cp = unquote(cp);
+
+ if (path_p) {
+ if (*path_p)
+ goto fail;
+ *path_p = (char*)malloc(strlen(cp)+1);
+ if (!*path_p)
+ return -1;
+ strcpy(*path_p, cp);
+ }
+ } else if (strncmp(cp, "owner:", 6) == 0) {
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (uid_p) {
+ if (*uid_p != ACL_UNDEFINED_ID)
+ goto fail;
+ if (get_uid(unquote(cp), uid_p) != 0)
+ continue;
+ }
+ } else if (strncmp(cp, "group:", 6) == 0) {
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (gid_p) {
+ if (*gid_p != ACL_UNDEFINED_ID)
+ goto fail;
+ if (get_gid(unquote(cp), gid_p) != 0)
+ continue;
+ }
+ } else if (strncmp(cp, "flags:", 6) == 0) {
+ mode_t f = 0;
+
+ cp += 6;
+ SKIP_WS(cp);
+
+ if (cp[0] == 's')
+ f |= S_ISUID;
+ else if (cp[0] != '-')
+ goto fail;
+ if (cp[1] == 's')
+ f |= S_ISGID;
+ else if (cp[1] != '-')
+ goto fail;
+ if (cp[2] == 't')
+ f |= S_ISVTX;
+ else if (cp[2] != '-')
+ goto fail;
+ if (cp[3] != '\0')
+ goto fail;
+
+ if (flags)
+ *flags = f;
+ }
+ }
+ if (ferror(file))
+ return -1;
+ return comments_read;
+fail:
+ if (path_p && *path_p) {
+ free(*path_p);
+ *path_p = NULL;
+ }
+ return -EINVAL;
+}
+
+
+int
+read_acl_seq(
+ FILE *file,
+ seq_t seq,
+ int seq_cmd,
+ int parse_mode,
+ int *line,
+ int *which)
+{
+ char linebuf[1024];
+ const char *cp;
+ cmd_t cmd;
+
+ if (which)
+ *which = -1;
+
+ for(;;) {
+ if (fgets(linebuf, sizeof(linebuf), file) == NULL)
+ break;
+ if (line)
+ (*line)++;
+
+ cp = linebuf;
+ SKIP_WS(cp);
+ if (*cp == '\0') {
+ if (!(parse_mode & SEQ_PARSE_MULTI))
+ continue;
+ break;
+ } else if (*cp == '#') {
+ continue;
+ }
+
+ cmd = parse_acl_cmd(&cp, seq_cmd, parse_mode);
+ if (cmd == NULL) {
+ errno = EINVAL;
+ goto fail;
+ }
+ if (seq_append(seq, cmd) != 0) {
+ cmd_free(cmd);
+ goto fail;
+ }
+
+ SKIP_WS(cp);
+ if (*cp != '\0' && *cp != '#') {
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+
+ if (ferror(file))
+ goto fail;
+ return 0;
+
+fail:
+ if (which)
+ *which = (cp - linebuf);
+ return -1;
+}
+