7    Programming with ACLs

This chapter contains the following information:

7.1    ACL Overview

Tru64 UNIX access control lists (ACLs) are an optional extension to the discretionary access control (DAC) traditionaly provided on a UNIX system. Traditional UNIX DAC is the traditional UNIX permission bits; ACLs are an extension of the UNIX permission bits. A file or directory that has only the permission bits may be considered an object with an ACL containing only the three required or base entries that correspond to the usr, group, and other permission bits.

There are two types of ACLs:

The Tru64 UNIX ACL implementation is based on Draft 13 with some Draft 15 extensions of the POSIX P1003.6 standard.

ACLs can be applied to any file or directory on a file system that supports property lists. The file systems that support property lists are:

ACLs can be applied even if ACL processing is not enabled on the system; however, ACL access checks and default ACL inheritance do not take place.

See Security Administration for a more detailed description of using and administering ACLs. See the acl(4) reference page for more information on using and programming with ACLs. See the proplist(4) reference page for more information on property lists.

7.2    ACL Data Representations

An ACL has an internal and an external representation. The external representation consists of text and is used to enter and display ACLs. Library routines manipulate ACLs in working storage in an internal representation that is only indirectly accessible to the calling routine. This internal representation can be interpreted with the acl.h header file.

7.2.1    Internal Data Representation

The ACL routines manipulate the working storage representation, which is a set of opaque data structures for ACLs and ACL entries. Your program should operate on these data structures only through the defined routines. Because the working storage data structures are subject to change, the interface is the only reliable way to access the data.

The working storage representation is not contiguous in memory. Also, a program cannot determine the sizes of ACL entries and ACL descriptors. The working storage data structures contain internal pointer references and are therefore meaningless if passed between processes or stored in a file. A program can convert the working storage representation of an ACL to other representations of an ACL.

The two types most commonly used to access the opaque data are acl_t, a pointer to type acl (the ACL structure), and acl_entry_t, a pointer to an ACL entry structure.

Note

The structures in the following sections are opaque internal data structures that are subject to change. Always use the defined types and the supplied library routines to access these structures.

The internal representation uses the following basic types and data structures.

7.2.1.1    typedef struct acl *acl_t;

The acl_t type is used to specify an internal (working storage) format ACL.

struct acl {
  int          acl_magic;      /* validation member */
  int          acl_num;        /* number of actual acl entries */
  int          acl_alloc_size; /* size available in the acl */
  acl_entry_t  acl_current;    /* pointer to current entry in */
  acl_entry_t  acl_first;      /* pointer to ACL linked list */
  attribute_t  *attr_data;     /* Pointer to the attr data */
};

7.2.1.2    typedef struct acl_entry *acl_entry_t;

The acl_entry_t type is used to specify an entry within an ACL.

struct acl_entry{
   acle_t            *entry;
   void              *head;
   struct acl_entry  *next;
   struct acl_entry  *prev;
   int               acl_magic;
   int               size;
};

7.2.1.3    typedef uint_t acl_type_t;

The ACL types supported are as follows:

#define  ACL_TYPE_ACC  0
#define  ACL_TYPE_ACCESS  ACL_TYPE_ACC
 /* The ACL is an access ACL.  The property list
    entry name for an access ACL is "DEC_ACL_ACC" */
 
#define  ACL_TYPE_DEF  1
#define ACL_TYPE_DEFAULT  ACL_TYPE_DEF
 /* The ACL is a default access ACL. The property list
    entry name for a default access ACL is "DEC_ACL_ACC" */
#define  ACL_TYPE_DEF_DIR  2
#define ACL_TYPE_DEFAULT_DIR  ACL_TYPE_DEF_DIR
 /* The ACL is a default directory ACL. The property list
    entry name for a default directory ACL is "DEC_ACL_DEF_DIR" */
 
 

acl_type_t is used to specify the ACL type.

7.2.1.4    typedef uint acl_tag_t;

The acl_tag_t type is used to specify the tag ( the type) of an ACL entry. ACL entries with a tag type of ACL_USER or ACL_GROUP also have an associated tag qualifier. The tag qualifier is the ID of the user or group. The ACL entry tag types supported are:

#define  ACL_USER_OBJ  0
 /* entry that equates to the owning user permission bits. */
#define  ACL_GROUP_OBJ  1
 /* entry that equates to the owning group permission bits. */
#define  ACL_OTHER  2
#define  ACL_OTHER_OBJ  ACL_OTHER
 /* entry that equates to the other permission bits. */
#define  ACL_USER  23
 /* entry specifying permissions for a given user.  */
