(root)/
acl-2.3.1/
tools/
parse.c
       1  /*
       2    File: parse.c
       3    (Linux Access Control List Management)
       4  
       5    Copyright (C) 1999, 2000
       6    Andreas Gruenbacher, <andreas.gruenbacher@gmail.com>
       7   	
       8    This program is free software; you can redistribute it and/or
       9    modify it under the terms of the GNU Lesser General Public
      10    License as published by the Free Software Foundation; either
      11    version 2.1 of the License, or (at your option) any later version.
      12  
      13    This program is distributed in the hope that it will be useful,
      14    but WITHOUT ANY WARRANTY; without even the implied warranty of
      15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      16    Lesser General Public License for more details.
      17  
      18    You should have received a copy of the GNU Lesser General Public
      19    License along with this library; if not, write to the Free Software
      20    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
      21  */
      22  
      23  #include "config.h"
      24  #include <stdio.h>
      25  #include <stdlib.h>
      26  #include <string.h>
      27  #include <errno.h>
      28  #include <limits.h>
      29  
      30  #include <sys/types.h>
      31  #include <sys/stat.h>
      32  #include <pwd.h>
      33  #include <grp.h>
      34  #include "sys/acl.h"
      35  
      36  #include "sequence.h"
      37  #include "parse.h"
      38  #include "misc.h"
      39  
      40  #define SKIP_WS(x) ({ \
      41  	while (*(x)==' ' || *(x)=='\t' || *(x)=='\n' || *(x)=='\r') \
      42  		(x)++; \
      43  	})
      44  
      45  
      46  static int
      47  skip_tag_name(
      48  	const char **text_p,
      49  	const char *token)
      50  {
      51  	size_t len = strlen(token);
      52  	const char *text = *text_p;
      53  
      54  	SKIP_WS(text);
      55  	if (strncmp(text, token, len) == 0) {
      56  		text += len;
      57  		goto delimiter;
      58  	}
      59  	if (*text == *token) {
      60  		text++;
      61  		goto delimiter;
      62  	}
      63  	return 0;
      64  
      65  delimiter:
      66  	SKIP_WS(text);
      67  	if (*text == ':') {
      68  		*text_p = text+1;
      69  		return 1;
      70  	}
      71  	if (*text == ',' || *text == '\0') {
      72  		*text_p = text;
      73  		return 1;
      74  	}
      75  	return 0;
      76  }
      77  
      78  
      79  static char *
      80  get_token(
      81  	const char **text_p)
      82  {
      83  	char *token = NULL, *t;
      84  	const char *bp, *ep;
      85  
      86  	bp = *text_p;
      87  	SKIP_WS(bp);
      88  	ep = bp;
      89  
      90  	while (*ep!='\0' && *ep!='\r' && *ep!='\n' && *ep!=':' && *ep!=',')
      91  		ep++;
      92  	if (ep == bp)
      93  		goto after_token;
      94  	token = (char*)malloc(ep - bp + 1);
      95  	if (token == NULL)
      96  		goto after_token;
      97  	memcpy(token, bp, ep - bp);
      98  
      99  	/* Trim trailing whitespace */
     100  	t = token + (ep - bp - 1);
     101  	while (t >= token &&
     102  	       (*t==' ' || *t=='\t' || *t=='\n' || *t=='\r'))
     103  		t--;
     104  	*(t+1) = '\0';
     105  
     106  after_token:
     107  	if (*ep == ':')
     108  		ep++;
     109  	*text_p = ep;
     110  	return token;
     111  }
     112  
     113  
     114  static int
     115  get_id(
     116  	const char *token,
     117  	id_t *id_p)
     118  {
     119  	char *ep;
     120  	long l;
     121  	l = strtol(token, &ep, 0);
     122  	if (*ep != '\0')
     123  		return -1;
     124  	if (l < 0) {
     125  		/*
     126  		  Negative values are interpreted as 16-bit numbers,
     127  		  so that id -2 maps to 65534 (nobody/nogroup), etc.
     128  		*/
     129  		l &= 0xFFFF;
     130  	}
     131  	*id_p = l;
     132  	return 0;
     133  }
     134  
     135  
     136  static int
     137  get_uid(
     138  	const char *token,
     139  	uid_t *uid_p)
     140  {
     141  	struct passwd *passwd;
     142  
     143  	if (get_id(token, (id_t *)uid_p) == 0)
     144  		goto accept;
     145  	passwd = getpwnam(token);
     146  	if (passwd) {
     147  		*uid_p = passwd->pw_uid;
     148  		goto accept;
     149  	}
     150  	return -1;
     151  
     152  accept:
     153  	return 0;
     154  }
     155  
     156  
     157  static int
     158  get_gid(
     159  	const char *token,
     160  	gid_t *gid_p)
     161  {
     162  	struct group *group;
     163  
     164  	if (get_id(token, (id_t *)gid_p) == 0)
     165  		goto accept;
     166  	group = getgrnam(token);
     167  	if (group) {
     168  		*gid_p = group->gr_gid;
     169  		goto accept;
     170  	}
     171  	return -1;
     172  
     173  accept:
     174  	return 0;
     175  }
     176  
     177  
     178  /*
     179  	Parses the next acl entry in text_p.
     180  
     181  	Returns:
     182  		-1 on error, 0 on success.
     183  */
     184  
     185  cmd_t
     186  parse_acl_cmd(
     187  	const char **text_p,
     188  	int seq_cmd,
     189  	int parse_mode)
     190  {
     191  	cmd_t cmd = cmd_init();
     192  	char *str;
     193  	const char *backup;
     194  	int error, perm_chars;
     195  	if (!cmd)
     196  		return NULL;
     197  
     198  	cmd->c_cmd = seq_cmd;
     199  	if (parse_mode & SEQ_PROMOTE_ACL)
     200  		cmd->c_type = ACL_TYPE_DEFAULT;
     201  	else
     202  		cmd->c_type = ACL_TYPE_ACCESS;
     203  	cmd->c_id   = ACL_UNDEFINED_ID;
     204  	cmd->c_perm = 0;
     205  
     206  	if (parse_mode & SEQ_PARSE_DEFAULT) {
     207  		/* check for default acl entry */
     208  		backup = *text_p;
     209  		if (skip_tag_name(text_p, "default")) {
     210  			if (parse_mode & SEQ_PROMOTE_ACL) {
     211  				/* if promoting from acl to default acl and
     212  				   a default acl entry is found, fail. */
     213  				*text_p = backup;
     214  				goto fail;
     215  			}
     216  			cmd->c_type = ACL_TYPE_DEFAULT;
     217  		}
     218  	}
     219  
     220  	/* parse acl entry type */
     221  	switch (**text_p) {
     222  		case 'u':  /* user */
     223  			skip_tag_name(text_p, "user");
     224  
     225  user_entry:
     226  			backup = *text_p;
     227  			str = get_token(text_p);
     228  			if (str) {
     229  				cmd->c_tag = ACL_USER;
     230  				error = get_uid(__acl_unquote(str), &cmd->c_id);
     231  				free(str);
     232  				if (error) {
     233  					*text_p = backup;
     234  					goto fail;
     235  				}
     236  			} else {
     237  				cmd->c_tag = ACL_USER_OBJ;
     238  			}
     239  			break;
     240  
     241  		case 'g':  /* group */
     242  			if (!skip_tag_name(text_p, "group"))
     243  				goto user_entry;
     244  
     245  			backup = *text_p;
     246  			str = get_token(text_p);
     247  			if (str) {
     248  				cmd->c_tag = ACL_GROUP;
     249  				error = get_gid(__acl_unquote(str), &cmd->c_id);
     250  				free(str);
     251  				if (error) {
     252  					*text_p = backup;
     253  					goto fail;
     254  				}
     255  			} else {
     256  				cmd->c_tag = ACL_GROUP_OBJ;
     257  			}
     258  			break;
     259  
     260  		case 'o':  /* other */
     261  			if (!skip_tag_name(text_p, "other"))
     262  				goto user_entry;
     263  			/* skip empty entry qualifier field (this field may
     264  			   be missing for compatibility with Solaris.) */
     265  			SKIP_WS(*text_p);
     266  			if (**text_p == ':')
     267  				(*text_p)++;
     268  			cmd->c_tag = ACL_OTHER;
     269  			break;
     270  
     271  		case 'm':  /* mask */
     272  			if (!skip_tag_name(text_p, "mask"))
     273  				goto user_entry;
     274  			/* skip empty entry qualifier field (this field may
     275  			   be missing for compatibility with Solaris.) */
     276  			SKIP_WS(*text_p);
     277  			if (**text_p == ':')
     278  				(*text_p)++;
     279  			cmd->c_tag = ACL_MASK;
     280  			break;
     281  
     282  		default:  /* assume "user:" */
     283  			goto user_entry;
     284  	}
     285  
     286  	SKIP_WS(*text_p);
     287  	if (**text_p == ',' || **text_p == '\0') {
     288  		if (parse_mode & SEQ_PARSE_NO_PERM)
     289  			return cmd;
     290  		else
     291  			goto fail;
     292  	}
     293  	if (!(parse_mode & SEQ_PARSE_WITH_PERM))
     294  		return cmd;
     295  
     296  	/* parse permissions */
     297  	SKIP_WS(*text_p);
     298  	if (**text_p >= '0' && **text_p <= '7') {
     299  		cmd->c_perm = 0;
     300  		while (**text_p == '0')
     301  			(*text_p)++;
     302  		if (**text_p >= '1' && **text_p <= '7') {
     303  			cmd->c_perm = (*(*text_p)++ - '0');
     304  		}
     305  
     306  		return cmd;
     307  	}
     308  
     309  	for (perm_chars=0;; perm_chars++, (*text_p)++) {
     310  		switch(**text_p) {
     311  			case 'r': /* read */
     312  				if (cmd->c_perm & CMD_PERM_READ)
     313  					goto fail;
     314  				cmd->c_perm |= CMD_PERM_READ;
     315  				break;
     316  
     317  			case 'w':  /* write */
     318  				if (cmd->c_perm & CMD_PERM_WRITE)
     319  					goto fail;
     320  				cmd->c_perm |= CMD_PERM_WRITE;
     321  				break;
     322  
     323  			case 'x':  /* execute */
     324  				if (cmd->c_perm & CMD_PERM_EXECUTE)
     325  					goto fail;
     326  				cmd->c_perm |= CMD_PERM_EXECUTE;
     327  				break;
     328  
     329  			case 'X':  /* execute only if directory or some
     330  				      entries already have execute permissions
     331  				      set */
     332  				if (cmd->c_perm & CMD_PERM_COND_EXECUTE)
     333  					goto fail;
     334  				cmd->c_perm |= CMD_PERM_COND_EXECUTE;
     335  				break;
     336  
     337  			case '-':
     338  				/* ignore */
     339  				break;
     340  
     341  			default:
     342  				if (perm_chars == 0)
     343  					goto fail;
     344  				return cmd;
     345  		}
     346  	}
     347  	return cmd;
     348  
     349  fail:
     350  	cmd_free(cmd);
     351  	return NULL;
     352  }
     353  
     354  
     355  /*
     356  	Parse a comma-separated list of acl entries.
     357  
     358  	which is set to the index of the first character that was not parsed,
     359  	or -1 in case of success.
     360  */
     361  int
     362  parse_acl_seq(
     363  	seq_t seq,
     364  	const char *text_p,
     365  	int *which,
     366  	int seq_cmd,
     367  	int parse_mode)
     368  {
     369  	const char *initial_text_p = text_p;
     370  	cmd_t cmd;
     371  
     372  	if (which)
     373  		*which = -1;
     374  
     375  	while (*text_p != '\0') {
     376  		cmd = parse_acl_cmd(&text_p, seq_cmd, parse_mode);
     377  		if (cmd == NULL) {
     378  			errno = EINVAL;
     379  			goto fail;
     380  		}
     381  		if (seq_append(seq, cmd) != 0) {
     382  			cmd_free(cmd);
     383  			goto fail;
     384  		}
     385  		SKIP_WS(text_p);
     386  		if (*text_p != ',')
     387  			break;
     388  		text_p++;
     389  	}
     390  
     391  	if (*text_p != '\0') {
     392  		errno = EINVAL;
     393  		goto fail;
     394  	}
     395  
     396  	return 0;
     397  
     398  fail:
     399  	if (which)
     400  		*which = (text_p - initial_text_p);
     401  	return -1;
     402  }
     403  
     404  
     405  
     406  int
     407  read_acl_comments(
     408  	FILE *file,
     409  	int *lineno,
     410  	char **path_p,
     411  	uid_t *uid_p,
     412  	gid_t *gid_p,
     413  	mode_t *flags)
     414  {
     415  	int c;
     416  	/*
     417  	  Max PATH_MAX bytes even for UTF-8 path names and additional 9
     418  	  bytes for "# file: ". Not a good solution but for now it is the
     419  	  best I can do without too much impact on the code. [tw]
     420  	*/
     421  	char *line, *cp, *p;
     422  	int comments_read = 0;
     423  	
     424  	if (path_p)
     425  		*path_p = NULL;
     426  	if (uid_p)
     427  		*uid_p = ACL_UNDEFINED_ID;
     428  	if (gid_p)
     429  		*gid_p = ACL_UNDEFINED_ID;
     430  	if (flags)
     431  		*flags = 0;
     432  
     433  	for(;;) {
     434  		c = fgetc(file);
     435  		if (c == EOF)
     436  			break;
     437  		if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
     438  			if (c=='\n')
     439  				(*lineno)++;
     440  			continue;
     441  		}
     442  		if (c != '#') {
     443  			ungetc(c, file);
     444  			break;
     445  		}
     446  		if (lineno)
     447  			(*lineno)++;
     448  
     449  		line = __acl_next_line(file);
     450  		if (line == NULL)
     451  			break;
     452  		
     453  		comments_read = 1;
     454  
     455  		p = strrchr(line, '\0');
     456  		while (p > line &&
     457  		       (*(p-1)=='\r' || *(p-1)=='\n')) {
     458  		       	p--;
     459  			*p = '\0';
     460  		}
     461  		
     462  		cp = line;
     463  		SKIP_WS(cp);
     464  		if (strncmp(cp, "file:", 5) == 0) {
     465  			cp += 5;
     466  			SKIP_WS(cp);
     467  			cp = __acl_unquote(cp);
     468  			
     469  			if (path_p) {
     470  				if (*path_p)
     471  					goto fail;
     472  				*path_p = (char*)malloc(strlen(cp)+1);
     473  				if (!*path_p)
     474  					return -1;
     475  				strcpy(*path_p, cp);
     476  			}
     477  		} else if (strncmp(cp, "owner:", 6) == 0) {
     478  			cp += 6;
     479  			SKIP_WS(cp);
     480  				
     481  			if (uid_p) {
     482  				if (*uid_p != ACL_UNDEFINED_ID)
     483  					goto fail;
     484  				if (get_uid(__acl_unquote(cp), uid_p) != 0)
     485  					continue;
     486  			}
     487  		} else if (strncmp(cp, "group:", 6) == 0) {
     488  			cp += 6;
     489  			SKIP_WS(cp);
     490  				
     491  			if (gid_p) {
     492  				if (*gid_p != ACL_UNDEFINED_ID)
     493  					goto fail;
     494  				if (get_gid(__acl_unquote(cp), gid_p) != 0)
     495  					continue;
     496  			}
     497  		} else if (strncmp(cp, "flags:", 6) == 0) {
     498  			mode_t f = 0;
     499  
     500  			cp += 6;
     501  			SKIP_WS(cp);
     502  
     503  			if (cp[0] == 's')
     504  				f |= S_ISUID;
     505  			else if (cp[0] != '-')
     506  				goto fail;
     507  			if (cp[1] == 's')
     508  				f |= S_ISGID;
     509  			else if (cp[1] != '-')
     510  				goto fail;
     511  			if (cp[2] == 't')
     512  				f |= S_ISVTX;
     513  			else if (cp[2] != '-')
     514  				goto fail;
     515  			if (cp[3] != '\0')
     516  				goto fail;
     517  
     518  			if (flags)
     519  				*flags = f;
     520  		}
     521  	}
     522  	if (ferror(file))
     523  		return -1;
     524  	return comments_read;
     525  fail:
     526  	if (path_p && *path_p) {
     527  		free(*path_p);
     528  		*path_p = NULL;
     529  	}
     530  	return -EINVAL;
     531  }
     532  
     533  
     534  int
     535  read_acl_seq(
     536  	FILE *file,
     537  	seq_t seq,
     538  	int seq_cmd,
     539  	int parse_mode,
     540  	int *lineno,
     541  	int *which)
     542  {
     543  	char *line;
     544  	const char *cp;
     545  	cmd_t cmd;
     546  
     547  	if (which)
     548  		*which = -1;
     549  
     550  	while ((line = __acl_next_line(file))) {
     551  		if (lineno)
     552  			(*lineno)++;
     553  
     554  		cp = line;
     555  		SKIP_WS(cp);
     556  		if (*cp == '\0') {
     557  			if (!(parse_mode & SEQ_PARSE_MULTI))
     558  				continue;
     559  			break;
     560  		} else if (*cp == '#') {
     561  			continue;
     562  		}
     563  
     564  		cmd = parse_acl_cmd(&cp, seq_cmd, parse_mode);
     565  		if (cmd == NULL) {
     566  			errno = EINVAL;
     567  			goto fail;
     568  		}
     569  		if (seq_append(seq, cmd) != 0) {
     570  			cmd_free(cmd);
     571  			goto fail;
     572  		}
     573  
     574  		SKIP_WS(cp);
     575  		if (*cp != '\0' && *cp != '#') {
     576  			errno = EINVAL;
     577  			goto fail;
     578  		}
     579  	}
     580  
     581  	if (ferror(file))
     582  		goto fail;
     583  	return 0;
     584  
     585  fail:
     586  	if (which)
     587  		*which = (cp - line);
     588  	return -1;
     589  }
     590