Blame modules/pam_console/modechange.c

Packit Service b29381
/* This file is derived from modechange.c included in the GNU fileutils
Packit Service b29381
   distribution.  It has been changed to be a library specifically
Packit Service b29381
   for use within pam_console
Packit Service b29381
   Changes Copyright 1999 Red Hat Software, Inc.
Packit Service b29381
 */
Packit Service b29381
Packit Service b29381
/* modechange.c -- file mode manipulation
Packit Service b29381
   Copyright (C) 1989, 1990 Free Software Foundation, Inc.
Packit Service b29381
Packit Service b29381
   This program is free software; you can redistribute it and/or modify
Packit Service b29381
   it under the terms of the GNU General Public License as published by
Packit Service b29381
   the Free Software Foundation; either version 2, or (at your option)
Packit Service b29381
   any later version.
Packit Service b29381
Packit Service b29381
   This program is distributed in the hope that it will be useful,
Packit Service b29381
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service b29381
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service b29381
   GNU General Public License for more details.
Packit Service b29381
Packit Service b29381
   You should have received a copy of the GNU General Public License
Packit Service b29381
   along with this program; if not, write to the Free Software Foundation,
Packit Service b29381
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
Packit Service b29381
Packit Service b29381
/* Written by David MacKenzie <djm@ai.mit.edu> */
Packit Service b29381
Packit Service b29381
/* The ASCII mode string is compiled into a linked list of `struct
Packit Service b29381
   modechange', which can then be applied to each file to be changed.
Packit Service b29381
   We do this instead of re-parsing the ASCII string for each file
Packit Service b29381
   because the compiled form requires less computation to use; when
Packit Service b29381
   changing the mode of many files, this probably results in a
Packit Service b29381
   performance gain. */
Packit Service b29381
Packit Service b29381
#include "config.h"
Packit Service b29381
#include <sys/types.h>
Packit Service b29381
#include <sys/stat.h>
Packit Service b29381
#include "modechange.h"
Packit Service b29381
Packit Service b29381
#include <stdlib.h>
Packit Service b29381
Packit Service b29381
/* Return newly allocated memory to hold one element of type TYPE. */
Packit Service b29381
#define talloc(type) ((type *) malloc (sizeof (type)))
Packit Service b29381
Packit Service b29381
#define isodigit(c) ((c) >= '0' && (c) <= '7')
Packit Service b29381
Packit Service b29381
/* Return a positive integer containing the value of the ASCII
Packit Service b29381
   octal number S.  If S is not an octal number, return -1.  */
Packit Service b29381
Packit Service b29381
static int
Packit Service b29381
oatoi (const char *s)
Packit Service b29381
{
Packit Service b29381
  register int i;
Packit Service b29381
Packit Service b29381
  if (*s == 0)
Packit Service b29381
    return -1;
Packit Service b29381
  for (i = 0; isodigit (*s); ++s)
Packit Service b29381
    i = i * 8 + *s - '0';
Packit Service b29381
  if (*s)
Packit Service b29381
    return -1;
Packit Service b29381
  return i;
Packit Service b29381
}
Packit Service b29381
Packit Service b29381
/* Return a linked list of file mode change operations created from
Packit Service b29381
   MODE_STRING, an ASCII string that contains either an octal number
Packit Service b29381
   specifying an absolute mode, or symbolic mode change operations with
Packit Service b29381
   the form:
Packit Service b29381
   [ugoa...][[+-=][rwxXstugo...]...][,...]
Packit Service b29381
   MASKED_OPS is a bitmask indicating which symbolic mode operators (=+-)
Packit Service b29381
   should not affect bits set in the umask when no users are given.
Packit Service b29381
   Operators not selected in MASKED_OPS ignore the umask.
Packit Service b29381
Packit Service b29381
   Return MODE_INVALID if `mode_string' does not contain a valid
Packit Service b29381
   representation of file mode change operations;
Packit Service b29381
   return MODE_MEMORY_EXHAUSTED if there is insufficient memory. */
Packit Service b29381
Packit Service b29381
STATIC struct mode_change *
Packit Service b29381
mode_compile (mode_string, masked_ops)
Packit Service b29381
     const char *mode_string;
Packit Service b29381
     unsigned masked_ops;