#define  ACL_GROUP  24
 /*  entry specifying permissions for a given group.  */
 
 

7.2.1.5    typedef uint_t acl_perm_t;

The acl_perm_t permission bit definitions are as follows:

#define  ACL_EXECUTE  0X001
#define  ACL_WRITE    0X002
#define  ACL_READ     0X004

7.2.1.6    typedef acl_perm_t *acl_permset_t;

The acl_permset_t type is used to point to the permissions assigned to an ACL entry.

7.2.1.7    Contiguous Internal Representation ACL

There is also a contiguous persistent data type for an ACL. This representation should be used only when the internal format ACL must persist between processes.

7.2.2    External Representation

The human-readable external representation of an ACL consists of a sequence of lines, each of which is terminated by a new-line character. The POSIX routines use the external representation when converting between the working storage representation and the text package.

The external representation is described in Security Administration. Table 7-1 shows the structure of individual entries.

Table 7-1:  ACL Entry External Representation

Entry Type acl_tag_t Value Entry
base user USER_OBJ user::perms
base group GROUP_OBJ group::perms
base other OTHER_OBJ other::perms
user USER user:user_name:perms
group GROUP group:group_name:perms

7.3    ACL Library Routines

The ACL routines are contained in the libpacl.a library. The ACL library routines are based on Draft 13 of the POSIX P1003.6 standard. See the reference page for each individual routine for detailed information.

The following routines are used to get, set, and validate ACLs:

acl_valid()

Checks the specified internal representation ACL for valid format.

acl_delete_def_fd()

Deletes the default access ACL from the designated directory using the file descriptor.

acl_delete_def_file()

Deletes the default access ACL from the designated directory.

acl_get_fd()

Retrieves the internal representation of the specified ACL type associated with the specific file or directory using the file descriptor.

acl_get_file()

Retrieves the internal representation of the specified ACL type associated with the specific file or directory.

acl_set_fd()

Sets the specified ACL type on the given file or directory to the specified ACL internal representation using the file descriptor.

acl_set_file()

Sets the specified ACL type on the given file or directory to the specified ACL internal representation.

The following routines retrieve and manipulate ACL entries:

acl_copy_entry()

Copies an ACL entry into the memory provided.

acl_create_entry()

Creates an empty ACL entry for the given ACL, allocating memory as necessary.

acl_delete_entry()

Deletes the designated ACL entry from an ACL.

acl_first_entry()

Resets the current ACL entry so that the next call to acl_get_entry() returns the first entry.

acl_get_entry()

Returns a pointer to the next ACL entry of the given ACL.

The following routines retrieve and manipulate fields in an ACL entry:

acl_add_perm()

Adds a permission to a set of permissions belonging to an ACL entry.

acl_clear_perm()

Clears a permission in a given ACL entry.

acl_delete_perm()

Removes permissions from a set of permissions belonging to an ACL entry.

acl_get_permset()

Copies the permissions from a given ACL entry to the location provided.

acl_get_qualifier()

Returns a pointer to the tag qualifier (ID) associated with a given ACL entry.

acl_get_tag_type()

Copies the tag (type) from the given ACL entry to the location provided.

acl_set_permset()

Sets the permissions in a given ACL entry to the given permissions.

acl_set_qualifier()

Sets the tag qualifier (ID) of the specified ACL entry to the given UID or GID.

acl_set_tag_type()

Sets the tag (type) of the specified ACL entry to the given type.

The following routines manage working storage for the ACL manipulation:

acl_free()

Releases all working storage associated with the given ACL.

acl_free_qualifier()

Releases working storage associated with the given tag qualifier.

acl_free_text()

Releases the buffer associated with the given external representation (text) ACL.

acl_init()

Allocates and initializes ACL internal representation working storage.

acl_copy_ext()

Copies the working storage internal format ACL data to the contiguous persistent ACL format.

acl_copy_int()

Copies contiguous persistent ACL data to working storage.

acl_dup()

Creates a copy of the designated ACL. The copy is independent of the original entry.

acl_size()

Calculates the size of the given ACL.

The following routines convert ACLs between external and internal representations:

acl_from_text()

Creates an internal representation ACL from the given external representation (text) ACL.

acl_to_text()

Creates an external representation (text) ACL from the given internal representation ACL.

7.4    ACL Rules

Some interactions between the ACL and the UNIX permissions are subtle. Unless you understand the interaction between ACL routines and the system calls that manipulate UNIX DAC attributes, you might get different permissions than you intended.

The following sections describe rules for programs that handle ACLs.

7.4.1    Object Creation

