/* cfengine for GNU
   
   Copyright (C) 1995
   Free Software Foundation, Inc.
   
   This file is part of GNU cfengine - written and maintained 
   by Mark Burgess, Dept of Computing and Engineering, Oslo College,
   Dept. of Theoretical physics, University of Oslo
   
   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, 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 General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
*/

/**************************************************************************/
/*                                                                        */
/* File: acl.c                                                            */
/* Author: Mark / Demosthenes / Patch by Goran Oberg                      */
/*                                                                        */
/**************************************************************************/

#include <stdio.h>

#include "cf.defs.h"
#include "cf.extern.h"

#ifdef HAVE_SYS_ACL_H
# include <sys/acl.h>
#endif

#ifdef HAVE_DCE_DACLIF_H
# include <dce/daclif.h>
# include <dce/secidmap.h>
# include <dce/binding.h>
# include <dcedfs/aclint.h>
# define SIZE_AVAIL (unsigned32) 2
# define HAVE_DCE

# define MAX_TYPES          (unsigned32)2
# define MAX_STRINGS        (unsigned32)16
#endif

/*****************************************************************************/

char *CFFSTYPES[] =
{
    "posix",
    "solaris",
    "dfs",
    "afs",
    "hpux",
    "nt",
    NULL
};

enum cffstype StringToFstype();
struct CFACL *GetACL();

/*****************************************************************************/

#if defined SOLARIS && defined HAVE_SYS_ACL_H
void
aclsortperror(int error)
{
 switch (error)
    {
    case GRP_ERROR:
	CfLog(cferror,"acl: There is more than one group_obj ACL entry.\n","");
	break;
    case USER_ERROR:
	CfLog(cferror,"acl: There is more than one user_obj ACL entry.\n","");
	break;
    case CLASS_ERROR:
	CfLog(cferror,"acl: There is more than one class_obj ACL entry.\n", "");
	break;
    case OTHER_ERROR:
	CfLog(cferror,"acl: There is more than one other_obj ACL entry.\n", "");
	break;
    case DUPLICATE_ERROR:
	CfLog(cferror,"acl: Duplicate entries of user or group.\n", "");
	break;
    case ENTRY_ERROR:
	CfLog(cferror,"acl: The entry type is invalid.\n", "");
	break;
    case MISS_ERROR:
	CfLog(cferror,"acl: Missing group_obj, user_obj, class_obj, or other_obj entries.\n", "");
	break;
    case MEM_ERROR:
	CfLog(cferror,"acl: The system can't allocate any memory.\n", "");
	break;
    default:
	sprintf(OUTPUT,"acl: Unknown ACL error code: %d !\n", error);
	CfLog(cferror,OUTPUT,"");
	break;
    }
 return;
}
#endif

/*****************************************************************************/

#ifdef HAVE_DCE
sec_acl_entry_type_t
BuildDceAclEntry_Type(char *t, char *n)
{
 if (strcmp(t, "user") == 0)
    {
    if (*n == 0)
       {
       return sec_acl_e_type_user_obj;
       }
    else
       {
       return sec_acl_e_type_user;
       }
    }
 else if (strcmp(t, "group") == 0)
    {
    if (*n == 0)
       {
       return sec_acl_e_type_group_obj;
       }
    else
       {
       return sec_acl_e_type_group;
       }
    }
 else if (strcmp(t, "other") == 0)
    {
    return sec_acl_e_type_other_obj;
    }
 else if (strcmp(t, "mask") == 0)
    {
    return sec_acl_e_type_mask_obj;
    }
 else if (strcmp(t, "any") == 0)
    {
    return sec_acl_e_type_any_other;
    }
 else if (strcmp(t, "unauthenticated") == 0)
    {
    return sec_acl_e_type_unauthenticated;
    }
 else if (strcmp(t, "foreign_other") == 0)
    {
    return sec_acl_e_type_foreign_other;
    }
 else if (strcmp(t, "foreign_user") == 0)
    {
    return sec_acl_e_type_foreign_user;
    }
 else if (strcmp(t, "foreign_group") == 0)
    {
    return sec_acl_e_type_foreign_group;
    }
 return 0;
}

/*****************************************************************************/