Packit Service b29381
{
Packit Service b29381
  struct mode_change *head;	/* First element of the linked list. */
Packit Service b29381
  struct mode_change *change;	/* An element of the linked list. */
Packit Service b29381
  int i;			/* General purpose temporary. */
Packit Service b29381
  int umask_value;		/* The umask value (surprise). */
Packit Service b29381
  unsigned short affected_bits;	/* Which bits in the mode are operated on. */
Packit Service b29381
  unsigned short affected_masked; /* `affected_bits' modified by umask. */
Packit Service b29381
  unsigned ops_to_mask;		/* Operators to actually use umask on. */
Packit Service b29381
Packit Service b29381
  i = oatoi (mode_string);
Packit Service b29381
  if (i >= 0)
Packit Service b29381
    {
Packit Service b29381
      if (i > 07777)
Packit Service b29381
	return MODE_INVALID;
Packit Service b29381
      head = talloc (struct mode_change);
Packit Service b29381
      if (head == NULL)
Packit Service b29381
	return MODE_MEMORY_EXHAUSTED;
Packit Service b29381
      head->next = NULL;
Packit Service b29381
      head->op = '=';
Packit Service b29381
      head->flags = 0;
Packit Service b29381
      head->value = i;
Packit Service b29381
      head->affected = 07777;	/* Affect all permissions. */
Packit Service b29381
      return head;
Packit Service b29381
    }
Packit Service b29381
Packit Service b29381
  umask_value = umask (0);
Packit Service b29381
  umask (umask_value);		/* Restore the old value. */
Packit Service b29381
Packit Service b29381
  head = NULL;
Packit Service b29381
  change = NULL;
Packit Service b29381
  --mode_string;
Packit Service b29381
Packit Service b29381
  /* One loop iteration for each "ugoa...=+-rwxXstugo...[=+-rwxXstugo...]". */
Packit Service b29381
  do
Packit Service b29381
    {
Packit Service b29381
      affected_bits = 0;
Packit Service b29381
      ops_to_mask = 0;
Packit Service b29381
      /* Turn on all the bits in `affected_bits' for each group given. */
Packit Service b29381
      for (++mode_string;; ++mode_string)
Packit Service b29381
	switch (*mode_string)
Packit Service b29381
	  {
Packit Service b29381
	  case 'u':
Packit Service b29381
	    affected_bits |= 04700;
Packit Service b29381
	    break;
Packit Service b29381
	  case 'g':
Packit Service b29381
	    affected_bits |= 02070;
Packit Service b29381
	    break;
Packit Service b29381
	  case 'o':
Packit Service b29381
	    affected_bits |= 01007;
Packit Service b29381
	    break;
Packit Service b29381
	  case 'a':
Packit Service b29381
	    affected_bits |= 07777;
Packit Service b29381
	    break;
Packit Service b29381
	  default:
Packit Service b29381
	    goto no_more_affected;
Packit Service b29381
	  }
Packit Service b29381
Packit Service b29381
    no_more_affected:
Packit Service b29381
      /* If none specified, affect all bits, except perhaps those
Packit Service b29381
	 set in the umask. */
Packit Service b29381
      if (affected_bits == 0)
Packit Service b29381
	{
Packit Service b29381
	  affected_bits = 07777;
Packit Service b29381
	  ops_to_mask = masked_ops;
Packit Service b29381
	}
Packit Service b29381
Packit Service b29381
      while (*mode_string == '=' || *mode_string == '+' || *mode_string == '-')
Packit Service b29381
	{
Packit Service b29381
	  /* Add the element to the tail of the list, so the operations
Packit Service b29381
	     are performed in the correct order. */
Packit Service b29381
	  if (head == NULL)
Packit Service b29381
	    {
Packit Service b29381
	      head = talloc (struct mode_change);
Packit Service b29381
	      if (head == NULL)
Packit Service b29381
		return MODE_MEMORY_EXHAUSTED;
Packit Service b29381
	      change = head;
Packit Service b29381
	    }
Packit Service b29381
	  else
Packit Service b29381
	    {
Packit Service b29381
	      change->next = talloc (struct mode_change);
Packit Service b29381
	      if (change->next == NULL)
Packit Service b29381
		{
Packit Service b29381
		  mode_free (change);
Packit Service b29381
		  return MODE_MEMORY_EXHAUSTED;
Packit Service b29381
		}
Packit Service b29381
	      change = change->next;
Packit Service b29381
	    }
Packit Service b29381
Packit Service b29381
	  change->next = NULL;
Packit Service b29381
	  change->op = *mode_string;	/* One of "=+-". */
Packit Service b29381
	  affected_masked = affected_bits;
Packit Service b29381
	  if (ops_to_mask & (*mode_string == '=' ? MODE_MASK_EQUALS
Packit Service b29381
			     : *mode_string == '+' ? MODE_MASK_PLUS
Packit Service b29381
			     : MODE_MASK_MINUS))
Packit Service b29381
	    affected_masked &= ~umask_value;
Packit Service b29381
	  change->affected = affected_masked;
Packit Service b29381
	  change->value = 0;
Packit Service b29381
	  change->flags = 0;
Packit Service b29381
Packit Service b29381
	  /* Set `value' according to the bits set in `affected_masked'. */
Packit Service b29381
	  for (++mode_string;; ++mode_string)
Packit Service b29381
	    switch (*mode_string)
Packit Service b29381
	      {
Packit Service b29381
	      case 'r':
Packit Service b29381
		change->value |= 00444 & affected_masked;
Packit Service b29381
		break;
Packit Service b29381
	      case 'w':
Packit Service b29381
		change->value |= 00222 & affected_masked;
Packit Service b29381
		break;
Packit Service b29381
	      case 'X':
Packit Service b29381
		change->flags |= MODE_X_IF_ANY_X;
Packit Service b29381
		/* Fall through. */
Packit Service b29381
	      case 'x':
Packit Service b29381
		change->value |= 00111 & affected_masked;
Packit Service b29381
		break;
Packit Service b29381
	      case 's':
Packit Service b29381
		/* Set the setuid/gid bits if `u' or `g' is selected. */
Packit Service b29381
		change->value |= 06000 & affected_masked;
Packit Service b29381
		break;
Packit Service b29381
	      case 't':
Packit Service b29381
		/* Set the "save text image" bit if `o' is selected. */
Packit Service b29381
		change->value |= 01000 & affected_masked;
Packit Service b29381
		break;
Packit Service b29381
	      case 'u':
Packit Service b29381
		/* Set the affected bits to the value of the `u' bits
Packit Service b29381
		   on the same file.  */
Packit Service b29381
		if (change->value)
Packit Service b29381
		  goto invalid;
Packit Service b29381
		change->value = 00700;
Packit Service b29381
		change->flags |= MODE_COPY_EXISTING;
Packit Service b29381
		break;
Packit Service b29381
	      case 'g':
Packit Service b29381
		/* Set the affected bits to the value of the `g' bits
Packit Service b29381
		   on the same file.  */
Packit Service b29381
		if (change->value)
Packit Service b29381
		  goto invalid;
Packit Service b29381
		change->value = 00070;
Packit Service b29381
		change->flags |= MODE_COPY_EXISTING;
Packit Service b29381
		break;
Packit Service b29381
	      case 'o':
Packit Service b29381
		/* Set the affected bits to the value of the `o' bits
Packit Service b29381
		   on the same file.  */
Packit Service b29381
		if (change->value)
Packit Service b29381
		  goto invalid;
Packit Service b29381
		change->value = 00007;
Packit Service b29381
		change->flags |= MODE_COPY_EXISTING;
Packit Service b29381
		break;
Packit Service b29381
	      default:
Packit Service b29381
		goto no_more_values;
Packit Service b29381
	      }
Packit Service b29381
	no_more_values:;
Packit Service b29381
	}
Packit Service b29381
  } while (*mode_string == ',');
Packit Service b29381
  if (*mode_string == 0)
Packit Service b29381
    return head;
Packit Service b29381
invalid:
Packit Service b29381
  mode_free (head);
Packit Service b29381
  return MODE_INVALID;
Packit Service b29381
}
Packit Service b29381
Packit Service b29381
/* Return file mode OLDMODE, adjusted as indicated by the list of change
Packit Service b29381
   operations CHANGES.  If OLDMODE is a directory, the type `X'
Packit Service b29381
   change affects it even if no execute bits were set in OLDMODE.
Packit Service b29381
   The returned value has the S_IFMT bits cleared. */
