/* * XDR loopback unix auth parameters. * NOTE: this is an XDR_ENCODE only routine. */ bool_t xdr_authloopback(XDR *xdrs) { uid_t uid; gid_t gid; int len; caddr_t groups; char *name = uts_nodename(); struct cred *cr; time_t now; if (xdrs->x_op != XDR_ENCODE) return (FALSE); cr = CRED(); uid = crgetuid(cr); gid = crgetgid(cr); len = crgetngroups(cr); groups = (caddr_t)crgetgroups(cr); now = gethrestime_sec(); if (xdr_uint32(xdrs, (uint32_t *)&now) && xdr_string(xdrs, &name, MAX_MACHINE_NAME) && xdr_uid_t(xdrs, &uid) && xdr_gid_t(xdrs, &gid) && xdr_array(xdrs, &groups, (uint_t *)&len, NGRPS_LOOPBACK, sizeof (int), (xdrproc_t)xdr_int)) return (TRUE); return (FALSE); }
/* * Load all permissions user based on cred belongs to. */ static void dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl, char checkflag, cred_t *cr) { //const gid_t *gids; //int ngids, i; uint64_t id; id = crgetuid(cr); (void) dsl_load_sets(mos, zapobj, ZFS_DELEG_USER_SETS, checkflag, &id, avl); id = crgetgid(cr); (void) dsl_load_sets(mos, zapobj, ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); (void) dsl_load_sets(mos, zapobj, ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl); #if 0 ngids = crgetngroups(cr); gids = crgetgroups(cr); for (i = 0; i != ngids; i++) { id = gids[i]; (void) dsl_load_sets(mos, zapobj, ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); } #endif }
static int splat_cred_test1(struct file *file, void *arg) { char str[GROUP_STR_SIZE]; uid_t uid, ruid, suid; gid_t gid, rgid, sgid, *groups; int ngroups, i, count = 0; uid = crgetuid(CRED()); ruid = crgetruid(CRED()); suid = crgetsuid(CRED()); gid = crgetgid(CRED()); rgid = crgetrgid(CRED()); sgid = crgetsgid(CRED()); crhold(CRED()); ngroups = crgetngroups(CRED()); groups = crgetgroups(CRED()); memset(str, 0, GROUP_STR_SIZE); for (i = 0; i < ngroups; i++) { count += sprintf(str + count, "%d ", groups[i]); if (count > (GROUP_STR_SIZE - GROUP_STR_REDZONE)) { splat_vprint(file, SPLAT_CRED_TEST1_NAME, "Failed too many group entries for temp " "buffer: %d, %s\n", ngroups, str); return -ENOSPC; } } crfree(CRED()); splat_vprint(file, SPLAT_CRED_TEST1_NAME, "uid: %d ruid: %d suid: %d " "gid: %d rgid: %d sgid: %d\n", uid, ruid, suid, gid, rgid, sgid); splat_vprint(file, SPLAT_CRED_TEST1_NAME, "ngroups: %d groups: %s\n", ngroups, str); if (uid || ruid || suid || gid || rgid || sgid) { splat_vprint(file, SPLAT_CRED_TEST1_NAME, "Failed expected all uids+gids to be %d\n", 0); return -EIDRM; } if (ngroups > NGROUPS_MAX) { splat_vprint(file, SPLAT_CRED_TEST1_NAME, "Failed ngroups must not exceed NGROUPS_MAX: " "%d > %d\n", ngroups, NGROUPS_MAX); return -EIDRM; } splat_vprint(file, SPLAT_CRED_TEST1_NAME, "Success sane CRED(): %d\n", 0); return 0; } /* splat_cred_test1() */
int getgroups(int gidsetsize, gid_t *gidset) { struct cred *cr; int n; cr = curthread->t_cred; n = crgetngroups(cr); if (gidsetsize != 0) { if (gidsetsize < n) return (set_errno(EINVAL)); if (copyout(crgetgroups(cr), gidset, n * sizeof (gid_t))) return (set_errno(EFAULT)); } return (n); }
/* * check a specified user/group for a requested permission */ static int dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, int checkflag, cred_t *cr) { //const gid_t *gids; // int ngids; //int i; uint64_t id; /* check for user */ id = crgetuid(cr); if (dsl_check_access(mos, zapobj, ZFS_DELEG_USER, checkflag, &id, perm) == 0) return (0); /* check for users primary group */ id = crgetgid(cr); if (dsl_check_access(mos, zapobj, ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) return (0); /* check for everyone entry */ id = -1; if (dsl_check_access(mos, zapobj, ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) return (0); /* check each supplemental group user is a member of */ /*XXX NOEL: get kauth equivs for the below, crgetngroups, crgetgroups*/ #ifndef __APPLE__ ngids = crgetngroups(cr); gids = crgetgroups(cr); for (i = 0; i != ngids; i++) { id = gids[i]; if (dsl_check_access(mos, zapobj, ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) return (0); } return (EPERM); #endif return 0; }
/* * * Cachefs used to know too much about how creds looked; since it's * committed to persistent storage, we can't change the layout so * it now has a "dl_cred_t" which (unsurprisingly) looks exactly like * an old credential. * * The dst argument needs to point to: * struct dl_cred_t; * <buffer space> buffer for groups * * The source is a proper kernel cred_t. * */ static size_t copy_cred(cred_t *src, dl_cred_t *dst) { int n; const gid_t *sgrp = crgetgroups(src); n = MIN(NGROUPS_MAX_DEFAULT, crgetngroups(src)); /* copy the fixed fields */ dst->cr_uid = crgetuid(src); dst->cr_ruid = crgetruid(src); dst->cr_suid = crgetsuid(src); dst->cr_gid = crgetgid(src); dst->cr_rgid = crgetrgid(src); dst->cr_sgid = crgetsgid(src); dst->cr_groups[0] = sgrp[0]; dst->cr_ngroups = n; bcopy(sgrp, (void *)(dst + 1), (n - 1) * sizeof (gid_t)); return (sizeof (dl_cred_t) + (n - 1) * sizeof (gid_t)); }
/* * check a specified user/group for a requested permission */ static int dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, int checkflag, cred_t *cr) { const gid_t *gids; int ngids; int i; uint64_t id; /* check for user */ id = crgetuid(cr); if (dsl_check_access(mos, zapobj, ZFS_DELEG_USER, checkflag, &id, perm) == 0) return (0); /* check for users primary group */ id = crgetgid(cr); if (dsl_check_access(mos, zapobj, ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) return (0); /* check for everyone entry */ id = -1; if (dsl_check_access(mos, zapobj, ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) return (0); /* check each supplemental group user is a member of */ ngids = crgetngroups(cr); gids = crgetgroups(cr); for (i = 0; i != ngids; i++) { id = gids[i]; if (dsl_check_access(mos, zapobj, ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) return (0); } return (EPERM); }
off_t /*ARGSUSED*/ cachefs_dlog_setsecattr(fscache_t *fscp, vsecattr_t *vsec, int flags, cnode_t *cp, cred_t *cr) { struct cfs_dlog_entry *entp; struct cfs_dlog_setsecattr *up; size_t alen, clen, len; off_t offset = 0; aclent_t *aclp; ASSERT(MUTEX_HELD(&cp->c_statelock)); /* paranoia */ ASSERT((vsec->vsa_mask & VSA_ACL) || (vsec->vsa_aclcnt == 0)); ASSERT((vsec->vsa_mask & VSA_DFACL) || (vsec->vsa_dfaclcnt == 0)); if ((vsec->vsa_mask & VSA_ACL) == 0) vsec->vsa_aclcnt = 0; if ((vsec->vsa_mask & VSA_DFACL) == 0) vsec->vsa_dfaclcnt = 0; /* calculate length of ACL and cred data */ alen = sizeof (aclent_t) * (vsec->vsa_aclcnt + vsec->vsa_dfaclcnt); clen = sizeof (dl_cred_t) + (((long)crgetngroups(cr)) * sizeof (gid_t)); /* * allocate entry. ACLs may be up to 24k currently, but they * usually won't, so we don't want to make cfs_dlog_entry_t * too big. so, we must compute the length here. */ len = sizeof (cfs_dlog_entry_t) - sizeof (up->dl_buffer) - sizeof (up->dl_cred) + alen + clen; #if 0 /* make up for weird behavior in cachefs_dlog_output */ /* XXX turn this on/off in sync with code in cachefs_dlog_output */ entp = cachefs_kmem_alloc(len + 32 + sizeof (struct cfs_dlog_trailer), KM_SLEEP); #else entp = cachefs_kmem_alloc(len, KM_SLEEP); #endif entp->dl_valid = CFS_DLOG_VAL_CRASH; entp->dl_op = CFS_DLOG_SETSECATTR; up = &entp->dl_u.dl_setsecattr; up->dl_cid = cp->c_id; CACHEFS_DLOG_TS_COPY(&cp->c_metadata.md_vattr.va_mtime, &up->dl_times.tm_mtime, "cachefs_dlog_setsecattr: ", "mtime"); CACHEFS_DLOG_TS_COPY(&cp->c_metadata.md_vattr.va_ctime, &up->dl_times.tm_ctime, "cachefs_dlog_setsecattr: ", "ctime"); /* get the creds */ (void) copy_cred(cr, &up->dl_cred); /* mask and counts */ up->dl_mask = vsec->vsa_mask; up->dl_aclcnt = vsec->vsa_aclcnt; up->dl_dfaclcnt = vsec->vsa_dfaclcnt; /* get the acls themselves */ aclp = (aclent_t *)((uintptr_t)(&up->dl_cred) + clen); if (vsec->vsa_aclcnt > 0) { bcopy(vsec->vsa_aclentp, aclp, vsec->vsa_aclcnt * sizeof (aclent_t)); aclp += vsec->vsa_aclcnt; } if (vsec->vsa_dfaclcnt > 0) { bcopy(vsec->vsa_dfaclentp, aclp, vsec->vsa_dfaclcnt * sizeof (aclent_t)); } entp->dl_len = (int)len; offset = cachefs_dlog_output(fscp, entp, NULL); #if 0 /* XXX turn on/off in sync with code in cachefs_dlog_output */ cachefs_kmem_free(entp, len + 32 + sizeof (struct cfs_dlog_trailer)); #else cachefs_kmem_free(entp, len); #endif return (offset); }
static bool_t authkern_marshal(AUTH *auth, XDR *xdrs, struct cred *cr) { char *sercred; XDR xdrm; struct opaque_auth *cred; bool_t ret = FALSE; const gid_t *gp, *gpend; int gidlen, credsize, namelen, rounded_namelen; int32_t *ptr; char *nodename = uts_nodename(); /* * First we try a fast path to get through * this very common operation. */ gp = crgetgroups(cr); gidlen = crgetngroups(cr); if (gidlen > NGRPS) gidlen = NGRPS; gpend = &gp[gidlen-1]; namelen = (int)strlen(nodename); rounded_namelen = RNDUP(namelen); credsize = 4 + 4 + rounded_namelen + 4 + 4 + 4 + gidlen * 4; ptr = XDR_INLINE(xdrs, 4 + 4 + credsize + 4 + 4); if (ptr) { /* * We can do the fast path. */ IXDR_PUT_INT32(ptr, AUTH_UNIX); /* cred flavor */ IXDR_PUT_INT32(ptr, credsize); /* cred len */ IXDR_PUT_INT32(ptr, gethrestime_sec()); IXDR_PUT_INT32(ptr, namelen); bcopy(nodename, (caddr_t)ptr, namelen); if (rounded_namelen - namelen) bzero(((caddr_t)ptr) + namelen, rounded_namelen - namelen); ptr += rounded_namelen / BYTES_PER_XDR_UNIT; IXDR_PUT_INT32(ptr, crgetuid(cr)); IXDR_PUT_INT32(ptr, crgetgid(cr)); IXDR_PUT_INT32(ptr, gidlen); while (gp <= gpend) { IXDR_PUT_INT32(ptr, *gp++); } IXDR_PUT_INT32(ptr, AUTH_NULL); /* verf flavor */ IXDR_PUT_INT32(ptr, 0); /* verf len */ return (TRUE); } sercred = kmem_alloc(MAX_AUTH_BYTES, KM_SLEEP); /* * serialize u struct stuff into sercred */ xdrmem_create(&xdrm, sercred, MAX_AUTH_BYTES, XDR_ENCODE); if (!xdr_authkern(&xdrm)) { printf("authkern_marshal: xdr_authkern failed\n"); ret = FALSE; goto done; } /* * Make opaque auth credentials that point at serialized u struct */ cred = &(auth->ah_cred); cred->oa_length = XDR_GETPOS(&xdrm); cred->oa_base = sercred; /* * serialize credentials and verifiers (null) */ if ((xdr_opaque_auth(xdrs, &(auth->ah_cred))) && (xdr_opaque_auth(xdrs, &(auth->ah_verf)))) ret = TRUE; else ret = FALSE; done: kmem_free(sercred, MAX_AUTH_BYTES); return (ret); }
/* * Callup to the mountd to get access information in the kernel. */ static bool_t nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor, struct netbuf *addr, int *access, cred_t *clnt_cred, uid_t *srv_uid, gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids) { varg_t varg = {0}; nfsauth_res_t res = {0}; XDR xdrs; size_t absz; caddr_t abuf; int last = 0; door_arg_t da; door_info_t di; door_handle_t dh; uint_t ntries = 0; /* * No entry in the cache for this client/flavor * so we need to call the nfsauth service in the * mount daemon. */ varg.vers = V_PROTO; varg.arg_u.arg.cmd = NFSAUTH_ACCESS; varg.arg_u.arg.areq.req_client.n_len = addr->len; varg.arg_u.arg.areq.req_client.n_bytes = addr->buf; varg.arg_u.arg.areq.req_netid = req_netid; varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path; varg.arg_u.arg.areq.req_flavor = flavor; varg.arg_u.arg.areq.req_clnt_uid = crgetuid(clnt_cred); varg.arg_u.arg.areq.req_clnt_gid = crgetgid(clnt_cred); varg.arg_u.arg.areq.req_clnt_gids.len = crgetngroups(clnt_cred); varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)crgetgroups(clnt_cred); DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg); /* * Setup the XDR stream for encoding the arguments. Notice that * in addition to the args having variable fields (req_netid and * req_path), the argument data structure is itself versioned, * so we need to make sure we can size the arguments buffer * appropriately to encode all the args. If we can't get sizing * info _or_ properly encode the arguments, there's really no * point in continuting, so we fail the request. */ if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) { *access = NFSAUTH_DENIED; return (FALSE); } abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP); xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE); if (!xdr_varg(&xdrs, &varg)) { XDR_DESTROY(&xdrs); goto fail; } XDR_DESTROY(&xdrs); /* * Prepare the door arguments * * We don't know the size of the message the daemon * will pass back to us. By setting rbuf to NULL, * we force the door code to allocate a buf of the * appropriate size. We must set rsize > 0, however, * else the door code acts as if no response was * expected and doesn't pass the data to us. */ da.data_ptr = (char *)abuf; da.data_size = absz; da.desc_ptr = NULL; da.desc_num = 0; da.rbuf = NULL; da.rsize = 1; retry: mutex_enter(&mountd_lock); dh = mountd_dh; if (dh != NULL) door_ki_hold(dh); mutex_exit(&mountd_lock); if (dh == NULL) { /* * The rendezvous point has not been established yet! * This could mean that either mountd(1m) has not yet * been started or that _this_ routine nuked the door * handle after receiving an EINTR for a REVOKED door. * * Returning NFSAUTH_DROP will cause the NFS client * to retransmit the request, so let's try to be more * rescillient and attempt for ntries before we bail. */ if (++ntries % NFSAUTH_DR_TRYCNT) { delay(hz); goto retry; } kmem_free(abuf, absz); sys_log("nfsauth: mountd has not established door"); *access = NFSAUTH_DROP; return (FALSE); } ntries = 0; /* * Now that we've got what we need, place the call. */ switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) { case 0: /* Success */ door_ki_rele(dh); if (da.data_ptr == NULL && da.data_size == 0) { /* * The door_return that contained the data * failed! We're here because of the 2nd * door_return (w/o data) such that we can * get control of the thread (and exit * gracefully). */ DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil, door_arg_t *, &da); goto fail; } break; case EAGAIN: /* * Server out of resources; back off for a bit */ door_ki_rele(dh); delay(hz); goto retry; /* NOTREACHED */ case EINTR: if (!door_ki_info(dh, &di)) { door_ki_rele(dh); if (di.di_attributes & DOOR_REVOKED) { /* * The server barfed and revoked * the (existing) door on us; we * want to wait to give smf(5) a * chance to restart mountd(1m) * and establish a new door handle. */ mutex_enter(&mountd_lock); if (dh == mountd_dh) { door_ki_rele(mountd_dh); mountd_dh = NULL; } mutex_exit(&mountd_lock); delay(hz); goto retry; } /* * If the door was _not_ revoked on us, * then more than likely we took an INTR, * so we need to fail the operation. */ goto fail; } /* * The only failure that can occur from getting * the door info is EINVAL, so we let the code * below handle it. */ /* FALLTHROUGH */ case EBADF: case EINVAL: default: /* * If we have a stale door handle, give smf a last * chance to start it by sleeping for a little bit. * If we're still hosed, we'll fail the call. * * Since we're going to reacquire the door handle * upon the retry, we opt to sleep for a bit and * _not_ to clear mountd_dh. If mountd restarted * and was able to set mountd_dh, we should see * the new instance; if not, we won't get caught * up in the retry/DELAY loop. */ door_ki_rele(dh); if (!last) { delay(hz); last++; goto retry; } sys_log("nfsauth: stale mountd door handle"); goto fail; }