sec_acl_permset_t
BuildDceAclEntry_Perms(char *mode, sec_acl_permset_t oldmode)
{
 sec_acl_permset_t perm = 0;
 char *a;
 enum {add, del} o;
 perm = oldmode;
 
 if (strcmp(mode, "noaccess") == 0)
    {
    return 0; /* No access for this user/group */
    }
 
 o = add;
 for (a = mode; *a != '\0' ; *a++)
    {
    if (*a == '+' || *a == ',')
       {
       o = add;
       }
    else if (*a == '-')
       {
       o = del;
       }
    else if (*a == '=')
       {
       o = add;
       perm = 0;
       }
    else
       {
       switch (*a)
	  {
	  case 'r':
	      if (o == add)
		 {
		 perm |= sec_acl_perm_read;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_read;
		 }
	      break;
	  case 'w': 
	      if (o == add)
		 {
		 perm |= sec_acl_perm_write;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_write;
		 }
	      break;
	  case 'x': 
	      if (o == add)
		 {
		 perm |= sec_acl_perm_execute;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_execute;
		 }
	      break;
	  case 'c': 
	      if (o == add)
		 {
		 perm |= sec_acl_perm_control;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_control;
		 }
	      break;
	  case 'i': 
	      if (o == add)
		 {
		 perm |= sec_acl_perm_insert;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_insert;
		 }
	      break;
	  case 'd': 
	      if (o == add)
		 {
		 perm |= sec_acl_perm_delete;
		 }
	      else
		 {
		 perm &= ~sec_acl_perm_delete;
		 }
	      break;
	  default:  
	      sprintf(OUTPUT,"Invalid mode string in DCE/DFS acl: %s\n", mode);
	      CfLog(cferror,OUTPUT,"");
	  }
       }
    }
 return perm;
}

/*****************************************************************************/

uuid_t
BuildDceAclEntry_Id(sec_rgy_handle_t rgy_site, sec_rgy_name_t name, sec_acl_entry_type_t type)
{
 uuid_t id;
 error_status_t status;
 
 bzero(id, sizeof(id));
 switch (type)
    {
    case sec_acl_e_type_group:
	sec_id_parse_group(rgy_site, name, NULL, NULL, NULL, &id, &status);
	if (status != error_status_ok)
	   {
	   sprintf(OUTPUT,"sec_rgy_parse_group: %ld\n", status);
	   CfLog(cferror,OUTPUT,"");
	   }
	break;
    case sec_acl_e_type_user:
	sec_id_parse_name(rgy_site, name, NULL, NULL, NULL, &id, &status);
	if (status != error_status_ok)
	   {
	   sprintf(OUTPUT,"sec_rgy_parse_name: %ld\n", status);
	   CfLog(cferror,OUTPUT,"");
	   }
	break;
    case sec_acl_e_type_foreign_group:
	CfLog(cferror,"acl type c_acl_e_type_foreign_group not supported yet\n","");
	break;
    case sec_acl_e_type_foreign_user:
	CfLog(cferror,"acl type sec_acl_e_type_foreign_user not supported yet.\n","");
	break;
    case sec_acl_e_type_foreign_other:
	CfLog(cferror,"acl type sec_acl_e_type_foreign_other not supported yet.\n","");
	break;
    default:
	CfLog(cferror,"Unknown acl type in BuildDceAclEntry_Id!\n","");
	break;
    }
 return id;
}
#endif

/*****************************************************************************/

#if defined SOLARIS && defined HAVE_SYS_ACL_H

int
ParseSolarisMode(char* mode, mode_t oldmode)
{
 char *a;
 mode_t perm;
 enum {add, del} o;

 perm = oldmode;
 if (strcmp(mode, "noaccess") == 0)
    {
    return 0; /* No access for this user/group */
    }
 
 o = add;
 for (a = mode; *a != '\0' ; *a++)
    {
    if (*a == '+' || *a == ',')
       {
       o = add;
       }
    else if (*a == '-')
       {
       o = del;
       }
    else if (*a == '=')
       {
       o = add;
       perm = 0;
       }
    else
       {
       switch (*a)
	  {
	  case 'r':
	      if (o == add)
		 {
		 perm |= 04;
		 }
	      else
		 {
		 perm &= ~04;
		 }
	      break;
	  case 'w': 
	      if (o == add)
		 {
		 perm |= 02;
		 }
	      else
		 {
		 perm &= ~02;
		 }
	      break;
	  case 'x': 
	      if (o == add)
		 {
		 perm |= 01;
		 }
	      else
		 {
		 perm &= ~01;
		 }
	      break;
	  default:  
	      sprintf(OUTPUT,"Invalid mode string in solaris acl: %s\n", mode);
	      CfLog(cferror,OUTPUT,"");
	  }
       }
    }
 
 return perm;
}

