summaryrefslogtreecommitdiff
path: root/internal/fs/permissions/permission_linux.c
diff options
context:
space:
mode:
authorPaul Bütow <pbuetow@mimecast.com>2020-01-26 11:26:53 +0000
committerPaul Bütow <pbuetow@mimecast.com>2020-02-07 13:31:15 +0000
commit0945da8dfefcbb723eecea0e5f4eafff63398253 (patch)
treef06dab4d2bf21d25d176b23d5baeca588d27f5d7 /internal/fs/permissions/permission_linux.c
parent2a8e5de265a0e0a31a5834909d6879f5c9941467 (diff)
Introduce drun command, refactor code to use context package
Diffstat (limited to 'internal/fs/permissions/permission_linux.c')
-rw-r--r--internal/fs/permissions/permission_linux.c395
1 files changed, 0 insertions, 395 deletions
diff --git a/internal/fs/permissions/permission_linux.c b/internal/fs/permissions/permission_linux.c
deleted file mode 100644
index cd10525..0000000
--- a/internal/fs/permissions/permission_linux.c
+++ /dev/null
@@ -1,395 +0,0 @@
-#include "permission_linux.h"
-
-#ifdef DEBUG
-void debug_print_checker(struct permission_checker *pc) {
- fprintf(stderr, "DEBUG: user_name:%s (%d)\n",
- pc->user_name, pc->uid);
-
- fprintf(stderr, "DEBUG: ngids:%d\n", pc->ngids);
- int j;
- for (j = 0; j < pc->ngids; j++) {
- fprintf(stderr, "DEBUG: %d", pc->gids[j]);
- struct group *gr = getgrgid(pc->gids[j]);
- if (gr != NULL)
- fprintf(stderr, " (%s)", gr->gr_name);
- fprintf(stderr, "\n");
- }
-
- fprintf(stderr, "DEBUG: file_path:%s (%d:%d)\n",
- pc->file_path, pc->file_stat.st_uid, pc->file_stat.st_gid);
-}
-#endif // DEBUG
-
-int stat_file(struct permission_checker *pc) {
- if (stat(pc->file_path, &pc->file_stat) != 0)
- return -1;
-
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: File'%s' is owned by '%d:%d'\n",
- pc->file_path, pc->file_stat.st_uid, pc->file_stat.st_gid);
-#endif
-
- return 0;
-}
-
-int get_user_uid(struct permission_checker *pc) {
- struct passwd *result = NULL;
-
- size_t bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
- if (bufsize == -1)
- bufsize = 16384;
-
- char *buf = malloc(bufsize);
- if (buf == NULL) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unabel to allocate bufer while retrieving user '%s'\n", pc->user_name);
-#endif
- return -1;
- }
-
- int rc = getpwnam_r(pc->user_name, &pc->pw, buf, bufsize, &result);
-
- if (result == NULL) {
-#ifdef DEBUG
- if (rc == 0) {
- fprintf(stderr, "DEBUG: No user '%s' found\n", pc->user_name);
- } else {
- fprintf(stderr, "DEBUG: Unknown error while retrieving user '%s'\n", pc->user_name);
- }
-#endif
-
- free(buf);
- return -1;
- }
-
- pc->uid = pc->pw.pw_uid;
-
- free(buf);
- return 0;
-}
-
-int get_user_groups(struct permission_checker *pc) {
- // First assume we are in 10 groups max
- pc->ngids = 10;
- pc->gids = malloc(pc->ngids * sizeof(gid_t));
-
- if (pc->gids == NULL) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unable to allocate space for gids.");
-#endif
- return -1;
- }
-
- // Try so many times to load group list until it fits into group array.
- while (getgrouplist(pc->user_name, pc->pw.pw_gid, pc->gids, &pc->ngids) == -1) {
- // Too many groups, enlarge group array and try again
- int newngids = pc->ngids + 100;
- size_t newsize = newngids * sizeof(gid_t);
-
- if (SIZE_MAX / newngids < sizeof(gid_t)) {
- // Overflow
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Overflow detected.");
-#endif
- return -1;
- }
-
- gid_t *newgids = realloc(pc->gids, newsize);
- if (newgids == NULL) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unable to allocate space for gids.");
-#endif
- free(pc->gids);
- return -1;
- }
-
- pc->gids = newgids;
- pc->ngids = newngids;
- }
-
- return 0;
-}
-
-int is_member_of_group(struct permission_checker *pc, gid_t gid) {
- int j;
- for (j = 0; j < pc->ngids; j++)
- if (pc->gids[j] == gid)
- return 1;
- return 0;
-}
-
-int check_acl_uid_matches(uid_t uid, acl_entry_t entry) {
- int ret = -1;
- uid_t *acl_uid = acl_get_qualifier(entry);
- if (acl_uid == NULL) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unable to retrieve user uid from ACL entry");
-#endif
- return -1;
- }
-
- ret = *acl_uid == uid ? 0 : -1;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL user match?: %d <=> %d: %d\n", *acl_uid, uid, ret);
-#endif
- acl_free(acl_uid);
- return ret;
-}
-
-int check_acl_gid_matches(gid_t *gids, int ngids, acl_entry_t entry) {
- int ret = -1;
- gid_t *acl_gid = acl_get_qualifier(entry);
- if (acl_gid == NULL) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unable to retrieve user uid from ACL entry");
-#endif
- return -1;
- }
-
- int j;
- for (j = 0; j < ngids; j++) {
- if (*acl_gid == gids[j]) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: User is in group %d", *acl_gid);
-#endif
- ret = 0;
- break;
- }
- }
-
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL group match?: %d <=> ...: %d\n", *acl_gid, ret);
-#endif
- acl_free(acl_gid);
- return ret;
-}
-
-int check_acl(struct permission_checker *pc, const int flag) {
- // By default user has no read perm.
- int has_read_perm = 0;
-
- // By default mask tells that there are read perm. However in order to have
- // read permissions both, has_read_perm and mask_allows_read_access must be 1!
- int mask_allows_read_access = 1;
-
- acl_type_t type = ACL_TYPE_ACCESS;
- acl_t acl = acl_get_file(pc->file_path, type);
-
- if (acl == NULL)
- // Unable to retrieve ACL.
- return -1;
-
- // Walk through each entry of this ACL.
- int id;
- for (id = ACL_FIRST_ENTRY; ; id = ACL_NEXT_ENTRY) {
- acl_entry_t entry;
- if (acl_get_entry(acl, id, &entry) != 1)
- // No more ACL entries.
- break;
-
- acl_tag_t tag;
- if (acl_get_tag_type(entry, &tag) == -1)
- // Unable to retrieve ACL tag.
- return -1;
-
- switch (tag) {
- case ACL_USER_OBJ:
- if (flag == GROUP_CHECK)
- continue;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_USER_OBJ\n");
-#endif
- // Ignore this ACL entry if user is not owner of file.
- if (pc->uid != pc->file_stat.st_uid)
- continue;
- break;
- case ACL_USER:
- if (flag == GROUP_CHECK)
- continue;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_USER\n");
-#endif
- // Ignore this ACL entry if uid does not match.
- if (check_acl_uid_matches(pc->uid, entry) != 0)
- continue;
- break;
- case ACL_GROUP_OBJ:
- if (flag == USER_CHECK)
- continue;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_GROUP_OBJ\n");
-#endif
- // Ignore ACL entry if user is not in group of file.
- if (!is_member_of_group(pc, pc->file_stat.st_gid))
- continue;
- break;
- case ACL_GROUP:
- if (flag == USER_CHECK)
- continue;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_GROUP\n");
-#endif
- // Ignore ACL entry if user is not in group of entry.
- if (check_acl_gid_matches(pc->gids, pc->ngids, entry) != 0)
- continue;
- break;
- case ACL_OTHER:
- if (flag == GROUP_CHECK)
- continue;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_OTHER\n");
-#endif
- break;
- case ACL_MASK:
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL_MASK\n");
-#endif
- break;
- default:
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Unknown ACL tag\n");
-#endif
- return -1;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Retrieving permset\n");
-#endif
- acl_permset_t permset;
- int permission;
- if (acl_get_permset(entry, &permset) == -1)
- // Unable to retrieve permset.
- return -1;
-
- if ((permission = acl_get_perm(permset, ACL_READ)) == -1)
- // Unable to retrieve permset value.
- return -1;
-
- if (permission == 1 && tag != ACL_MASK) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL says user has permission to read file.\n");
-#endif
- has_read_perm = 1;
- } else if (permission == 0 && tag == ACL_MASK) {
- // Mask says that there are no permissions to read.
- mask_allows_read_access = 0;
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL mask says no permission to read file.\n");
-#endif
- }
- }
-
- if (has_read_perm && mask_allows_read_access) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL end result: User has permission to read file.\n");
-#endif
- return 1;
- }
-
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: ACL end result: User has no permission to read file.\n");
-#endif
- return 0;
-}
-
-int check_traditional(struct permission_checker *pc, const int flag) {
- mode_t mode = pc->file_stat.st_mode;
- uid_t uid = pc->file_stat.st_uid;
- gid_t gid = pc->file_stat.st_gid;
-
- if (flag == USER_CHECK && (mode & S_IROTH)) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: Others can read file '%s'\n",
- pc->file_path);
-#endif
- return 1;
-
- } else if (flag == USER_CHECK && (mode & S_IRUSR) && uid == pc->uid) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: User '%s' can read file '%s'\n",
- pc->user_name, pc->file_path);
-#endif
- return 1;
-
- } else if (flag == GROUP_CHECK && (mode & S_IRGRP) && is_member_of_group(pc, gid)) {
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: User's '%s' group can read file '%s'\n",
- pc->user_name, pc->file_path);
-#endif
- return 1;
- }
-
- return 0;
-}
-
-int permission_to_read(char* user_name, char *file_path) {
- int rc = -1;
-
-#ifdef DEBUG
- fprintf(stderr, "DEBUG: User check '%s' for file '%s'\n", user_name, file_path);
-#endif
- struct permission_checker pc = {
- .user_name = user_name,
- .gids = NULL,
- .ngids = 0,
- .file_path = file_path,
- };
-
- // Gather user's UID.
- if ((rc = get_user_uid(&pc)) == -1)
- // Could not retrieve UID.
- goto cleanup;
-
- // Gather file owner (user and group).
- if ((rc = stat_file(&pc)) == -1)
- // Could not stat file.
- goto cleanup;
-
- // Check whether there is an ACL entry which would allow the user
- // to read the file. Don't check for any groups yet. The issue with
- // groups is that it can be very slow to retrieve the list of groups
- // of a specific user when done via a remote LDAP server!
- if ((rc = check_acl(&pc, USER_CHECK)) == 1)
- // Yes, has permissions.
- goto cleanup;
-
- // Check whether ACLs of file could be retrieved.
- if (rc == -1) {
- if (errno != ENOTSUP)
- // Unknown error.
- goto cleanup;
-
- // File system does not support ACLs.
- // Fallback to traditional permissions.
- if ((rc = check_traditional(&pc, USER_CHECK)) == 1)
- // Yes, has traditional permissions.
- goto cleanup;
-
- if ((rc = get_user_groups(&pc)) == -1)
- // Can not retrieve user's groups.
- goto cleanup;
-
- rc = check_traditional(&pc, GROUP_CHECK);
- goto cleanup;
- }
-
- if ((rc = get_user_groups(&pc)) == -1)
- // Can not retrieve use'r groups.
- goto cleanup;
-
- // Check whether there is an ACL entry which would allow any of the
- // user's groups to read the file.
- rc = check_acl(&pc, GROUP_CHECK);
-
-cleanup:
-#ifdef DEBUG
- debug_print_checker(&pc);
-#endif
-
- if (pc.ngids)
- free(pc.gids);
-
- return rc;
-}
-
-// vim: set tabstop=8 softtabstop=0 expandtab shiftwidth=4 smarttab