If ACLs are enabled and are supported on the file system, the open(), creat(), and mkdir() functions perform ACL inheritance when creating a file or directory. See for a description of ACL inheritance.

When ACL inheritance is performed, the permissions on a created file come from the mode you provide and the inherited ACL, not the umask. Therefore, your program must set the mode when creating files and directories. The program must not depend on umask to protect the files and directories.

When copying one file to another, it is a common practice for a program to create a new file and propagate the owner, group, and mode. If the source file has an ACL, your program should propagate that ACL to the target file in all cases where the mode is propagated.

7.4.2    ACL Replication

Programs that replicate permissions must preserve the ACL. The discretionary protection of a file or directory is no longer described by the owner, group, and permissions; it includes the ACL which is a superset of the permissions. Neglecting to copy the ACL could allow unintended access to the file or directory.

7.4.3    ACL Validity

Any ACL you create must be valid according to the following POSIX ACL rules:

You can use the acl_valid() routine to check your ACLs.

7.5    ACL Creation Example

Assume that you want to set a file's access ACL to the following permissions:

user::rwx
user:june:r-x
user:sally:r-x
group::rwx
group:mktg:rwx
other::r-x

The following code takes a tabular form of the ACL, creates a working storage representation of the ACL, and applies it to a file. If you extract the following code into a file named acl_example.c, use the following command to compile it:

# cc -o acl_example -lpacl -lsecurity acl_example.c

#include <unistd.h>
#include <sys/types.h>
#include <prot.h>
#include <errno.h>
#include <sys/acl.h>
 
struct entries {
    acl_tag_t       tag_type;
    char            *qualifier;
    acl_perm_t      perms;
} table[] = {
    /* An ACL must (at a minimum) have the three base */
    /* entries that correspond to the permission bits */
    { ACL_USER_OBJ,  NULL,    ACL_READ | ACL_WRITE | ACL_EXECUTE },
    { ACL_USER,      "june",  ACL_READ | ACL_EXECUTE },
    { ACL_USER,      "sally", ACL_READ | ACL_EXECUTE },
    { ACL_GROUP_OBJ, NULL,    ACL_READ | ACL_WRITE | ACL_EXECUTE },
    { ACL_GROUP,     "mktg",  ACL_READ | ACL_WRITE | ACL_EXECUTE },
    { ACL_OTHER_OBJ, NULL,    ACL_READ | ACL_EXECUTE },
};
 
#define TABLE_ENTRIES (sizeof(table)/sizeof(table[0]))
 