int
BuildAclEntry(struct stat *sb, char *acltype, char *name, struct acl *newaclbufp)
{
 struct passwd *pw;
 struct group *gr;
 
 if (strcmp(acltype, "user") == 0)
    {
    if (*name == 0)
       {
       newaclbufp->a_type = USER_OBJ;
       newaclbufp->a_id = sb->st_uid;
       }
    else 
       {
       newaclbufp->a_type = USER;
       if ((pw = getpwnam(name)) != NULL)
	  {
	  newaclbufp->a_id = pw->pw_uid;
	  }
       else
	  {
	  sprintf(OUTPUT,"acl: no such user: %s\n",name);
	  CfLog(cferror,OUTPUT,"");
	  return -1;
	  }
       }
    }
 else if (strcmp(acltype, "group") == 0)
    {
    if (*name == 0)
       {
       newaclbufp->a_type = GROUP_OBJ;
       newaclbufp->a_id = sb->st_gid;
       }
    else
       {
       newaclbufp->a_type = GROUP;
       if ((gr = getgrnam(name)) != NULL)
	  {
	  newaclbufp->a_id = gr->gr_gid;
	  }
       else
	  {
	  sprintf(OUTPUT,"acl: no such group: %s\n",name);
	  CfLog(cferror,OUTPUT,"");
	  return -1;
	  }
       }
    }
 else if (strcmp(acltype, "mask") == 0)
    {
    newaclbufp->a_type = CLASS_OBJ;
    newaclbufp->a_id = 0;
    }
 else if (strcmp(acltype, "other") == 0)
    {
    newaclbufp->a_type = OTHER_OBJ;
    newaclbufp->a_id = 0;
    }
 else if (strcmp(acltype, "default_user") == 0)
    {
    if (*name == 0)
       {
       newaclbufp->a_type = DEF_USER_OBJ;
       newaclbufp->a_id = 0;
       }
    else
       {
       newaclbufp->a_type = DEF_USER;
       if ((pw = getpwnam(name)) != NULL)
	  {
	  newaclbufp->a_id = pw->pw_uid;
	  }
       else
	  {
	  sprintf(OUTPUT,"%s: acl: no such user: %s\n", VPREFIX, name);
	  CfLog(cferror,OUTPUT,"");
	  return -1;
	  }
       }
    }
 else if (strcmp(acltype, "default_group") == 0)
    {
    if (*name == 0)
       {
       newaclbufp->a_type = DEF_GROUP_OBJ;
       newaclbufp->a_id = 0;
       }
    else
       {
       newaclbufp->a_type = DEF_GROUP;
       if ((gr = getgrnam(name)) != NULL)
	  {
	  newaclbufp->a_id = gr->gr_gid;
	  }
       else
	  {
	  sprintf(OUTPUT,"acl: no such group: %s\n",name);
	  CfLog(cferror,OUTPUT,"");
	  return -1;
	  }
       }
    }
 else if (strcmp(acltype, "default_mask") == 0)
    {
    newaclbufp->a_type = DEF_CLASS_OBJ;
    newaclbufp->a_id = 0;
    }
 else if (strcmp(acltype, "default_other") == 0)
    {
    newaclbufp->a_type = DEF_OTHER_OBJ;
    newaclbufp->a_id = 0;
    }

 newaclbufp->a_perm = 0;

 return 0;
}
#endif

/*****************************************************************************/

InstallACL(alias,classes)

char *alias, *classes;

{ struct CFACL *ptr;
 
 Debug1("InstallACL(%s,%s)\n",alias,classes);
 
 if (! IsInstallable(classes))
    {
    Debug1("Not installing ACL no match\n");
    return;
    }
 
 VBUFF[0]='\0';                         /* Expand any variables */
 ExpandVarstring(alias,VBUFF,"");
 
 if ((ptr = (struct CFACL *)malloc(sizeof(struct CFACL))) == NULL)
    {
    FatalError("Memory Allocation failed for InstallACL() #1");
    }
 
 if ((ptr->acl_alias = strdup(VBUFF)) == NULL)
    {
    FatalError("Memory Allocation failed for InstallEditFile() #2");
    }
 
 if (VACLLISTTOP == NULL)                 /* First element in the list */
    {
    VACLLIST = ptr;
    }
 else
    {
    VACLLISTTOP->next = ptr;
    }
 
 ptr->aces = NULL;
 ptr->next = NULL;
 ptr->method = 'o';
 ptr->type = StringToFstype("none"); 
 VACLLISTTOP = ptr;
}

