summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsec/common/acltext.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsec/common/acltext.c')
-rw-r--r--usr/src/lib/libsec/common/acltext.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/usr/src/lib/libsec/common/acltext.c b/usr/src/lib/libsec/common/acltext.c
new file mode 100644
index 0000000000..da3195379c
--- /dev/null
+++ b/usr/src/lib/libsec/common/acltext.c
@@ -0,0 +1,464 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+/*LINTLIBRARY*/
+
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/acl.h>
+
+/*
+ * acltotext() converts each ACL entry to look like this:
+ *
+ * entry_type:uid^gid^name:perms
+ *
+ * The maximum length of entry_type is 14 ("defaultgroup::" and
+ * "defaultother::") hence ENTRYTYPELEN is set to 14.
+ *
+ * The max length of a uid^gid^name entry (in theory) is 8, hence we use
+ * LOGNAME_MAX.
+ *
+ * The length of a perms entry is 4 to allow for the comma appended to each
+ * to each acl entry. Hence PERMS is set to 4.
+ */
+
+#define ENTRYTYPELEN 14
+#define PERMS 4
+#define ACL_ENTRY_SIZE (ENTRYTYPELEN + LOGNAME_MAX + PERMS)
+
+struct dynaclstr {
+ size_t bufsize; /* current size of aclexport */
+ char *aclexport;
+};
+
+static char *strappend(char *, char *);
+static char *convert_perm(char *, o_mode_t);
+static int increase_length(struct dynaclstr *, size_t);
+
+#define FREE free(aclp);\
+ free(allocp)
+
+/*
+ * Convert internal acl representation to external representation.
+ *
+ * The length of a non-owning user name or non-owning group name ie entries
+ * of type DEF_USER, USER, DEF_GROUP or GROUP, can exceed LOGNAME_MAX. We
+ * thus check the length of these entries, and if greater than LOGNAME_MAX,
+ * we realloc() via increase_length().
+ *
+ * The LOGNAME_MAX, ENTRYTYPELEN and PERMS limits are otherwise always
+ * adhered to.
+ */
+char *
+acltotext(aclent_t *aclp, int aclcnt)
+{
+ char *aclexport;
+ char *where;
+ struct group *groupp;
+ struct passwd *passwdp;
+ struct dynaclstr *dstr;
+ int i, rtn;
+ size_t excess = 0;
+
+ if (aclp == NULL)
+ return (NULL);
+ if ((dstr = malloc(sizeof (struct dynaclstr))) == NULL)
+ return (NULL);
+ dstr->bufsize = aclcnt * ACL_ENTRY_SIZE;
+ if ((dstr->aclexport = malloc(dstr->bufsize)) == NULL) {
+ free(dstr);
+ return (NULL);
+ }
+ *dstr->aclexport = '\0';
+ where = dstr->aclexport;
+
+ for (i = 0; i < aclcnt; i++, aclp++) {
+ switch (aclp->a_type) {
+ case DEF_USER_OBJ:
+ case USER_OBJ:
+ if (aclp->a_type == USER_OBJ)
+ where = strappend(where, "user::");
+ else
+ where = strappend(where, "defaultuser::");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ case DEF_USER:
+ case USER:
+ if (aclp->a_type == USER)
+ where = strappend(where, "user:");
+ else
+ where = strappend(where, "defaultuser:");
+ passwdp = getpwuid(aclp->a_id);
+ if (passwdp == (struct passwd *)NULL) {
+ /* put in uid instead */
+ (void) sprintf(where, "%d", aclp->a_id);
+ } else {
+ excess = strlen(passwdp->pw_name) - LOGNAME_MAX;
+ if (excess > 0) {
+ rtn = increase_length(dstr, excess);
+ if (rtn == 1) {
+ /* reset where */
+ where = dstr->aclexport +
+ strlen(dstr->aclexport);
+ } else {
+ free(dstr->aclexport);
+ free(dstr);
+ return (NULL);
+ }
+ }
+ where = strappend(where, passwdp->pw_name);
+ }
+ where = strappend(where, ":");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ case DEF_GROUP_OBJ:
+ case GROUP_OBJ:
+ if (aclp->a_type == GROUP_OBJ)
+ where = strappend(where, "group::");
+ else
+ where = strappend(where, "defaultgroup::");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ case DEF_GROUP:
+ case GROUP:
+ if (aclp->a_type == GROUP)
+ where = strappend(where, "group:");
+ else
+ where = strappend(where, "defaultgroup:");
+ groupp = getgrgid(aclp->a_id);
+ if (groupp == (struct group *)NULL) {
+ /* put in gid instead */
+ (void) sprintf(where, "%d", aclp->a_id);
+ } else {
+ excess = strlen(groupp->gr_name) - LOGNAME_MAX;
+ if (excess > 0) {
+ rtn = increase_length(dstr, excess);
+ if (rtn == 1) {
+ /* reset where */
+ where = dstr->aclexport +
+ strlen(dstr->aclexport);
+ } else {
+ free(dstr->aclexport);
+ free(dstr);
+ return (NULL);
+ }
+ }
+ where = strappend(where, groupp->gr_name);
+ }
+ where = strappend(where, ":");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ case DEF_CLASS_OBJ:
+ case CLASS_OBJ:
+ if (aclp->a_type == CLASS_OBJ)
+ where = strappend(where, "mask:");
+ else
+ where = strappend(where, "defaultmask:");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ case DEF_OTHER_OBJ:
+ case OTHER_OBJ:
+ if (aclp->a_type == OTHER_OBJ)
+ where = strappend(where, "other:");
+ else
+ where = strappend(where, "defaultother:");
+ where = convert_perm(where, aclp->a_perm);
+ break;
+ default:
+ free(dstr->aclexport);
+ free(dstr);
+ return (NULL);
+
+ }
+ if (i < aclcnt - 1)
+ where = strappend(where, ",");
+ }
+ aclexport = dstr->aclexport;
+ free(dstr);
+ return (aclexport);
+}
+
+/*
+ * Convert external acl representation to internal representation.
+ * The accepted syntax is: <acl_entry>[,<acl_entry>]*[,]
+ * The comma at the end is not prescribed by the man pages.
+ * But it is needed not to break the old programs.
+ */
+aclent_t *
+aclfromtext(char *aclstr, int *aclcnt)
+{
+ char *fieldp;
+ char *tp;
+ char *nextp;
+ char *allocp;
+ char *aclimport;
+ int entry_type;
+ int id;
+ int len;
+ o_mode_t perm;
+ aclent_t *tmpaclp;
+ aclent_t *aclp;
+ struct group *groupp;
+ struct passwd *passwdp;
+
+ *aclcnt = 0;
+ aclp = NULL;
+
+ if (! aclstr)
+ return (NULL);
+
+ len = strlen(aclstr);
+
+ if ((aclimport = allocp = strdup(aclstr)) == NULL) {
+ fprintf(stderr, "malloc() failed\n");
+ return (NULL);
+ }
+
+ if (aclimport[len - 1] == ',')
+ aclimport[len - 1] = '\0';
+
+ for (; aclimport; ) {
+ /* look for an ACL entry */
+ tp = strchr(aclimport, ',');
+ if (tp == NULL) {
+ nextp = NULL;
+ } else {
+ *tp = '\0';
+ nextp = tp + 1;
+ }
+
+ *aclcnt += 1;
+
+ /*
+ * get additional memory:
+ * can be more efficient by allocating a bigger block
+ * each time.
+ */
+ if (*aclcnt > 1)
+ tmpaclp = (aclent_t *)realloc(aclp,
+ sizeof (aclent_t) * (*aclcnt));
+ else
+ tmpaclp = (aclent_t *)malloc(sizeof (aclent_t));
+ if (tmpaclp == NULL) {
+ free(allocp);
+ if (aclp)
+ free(aclp);
+ return (NULL);
+ }
+ aclp = tmpaclp;
+ tmpaclp = aclp + (*aclcnt - 1);
+
+ /* look for entry type field */
+ tp = strchr(aclimport, ':');
+ if (tp == NULL) {
+ FREE;
+ return (NULL);
+ } else
+ *tp = '\0';
+ if (strcmp(aclimport, "user") == 0) {
+ if (*(tp+1) == ':')
+ entry_type = USER_OBJ;
+ else
+ entry_type = USER;
+ } else if (strcmp(aclimport, "group") == 0) {
+ if (*(tp+1) == ':')
+ entry_type = GROUP_OBJ;
+ else
+ entry_type = GROUP;
+ } else if (strcmp(aclimport, "other") == 0)
+ entry_type = OTHER_OBJ;
+ else if (strcmp(aclimport, "mask") == 0)
+ entry_type = CLASS_OBJ;
+ else if (strcmp(aclimport, "defaultuser") == 0) {
+ if (*(tp+1) == ':')
+ entry_type = DEF_USER_OBJ;
+ else
+ entry_type = DEF_USER;
+ } else if (strcmp(aclimport, "defaultgroup") == 0) {
+ if (*(tp+1) == ':')
+ entry_type = DEF_GROUP_OBJ;
+ else
+ entry_type = DEF_GROUP;
+ } else if (strcmp(aclimport, "defaultmask") == 0)
+ entry_type = DEF_CLASS_OBJ;
+ else if (strcmp(aclimport, "defaultother") == 0)
+ entry_type = DEF_OTHER_OBJ;
+ else {
+ FREE;
+ return (NULL);
+ }
+
+ /* look for user/group name */
+ if (entry_type != CLASS_OBJ && entry_type != OTHER_OBJ &&
+ entry_type != DEF_CLASS_OBJ &&
+ entry_type != DEF_OTHER_OBJ) {
+ fieldp = tp + 1;
+ tp = strchr(fieldp, ':');
+ if (tp == NULL) {
+ FREE;
+ return (NULL);
+ } else
+ *tp = '\0';
+ if (fieldp != tp) {
+ /*
+ * The second field could be empty. We only care
+ * when the field has user/group name.
+ */
+ if (entry_type == USER ||
+ entry_type == DEF_USER) {
+ /*
+ * The reentrant interface getpwnam_r()
+ * is uncommitted and subject to
+ * change. Use the friendlier interface
+ * getpwnam().
+ */
+ passwdp = getpwnam(fieldp);
+ if (passwdp == NULL) {
+ (void) fprintf(stderr,
+ "user %s not found\n", fieldp);
+ id = UID_NOBODY; /* nobody */
+ }
+ else
+ id = passwdp->pw_uid;
+ } else {
+ if (entry_type == GROUP ||
+ entry_type == DEF_GROUP) {
+ groupp = getgrnam(fieldp);
+ if (groupp == NULL) {
+ (void) fprintf(stderr,
+ "group %s not found\n",
+ fieldp);
+ /* no group? */
+ id = GID_NOBODY;
+ }
+ else
+ id = groupp->gr_gid;
+ } else {
+ (void) fprintf(stderr,
+ "acl import errors\n");
+ FREE;
+ return (NULL);
+ }
+ }
+ } else {
+ /*
+ * The second field is empty.
+ * Treat it as undefined (-1)
+ */
+ id = -1;
+ }
+ } else {
+ /*
+ * Let's not break the old applications
+ * that use mask::rwx, other::rwx format,
+ * though they violate the man pages.
+ */
+ if (*(tp + 1) == ':')
+ *++tp = 0;
+ }
+
+ /* next field: permission */
+ fieldp = tp + 1;
+ if (strlen(fieldp) != 3) {
+ /* not "rwx" format */
+ FREE;
+ return (NULL);
+ } else {
+ char s[] = "rwx";
+ int mask = 0x04;
+ int i;
+ perm = 0;
+
+ for (i = 0; i < 3; i++, mask /= 2) {
+ if (fieldp[i] == s[i])
+ perm |= mask;
+ else if (fieldp[i] != '-') {
+ FREE;
+ return (NULL);
+ }
+ }
+ }
+
+ tmpaclp->a_type = entry_type;
+ tmpaclp->a_id = id;
+ tmpaclp->a_perm = perm;
+ aclimport = nextp;
+ }
+ free(allocp);
+ return (aclp);
+}
+
+static char *
+strappend(char *where, char *newstr)
+{
+ (void) strcat(where, newstr);
+ return (where + strlen(newstr));
+}
+
+static char *
+convert_perm(char *where, o_mode_t perm)
+{
+ if (perm & 04)
+ where = strappend(where, "r");
+ else
+ where = strappend(where, "-");
+ if (perm & 02)
+ where = strappend(where, "w");
+ else
+ where = strappend(where, "-");
+ if (perm & 01)
+ where = strappend(where, "x");
+ else
+ where = strappend(where, "-");
+ /* perm is the last field */
+ return (where);
+}
+
+/*
+ * Callers should check the return code as this routine may change the string
+ * pointer in dynaclstr.
+ */
+static int
+increase_length(struct dynaclstr *dacl, size_t increase)
+{
+ char *tptr;
+ size_t newsize;
+
+ newsize = dacl->bufsize + increase;
+ tptr = realloc(dacl->aclexport, newsize);
+ if (tptr != NULL) {
+ dacl->aclexport = tptr;
+ dacl->bufsize = newsize;
+ return (1);
+ } else
+ return (0);
+}