main ( argc, argv )
int argc;
char *argv[];
{
    acl_t acl_p;
    acl_entry_t entry_p;
    acl_entry_t check_p;
    int     i, ret;
    uid_t   uid;
    gid_t   gid;
 
    /* Did the user enter a filename? */
    if (argc != 2) {
         printf("Usage: %s filename\n",argv[0]);
         exit(1);
    }
 
    /* Check to see if ACLs are supported and enabled for filename */
    ret = pathconf(argv[1], _PC_ACL_EXTENDED);              /*[1]*/
    if (ret == 1) {
         printf("  ACLs are enabled for file %s\n",argv[1]);
    }
    else if (ret == 0) {
         printf("  ACLs are supported for file %s,\n",argv[1]);
         printf("  but ACLs are not currently enabled on \n");
         printf("  the system\n");
    }
    else if ((ret == -1) && (errno == EINVAL)) {
         printf("  ACLs not supported on filesystem\n");
         exit(1);
    }
    else {
         printf("  Error checking file ACL status for \n");
         printf("  file %s, exiting...\n",argv[1]);
         perror("pathconf");
         exit(1);
    }
 
 
    /* Allocate an ACL */
    acl_p = acl_init(1024);                    /* [2] */
 
    /* Walk through the table creating corresponding ACL entries */
    for(i=0;i<TABLE_ENTRIES;i++) {
 
    /* Initialize the entry */
    entry_p = acl_create_entry(&acl_p);            /* [3] */
 
     /* Set the permissions for the entry */
   acl_set_permset(entry_p,&table[i].perms);        /* [4] */
 
   /* Set the user or group information for the entry */
   switch(table[i].tag_type) {
 
      case ACL_USER:
 
          /* Get the uid from the user name */
          uid=pw_nametoid(table[i].qualifier);        /* [5] */
          if (uid == (uid_t) -1) {
              printf("  No translation for user name %s\n",
              table[i].qualifier);
              printf("  Exiting...\n");
              exit(1);
          }
 
          /* Specify this is a "USER:" entry */
          acl_set_tag_type(entry_p,table[i].tag_type);    /* [6] */
 
          /* Set the uid (entry qualifier) */
          acl_set_qualifier(entry_p,(void *)&uid);    /* [7] */
          break;
 
      case ACL_GROUP:
 
          /* Get the gid from the group name */
          gid=gr_nametoid(table[i].qualifier);        /* [8] */
          if (gid == (gid_t) -1) {
              printf("  No translation for group name %s\n",
              table[i].qualifier);
              printf("  Exiting...\n");
              exit(1);
          }
 
          /* Specify this is a "GROUP:" entry */
          acl_set_tag_type(entry_p,table[i].tag_type);
 
          /* Set the gid (entry qualifier) */
          acl_set_qualifier(entry_p,(void *) &gid);
          break;
 
      default:
 
          /* The three entries corresponding to the */
          /* Permission bits don't have qualifiers */
          acl_set_tag_type(entry_p,table[i].tag_type);
          acl_set_qualifier(entry_p,NULL);
          break;
      }
   }
 
   /* Is the created ACL valid? */
   if (acl_valid(acl_p, &check_p) < 0) {            /* [9] */
       printf("  Not Valid ACL\n");
       if (check_p) printf("  Duplicate entries\n");
       printf("  Exiting...\n");
       exit(1);
   }
 
   /* Set the ACL on the file */
   if (acl_set_file(argv[1],ACL_TYPE_ACCESS, acl_p) < 0)
        perror("acl_set_file");
 
    /* Free the storage allocated for the ACL */
    acl_free(acl_p);
}
 
 

  1. The _PC_ACL_ENABLED attribute of pathconf() returns the status of ACL processing for the given file. [Return to example]

  2. This demonstrates the use of the initialization call for a working storage representation of the ACL. If the storage allocated is not big enough for all of the entries in the completed ACL, the acl_create_entry() routine will allocate more memory. [Return to example]

  3. A new ACL entry is allocated with this call. The tag type, qualifier, and permissions in this new entry are unspecified. [Return to example]

  4. The acl_set_permset() routine sets the permissions for the ACL entry. [Return to example]

  5. The pw_nametoid() routine is an optimized mapping from user name to user ID and works with either Base or Enhanced security enabled. The pw_nametoid() routine is described in the pw_mapping(3) reference page. [Return to example]

  6. The acl_set_tag_type() function sets the type for the given ACL entry. The current tag types are: ACL_USER_OBJ (owner permission bits), ACL_GROUP_OBJ (group permission bits), ACL_OTHER_OBJ (other permission bits), ACL_USER (permissions for specified user), ACL_GROUP (permissions for specified group). [Return to example]

  7. The acl_set_qualifier() function sets the ID for the ACL_USER and ACL_GROUP tag types. This specifies which user or group the entry refers to. The other tag types do not require an ID. [Return to example]

  8. The gr_grouptoid() routine provides an optimized mapping from group name to group ID and works with either Base or Enhanced security enabled. It is described in the pw_mapping(3) reference page. [Return to example]

  9. The acl_valid() routine checks for missing and duplicate entries. [Return to example]

7.6    ACL Inheritance Example

This section shows how a program can specify a default access ACL on a directory and then describes what happens when a file and a directory are created in that directory. There is another type of default ACL called a default directory ACL. ACLs are inherited differently if a directory has a default directory ACL in addition to or in place of a default access ACL. See Security Administration for a complete description of the ACL inheritance rules.

Assume that directory /usr/john/acl_dir has the following access and default access ACLs:

% getacl /usr/john/acl_dir
 
# file: /usr/john/acl_dir
# owner: john
# group: prog
#
user::rwx
user:june:r-x
user:fred:r-x
group::rwx
group:mktg:rwx
other::r-x
 
% getacl -d /usr/john/acl_dir
 
# file: /usr/john/acl_dir
# owner: john
# group: prog
#
user::rwx
user:june:r-x
user:sally:r-x
group::rwx
group:mktg:rwx
other::rwx

The following program can be used to update the default access ACL on a directory to remove read and write permissions from a group entry and then create a regular file and a directory in the given directory. If you extract the following code into a file named acl_inheritance.c, it can be compiled with the following command:

% cc -o acl_inheritance -lpacl -lsecurity acl_inheritance.c

#include <unistd.h>
#include <sys/types.h>
#include <prot.h>
#include <errno.h>
#include <sys/acl.h>
 
#define REGULAR_FILE "regular"
#define DIRECTORY_FILE "dir"
 