/*****************************************************************************/

AddACE(acl,string,classes)

char *acl, *string, *classes;

{ struct CFACL *ptr;
 struct CFACE *new, *top;
 char varbuff[bufsize], *cp;
 char comm[maxvarsize],data1[maxvarsize],data2[maxvarsize];
 int i;
 
 Debug1("AddACE(%s,%s,[%s])\n",acl,string,classes);
 
 if ( ! IsInstallable(CLASSBUFF))
    {
    Debug1("Not installing ACE no match\n");
    return;
    }
 
 for (ptr = VACLLIST; ptr != NULL; ptr=ptr->next)
    {
    varbuff[0] = '\0';
    ExpandVarstring(acl,varbuff,"");
    
    if (strcmp(ptr->acl_alias,varbuff) == 0)
       {
       comm[0] = data1[0] = data2[0] = '\0';
       
       if ((new = (struct CFACE *)calloc(1,sizeof(struct CFACE))) == NULL)
	  {
	  FatalError("Memory Allocation failed for AddEditAction() #1");
	  }
       
       if (ptr->aces == NULL)
	  {
	  ptr->aces = new;
	  }
       else
	  {
	  for (top = ptr->aces; top->next != NULL; top=top->next)
	     {
	     }
	  top->next = new;
	  new->next = NULL;
	  }
       
       if (string == NULL)
	  {
	  new->name = NULL;
	  new->next = NULL;
	  }
       else
	  {
	  bzero(VBUFF, sizeof(VBUFF));
	  VBUFF[0]='\0';                         /* Expand any variables */
	  ExpandVarstring(string,VBUFF,"");
	  
	  cp = VBUFF;
	  i = 0;
	  while (*cp != ':' && *cp != '\0') {
	  comm[i++] = *cp++;
	  }
	  comm[i] = 0;
	  *cp++;
	  
	  i = 0;
	  while (*cp != ':' && *cp != '\0') {
	  data1[i++] = *cp++;
	  }
	  data1[i] = 0;
	  *cp++;
	  
	  i = 0;
	  while (*cp != ':' && *cp != '\0') {
	  data2[i++] = *cp++;
	  }
	  data2[i] = 0;
	  
	  if (strncmp("fstype",comm,strlen(comm)) == 0)
	     {
	     ptr->type = StringToFstype(data1);
	     new->next = NULL;
	     return;
	     }
	  
	  if (strncmp("method",comm,strlen(comm)) == 0)
	     {
	     ptr->method = ToLower(*data1);
	     new->next = NULL;
	     return;
	     }
	  
	  if ((new->acltype = strdup(comm)) == NULL)
	     {
	     FatalError("Memory Allocation failed for AddACE() #1");
	     }
	  
	  if ((new->name = strdup(data1)) == NULL)
	     {
	     FatalError("Memory Allocation failed for AddACE() #2");
	     }
	  
	  if ((new->mode = strdup(data2)) == NULL)
	     {
	     FatalError("Memory Allocation failed for AddACE() #2");
	     }
	  }
       
       if ((new->classes = strdup(classes)) == NULL)
	  {
	  FatalError("Memory Allocation failed for InstallEditFile() #3");
	  }
       return;
       }
    }
 
 printf("cfengine: software error - no ACL matched installing %s \n",acl);
}

/*****************************************************************************/

CheckACLs(filename,action,acl_aliases)

char *filename;
enum fileactions action;
struct Item *acl_aliases;

{ struct Item *ip;
 struct CFACL *ap;
 int status = false;
 
 Debug1("CheckACLs(%s)\n",filename);
 
