[Libsysio-commit] HEAD: libsysio/src access.c
Brought to you by:
lward
From: Lee W. <lw...@us...> - 2009-05-05 16:30:46
|
Update of /cvsroot/libsysio/libsysio/src In directory ddv4jf1.ch3.sourceforge.com:/tmp/cvs-serv29952/src Modified Files: access.c Log Message: Modified to allow the package user to specify alternate routines for the acquisition of credentials and implementation of permission check policy. In order to arrange the acquisition of alternate credentials, the pointers to _sysio_get_credentials and _sysio_put_credentials should be overwritten. Your get_credentials routine should set the passed pointer to a credentials structure with the uid and gids set with the real values, if the formal called 'effective', is zero or the effective values if non-zero. Your put_credentials routine is called when the core no longer requires your credentials. If you do not need a release routine, you may set _sysio_put_credentials to NULL. In order to arrange the use of an alternate permission check the pointer to _sysio_check_permission should be overwritten. If any of these pointers are overwritten by the user, it must be done prior to calling _sysio_init and may not be reset thereafter. Index: access.c =================================================================== RCS file: /cvsroot/libsysio/libsysio/src/access.c,v retrieving revision 1.19 retrieving revision 1.20 diff -u -w -b -B -p -r1.19 -r1.20 --- access.c 17 Jun 2008 17:18:57 -0000 1.19 +++ access.c 5 May 2009 16:30:18 -0000 1.20 @@ -41,6 +41,7 @@ * le...@sa... */ +#include <stddef.h> /* ptrdiff_t */ #include <stdlib.h> #include <errno.h> #include <assert.h> @@ -56,18 +57,32 @@ #include "inode.h" #include "sysio-symbols.h" -/* - * 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; +static int get_credentials(struct creds **crp, int effective); +static void put_credentials(struct creds *cr); +static int check_permission(struct pnode *pno, struct creds *cr, int amode); + +/* + * If the application will be managing credentials externally, these + * are the interface. Before calling this librariy's initialization + * routines, reset them to your own. + */ +int (*_sysio_get_credentials)(struct creds **, int) = + get_credentials; +void (*_sysio_put_credentials)(struct creds *) = + put_credentials; +int (*_sysio_check_permission)(struct pnode *, struct creds *, int) = + check_permission; + +static struct user_credentials { + struct creds creds; + unsigned refs; +} *_current_ucreds = NULL; /* * Check given access type on given inode. */ -int -_sysio_check_permission(struct pnode *pno, struct creds *crp, int amode) +static int +check_permission(struct pnode *pno, struct creds *crp, int amode) { mode_t mask; struct inode *ino; @@ -160,27 +175,18 @@ _sysio_check_permission(struct pnode *pn } /* - * Cache groups. + * (Re)fill credentials */ static int -_sysio_ldgroups(gid_t gid0, gid_t **gidsp, int *gidslenp) +ldgroups(gid_t gid0, gid_t **gidsp, int *gidslenp) { int n, i; void *p; n = *gidslenp; - if (n < 8) { - *gidsp = NULL; + if (n < 8) n = 8; - } for (;;) { - /* - * This is far more expensive than I would like. Each time - * called it has to go to some length to acquire the - * current uid and groups membership. We can't just cache - * the result, either. The caller could have altered something - * asynchronously. Wish we had easy access to this info. - */ if (n > *gidslenp) { p = realloc(*gidsp, (size_t )n * sizeof(gid_t)); if (!p) @@ -211,45 +217,133 @@ _sysio_ldgroups(gid_t gid0, gid_t **gids * Get current credentials. */ static int -_sysio_ldcreds(uid_t uid, gid_t gid, struct creds *crp) +ldcreds(uid_t uid, gid_t gid, struct creds *cr) { int n; - n = _sysio_ldgroups(gid, &gids, &gidslen); + n = ldgroups(gid, &cr->creds_gids, &cr->creds_ngids); if (n < 0) return n; - crp->creds_uid = uid; - crp->creds_gids = gids; - crp->creds_ngids = n; + cr->creds_ngids = n; + cr->creds_uid = uid; return 0; } -#if 0 static int -_sysio_getcreds(struct creds *crp) +get_credentials(struct creds **crp, int effective) { + int err; + uid_t uid; + gid_t gid; - return _sysio_ldcreds(getuid(), getgid(), crp); + /* + * This is not a particularly efficient implementation. We + * don't expect it to ever be used in a thread-safe environment + * though, so expect no problems because of it. + */ + err = 0; + mutex_lock(&_sysio_biglock); + do { + if (!_current_ucreds || _current_ucreds->refs) { + _current_ucreds = + malloc(sizeof(struct user_credentials)); + if (!_current_ucreds) { + err = -ENOMEM; + break; + } + _current_ucreds->creds.creds_uid = -1; + _current_ucreds->creds.creds_gids = NULL; + _current_ucreds->creds.creds_ngids = 0; + _current_ucreds->refs = 0; + } + if (effective) { + uid = geteuid(); + gid = getegid(); + } else { + uid = getuid(); + gid = getgid(); + } + err = + ldcreds(uid, + gid, + &_current_ucreds->creds); + if (err) + break; + _current_ucreds->refs++; + } while (0); + mutex_unlock(&_sysio_biglock); + if (err) + return err; + *crp = &_current_ucreds->creds; + return 0; +} + +static void +release_ucreds(struct user_credentials *ucr) +{ + + free(ucr->creds.creds_gids); + free(ucr); +} + +static void +put_credentials(struct creds *cr) +{ + struct user_credentials *ucr; + + mutex_lock(&_sysio_biglock); + do { + ucr = CONTAINER(user_credentials, creds, cr); + assert(ucr->refs); + if (--ucr->refs || ucr == _current_ucreds) { + ucr = NULL; + break; + } + } while (0); + mutex_unlock(&_sysio_biglock); + if (ucr) + release_ucreds(ucr); } -#endif /* - * Determine if a given access is permitted to a given file. + * Common permission check. */ int -_sysio_permitted(struct pnode *pno, int amode) +_sysio_p_generic_perms_check(struct pnode *pno, int amode, int effective) { - struct creds cr; int err; + struct creds *cr; - err = _sysio_ldcreds(geteuid(), getegid(), &cr); - if (err < 0) - return err; - err = _sysio_check_permission(pno, &cr, amode); + do { + if ((err = (*_sysio_get_credentials)(&cr, effective))) { + cr = NULL; + break; + } + err = (*_sysio_check_permission)(pno, cr, amode); + } while (0); + if (cr) + (*_sysio_put_credentials)(cr); return err; } +static int +permitted(struct pnode *pno, int amode, int effective) +{ + + return PNOP_PERMS_CHECK(pno, amode, effective); +} + +/* + * Determine if a given access is permitted to a given file. + */ +int +_sysio_permitted(struct pnode *pno, int amode) +{ + + return permitted(pno, amode, 0); +} + #ifdef ZERO_SUM_MEMORY /* * Clean up persistent resource on shutdown. @@ -258,10 +352,10 @@ void _sysio_access_shutdown() { - if (gids) - free(gids); - gids = NULL; - gidslen = 0; + if (_current_ucreds) { + release_ucreds(_current_ucreds); + _current_ucreds = NULL; + } } #endif @@ -271,22 +365,21 @@ SYSIO_INTERFACE_NAME(access)(const char struct intent intent; int err; struct pnode *pno; - struct creds cr; SYSIO_INTERFACE_DISPLAY_BLOCK; SYSIO_INTERFACE_ENTER(access, "%s%d", path, amode); + do { INTENT_INIT(&intent, INT_GETATTR, NULL, NULL); err = _sysio_namei(_sysio_cwd, path, 0, &intent, &pno); - if (err) - SYSIO_INTERFACE_RETURN(-1, err, access, "%d", 0); - err = _sysio_ldcreds(geteuid(), getegid(), &cr); - if (err < 0) - goto out; - err = - _sysio_check_permission(pno, &cr, amode); -out: + if (err) { + pno = NULL; + break; + } + err = permitted(pno, amode, 1); + } while (0); + if (pno) P_PUT(pno); SYSIO_INTERFACE_RETURN(err ? -1 : 0, err, access, "%d", 0); } |