/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* * Implements: * afsDFS_SetPagInCred (shared with SGI) * osi_DFSGetPagFromCred (shared with SGI) * Afs_xsetgroups (syscall) * setpag * */ #include #include "afs/param.h" RCSID ("$Header$"); #include "afs/sysincludes.h" #include "afsincludes.h" #include "afs/afs_stats.h" /* statistics */ static int afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset); static int afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset, int change_parent); /* This is common code between SGI's DFS and our AFS. Do *not* alter it's * interface or semantics without notifying SGI. */ #ifdef AFS_SGI65_ENV /* fixup_pags returns error code if relevant or 0 on no error. * Sets up the cred for the call to estgroups. This is pretty convoluted * in order to avoid including the private proc.h header file. */ int fixup_pags(int **credpp, int ngroups, gid_t * gidset, int old_afs_pag, int old_dfs_pag) { int new_afs_pag = 0; int new_dfs_pag = 0; int new, old; int changed = 0; cred_t *cr; gid_t groups[NGROUPS_UMAX]; int code; if (ngroups < 0 || ngroups > ngroups_max) return EINVAL; if (ngroups) { AFS_COPYIN(gidset, groups, ngroups * sizeof(gid_t), code); if (code) return EFAULT; } if (ngroups >= 2) { /* possibly an AFS PAG */ new_afs_pag = (afs_get_pag_from_groups(groups[0], groups[1]) != NOPAG); } if (ngroups >= 1) { /* possibly a DFS PAG */ new_dfs_pag = (int)groups[ngroups - 1]; if (((new_dfs_pag >> 24) & 0xff) == 'A') new_dfs_pag = (int)groups[ngroups - 1]; else new_dfs_pag = 0; } /* Now compute the number of groups we will need. */ new = ngroups; if (old_afs_pag && !new_afs_pag) /* prepend old AFS pag */ new += 2; if (old_dfs_pag && !new_dfs_pag) /* append old DFS pag */ new++; if (new > ngroups_max) return EINVAL; /* sorry */ cr = crdup(OSI_GET_CURRENT_CRED()); /* we will replace all the groups. */ memset((char *)&cr->cr_groups, 0, ngroups_max * sizeof(gid_t)); /* Now cobble the new groups list together. */ new = 0; old = 0; if (old_afs_pag && !new_afs_pag) { /* prepend old AFS pag */ gid_t g0, g1; changed = 1; afs_get_groups_from_pag(old_afs_pag, &g0, &g1); cr->cr_groups[new++] = g0; cr->cr_groups[new++] = g1; } for (old = 0; old < ngroups; old++) cr->cr_groups[new++] = groups[old]; if (old_dfs_pag && !new_dfs_pag) { /* append old DFS pag */ changed = 1; cr->cr_groups[new++] = old_dfs_pag; } /* Now, did we do anything? */ if (changed) { cr->cr_ngroups = new; *credpp = cr; } else { crfree(cr); *credpp = NULL; } return 0; } #else /* * Generic routine to set the PAG in the cred for AFS and DFS. * If flag = 0 this is a DFS pag held in one group. * If flag = 1 this is a AFS pag held in two group entries */ static int afsDFS_SetPagInCred(struct ucred *credp, int pag, int flag) { int *gidset; int i, ngrps; gid_t g0, g1; int n = 0; struct ucred *newcredp; int groups_taken = (flag ? 2 : 1); ngrps = credp->cr_ngroups + groups_taken; if (ngrps >= ngroups_max) return E2BIG; if (flag) { /* Break out the AFS pag into two groups */ afs_get_groups_from_pag(pag, &g0, &g1); } newcredp = crdup(credp); newcredp->cr_ngroups = ngrps; if (flag) { /* AFS case */ newcredp->cr_groups[0] = g0; newcredp->cr_groups[1] = g1; } else { /* DFS case */ if (PagInCred(newcredp) != NOPAG) { /* found an AFS PAG is set in this cred */ n = 2; } newcredp->cr_groups[n] = pag; } for (i = n; i < credp->cr_ngroups; i++) newcredp->cr_groups[i + groups_taken] = credp->cr_groups[i]; /* estgroups sets current threads cred from newcredp and crfree's credp */ estgroups(credp, newcredp); return 0; } #endif /* AFS_SGI65_ENV */ /* SGI's osi_GetPagFromCred - They return a long. */ int osi_DFSGetPagFromCred(struct ucred *credp) { int pag; int ngroups; /* * For IRIX, the PAG is stored in the first entry * of the gruop list in the cred structure. gid_t's * are 32 bits on 64 bit and 32 bit hardware types. * As of Irix 6.5, the DFS pag is the last group in the list. */ ngroups = credp->cr_ngroups; if (ngroups < 1) return NOPAG; /* * Keep in mind that we might be living with AFS here. * This means we don't really know if our DFS PAG is in * the first or third group entry. */ #ifdef AFS_SGI65_ENV pag = credp->cr_groups[ngroups - 1]; #else pag = credp->cr_groups[0]; if (PagInCred(credp) != NOPAG) { /* AFS has a PAG value in the first two group entries */ if (ngroups < 3) return NOPAG; pag = credp->cr_groups[2]; } #endif if (((pag >> 24) & 0xff) == 'A') return pag; else return NOPAG; } int Afs_xsetgroups(int ngroups, gid_t * gidset) { int old_afs_pag = NOPAG; int old_dfs_pag = NOPAG; int code = 0; struct ucred *credp = OSI_GET_CURRENT_CRED(); struct ucred *modcredp; credp = OSI_GET_CURRENT_CRED(); /* First get any old PAG's */ old_afs_pag = PagInCred(credp); old_dfs_pag = osi_DFSGetPagFromCred(credp); /* Set the passed in group list. */ if (code = setgroups(ngroups, gidset)) return code; #ifdef AFS_SGI65_ENV if (old_afs_pag == NOPAG && old_dfs_pag == NOPAG) return 0; /* Well, we could get the cred, except it's in the proc struct which * is not a publicly available header. And the cred won't be valid on * the uthread until we return to user space. So, we examine the passed * in groups in fixup_pags. */ code = fixup_pags(&modcredp, ngroups, gidset, (old_afs_pag == NOPAG) ? 0 : old_afs_pag, (old_dfs_pag == NOPAG) ? 0 : old_dfs_pag); if (!code && modcredp) estgroups(OSI_GET_CURRENT_PROCP(), modcredp); #else /* * The setgroups gave our curent thread a new cred pointer * Get the value again */ credp = OSI_GET_CURRENT_CRED(); if ((PagInCred(credp) == NOPAG) && (old_afs_pag != NOPAG)) { /* reset the AFS PAG */ code = afsDFS_SetPagInCred(credp, old_afs_pag, 1); } /* * Once again get the credp because the afsDFS_SetPagInCred might have * assigned a new one. */ credp = OSI_GET_CURRENT_CRED(); if ((osi_DFSGetPagFromCred(credp) == NOPAG) && (old_dfs_pag != NOPAG)) { code = afsDFS_SetPagInCred(credp, old_dfs_pag, 0); } #endif /* AFS_SGI65_ENV */ return code; } int setpag(cred, pagvalue, newpag, change_parent) struct ucred **cred; afs_uint32 pagvalue; afs_uint32 *newpag; afs_uint32 change_parent; { gid_t gidset[NGROUPS]; int ngroups, code; int j; AFS_STATCNT(setpag); 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 */ if (ngroups + 2 > NGROUPS) { #if defined(KERNEL_HAVE_UERROR) return (setuerror(E2BIG), E2BIG); #else return (E2BIG); #endif } for (j = ngroups - 1; j >= 0; j--) { gidset[j + 2] = gidset[j]; } ngroups += 2; } *newpag = (pagvalue == -1 ? genpag() : 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) return (setuerror(code), code); #else return code; #endif } return code; } static int afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset) { int ngrps, savengrps; gid_t *gp; gidset[0] = gidset[1] = 0; AFS_STATCNT(afs_getgroups); savengrps = ngrps = MIN(ngroups, cred->cr_ngroups); gp = cred->cr_groups; while (ngrps--) *gidset++ = *gp++; return savengrps; } static int afs_setgroups(struct ucred **cred, int ngroups, gid_t * gidset, int change_parent) { gid_t *gp; cred_t *cr, *newcr; AFS_STATCNT(afs_setgroups); if (ngroups > ngroups_max) return EINVAL; cr = *cred; if (!change_parent) newcr = crdup(cr); else newcr = cr; newcr->cr_ngroups = ngroups; gp = newcr->cr_groups; while (ngroups--) *gp++ = *gidset++; if (!change_parent) { #ifdef AFS_SGI65_ENV estgroups(OSI_GET_CURRENT_PROCP(), newcr); #else estgroups(cr, newcr); #endif } *cred = newcr; return (0); }