[Libsysio-commit] HEAD: libsysio/src access.c inode.c mkdir.c
Brought to you by:
lward
From: Lee W. <lw...@us...> - 2006-06-01 21:29:02
|
Update of /cvsroot/libsysio/libsysio/src In directory sc8-pr-cvs6.sourceforge.net:/tmp/cvs-serv28393/src Modified Files: access.c inode.c mkdir.c Log Message: Pave the way for alternate credentials and ready work for strict permission checking. Index: access.c =================================================================== RCS file: /cvsroot/libsysio/libsysio/src/access.c,v retrieving revision 1.12 retrieving revision 1.13 diff -u -w -b -B -p -r1.12 -r1.13 --- access.c 3 May 2006 22:31:04 -0000 1.12 +++ access.c 1 Jun 2006 21:28:57 -0000 1.13 @@ -55,21 +55,25 @@ #include "inode.h" #include "sysio-symbols.h" -static gid_t *gids = NULL; /* space for gids */ -static unsigned ngids = 0; /* max # gids */ +/* + * Use a persistent buffer for gids. No, not a cache. We just want to + * avoid calling malloc over, and over, and... + */ +static gid_t *gids = NULL; +static int gidslen = 0; /* * Check given access type on given inode. */ -static int -_sysio_check_permission(struct pnode *pno, - uid_t uid, gid_t gid, - gid_t gids[], size_t ngids, - int amode) +int +_sysio_check_permission(struct pnode *pno, struct creds *crp, int amode) { mode_t mask; struct inode *ino; + int err; struct intnl_stat *stat; + gid_t *gids; + int ngids; /* * Check amode. @@ -88,48 +92,68 @@ _sysio_check_permission(struct pnode *pn if (amode & X_OK) mask |= S_IXUSR; - /* - * Check for RO access to the file due to mount - * options. - */ - if (amode & W_OK && IS_RDONLY(pno)) - return -EROFS; - ino = pno->p_base->pb_ino; assert(ino); + err = 0; /* assume success */ + + /* + * Owner? + */ stat = &ino->i_stbuf; - if (stat->st_uid == uid && (stat->st_mode & mask) == mask) - return 0; + if (stat->st_uid == crp->creds_uid && + (stat->st_mode & mask) == mask) + goto out; + /* + * Group? + */ mask >>= 3; - if (stat->st_gid == gid && (stat->st_mode & mask) == mask) - return 0; - + gids = crp->creds_gids; + ngids = crp->creds_ngids; while (ngids) { ngids--; - if (stat->st_gid == *gids++ && (stat->st_mode & mask) == mask) - return 0; + if (stat->st_gid == *gids++ && + (stat->st_mode & mask) == mask) + goto out; } + /* + * Other? + */ mask >>= 3; if ((stat->st_mode & mask) == mask) - return 0; + goto out; + + err = -EACCES; /* fail */ +out: + if (err) + return err; - return -EACCES; + /* + * Check for RO access to the file due to mount + * options. + */ + if (amode & W_OK && IS_RDONLY(pno)) + return -EROFS; + + return 0; } /* - * Determine if a given access is permitted to a give file. + * Cache groups. */ -int -_sysio_permitted(struct pnode *pno, int amode) +static int +_sysio_ldgroups(gid_t gid0, gid_t **gidsp, int *gidslenp) { - int err; - int n; + int n, i; void *p; - err = 0; + n = *gidslenp; + if (n < 8) { + *gidsp = NULL; + n = 8; + } for (;;) { /* * This is far more expensive than I would like. Each time @@ -138,38 +162,66 @@ _sysio_permitted(struct pnode *pno, int * the result, either. The caller could have altered something * asynchronously. Wish we had easy access to this info. */ - n = getgroups(0, NULL); - if (n < 0) { - err = -errno; - break; + if (n > *gidslenp) { + p = realloc(*gidsp, (size_t )n * sizeof(gid_t)); + if (!p) + return -errno; + *gidsp = p; + *gidslenp = n; + } + (*gidsp)[0] = gid0; + i = getgroups(n - 1, *gidsp + 1); + if (i < 0) { + if (errno != EINVAL) + return -errno; + if (INT_MAX / 2 < n) + return -EINVAL; + n *= 2; + continue; } - if ((unsigned )n > ngids) { - p = realloc(gids, n * sizeof(gid_t)); - if (!p && gids) { - err = -ENOMEM; break; } - ngids = n; - gids = p; - } - if (n) { - err = getgroups(n, gids); - if (err < 0) { - if (errno == EINVAL) - continue; - err = -errno; - break; + return i; } + +/* + * Get current credentials. + */ +static int +_sysio_ldcreds(uid_t uid, gid_t gid, struct creds *crp) +{ + int n; + + n = _sysio_ldgroups(gid, &gids, &gidslen); + if (n < 0) + return n; + crp->creds_uid = uid; + crp->creds_gids = gids; + crp->creds_ngids = n; + + return 0; } - err = - _sysio_check_permission(pno, - geteuid(), getegid(), - gids, (size_t )n, - amode); - break; + +static int +_sysio_getcreds(struct creds *crp) +{ + + return _sysio_ldcreds(getuid(), getgid(), crp); } - if (!gids) + +/* + * Determine if a given access is permitted to a given file. + */ +int +_sysio_permitted(struct pnode *pno, int amode) +{ + struct creds cr; + int err; + + err = _sysio_ldcreds(geteuid(), getegid(), &cr); + if (err < 0) return err; + err = _sysio_check_permission(pno, &cr, amode); return err; } @@ -184,7 +236,7 @@ _sysio_access_shutdown() if (gids) free(gids); gids = NULL; - ngids = 0; + gidslen = 0; } #endif @@ -194,6 +246,7 @@ SYSIO_INTERFACE_NAME(access)(const char struct intent intent; int err; struct pnode *pno; + struct creds cr; SYSIO_INTERFACE_DISPLAY_BLOCK; @@ -203,11 +256,12 @@ SYSIO_INTERFACE_NAME(access)(const char err = _sysio_namei(_sysio_cwd, path, 0, &intent, &pno); if (err) SYSIO_INTERFACE_RETURN(-1, err); + err = _sysio_ldcreds(geteuid(), getegid(), &cr); + if (err < 0) + goto out; err = - _sysio_check_permission(pno, - getuid(), getgid(), - NULL, 0, - amode); + _sysio_check_permission(pno, &cr, amode); +out: P_RELE(pno); SYSIO_INTERFACE_RETURN(err ? -1 : 0, err); } Index: inode.c =================================================================== RCS file: /cvsroot/libsysio/libsysio/src/inode.c,v retrieving revision 1.24 retrieving revision 1.25 diff -u -w -b -B -p -r1.24 -r1.25 --- inode.c 3 May 2006 22:34:46 -0000 1.24 +++ inode.c 1 Jun 2006 21:28:57 -0000 1.25 @@ -861,6 +861,35 @@ _sysio_pb_path(struct pnode_base *pb, co } /* + * Common set attributes routine. + */ +int +_sysio_setattr(struct pnode *pno, + struct inode *ino, + unsigned mask, + struct intnl_stat *stbuf) +{ + /* + * It is possible that pno is null (for ftruncate call). + */ + + if (pno) + assert(!ino || pno->p_base->pb_ino == ino); + if (!ino) + ino = pno->p_base->pb_ino; + assert(ino); + + if (pno && IS_RDONLY(pno)) + return -EROFS; + + /* + * Determining permission to change the attributes is + * difficult, at best. Just try it. + */ + return (*ino->i_ops.inop_setattr)(pno, ino, mask, stbuf); +} + +/* * Do nothing. */ void Index: mkdir.c =================================================================== RCS file: /cvsroot/libsysio/libsysio/src/mkdir.c,v retrieving revision 1.18 retrieving revision 1.19 diff -u -w -b -B -p -r1.18 -r1.19 --- mkdir.c 4 May 2006 02:30:27 -0000 1.18 +++ mkdir.c 1 Jun 2006 21:28:57 -0000 1.19 @@ -57,19 +57,20 @@ int _sysio_mkdir(struct pnode *pno, mode_t mode) { - struct inode *ino; + int err; + struct inode *parenti; if (pno->p_base->pb_ino) return -EEXIST; - ino = pno->p_parent->p_base->pb_ino; - assert(ino); - - if (IS_RDONLY(pno)) - return -EROFS; + err = _sysio_permitted(pno->p_parent, W_OK); + if (err) + return err; mode |= S_IFDIR; - return (*ino->i_ops.inop_mkdir)(pno, mode); + parenti = pno->p_parent->p_base->pb_ino; + assert(parenti); + return (*parenti->i_ops.inop_mkdir)(pno, mode); } int @@ -86,13 +87,8 @@ SYSIO_INTERFACE_NAME(mkdir)(const char * if (err) goto out; - err = _sysio_permitted(pno->p_parent, W_OK); - if (err) - goto error; - mode &= ~(_sysio_umask & 0777); /* apply umask */ - err = (*pno->p_parent->p_base->pb_ino->i_ops.inop_mkdir)(pno, mode); -error: + err = _sysio_mkdir(pno, mode); P_RELE(pno); out: SYSIO_INTERFACE_RETURN(err ? -1 : 0, err); |