 for (ip = acl_aliases; ip != NULL; ip = ip->next)
    {
    if ((ap = GetACL(ip->name)) != NULL)
       {
       Verbose(" ACL method (overwrite/append) = %c on %s\n",ap->method,filename);
       
       switch(ap->type)
	  {
	  case solarisfs:
	  case posixfs:
	      status = CheckPosixACE(ap->aces,ap->method,filename,action);
	      break;
	  case dfsfs:
	      status = CheckDFSACE(ap->aces,ap->method,filename,action);
	      break;
	  default:
	      sprintf("Unknown filesystem type in ACL %s\n",ip->name);
	      CfLog(cferror,OUTPUT,"");
	  }
       }
    else
       {
       Verbose("ACL %s does not exist (file=%s)\n",ip->name,filename);
       }
    }
 return status;
}


/*****************************************************************************/
/* Level 2                                                                   */
/*****************************************************************************/

enum cffstype StringToFstype(string)

char *string;

{ int i;
 
 for (i = 0; CFFSTYPES[i] != NULL; i++)
    {
    if (strcmp(string,CFFSTYPES[i]) == 0)
       {
       return (enum cffstype) i;
       }
    }
 
 return (enum cffstype) i;
}

/*****************************************************************************/

struct CFACL *GetACL(acl_alias)

char *acl_alias;

{ struct CFACL *ap;
 
 for (ap = VACLLIST; ap != NULL; ap = ap->next)
    {
    if (strcmp(acl_alias,ap->acl_alias) == 0)
       {
       return ap;
       }
    }
 
 return NULL; 
}

/*****************************************************************************/

CheckPosixACE(aces,method,filename,action)
struct CFACE *aces;
char method;
char *filename;
enum fileactions action;
{
#if defined(HAVE_SYS_ACL_H) && defined(SOLARIS)
 struct CFACE *ep;
 aclent_t aclbufp[MAX_ACL_ENTRIES], newaclbufp[MAX_ACL_ENTRIES], tmpacl;
 int nacl = 0, newacls = 0, status = 0, update = 0, i, j;
 struct stat sb;
 uid_t myuid;
 
 bzero(aclbufp, sizeof(aclbufp));
 bzero(newaclbufp, sizeof(newaclbufp));
 nacl = acl (filename, GETACLCNT, 0, NULL);
 nacl = acl (filename, GETACL, nacl, aclbufp);
 
 if (stat(filename, &sb) != 0)
    {
    CfLog(cferror,"","stat");
    return -1;
    }

 Verbose(" Old acl has %d entries and is:\n", nacl);
 
 for (i = 0; i < nacl; i++)
    {
    Debug1(" a_type = %x, \ta_id = %d, \ta_perm = %o\n",
	   aclbufp[i].a_type, aclbufp[i].a_id, aclbufp[i].a_perm);
    }

 Debug1("method = %c\n", method);
 
 if (method == 'a')
    {
    newacls = nacl;
    for (i = 0; i < nacl; i++)
       {
       newaclbufp[i].a_id = aclbufp[i].a_id;
       newaclbufp[i].a_type = aclbufp[i].a_type;
       newaclbufp[i].a_perm = aclbufp[i].a_perm;
       }
    }   
 
 for (ep = aces; ep !=NULL; ep = ep->next)
    {
    if (ep->name == NULL)
       {
       continue;
       }
    if(IsExcluded(ep->classes))
       {
       continue;
       }
    
    Verbose("%s: Mode =%s, name=%s, type=%s\n",VPREFIX,ep->mode,ep->name,ep->acltype);
    if (strcmp(ep->name, "*") == 0)
       {
       bzero (ep->name, sizeof(ep->name));
       }
    
    if (BuildAclEntry(&sb,ep->acltype,ep->name,&tmpacl) == 0)
       {
       status = 0;
       for (i = 0; i < newacls; i++)
	  {
	  if (newaclbufp[i].a_id == tmpacl.a_id && newaclbufp[i].a_type == tmpacl.a_type)
	     {
	     status = 1;
	     if (strcmp(ep->mode, "default") == 0)
		{
		sprintf(OUTPUT,"Deleting ACL entry %d: type = %x,\tid = %d,\tperm = %o\n",
			i, newaclbufp[i].a_type, newaclbufp[i].a_id, newaclbufp[i].a_perm);
		CfLog(cfverbose,OUTPUT,"");
		newacls--;
		newaclbufp[i].a_id = newaclbufp[newacls].a_id;
		newaclbufp[i].a_type = newaclbufp[newacls].a_type;
		newaclbufp[i].a_perm = newaclbufp[newacls].a_perm;
		}
	     else
		{
		tmpacl.a_perm = ParseSolarisMode(ep->mode, newaclbufp[i].a_perm);
		if (tmpacl.a_perm != newaclbufp[i].a_perm)
		   {
		   newaclbufp[i].a_id = tmpacl.a_id;
		   newaclbufp[i].a_type = tmpacl.a_type;
		   newaclbufp[i].a_perm = tmpacl.a_perm;
		   
		   sprintf(OUTPUT,"Replaced ACL entry %d: type = %x,\tid = %d,\tperm = %o\n",
			   i, newaclbufp[i].a_type, newaclbufp[i].a_id, newaclbufp[i].a_perm);
		   CfLog(cfverbose,OUTPUT,"");
		   }
		}
	     }
	  }
       }
    if (status == 0 && (strcmp(ep->mode, "default") != 0))
       {
       newaclbufp[newacls].a_id = tmpacl.a_id;
       newaclbufp[newacls].a_type = tmpacl.a_type;
       newaclbufp[newacls].a_perm = ParseSolarisMode(ep->mode, 0);
       newacls++;
       
       sprintf(OUTPUT,"Added ACL entry %d: type = %x,\tid = %d,\tperm = %o\n",
	       i, newaclbufp[i].a_type, newaclbufp[i].a_id, newaclbufp[i].a_perm);
       CfLog(cfverbose,OUTPUT,"");
       }
    }
 
