/* Check to see whether USER should be considered the owner of the file identified by ST. If so, return zero; otherwise return an appropriate error code. */ error_t fshelp_isowner (struct stat *st, struct iouser *user) { /* Permitted if the user has the owner UID, the superuser UID, or if the user is in the group of the file and has the group ID as their user ID. */ if (idvec_contains (user->uids, st->st_uid) || idvec_contains (user->uids, 0) || (idvec_contains (user->gids, st->st_gid) && idvec_contains (user->uids, st->st_gid))) return 0; else return EPERM; }
/* Return an NFS file handle for CRED in FH & FN_LEN. */ error_t diskfs_S_file_getfh (struct protid *cred, char **fh, size_t *fh_len) { struct node *node; union diskfs_fhandle *f; if (! cred) return EOPNOTSUPP; if (! idvec_contains (cred->user->uids, 0)) return EPERM; assert (sizeof *f == sizeof f->bytes); node = cred->po->np; pthread_mutex_lock (&node->lock); if (*fh_len < sizeof (union diskfs_fhandle)) *fh = mmap (0, sizeof (union diskfs_fhandle), PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); *fh_len = sizeof *f; f = (union diskfs_fhandle *) *fh; memset (f, 0, sizeof *f); f->data.cache_id = node->cache_id; f->data.gen = node->dn_stat.st_gen; pthread_mutex_unlock (&node->lock); return 0; }
/* If IDVEC doesn't contain ID, add it onto the end, returning ENOMEM if there's not enough memory; otherwise, do nothing. */ error_t idvec_add_new (struct idvec *idvec, uid_t id) { if (idvec_contains (idvec, id)) return 0; else return idvec_add (idvec, id); }
error_t iohelp_restrict_iouser (struct iouser **new_user, const struct iouser *old_user, const uid_t *uids, int nuids, const gid_t *gids, int ngids) { if (idvec_contains (old_user->uids, 0)) /* OLD_USER has root access, and so may use any ids. */ return iohelp_create_complex_iouser (new_user, uids, nuids, gids, ngids); else { struct idvec *uvec, *gvec; int i; error_t err; uvec = make_idvec (); if (! uvec) return ENOMEM; gvec = make_idvec (); if (! gvec) { idvec_free (uvec); return ENOMEM; } /* Otherwise, use any of the requested ids that OLD_USER already has. */ for (i = 0; i < old_user->uids->num; i++) if (listmember (uids, old_user->uids->ids[i], nuids)) { err = idvec_add (uvec, old_user->uids->ids[i]); if (err) goto out; } for (i = 0; i < old_user->gids->num; i++) if (listmember (gids, old_user->gids->ids[i], ngids)) { err = idvec_add (gvec, old_user->gids->ids[i]); if (err) goto out; } err = iohelp_create_iouser (new_user, uvec, gvec); if (err) { out: idvec_free (uvec); idvec_free (gvec); } return err; } }
kern_return_t trivfs_S_io_restrict_auth (struct trivfs_protid *cred, mach_port_t reply, mach_msg_type_name_t replytype, mach_port_t *newport, mach_msg_type_name_t *newporttype, uid_t *uids, size_t nuids, uid_t *gids, size_t ngids) { int i; error_t err; struct trivfs_protid *newcred; struct idvec *uvec, *gvec; struct iouser *user; if (!cred) return EOPNOTSUPP; if (cred->isroot) /* CRED has root access, and so may use any ids. */ { err = iohelp_create_complex_iouser (&user, uids, nuids, gids, ngids); if (err) return err; } else { uvec = make_idvec (); if (! uvec) return ENOMEM; gvec = make_idvec (); if (! gvec) { idvec_free (uvec); return ENOMEM; } /* Otherwise, use any of the requested ids that CRED already has. */ for (i = 0; i < cred->user->uids->num; i++) if (listmember (uids, cred->user->uids->ids[i], nuids)) { err = idvec_add (uvec, cred->user->uids->ids[i]); if (err) goto out; } for (i = 0; i < cred->user->gids->num; i++) if (listmember (gids, cred->user->gids->ids[i], ngids)) { err = idvec_add (gvec, cred->user->gids->ids[i]); if (err) goto out; } err = iohelp_create_iouser (&user, uvec, gvec); if (err) { out: idvec_free (uvec); idvec_free (gvec); return err; } } err = ports_create_port (cred->po->cntl->protid_class, cred->po->cntl->protid_bucket, sizeof (struct trivfs_protid), &newcred); if (err) { iohelp_free_iouser (user); return err; } newcred->isroot = 0; mutex_lock (&cred->po->cntl->lock); newcred->po = cred->po; newcred->po->refcnt++; mutex_unlock (&cred->po->cntl->lock); if (cred->isroot && idvec_contains (user->uids, 0)) newcred->isroot = 1; newcred->user = user; newcred->hook = cred->hook; err = io_restrict_auth (cred->realnode, &newcred->realnode, user->uids->ids, user->uids->num, user->gids->ids, user->gids->num); if (!err && trivfs_protid_create_hook) { err = (*trivfs_protid_create_hook) (newcred); if (err) mach_port_deallocate (mach_task_self (), newcred->realnode); } if (err) /* Signal that the user destroy hook shouldn't be called on NEWCRED. */ newcred->realnode = MACH_PORT_NULL; else { *newport = ports_get_right (newcred); *newporttype = MACH_MSG_TYPE_MAKE_SEND; } /* This will destroy NEWCRED if we got an error and didn't do the ports_get_right above. */ ports_port_deref (newcred); return 0; }