21    Programming with ACLs

This chapter discusses the following topics:

21.1    Introduction to ACLs

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 Chapter 5 and Chapter 11 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.

21.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.

21.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.

21.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 */
};

21.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;
};

21.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.

21.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.  */
 
 

21.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

21.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.

21.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.

21.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 Section 5.6. Table 21-1 shows the structure of individual entries.

Table 21-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

21.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.

21.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.

21.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 Section 5.7 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.

21.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.

21.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.

21.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:

struct entries {
    acl_tag_t     tag_type;
    char         *qualifier;
    acl_permset_t perms;
} table[] = {
  { USER_OBJ,  NULL,    ACL_PRDWREX },
  { USER,      "june",  ACL_PRDEX },
  { USER,      "sally", ACL_PRDEX },
  { GROUP_OBJ, NULL,    ACL_PRDWREX },
  { GROUP,     "mktg",  ACL_PRDWREX },
  { OTHER_OBJ, NULL,    ACL_PRDEX }
};
 
#define TABLE_ENTRIES (sizeof(table)/sizeof(table[0]))
 
acl_t       acl_p;
acl_entry_t entry_p;
int         i;
uid_t       uid;
gid_t       gid;
 
/*  allocate an ACL */
acl_init(&acl_p);                          [1]
 
/* walk through the table and create entries */
for (i = 0; i < TABLE_ENTRIES; i++) {
 
     /* allocate the entry */
     acl_create_entry(acl_p, &entry_p);     [2]
 
     /* set the permissions */
     acl_set_perm(entry_p, table[i].perms);
 
     /* setting the tag type and qualifier depends
        on the type */
     switch (table[i].tag_type) {
 
     case USER:
 
        /* map user name to ID and specify as qualifier */
 
        uid = pw_nametoid(table[i].qualifier);  [3]
        acl_set_tag(entry_p, table[i].tag_type,
                    (void *) uid);              [4]
 
        break;
 
     case GROUP:
 
        /* map group name to ID and specify as qualifier */
 
           gid = gr_nametoid(table[i].qualifier);  [5]
           acl_set_tag(entry_p, table[i].tag_type,
                       (void *) gid);
           break;
 
 
     default:
 
           /* qualifier is NULL for other types */
 
           acl_set_tag(entry_p, table[i].tag_type, NULL);
           break;
     }
}
 
 
/* set the ACL on the file */
 
if (acl_set_file(filename, ACL_TYPE_ACCESS, acl_p) < 0)
    perror(filename);
 
/* free storage allocated for the ACL */
 
acl_free(acl_p);

Notes:

  1. This demonstrates the use of the initialization call for a working storage representation of the ACL. [Return to example]

  2. A new ACL entry is allocated with this call. The tag type, qualifier, and permissions have an unspecified type. [Return to example]

  3. The pw_nametoid() routine is an optimized mapping from user name to ID. It is described in the pw_mapping(3) reference page. [Return to example]

  4. The acl_set_tag() function takes a void argument for the qualifier, and casts it to the appropriate data type depending on obj_type. [Return to example]

  5. The gr_nametoid() routine is an optimized mapping from group name to ID. It is described in the pw_mapping(3) reference page. [Return to example]

21.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 Section 5.7 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::r-x

The following code updates the default access ACL to remove read and write permission from the mktg group entry:

acl_permset_t perms;
acl_id_type   qualifier;
acl_tag_t     tag_type;
acl_t         acl;
acl_entry_t   acl_entry;
char         *filename;
gid_t         mktg_gid;
 
/* map the "mktg" group name to an ID */
mktg_gid = gr_nametoid("mktg");
 
/* allocate an ACL entry */
acl_init(&acl);
 
/* read the default ACL from the file */
acl=acl_get_file(filename, ACL_TYPE_DEFAULT);
 
/* scan the ACL looking for the entry */
while (acl_get_entry(acl, &acl_entry) == 1) {
 
     /* retrieve the tag type and qualifier */
     acl_get_tag(acl_entry, &tag_type, &qualifier);
 
    /* check for appropriate entry and remove 'r' and 'w' */
    if (tag_type == GROUP && qualifier.acl_gid == mktg_gid) {
        acl_delete_perm(acl_entry, ACL_PRDWR);
 
        /* put the new default ACL on the file */
        if (acl_set_file(filename, ACL_TYPE_DEFAULT, acl) < 0) {
            fprintf(stderr, "acl_set_file on ");
            psecerror(filename);
        }
    }
}

The following code creates a file and a directory in the directory and demonstrates the ACL inheritance rules:

#define REGULAR_FILE        "regular"
#define DIRECTORY_FILE      "dir"
 
  char         pathname[100];
  int          fd;
 
  /* Create the regular file */
  sprintf(pathname, "%s/%s", filename, REGULAR_FILE);
 
  fd = creat(pathname, 0644);
 
  /* Create the directory */
  sprintf(pathname, "%s/%s", filename, DIRECTORY_FILE);
 
  mkdir(pathname, 0700);

When the preceding code 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 owning user, 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.

# 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 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.

# 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::r-x

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