main (argc, argv)
int argc;
char *argv[];
{
    acl_permset_t  acl_permset;
    gid_t          *qualifier = NULL;
    acl_tag_t      tag_type;
    acl_t          acl;
    acl_entry_t    acl_entry;
    gid_t          my_gid;
    int            ret;
    char           pathname[PATH_MAX + 1];
    int            fd;
 
    /* Did the user enter a directory name and group name? */
    if (argc != 3) {
       printf("Usage: %s directory group\n",argv[0]);
       exit(1);
    }
 
    /* Map the group name to a gid */
    my_gid = gr_nametoid(argv[2]);
    if (my_gid == (gid_t) -1) {
        printf("No translation for group %s\n",argv[2]);
        exit(1);
    }
 
    /* Read the default ACL from the directory */
    acl = acl_get_file(argv[1], ACL_TYPE_DEFAULT);
    if (!acl) {
      if (errno) {
         perror("acl_get_file");
      }
      else {
         printf("No default ACL found on %s\n", argv[1]);
      }
      exit(1);
    }
 
    ret = acl_first_entry(acl);
    if (ret) {
       perror("acl_first_entry");
       exit(1);
    }
 
    /* Scan the ACL looking for the entry */
    while (acl_entry = acl_get_entry(acl)) {
 
       /* retrieve the entry type */
       ret = acl_get_tag_type(acl_entry, &tag_type);
       if (ret) {
          perror("acl_get_tag_type");
          exit(1);
       }
 
       if (tag_type != ACL_GROUP) continue;
 
       qualifier = (gid_t *)acl_get_qualifier(acl_entry);
       if (!qualifier) {
          perror("acl_get_qualifier");
          exit(1);
       }
 
       /* Check for appropriate entry */
       if (*qualifier != my_gid) continue;
 
       ret = acl_get_permset(acl_entry, &acl_permset);
       if (ret) {
          perror("acl_get_permset");
          exit(1);
       }
 
       *acl_permset = *acl_permset & ~(ACL_READ | ACL_WRITE);
 
       ret = acl_set_permset(acl_entry, acl_permset);
       if (ret) {
          perror("acl_set_permset");
          exit(1);
       }
 
       ret = acl_set_file(argv[1], ACL_TYPE_DEFAULT, acl);
       if (ret) {
          perror("acl_set_file");
          exit(1);
       }
 
       break;
 
    }
 
    if (!acl_entry) {
       if (errno) {
          perror("acl_get_entry");
       }
       else {
          printf("ACL entry for %s not found\n", argv[2]);
       }
       exit(1);
    }
 
    /* Create the regular file */
    sprintf(pathname, "%s/%s", argv[1], REGULAR_FILE);
 
    fd = creat(pathname, 0644);
    if (fd == -1) {
       perror("creat");
       exit(1);
    }
    close(fd);
 
    /* Create the directory */
    sprintf(pathname, "%s/%s", argv[1], DIRECTORY_FILE);
 
    ret = mkdir(pathname, 0700); 
    if (ret == -1) {
       perror("mkdir");
       exit(1);
    }
}
 
 

When you run the previous example program, it removes the read and write permissions from the mktg group in the default ACL shown above. The program then creates a regular file and a directory in that directory to demonstrate ACL inheritance. Enter the following command to execute the example program:

% ./acl_inheritance /usr/john/acl_dir mktg

When the example program is executed, the access ACL on the newly created file and the access and default access ACLs on the newly created directory are as follows:

% getacl /usr/john/acl_dir/regular
# file: /usr/john/acl_dir/regular
# owner: john
# group: prog
#
user::rw-
user:june:r-x
user:sally:r-x
group::r--
group:mktg:--x
other::r--
 
 

Note that the permissions for the owning user, the owning group, and other are set to the logical AND of the default access ACL and the mode specified with the creat() call. The umask is not used when ACL inheritance takes place. The other entries are taken from the default access ACL of the parent directory, not the access ACL.

% getacl /usr/john/acl_dir/dir
# file: /usr/john/acl_dir/dir
# owner: john
# group: prog
#
user::rwx
user:june:r-x
user:sally:r-x
group::---
group:mktg:--x
other::---
 
 

Note that the access ACL inheritance rules for a subdirectory created in a directory that has a default access ACL are the same as those for a file. This is true only if there is not a default directory ACL on the parent directory in addition to the default access ACL.

The following command line displays the default access ACL:

% getacl -d /usr/john/acl_dir/dir
# file: /usr/john/acl_dir/dir
# owner: john
# group: prog
#
user::rwx
user:june:r-x
user:sally:r-x
group::rwx
group:mktg:--x
other::rwx
 
 

Note that the default ACL is inherited from the directory's parent.