Here's a patch to make keyutils support ACLs on keys.

[root@andromeda ~]# LD_LIBRARY_PATH=~/keyutils ~/keyutils/keyctl setacl $k 
all:all u.4:r pos:0x37f root:I net:c
[root@andromeda ~]# LD_LIBRARY_PATH=~/keyutils ~/keyutils/keyctl getacl $k
IDENTITY  ID         PERMS
========= ========== ===================
all       -          0fffffff cjSRIlswrv
uid                4 00000002 --------r-
possessor -          0000037f cj-RIlswrv
root      -          00000020 ----I-----
net       -          00000200 c---------


David
---
diff --git a/keyctl.c b/keyctl.c
index 61990b4..c8b6f6f 100644
--- a/keyctl.c
+++ b/keyctl.c
@@ -71,6 +71,9 @@ static nr void act_keyctl_dh_compute(int argc, char *argv[]);
 static nr void act_keyctl_dh_compute_kdf(int argc, char *argv[]);
 static nr void act_keyctl_dh_compute_kdf_oi(int argc, char *argv[]);
 static nr void act_keyctl_restrict_keyring(int argc, char *argv[]);
+static nr void act_keyctl_getacl(int argc, char *argv[]);
+static nr void act_keyctl_rgetacl(int argc, char *argv[]);
+static nr void act_keyctl_setacl(int argc, char *argv[]);
 
 const struct command commands[] = {
        { act_keyctl___version, "--version",    "" },
@@ -82,9 +85,10 @@ const struct command commands[] = {
        { act_keyctl_dh_compute, "dh_compute",  "<private> <prime> <base>" },
        { act_keyctl_dh_compute_kdf, "dh_compute_kdf", "<private> <prime> 
<base> <len> <hash_name>" },
        { act_keyctl_dh_compute_kdf_oi, "dh_compute_kdf_oi", "<private> <prime> 
<base> <len> <hash_name>" },
+       { act_keyctl_getacl,    "getacl",       "<key>" },
+       { act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
        { act_keyctl_instantiate, "instantiate","<key> <data> <keyring>" },
        { act_keyctl_invalidate,"invalidate",   "<key>" },
-       { act_keyctl_get_persistent, "get_persistent", "<keyring> [<uid>]" },
        { act_keyctl_link,      "link",         "<key> <keyring>" },
        { act_keyctl_list,      "list",         "<keyring>" },
        { act_keyctl_negate,    "negate",       "<key> <timeout> <keyring>" },
@@ -107,12 +111,14 @@ const struct command commands[] = {
        { act_keyctl_request2,  "request2",     "<type> <desc> <info> 
[<dest_keyring>]" },
        { act_keyctl_restrict_keyring, "restrict_keyring", "<keyring> [<type> 
[<restriction>]]" },
        { act_keyctl_revoke,    "revoke",       "<key>" },
+       { act_keyctl_rgetacl,   "rgetacl",      "<key>" },
        { act_keyctl_rlist,     "rlist",        "<keyring>" },
        { act_keyctl_search,    "search",       "<keyring> <type> <desc> 
[<dest_keyring>]" },
        { act_keyctl_security,  "security",     "<key>" },
        { act_keyctl_session,   "session",      "" },
        { NULL,                 "session",      "- [<prog> <arg1> <arg2> ...]" 
},
        { NULL,                 "session",      "<name> [<prog> <arg1> <arg2> 
...]" },
+       { act_keyctl_setacl,    "setacl",       "<key> [<ace> <ace> ...]" },
        { act_keyctl_setperm,   "setperm",      "<key> <mask>" },
        { act_keyctl_show,      "show",         "[-x] [<keyring>]" },
        { act_keyctl_timeout,   "timeout",      "<key> <timeout>" },
@@ -1841,6 +1847,226 @@ static void act_keyctl_restrict_keyring(int argc, char 
*argv[])
        exit(0);
 }
 
+static const char *special_ace_ids[] = {
+       [0]                     = "?0",
+       [KEY_ACE_EVERYONE]      = "all",
+       [KEY_ACE_GROUP]         = "group",
+       [KEY_ACE_OWNER]         = "owner",
+       [KEY_ACE_POSSESSOR]     = "possessor",
+       [KEY_ACE_ROOT]          = "root",
+       [KEY_ACE_SYS_ADMIN]     = "sys",
+       [KEY_ACE_NET_ADMIN]     = "net",
+};
+
+/*
+ * Display ACL.
+ */
+static void display_acl(const struct key_ace *acl, int nr_ace)
+{
+       int i;
+
+       printf("IDENTITY  ID         PERMS\n");
+       printf("========= ========== ===================\n");
+
+       for (i = 0; i < nr_ace; i++) {
+               const struct key_ace *ace = &acl[i];
+
+               switch (ace->mask & KEY_ACE__IDENTITY) {
+               case KEY_ACE_SPECIAL:
+                       if (ace->special_id <= KEY_ACE_NET_ADMIN)
+                               printf("%-9.9s -         ", 
special_ace_ids[ace->special_id]);
+                       else
+                               printf("?special  %10u", ace->special_id);
+                       break;
+               case KEY_ACE_UID:
+                       printf("uid       %10u", ace->uid);
+                       break;
+               case KEY_ACE_GID:
+                       printf("gid       %10u", ace->gid);
+                       break;
+               default:
+                       printf("?%x        %10u",
+                              (ace->mask & KEY_ACE__IDENTITY) >> 28,
+                              ace->special_id);
+                       break;
+               }
+
+               printf(" %08x ", ace->mask & KEY_ACE__PERMS);
+               printf("%c%c%c%c%c%c%c%c%c%c\n",
+                      ace->mask & KEY_ACE_CLEAR        ? 'c' : '-',
+                      ace->mask & KEY_ACE_JOIN         ? 'j' : '-',
+                      ace->mask & KEY_ACE_SET_SECURITY ? 'S' : '-',
+                      ace->mask & KEY_ACE_REVOKE       ? 'R' : '-',
+                      ace->mask & KEY_ACE_INVAL        ? 'I' : '-',
+                      ace->mask & KEY_ACE_LINK         ? 'l' : '-',
+                      ace->mask & KEY_ACE_SEARCH       ? 's' : '-',
+                      ace->mask & KEY_ACE_WRITE        ? 'w' : '-',
+                      ace->mask & KEY_ACE_READ         ? 'r' : '-',
+                      ace->mask & KEY_ACE_VIEW         ? 'v' : '-');
+       }
+}
+
+/*
+ * Get a key's ACL.
+ */
+static void act_keyctl_getacl(int argc, char *argv[])
+{
+       struct key_ace *acl;
+       key_serial_t key;
+       int ret, nr_ace;
+
+       if (argc != 2)
+               format();
+
+       key = get_key_id(argv[1]);
+
+       ret = keyctl_get_acl_alloc(key, &acl);
+       if (ret < 0)
+               error("keyctl_get_acl_alloc");
+       nr_ace = ret / sizeof(*acl);
+
+       display_acl(acl, nr_ace);
+       exit(0);
+}
+
+/*
+ * Get a key's ACL in raw hex form.
+ */
+static void act_keyctl_rgetacl(int argc, char *argv[])
+{
+       struct key_ace *acl;
+       key_serial_t key;
+       int ret, nr_ace, i;
+
+       if (argc != 2)
+               format();
+
+       key = get_key_id(argv[1]);
+
+       ret = keyctl_get_acl_alloc(key, &acl);
+       if (ret < 0)
+               error("keyctl_get_acl_alloc");
+       nr_ace = ret / sizeof(*acl);
+
+       for (i = 0; i < nr_ace; i++) {
+               if (i != 0)
+                       putchar(';');
+               printf("%x:%x", acl[i].mask, acl[i].special_id);
+       }
+
+       putchar('\n');
+       exit(0);
+}
+
+/*
+ * Set a key's ACL.
+ */
+static void act_keyctl_setacl(int argc, char *argv[])
+{
+       struct key_ace *acl = NULL, *ace;
+       key_serial_t key;
+       unsigned ltmp;
+       unsigned tmp, len;
+       char *id, *colon, *perm, *p;
+       int nr_ace, i;
+
+       if (argc < 2)
+               format();
+
+       key = get_key_id(argv[1]);
+       nr_ace = argc - 2;
+
+       if (nr_ace > 0) {
+               acl = alloca(sizeof(*acl) * nr_ace);
+               memset(acl, 0, sizeof(*acl) * nr_ace);
+
+               for (i = 0; i < nr_ace; i++) {
+                       ace = &acl[i];
+                       id = argv[i + 2];
+                       colon = strchr(id, ':');
+                       if (!colon || colon == id)
+                               goto invalid_ace;
+
+                       *colon = 0;
+                       if (strcmp(id, "all") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_EVERYONE;
+                       } else if (strcmp(id, "grp") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_GROUP;
+                       } else if (strcmp(id, "own") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_OWNER;
+                       } else if (strcmp(id, "pos") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_POSSESSOR;
+                       } else if (strcmp(id, "root") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_ROOT;
+                       } else if (strcmp(id, "sys") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_SYS_ADMIN;
+                       } else if (strcmp(id, "net") == 0) {
+                               ace->mask = KEY_ACE_SPECIAL;
+                               ace->special_id = KEY_ACE_NET_ADMIN;
+                       } else if (strncmp(id, "u.", 2) == 0) {
+                               ace->mask = KEY_ACE_UID;
+                               if (sscanf(id + 2, "%u%n", &tmp, &len) != 1)
+                                       goto invalid_ace_colon;
+                               if (id + 2 + len != colon)
+                                       goto invalid_ace_colon;
+                               ace->uid = tmp;
+                       } else if (strncmp(id, "g.", 2) == 0) {
+                               ace->mask = KEY_ACE_GID;
+                               if (sscanf(id + 2, "%u%n", &tmp, &len) != 1)
+                                       goto invalid_ace_colon;
+                               if (id + 2 + len != colon)
+                                       goto invalid_ace_colon;
+                               ace->gid = tmp;
+                       } else {
+                               goto invalid_ace_colon;
+                       }
+
+                       perm = colon + 1;
+                       if (isdigit(*perm)) {
+                               ltmp = strtoul(perm, &p, 0);
+                               if (ltmp & ~(unsigned long)KEY_ACE__PERMS)
+                                       goto invalid_ace_colon;
+                               ace->mask |= ltmp;
+                       } else if (strcmp(perm, "all") == 0) {
+                               ace->mask |= KEY_ACE__PERMS;
+                       } else {
+                               for (; *perm; perm++) {
+                                       switch (*perm) {
+                                       case 'v': ace->mask |= KEY_ACE_VIEW;    
break;
+                                       case 'r': ace->mask |= KEY_ACE_READ;    
break;
+                                       case 'w': ace->mask |= KEY_ACE_WRITE;   
break;
+                                       case 's': ace->mask |= KEY_ACE_SEARCH;  
break;
+                                       case 'l': ace->mask |= KEY_ACE_LINK;    
break;
+                                       case 'I': ace->mask |= KEY_ACE_INVAL;   
break;
+                                       case 'R': ace->mask |= KEY_ACE_REVOKE;  
break;
+                                       case 'S': ace->mask |= 
KEY_ACE_SET_SECURITY; break;
+                                       case 'j': ace->mask |= KEY_ACE_JOIN;    
break;
+                                       case 'c': ace->mask |= KEY_ACE_CLEAR;   
break;
+                                       default: goto invalid_ace_colon;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       if (keyctl_set_acl(key, acl, nr_ace * sizeof(*acl)) < 0)
+               error("keyctl_set_acl");
+
+       exit(0);
+
+invalid_ace_colon:
+       *colon = ':';
+invalid_ace:
+       fprintf(stderr, "Invalid ACE: '%s'\n", id);
+       exit(2);
+}
+
 /*****************************************************************************/
 /*
  * parse a key identifier
diff --git a/keyutils.c b/keyutils.c
index d2bb34c..a37acbc 100644
--- a/keyutils.c
+++ b/keyutils.c
@@ -264,6 +264,16 @@ long keyctl_restrict_keyring(key_serial_t keyring, const 
char *type,
        return keyctl(KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);
 }
 
+long keyctl_get_acl(key_serial_t id, struct key_ace *acl, size_t max_acl_size)
+{
+       return keyctl(KEYCTL_GET_ACL, id, acl, max_acl_size);
+}
+
+long keyctl_set_acl(key_serial_t id, const struct key_ace *acl, size_t 
acl_size)
+{
+       return keyctl(KEYCTL_SET_ACL, id, acl, acl_size);
+}
+
 /*****************************************************************************/
 /*
  * fetch key description into an allocated buffer
@@ -405,6 +415,41 @@ int keyctl_dh_compute_alloc(key_serial_t priv, 
key_serial_t prime,
        return ret;
 }
 
+/*****************************************************************************/
+/*
+ * Fetch key ACL into an allocated buffer
+ * - Returns size of ACL in bytes.
+ */
+int keyctl_get_acl_alloc(key_serial_t id, struct key_ace **_acl)
+{
+       struct key_ace *acl;
+       long acl_size, ret;
+
+       ret = keyctl_get_acl(id, NULL, 0);
+       if (ret < 0)
+               return -1;
+
+       for (;;) {
+               acl_size = ret;
+               acl = malloc(acl_size);
+               if (!acl)
+                       return -1;
+
+               ret = keyctl_get_acl(id, acl, acl_size);
+               if (ret < 0) {
+                       free(acl);
+                       return -1;
+               }
+
+               if (acl_size >= ret)
+                       break;
+               free(acl);
+       }
+
+       *_acl = acl;
+       return ret;
+}
+
 /*
  * Depth-first recursively apply a function over a keyring tree
  */
diff --git a/keyutils.h b/keyutils.h
index 89c5b08..de1b0b4 100644
--- a/keyutils.h
+++ b/keyutils.h
@@ -101,6 +101,8 @@ typedef uint32_t key_perm_t;
 #define KEYCTL_GET_PERSISTENT          22      /* get a user's persistent 
keyring */
 #define KEYCTL_DH_COMPUTE              23      /* Compute Diffie-Hellman 
values */
 #define KEYCTL_RESTRICT_KEYRING                29      /* Restrict keys 
allowed to link to a keyring */
+#define KEYCTL_GET_ACL                 30      /* Get a key's ACL */
+#define KEYCTL_SET_ACL                 31      /* Set a key's ACL */
 
 /* keyctl structures */
 struct keyctl_dh_params {
@@ -117,6 +119,42 @@ struct keyctl_kdf_params {
 };
 
 /*
+ * Key ACL definitions.
+ */
+struct key_ace {
+       unsigned int            mask;
+#define KEY_ACE_VIEW           0x00000001 /* Can describe the key */
+#define KEY_ACE_READ           0x00000002 /* Can read the key content */
+#define KEY_ACE_WRITE          0x00000004 /* Can update/modify the key content 
*/
+#define KEY_ACE_SEARCH         0x00000008 /* Can find the key by search */
+#define KEY_ACE_LINK           0x00000010 /* Can make a link to the key */
+#define KEY_ACE_INVAL          0x00000020 /* Can invalidate the key */
+#define KEY_ACE_REVOKE         0x00000040 /* Can revoke the key */
+#define KEY_ACE_SET_SECURITY   0x00000080 /* Can set owner, group, ACL */
+#define KEY_ACE_JOIN           0x00000100 /* Can join keyring */
+#define KEY_ACE_CLEAR          0x00000200 /* Can clear keyring */
+#define KEY_ACE__ORDINARY      0x0000001f /* Ordinary permissions */
+#define KEY_ACE__PERMS         0x0fffffff
+#define KEY_ACE_SPECIAL                0x10000000 /* A specific subject */
+#define KEY_ACE_UID            0x20000000 /* A nominated UID */
+#define KEY_ACE_GID            0x30000000 /* A nominated GID */
+#define KEY_ACE__IDENTITY      0xf0000000
+
+       union {
+               uid_t           uid;
+               gid_t           gid;
+               unsigned int    special_id;
+#define KEY_ACE_EVERYONE       1       /* Everyone, including owner and group 
*/
+#define KEY_ACE_GROUP          2       /* The key's group */
+#define KEY_ACE_OWNER          3       /* The owner of the key */
+#define KEY_ACE_POSSESSOR      4       /* Any process that possesses of the 
key */
+#define KEY_ACE_ROOT           5       /* The user namespace root user */
+#define KEY_ACE_SYS_ADMIN      6       /* Anyone with CAP_SYS_ADMIN */
+#define KEY_ACE_NET_ADMIN      7       /* Anyone with CAP_NET_ADMIN */
+       };
+};
+
+/*
  * syscall wrappers
  */
 extern key_serial_t add_key(const char *type,
@@ -177,6 +215,8 @@ extern long keyctl_dh_compute_kdf(key_serial_t private, 
key_serial_t prime,
                                  char *buffer, size_t buflen);
 extern long keyctl_restrict_keyring(key_serial_t keyring, const char *type,
                                    const char *restriction);
+extern long keyctl_get_acl(key_serial_t id, struct key_ace *acl, size_t 
max_acl_size);
+extern long keyctl_set_acl(key_serial_t id, const struct key_ace *acl, size_t 
max_acl_size);
 
 /*
  * utilities
@@ -186,6 +226,7 @@ extern int keyctl_read_alloc(key_serial_t id, void 
**_buffer);
 extern int keyctl_get_security_alloc(key_serial_t id, char **_buffer);
 extern int keyctl_dh_compute_alloc(key_serial_t priv, key_serial_t prime,
                                   key_serial_t base, void **_buffer);
+extern int keyctl_get_acl_alloc(key_serial_t id, struct key_ace **_acl);
 
 typedef int (*recursive_key_scanner_t)(key_serial_t parent, key_serial_t key,
                                       char *desc, int desc_len, void *data);
diff --git a/tests/keyctl/dh_compute/bad-args/runtest.sh 
b/tests/keyctl/dh_compute/bad-args/runtest.sh
index 7e8828b..f58a44c 100644
--- a/tests/keyctl/dh_compute/bad-args/runtest.sh
+++ b/tests/keyctl/dh_compute/bad-args/runtest.sh
@@ -72,7 +72,7 @@ expect_keyid logonid
 
 marker "CHECK WRONG KEY TYPE"
 dh_compute --fail $privateid $primeid $logonid
-expect_error ENOKEY
+expect_error EOPNOTSUPP
 dh_compute --fail $privateid $primeid @s
 expect_error EOPNOTSUPP
 
diff --git a/tests/keyctl/permitting/valid/runtest.sh 
b/tests/keyctl/permitting/valid/runtest.sh
index 70600e7..0b3d55f 100644
--- a/tests/keyctl/permitting/valid/runtest.sh
+++ b/tests/keyctl/permitting/valid/runtest.sh
@@ -72,12 +72,11 @@ set_key_perm $keyid 0x00201f00
 describe_key --fail $keyid
 expect_error EACCES
 
-# check that we can't use other perms instead of user perms to view the key
-# (our UID matches that of the key)
-marker "VIEW OTHER PERMISSIONS"
+# check that permissions for everyone allow the user to view the key,
+# even if our user perms don't.
+marker "VIEW EVERYONE PERMISSIONS"
 set_key_perm $keyid 0x0020001f
-describe_key --fail $keyid
-expect_error EACCES
+describe_key $keyid
 
 # check that taking away setattr permission renders the key immune to setperm
 marker "REMOVE SETATTR"

Reply via email to