 if (newacls != nacl)
    {
    update = 1;
    }
 else
    {
    for (i = 0 ; i < nacl ; i++)
       {
       status = 0;
       for (j = 0 ; j < newacls ; j++)
	  {
	  if (aclbufp[i].a_id == newaclbufp[j].a_id && aclbufp[i].a_type == newaclbufp[j].a_type && aclbufp[i].a_perm == newaclbufp[j].a_perm)
	     {
	     status = 1;
	     }
	  }
       if (status == 0)
	   update = 1;
       }
    }
 
 if (update == 0)
    {
    Verbose("no update necessary\n");
    return false;
    }
 
 if (action == warnall || action == warnplain || action == warndirs)
    {
    sprintf(OUTPUT,"File %s needs ACL update\n",filename);
    CfLog(cfinform,OUTPUT,"");
    return false;
    }
 
 if ((status = aclcheck(newaclbufp, newacls, &i)) != 0)
    {
    printf("aclcheck failed\n");
    aclsortperror(status);
    return false;
    }
 
 if (aclsort(newacls, 0, (aclent_t *) newaclbufp) != 0)
    {
    printf("%s: aclsort failed\n", VPREFIX);
    return false;
    }
 
 Debug1("new acl has %d entries and is:\n", newacls);
 for (i = 0; i < newacls; i++)
    {
    Debug1 ("a_type = %x,\ta_id = %d,\ta_perm = %o\n",
	    newaclbufp[i].a_type, newaclbufp[i].a_id, newaclbufp[i].a_perm);
    }

 Debug1("setting acl of %s with %d acl-entries\n", filename, newacls);
 
 myuid = getuid();
 if (sb.st_uid != myuid)
    {
    if (myuid == 0)
       {
       Verbose("Changing effective uid to %ld\n", (long)sb.st_uid);
       if (seteuid(sb.st_uid) == -1)
          {
	  sprintf(OUTPUT,"Couldn't set effective uid from %ld to %ld\n",(long)myuid,(long)sb.st_uid);
	  CfLog(cferror,OUTPUT,"seteuid");
	  return false;
          }
       Debug1("effective uid now %ld\n", (long)sb.st_uid);
       }
    else
       {
       sprintf(OUTPUT,"Can't set effective uid from %ld to %ld, not super-user!\n",
	       (long)myuid, (long)sb.st_uid);
       CfLog(cferror,OUTPUT,"seteuid");
       return false;
       }
    
    Debug1("now the correct uid to manage acl of %s\n", filename, newacls);
    
    
    if (acl (filename, SETACL, newacls, newaclbufp) != newacls)
       {
       CfLog(cferror,"","acl");
       return false;
       }
    
    Debug1("setting acl of %s resulted in %d acl-entries\n",
           filename, newacls);
    
    if (seteuid(myuid) == -1)
       {
       sprintf(OUTPUT,"Unable to regain privileges of user %ld\n",
	       (long)myuid);
       CfLog(cferror,OUTPUT,"seteuid");
       FatalError("Aborting cfengine");
       }
    }
 else
    {
    if (acl(filename, SETACL, newacls, newaclbufp) != newacls)
       {
       CfLog(cferror,"","acl");
       return false;
       }
    Debug1("setting acl of %s resulted in %d acl-entries\n", filename, newacls);
    }
 
