OPENAFS-SA-2024-001: afs: Introduce afs_genpag()

CVE-2024-10394

Currently, several areas in the code call genpag() to generate a new
PAG id, but the signature of genpag() is very limited. To allow for
the code in genpag() to return errors and to examine the calling
user's credentials, introduce a new function, afs_genpag(), that does
the same thing as genpag(), but accepts creds and allows errors to be
returned.

Convert all existing callers to use afs_genpag() and to handle any
errors, though no errors are ever returned in this commit on its own.

To ensure there are no old callers of genpag() left around, change the
existing genpag() to be called genpagval(), and declare it static.

FIXES 135062

Change-Id: I5c96c0134db901f21ca30c8b3f57aeec1eb67aa5
Reviewed-on: https://gerrit.openafs.org/14090
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>
Tested-by: Benjamin Kaduk <kaduk@mit.edu>
This commit is contained in:
Andrew Deason 2020-01-10 12:01:50 -06:00 committed by Benjamin Kaduk
parent f4dfc2d718
commit f701f704c7
14 changed files with 128 additions and 32 deletions

View File

@ -87,6 +87,14 @@ setpag(cred, pagvalue, newpag, change_parent)
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return (setuerror(code), code);
}
}
#ifndef AFS_AIX51_ENV
ngroups = afs_getgroups(*cred, NGROUPS, gidset);
if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
@ -100,7 +108,7 @@ setpag(cred, pagvalue, newpag, change_parent)
ngroups += 2;
}
#endif
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
#ifdef AFS_AIX51_ENV
if (change_parent) {
code = kcred_setpag(*cred, PAG_AFS, *newpag);

View File

@ -98,6 +98,14 @@ setpag(proc, cred, pagvalue, newpag, change_parent)
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
ngroups = afs_getgroups(*cred, NGROUPS, gidset);
if (afs_get_pag_from_groups(gidset[1], gidset[2]) == NOPAG) {
/* We will have to shift grouplist to make room for pag */
@ -109,7 +117,7 @@ setpag(proc, cred, pagvalue, newpag, change_parent)
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[1], &gidset[2]);
code = afs_setgroups(proc, cred, ngroups, gidset, change_parent);
return code;

View File

@ -80,6 +80,14 @@ setpag(struct thread *td, struct ucred **cred, afs_uint32 pagvalue,
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
gidset = osi_Alloc(gidset_len * sizeof(gid_t));
ngroups = afs_getgroups(*cred, gidset_len, gidset);
if (afs_get_pag_from_groups(gidset[1], gidset[2]) == NOPAG) {
@ -92,7 +100,7 @@ setpag(struct thread *td, struct ucred **cred, afs_uint32 pagvalue,
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[1], &gidset[2]);
code = afs_setgroups(td, cred, ngroups, gidset, change_parent);
osi_Free(gidset, gidset_len * sizeof(gid_t));

View File

@ -71,6 +71,14 @@ setpag(cred, pagvalue, newpag, change_parent)
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return (setuerror(code), code);
}
}
ngroups = afs_getgroups(*cred, NGROUPS, gidset);
if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
/* We will have to shift grouplist to make room for pag */
@ -82,7 +90,7 @@ setpag(cred, pagvalue, newpag, change_parent)
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {

View File

@ -194,6 +194,17 @@ setpag(cred, pagvalue, newpag, change_parent)
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
#if defined(KERNEL_HAVE_UERROR)
return (setuerror(code), code);
#else
return code;
#endif
}
}
ngroups = afs_getgroups(*cred, NGROUPS, gidset);
if (afs_get_pag_from_groups(gidset[0], gidset[1]) == NOPAG) {
/* We will have to shift grouplist to make room for pag */
@ -209,7 +220,7 @@ setpag(cred, pagvalue, newpag, change_parent)
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
if (code = afs_setgroups(cred, ngroups, gidset, change_parent)) {
#if defined(KERNEL_HAVE_UERROR)

View File

@ -150,11 +150,19 @@ __setpag(cred_t **cr, afs_uint32 pagvalue, afs_uint32 *newpag,
{
struct group_info *group_info;
struct group_info *tmp;
int code;
if (pagvalue == -1) {
code = afs_genpag(*cr, &pagvalue);
if (code != 0) {
return code;
}
}
get_group_info(afs_cr_group_info(*cr));
group_info = afs_cr_group_info(*cr);
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_linux_pag_to_groups(*newpag, group_info, &tmp);
if (old_groups) {

View File

@ -84,6 +84,13 @@ setpag(afs_proc_t *proc, afs_ucred_t **cred, afs_uint32 pagvalue,
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
ngroups = osi_getgroups(*cred, NGROUPS, gidset);
if (afs_get_pag_from_groups(gidset[1], gidset[2]) == NOPAG) {
/* We will have to shift grouplist to make room for pag */
@ -95,7 +102,7 @@ setpag(afs_proc_t *proc, afs_ucred_t **cred, afs_uint32 pagvalue,
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[1], &gidset[2]);
code = osi_setgroups(proc, cred, ngroups, gidset, change_parent);
return code;

View File

@ -81,6 +81,14 @@ setpag(struct proc *proc, struct ucred **cred, afs_uint32 pagvalue,
int j;
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
ngroups = afs_getgroups(*cred, NGROUPS, gidset);
/*
* If the group list is empty, use the task's primary group as the group
@ -102,7 +110,7 @@ setpag(struct proc *proc, struct ucred **cred, afs_uint32 pagvalue,
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[1], &gidset[2]);
code = afs_setgroups(proc, cred, ngroups, gidset, change_parent);
return code;

View File

@ -168,6 +168,13 @@ setpag(cred, pagvalue, newpag, change_parent)
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
/* Derive gidset size from running kernel's ngroups_max;
* default 16, but configurable up to 32 (Sol10) or
* 1024 (Sol11).
@ -177,8 +184,6 @@ setpag(cred, pagvalue, newpag, change_parent)
/* must use osi_Alloc, osi_AllocSmallSpace may not be enough. */
gidset = osi_Alloc(gidset_sz);
pagvalue = (pagvalue == -1 ? genpag() : pagvalue);
mutex_enter(&curproc->p_crlock);
ngroups = afs_getgroups(*cred, gidset);

View File

@ -74,6 +74,13 @@ usr_setpag(struct usr_ucred **cred, afs_uint32 pagvalue, afs_uint32 * newpag,
AFS_STATCNT(setpag);
if (pagvalue == -1) {
code = afs_genpag(*cred, &pagvalue);
if (code != 0) {
return code;
}
}
gidset = osi_AllocSmallSpace(AFS_SMALLOCSIZ);
ngroups = afs_getgroups(*cred, gidset);
@ -88,7 +95,7 @@ usr_setpag(struct usr_ucred **cred, afs_uint32 pagvalue, afs_uint32 * newpag,
}
ngroups += 2;
}
*newpag = (pagvalue == -1 ? genpag() : pagvalue);
*newpag = pagvalue;
afs_get_groups_from_pag(*newpag, &gidset[0], &gidset[1]);
if ((code = afs_setgroups(cred, ngroups, gidset, change_parent))) {
osi_FreeSmallSpace((char *)gidset);

View File

@ -9,7 +9,7 @@
/*
* Implements:
* genpag
* genpagval
* getpag
* afs_setpag
* AddPag
@ -67,8 +67,8 @@ afs_uint32 pagCounter = 0;
* secure (although of course not absolutely secure).
*/
#if !defined(UKERNEL)
afs_uint32
genpag(void)
static afs_uint32
genpagval(void)
{
AFS_STATCNT(genpag);
#ifdef AFS_LINUX_ENV
@ -96,8 +96,8 @@ getpag(void)
/* Web enhancement: we don't need to restrict pags to 41XXXXXX since
* we are not sharing the space with anyone. So we use the full 32 bits. */
afs_uint32
genpag(void)
static afs_uint32
genpagval(void)
{
AFS_STATCNT(genpag);
#ifdef AFS_LINUX_ENV
@ -182,6 +182,13 @@ afs_pag_wait(afs_ucred_t *acred)
return code;
}
afs_int32
afs_genpag(afs_ucred_t *acred, afs_uint32 *apag)
{
*apag = genpagval();
return 0;
}
int
#if defined(AFS_SUN5_ENV)
afs_setpag(afs_ucred_t **credpp)
@ -205,6 +212,7 @@ afs_setpag(void)
#endif
int code = 0;
afs_uint32 pag;
#if defined(AFS_SGI_ENV) && defined(MP)
/* This is our first chance to get the global lock. */
@ -218,15 +226,19 @@ afs_setpag(void)
goto done;
}
code = afs_genpag(acred, &pag);
if (code) {
goto done;
}
#if defined(AFS_SUN5_ENV)
code = AddPag(genpag(), credpp);
code = AddPag(pag, credpp);
#elif defined(AFS_FBSD_ENV)
code = AddPag(td, genpag(), &td->td_ucred);
code = AddPag(td, pag, &td->td_ucred);
#elif defined(AFS_NBSD40_ENV)
code = AddPag(p, genpag(), &p->l_proc->p_cred);
code = AddPag(p, pag, &p->l_proc->p_cred);
#elif defined(AFS_XBSD_ENV)
code = AddPag(p, genpag(), &p->p_rcred);
code = AddPag(p, pag, &p->p_rcred);
#elif defined(AFS_AIX41_ENV)
{
struct ucred *credp;
@ -234,7 +246,7 @@ afs_setpag(void)
credp = crref();
credp0 = credp;
code = AddPag(genpag(), &credp);
code = AddPag(pag, &credp);
/* If AddPag() didn't make a new cred, then free our cred ref */
if (credp == credp0) {
crfree(credp);
@ -243,36 +255,36 @@ afs_setpag(void)
#elif defined(AFS_HPUX110_ENV)
{
struct ucred *credp = p_cred(u.u_procp);
code = AddPag(genpag(), &credp);
code = AddPag(pag, &credp);
}
#elif defined(AFS_SGI_ENV)
{
cred_t *credp;
credp = OSI_GET_CURRENT_CRED();
code = AddPag(genpag(), &credp);
code = AddPag(pag, &credp);
}
#elif defined(AFS_LINUX_ENV)
{
afs_ucred_t *credp = crref();
code = AddPag(genpag(), &credp);
code = AddPag(pag, &credp);
crfree(credp);
}
#elif defined(AFS_DARWIN80_ENV)
{
afs_ucred_t *credp = kauth_cred_proc_ref(p);
code = AddPag(p, genpag(), &credp);
code = AddPag(p, pag, &credp);
crfree(credp);
}
#elif defined(AFS_DARWIN_ENV)
{
afs_ucred_t *credp = crdup(p->p_cred->pc_ucred);
code = AddPag(p, genpag(), &credp);
code = AddPag(p, pag, &credp);
crfree(credp);
}
#elif defined(UKERNEL)
code = AddPag(genpag(), &(get_user_struct()->u_cred));
code = AddPag(pag, &(get_user_struct()->u_cred));
#else
code = AddPag(genpag(), &u.u_cred);
code = AddPag(pag, &u.u_cred);
#endif
done:
@ -294,7 +306,7 @@ afs_setpag(void)
/*
* afs_setpag_val
* This function is like setpag but sets the current thread's pag id to a
* caller-provided value instead of calling genpag(). This implements a
* caller-provided value instead of calling afs_genpag(). This implements a
* form of token caching since the caller can recall a particular pag value
* for the thread to restore tokens, rather than reauthenticating.
*/

View File

@ -4721,7 +4721,13 @@ HandleClientContext(struct afs_ioctl *ablob, int *com,
*acred = newcred;
if (!code && *com == PSETPAG) {
/* Special case for 'setpag' */
afs_uint32 pagvalue = genpag();
afs_uint32 pagvalue;
code = afs_genpag(*acred, &pagvalue);
if (code != 0) {
EXP_RELE(outexporter);
return code;
}
au = afs_GetUser(pagvalue, -1, WRITE_LOCK); /* a new unixuser struct */
/*

View File

@ -526,7 +526,7 @@ extern int afs_setpag(afs_proc_t *p, void *args, int *retval);
extern int afs_setpag(void);
#endif
extern afs_uint32 genpag(void);
extern afs_int32 afs_genpag(afs_ucred_t *acred, afs_uint32 *apag);
extern afs_uint32 getpag(void);
#if defined(AFS_FBSD_ENV)
extern int AddPag(struct thread *td, afs_int32 aval, afs_ucred_t **credpp);

View File

@ -545,7 +545,7 @@ struct afs_MeanStats {
AFS_CS(afs_swapvp) /* afs_vfsops.c */ \
AFS_CS(afs_AddMarinerName) /* afs_vnodeops.c */ \
AFS_CS(afs_setpag) /* afs_vnodeops.c */ \
AFS_CS(genpag) /* afs_vnodeops.c */ \
AFS_CS(genpag) /* afs_vnodeops.c, renamed to genpagval() */ \
AFS_CS(getpag) /* afs_vnodeops.c */ \
AFS_CS(afs_GetMariner) /* afs_vnodeops.c */ \
AFS_CS(afs_badop) /* afs_vnodeops.c */ \