From 3fb950c590d08026f3f938760aeb665a39c01b4c Mon Sep 17 00:00:00 2001 From: jpandre Date: Tue, 11 Mar 2008 15:36:40 +0000 Subject: [PATCH] Implemented dynamic groups and a generic user mapping --- include/ntfs-3g/security.h | 1 + libntfs-3g/security.c | 216 +++++++++++++++++++++++++++++++------ src/ntfs-3g.c | 1 + 3 files changed, 188 insertions(+), 30 deletions(-) diff --git a/include/ntfs-3g/security.h b/include/ntfs-3g/security.h index d40b5c09..267bb458 100644 --- a/include/ntfs-3g/security.h +++ b/include/ntfs-3g/security.h @@ -119,6 +119,7 @@ struct SECURITY_CONTEXT { struct PERMISSIONS_CACHE **pseccache; uid_t uid; /* uid of user requesting (not the mounter) */ gid_t gid; /* gid of user requesting (not the mounter) */ + pid_t tid; /* thread id of thread requesting */ } ; extern const GUID *const zero_guid; diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c index c8649882..ac27d3c6 100644 --- a/libntfs-3g/security.c +++ b/libntfs-3g/security.c @@ -34,6 +34,7 @@ #define LINESZ 120 /* maximum useful size of a mapping line */ #define CACHE_PERMISSIONS_BITS 6 /* log2 of unitary allocation of permissions */ #define CACHE_PERMISSIONS_SIZE 262144 /* max cacheable permissions */ +#define DYNGROUPS 1 /* use dynamic groups */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -194,6 +195,12 @@ struct MAPLIST { char maptext[LINESZ + 1]; }; +/* + * A type large enough to hold any SID + */ + +typedef char BIGSID[40]; + /* * A few useful constants */ @@ -735,17 +742,14 @@ static SID *encodesid(const char *sidstr) { SID *sid; int cnt; - union { - SID sid; - char bytes[8 * 4 + 8]; /* maximum size for 8 authorities */ - } bigsid; + BIGSID bigsid; SID *bsid; long auth; const char *p; sid = (SID*) NULL; if (!strncmp(sidstr, "S-1-", 4)) { - bsid = &bigsid.sid; + bsid = (SID*)&bigsid; bsid->revision = SID_REVISION; p = &sidstr[4]; auth = atoul(p); @@ -760,7 +764,7 @@ static SID *encodesid(const char *sidstr) cnt++; } bsid->sub_authority_count = cnt; - if (cnt > 0) { + if ((cnt > 0) && valid_sid(bsid)) { sid = (SID*) ntfs_malloc(4 * cnt + 8); if (sid) memcpy(sid, bsid, 4 * cnt + 8); @@ -1520,14 +1524,32 @@ static int upgrade_secur_desc(ntfs_volume *vol, const char *path, * Returns 0 (root) if not found */ -static int findowner(struct SECURITY_CONTEXT *scx, const SID * usid) +static int findowner(struct SECURITY_CONTEXT *scx, const SID *usid) { struct MAPPING *p; + BIGSID defsid; + SID *psid; + uid_t uid; + int cnt; p = scx->usermapping; - while (p && !same_sid(usid, p->sid)) + while (p && p->xid && !same_sid(usid, p->sid)) p = p->next; - return (p ? p->xid : 0); + if (p && !p->xid) { + /* + * No explicit mapping found, + * check whether default mapping applies + */ + memcpy(&defsid,p->sid,sid_size(p->sid)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + psid->sub_authority[cnt-1] = usid->sub_authority[cnt-1]; + if (same_sid(psid,usid)) + uid = (usid->sub_authority[cnt-1] + - p->sid->sub_authority[cnt-1]) >> 1; + else uid = 0; + } else uid = (p ? p->xid : 0); + return (uid); } /* @@ -1535,16 +1557,34 @@ static int findowner(struct SECURITY_CONTEXT *scx, const SID * usid) * Returns 0 (root) if not found */ -static int findgroup(struct SECURITY_CONTEXT *scx, const SID * gsid) +static gid_t findgroup(struct SECURITY_CONTEXT *scx, const SID * gsid) { struct MAPPING *p; int gsidsz; + BIGSID defsid; + SID *psid; + gid_t gid; + int cnt; gsidsz = sid_size(gsid); p = scx->groupmapping; - while (p && !same_sid(gsid, p->sid)) + while (p && p->xid && !same_sid(gsid, p->sid)) p = p->next; - return (p ? p->xid : 0); + if (p && !p->xid) { + /* + * No explicit mapping found, + * check whether default mapping applies + */ + memcpy(&defsid,p->sid,sid_size(p->sid)); + psid = (SID*)&defsid; + cnt = psid->sub_authority_count; + psid->sub_authority[cnt-1] = gsid->sub_authority[cnt-1]; + if (same_sid(psid,gsid)) + gid = (gsid->sub_authority[cnt-1] + - p->sid->sub_authority[cnt-1]) >> 1; + else gid = 0; + } else gid = (p ? p->xid : 0); + return (gid); } /* @@ -1552,18 +1592,30 @@ static int findgroup(struct SECURITY_CONTEXT *scx, const SID * gsid) * Returns NULL if not found */ -static const SID *find_usid(struct SECURITY_CONTEXT *scx, uid_t uid) +static const SID *find_usid(struct SECURITY_CONTEXT *scx, + uid_t uid, SID *defusid) { struct MAPPING *p; const SID *sid; + int cnt; if (!uid) sid = adminsid; else { p = scx->usermapping; - while (p && ((uid_t)p->xid != uid)) + while (p && p->xid && ((uid_t)p->xid != uid)) p = p->next; - sid = (p ? p->sid : (const SID*)NULL); + if (p && !p->xid) { + /* + * default has been reached : + * build a specific SID according to pattern + */ + memcpy(defusid, p->sid, sid_size(p->sid)); + cnt = defusid->sub_authority_count; + defusid->sub_authority[cnt-1] += 2*uid; + sid = defusid; + } else + sid = (p ? p->sid : (const SID*)NULL); } return (sid); } @@ -1573,24 +1625,112 @@ static const SID *find_usid(struct SECURITY_CONTEXT *scx, uid_t uid) * Returns 0 (root) if not found */ -static const SID *find_gsid(struct SECURITY_CONTEXT *scx, gid_t gid) +static const SID *find_gsid(struct SECURITY_CONTEXT *scx, + gid_t gid, SID *defgsid) { struct MAPPING *p; const SID *sid; + int cnt; if (!gid) sid = adminsid; else { p = scx->groupmapping; - while (p && ((gid_t)p->xid != gid)) + while (p && p->xid && ((gid_t)p->xid != gid)) p = p->next; - sid = (p ? p->sid : (const SID*)NULL); + if (p && !p->xid) { + /* + * default has been reached : + * build a specific SID according to pattern + */ + memcpy(defgsid, p->sid, sid_size(p->sid)); + cnt = defgsid->sub_authority_count; + defgsid->sub_authority[cnt-1] += 2*gid + 1; + sid = defgsid; + } else + sid = (p ? p->sid : (const SID*)NULL); } return (sid); } +#if DYNGROUPS + /* - * Check whether current user is member of some group + * Check whether current thread owner is member of file group + * + * The group list is available in + * + * /proc/$PID/task/$TID/status + * + * and fuse supplies TID in get_fuse_context()->pid. The only problem is + * finding out PID, for which I have no good solution, except to iterate + * through all processes. This is rather slow, but may be speeded up + * with caching and heuristics (for single threaded programs PID = TID). + * + */ + +static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) +{ + char filename[64]; + int fd; + BOOL found; + BOOL done; + BOOL ismember; + int wanted; + int pos; + int got; + char *start; + char *p; + gid_t grp; + pid_t tid; + char buf[BUFSZ+1]; + + ismember = FALSE; /* default return */ + tid = scx->tid; + sprintf(filename,"/proc/%u/task/%u/status",tid,tid); + fd = open(filename,O_RDONLY); + if (fd >= 0) { + /* we expect a line such as "Groups: 601 603 605 607" */ + wanted = BUFSZ; + pos = 0; + found = FALSE; + done = FALSE; + do { + got = read(fd, &buf[pos], wanted); + buf[pos + got] = 0; + start = strstr(buf,"\nGroups:"); + found = start && (strchr(++start,'\n')); + if (!found && (got == wanted)) { + wanted = pos = BUFSZ/2; + memcpy(buf, &buf[BUFSZ/2], BUFSZ/2); + } else + done = TRUE; + } while (!done); + close(fd); + /* Groups record found, collect the gids */ + if (found) { + p = &start[7]; + done = FALSE; + do { + grp = 0; + while ((*p == ' ') || (*p == '\t')) p++; + if ((*p >= '0') && (*p <= '9')) { + while ((*p >= '0') && (*p <= '9')) + grp = grp*10 + (*p++) - '0'; + ismember = (grp == gid); + } else + done = TRUE; + } while (!done && !ismember); + } + } else + ntfs_log_error("Could not open %s\n",filename); + return (ismember); +} + +#else + +/* + * Check whether current user is member of file group * Note : this only takes into account the groups defined in * /etc/group at initialization time. * It does not take into account the groups dynamically set by @@ -1622,6 +1762,8 @@ static BOOL groupmember(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid) return (ingroup); } +#endif + /* * Cacheing is done two-way : * - from uid, gid and perm to securid (CACHED_SECURID) @@ -3131,6 +3273,8 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, int newattrsz; const SID *usid; const SID *gsid; + BIGSID defusid; + BIGSID defgsid; le32 securid; #endif @@ -3154,8 +3298,8 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, /* not in cache : make sure we can create ids */ if (!cached && (scx->vol->major_ver >= 3)) { - usid = find_usid(scx,uid); - gsid = find_gsid(scx,gid); + usid = find_usid(scx,uid,(SID*)&defusid); + gsid = find_gsid(scx,gid,(SID*)&defgsid); if (!usid || !gsid) { ntfs_log_error("File created by an unmapped user/group %d/%d\n", (int)uid, (int)gid); @@ -3203,6 +3347,8 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, char *newattr; const SID *usid; const SID *gsid; + BIGSID defusid; + BIGSID defgsid; BOOL isdir; res = 0; @@ -3232,8 +3378,8 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, * but recompute them to get repeatable results * which can be kept in cache. */ - usid = find_usid(scx,uid); - gsid = find_gsid(scx,gid); + usid = find_usid(scx,uid,(SID*)&defusid); + gsid = find_gsid(scx,gid,(SID*)&defgsid); if (!usid || !gsid) { ntfs_log_error("File made owned by an unmapped user/group %d/%d\n", uid, gid); @@ -3693,6 +3839,8 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, const ACL *ppacl; const SID *usid; const SID *gsid; + BIGSID defusid; + BIGSID defgsid; int offpacl; int offowner; int offgroup; @@ -3709,8 +3857,8 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, parentattrsz = attr_size(parentattr); if (scx->usermapping) { - usid = find_usid(scx, scx->uid); - gsid = find_gsid(scx, scx->gid); + usid = find_usid(scx, scx->uid, (SID*)&defusid); + gsid = find_gsid(scx, scx->gid, (SID*)&defgsid); } else usid = gsid = (const SID*)NULL; /* @@ -4063,7 +4211,12 @@ static struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem) if (pwd) uid = pwd->pw_uid; } } - if (uid) { + /* + * Records with no uid and no gid are inserted + * to build a default mapping + */ + if (uid + || (!item->uidstr[0] && !item->gidstr[0])) { sid = encodesid(item->sidstr); if (sid) { mapping = @@ -4104,7 +4257,7 @@ static struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) struct MAPPING *lastmapping; struct MAPPING *mapping; struct group *grp; - BOOL uidpresent; + BOOL secondstep; BOOL ok; int step; SID *sid; @@ -4114,8 +4267,9 @@ static struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) lastmapping = (struct MAPPING*)NULL; for (step=1; step<=2; step++) { for (item = firstitem; item; item = item->next) { - uidpresent = (item->uidstr[0] != '\0'); - ok = (step == 1 ? !uidpresent : uidpresent); + secondstep = (item->uidstr[0] != '\0') + || !item->gidstr[0]; + ok = (step == 1 ? !secondstep : secondstep); if ((item->gidstr[0] >= '0') && (item->gidstr[0] <= '9')) gid = atoi(item->gidstr); @@ -4126,7 +4280,9 @@ static struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem) if (grp) gid = grp->gr_gid; } } - if (gid && ok) { + if (ok + && (gid + || (!item->uidstr[0] && !item->gidstr[0]))) { sid = encodesid(item->sidstr); if (sid) { mapping = (struct MAPPING*) diff --git a/src/ntfs-3g.c b/src/ntfs-3g.c index af6c0d62..25ea3885 100644 --- a/src/ntfs-3g.c +++ b/src/ntfs-3g.c @@ -253,6 +253,7 @@ static BOOL ntfs_fuse_fill_security_context(struct SECURITY_CONTEXT *scx) fusecontext = fuse_get_context(); scx->uid = fusecontext->uid; scx->gid = fusecontext->gid; + scx->tid = fusecontext->pid; return (ctx->security.usermapping != (struct MAPPING*)NULL); }