 sprintf(OUTPUT,"ACL for file %s updated\n",filename);
 CfLog(cfinform,OUTPUT,"");
 
 return true;
#else
 Verbose("Can't do ACLs on this system...\n");
#endif
}

/*****************************************************************************/


CheckDFSACE(aces,method,filename,action)
struct CFACE *aces;
char method;
char *filename;
enum fileactions action;
{
#ifdef HAVE_DCE

 struct CFACE *ep;
 sec_acl_handle_t h;
 sec_acl_list_t acl_list;
 error_status_t status;
 unsigned32 size_used, num_types;
 uuid_t manager_types[SIZE_AVAIL];
 sec_rgy_handle_t rgy_site;
 uuid_t cell_uuid;
 sec_rgy_properties_t properties;
 sec_acl_p_t new_sec_acls[1];
 int update = 0;
 int index = 0;
 int i, j;
 sec_acl_entry_type_t type;
 sec_acl_permset_t perms, ptmp;
 uuid_t id;
 
 sec_rgy_site_open ("/.:", &rgy_site, &status);
 
 if (status != error_status_ok)
    {
    sprintf(OUTPUT,"DCE: sec_rgy_site_open: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 sec_id_parse_name (rgy_site, "", NULL, &cell_uuid, NULL, NULL, &status);
 
 if (status != error_status_ok)
    {
    sprintf(OUTPUT,"DCE: sec_rgy_parse_name: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 sec_rgy_properties_get_info(rgy_site, &properties, &status);
 if (status != error_status_ok)
    {
    sprintf(OUTPUT,"DCE: sec_rgy_properties_get_info: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 sec_acl_bind (filename, FALSE, &h, &status);
 
 if (status != error_status_ok)
    {
    sprintf(OUTPUT,"DCE: sec_acl_bind: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 sec_acl_get_manager_types (h, sec_acl_type_object, SIZE_AVAIL,
                            &size_used, &num_types, manager_types, &status);
 
 if (status != error_status_ok)
    {
    sprintf(OUTPUT,"DCE: sec_acl_get_manager_types: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 if (num_types == 0)
    {
    sprintf(OUTPUT,"DCE: No ACL manager!\n");
    CfLog(cferror,OUTPUT,"");
    }
 
 if (size_used < num_types)
    {
    sprintf(OUTPUT,"Warning: some manager types missed\n");
    CfLog(cfsilent,OUTPUT,"");
    }
 
 sec_acl_lookup (h, manager_types, sec_acl_type_object, &acl_list, &status);
 
 if (status != error_status_ok)
    {
    sprintf (OUTPUT,"DCE: sec_acl_lookup: %ld\n", status);
    CfLog(cferror,OUTPUT,"");
    }
 
 Debug1("method = %c\n", method);
 
 /* new_sec_acls[0]= calloc(1, sizeof(sec_acl_p_t));*/
 new_sec_acls[0]= calloc(1, sizeof(sec_acl_t));

 new_sec_acls[0]->sec_acl_entries = calloc(MAXDFSACL, sizeof(sec_acl_entry_t));
 new_sec_acls[0]->default_realm = acl_list.sec_acls[0]->default_realm;
 new_sec_acls[0]->sec_acl_manager_type = acl_list.sec_acls[0]->sec_acl_manager_type;
 new_sec_acls[0]->num_entries = 0;
 
 if (method == 'a')
    {
    new_sec_acls[0]->num_entries = acl_list.sec_acls[0]->num_entries;
    for (i = 0; i < acl_list.sec_acls[0]->num_entries; i++)
       {
       new_sec_acls[0]->sec_acl_entries[i].entry_info.entry_type = acl_list.sec_acls[0]->sec_acl_entries[i].entry_info.entry_type;
       new_sec_acls[0]->sec_acl_entries[i].perms = acl_list.sec_acls[0]->sec_acl_entries[i].perms;
       new_sec_acls[0]->sec_acl_entries[i].entry_info.tagged_union = acl_list.sec_acls[0]->sec_acl_entries[i].entry_info.tagged_union;
       }
    }
 
 for (ep = aces; ep !=NULL; ep = ep->next)
    {
    if (ep->name == NULL)
       {
       continue;
       }
    
    if(IsExcluded(ep->classes))
       {
       continue;
       }
    
    Verbose("%s: Mode =%s, name=%s, type=%s\n",VPREFIX,ep->mode,ep->name,ep->acltype);
    if (strcmp(ep->name, "*") == 0)
       {
       bzero (ep->name, sizeof(ep->name));
       }
    
    type = BuildDceAclEntry_Type(ep->acltype,ep->name);
    
    if (strlen(ep->name) != 0)
       {
       id = BuildDceAclEntry_Id(rgy_site, ep->name, type);
       }
    
    status = 0;
    for (i = 0; i < new_sec_acls[0]->num_entries; i++)
       {
       if (new_sec_acls[0]->sec_acl_entries[i].entry_info.entry_type == type &&
	   ((strlen(ep->name) != 0 && bcmp(new_sec_acls[0]->sec_acl_entries[i].entry_info.tagged_union.id.uuid, id, sizeof(uuid_t)) == 0) || strlen(ep->name) == 0))
	  {
	  status = 1;
	  if (strcmp(ep->mode, "default") == 0)
	     {
	     new_sec_acls[0]->num_entries--;
	     new_sec_acls[0]->sec_acl_entries[i] = new_sec_acls[0]->sec_acl_entries[new_sec_acls[0]->num_entries];
	     }
	  else
	     {
	     new_sec_acls[0]->sec_acl_entries[i].perms = BuildDceAclEntry_Perms(ep->mode, new_sec_acls[0]->sec_acl_entries[i].perms);
	     }
	  }
       }
    if (status == 0 && strcmp(ep->mode, "default") != 0)
       {
       if (strlen(ep->name) != 0)
	  {
	  new_sec_acls[0]->sec_acl_entries[new_sec_acls[0]->num_entries].entry_info.tagged_union.id.uuid = id;
	  }
       new_sec_acls[0]->sec_acl_entries[new_sec_acls[0]->num_entries].entry_info.entry_type = type;
       new_sec_acls[0]->sec_acl_entries[new_sec_acls[0]->num_entries++].perms = BuildDceAclEntry_Perms(ep->mode, 0);
       }
    }
 
 status = 0;
 sec_rgy_site_close (rgy_site, &status);
 if (status != error_status_ok)
    {
    printf ("error: sec_rgy_site_close: %ld\n", status);
    }
 
 if (acl_list.sec_acls[0]->num_entries != new_sec_acls[0]->num_entries)
    {
    update = 1;
    }
 else
    {
    for (i = 0; i < acl_list.sec_acls[0]->num_entries; i++)
       {
       status = 0;
       for (j = 0; j < new_sec_acls[0]->num_entries; j++)
	  {
	  if (new_sec_acls[0]->sec_acl_entries[j].perms == acl_list.sec_acls[0]->sec_acl_entries[i].perms &&
	      new_sec_acls[0]->sec_acl_entries[j].entry_info.entry_type == acl_list.sec_acls[0]->sec_acl_entries[i].entry_info.entry_type &&
	      bcmp(new_sec_acls[0]->sec_acl_entries[j].entry_info.tagged_union.id.uuid,acl_list.sec_acls[0]->sec_acl_entries[i].entry_info.tagged_union.id.uuid, sizeof(uuid_t)) == 0)
	     {
	     status = 1;
	     }
	  }
       if (status == 0)
	  {
	  update = 1;
	  }
       }
    }
 
 status = 0;
 
 if (update == 0)
    {
    Verbose("%s: No update necessary\n",VPREFIX);
    return false;
    }
 else
    {
    if (action == warnall || action == warnplain || action == warndirs)
       {
       sprintf(OUTPUT,"File %s needs ACL update\n",filename);
       CfLog(cfinform,OUTPUT,"");
       return false;
       }
    acl_list.sec_acls[0] = new_sec_acls[0];
    sec_acl_replace (h, manager_types, sec_acl_type_object, &acl_list, &status);
    if (status != error_status_ok)
       {
       printf ("error: sec_acl_replace: %ld\n", status);
       return false;
       }
    else
       {
       sprintf(OUTPUT,"ACL for file %s updated\n",filename);
       CfLog(cfinform,OUTPUT,"");
       return true;
       }
    }
 return true;
#endif
}