Packit Service b29381
Packit Service b29381
STATIC unsigned short
Packit Service b29381
mode_adjust (oldmode, changes)
Packit Service b29381
     unsigned oldmode;
Packit Service b29381
     const struct mode_change *changes;
Packit Service b29381
{
Packit Service b29381
  unsigned short newmode;	/* The adjusted mode and one operand. */
Packit Service b29381
  unsigned short value;		/* The other operand. */
Packit Service b29381
Packit Service b29381
  newmode = oldmode & 07777;
Packit Service b29381
Packit Service b29381
  for (; changes; changes = changes->next)
Packit Service b29381
    {
Packit Service b29381
      if (changes->flags & MODE_COPY_EXISTING)
Packit Service b29381
	{
Packit Service b29381
	  /* Isolate in `value' the bits in `newmode' to copy, given in
Packit Service b29381
	     the mask `changes->value'. */
Packit Service b29381
	  value = newmode & changes->value;
Packit Service b29381
Packit Service b29381
	  if (changes->value & 00700)
Packit Service b29381
	    /* Copy `u' permissions onto `g' and `o'. */
Packit Service b29381
	    value |= (value >> 3) | (value >> 6);
Packit Service b29381
	  else if (changes->value & 00070)
Packit Service b29381
	    /* Copy `g' permissions onto `u' and `o'. */
Packit Service b29381
	    value |= (value << 3) | (value >> 3);
Packit Service b29381
	  else
Packit Service b29381
	    /* Copy `o' permissions onto `u' and `g'. */
Packit Service b29381
	    value |= (value << 3) | (value << 6);
Packit Service b29381
Packit Service b29381
	  /* In order to change only `u', `g', or `o' permissions,
Packit Service b29381
	     or some combination thereof, clear unselected bits.
Packit Service b29381
	     This can not be done in mode_compile because the value
Packit Service b29381
	     to which the `changes->affected' mask is applied depends
Packit Service b29381
	     on the old mode of each file. */
Packit Service b29381
	  value &= changes->affected;
Packit Service b29381
	}
Packit Service b29381
      else
Packit Service b29381
	{
Packit Service b29381
	  value = changes->value;
Packit Service b29381
	  /* If `X', do not affect the execute bits if the file is not a
Packit Service b29381
	     directory and no execute bits are already set. */
Packit Service b29381
	  if ((changes->flags & MODE_X_IF_ANY_X)
Packit Service b29381
	      && !S_ISDIR (oldmode)
Packit Service b29381
	      && (newmode & 00111) == 0)
Packit Service b29381
	    value &= ~00111;	/* Clear the execute bits. */
Packit Service b29381
	}
Packit Service b29381
Packit Service b29381
      switch (changes->op)
Packit Service b29381
	{
Packit Service b29381
	case '=':
Packit Service b29381
	  /* Preserve the previous values in `newmode' of bits that are
Packit Service b29381
	     not affected by this change operation. */
Packit Service b29381
	  newmode = (newmode & ~changes->affected) | value;
Packit Service b29381
	  break;
Packit Service b29381
	case '+':
Packit Service b29381
	  newmode |= value;
Packit Service b29381
	  break;
Packit Service b29381
	case '-':
Packit Service b29381
	  newmode &= ~value;
Packit Service b29381
	  break;
Packit Service b29381
	}
Packit Service b29381
    }
Packit Service b29381
  return newmode;
Packit Service b29381
}
Packit Service b29381
Packit Service b29381
/* Free the memory used by the list of file mode change operations
Packit Service b29381
   CHANGES. */
Packit Service b29381
Packit Service b29381
STATIC void
Packit Service b29381
mode_free (changes)
Packit Service b29381
     register struct mode_change *changes;
Packit Service b29381
{
Packit Service b29381
  register struct mode_change *next;
Packit Service b29381
Packit Service b29381
  while (changes)
Packit Service b29381
    {
Packit Service b29381
      next = changes->next;
Packit Service b29381
      free (changes);
Packit Service b29381
      changes = next;
Packit Service b29381
    }
Packit Service b29381
}
Packit Service b29381