+ if (vname) {
+ vnode_putname(vname);
+ }
+ if (dvp != NULLVP) {
+ vnode_put(dvp);
+ }
+ return error;
+}
+#endif /* CONFIG_NFS4 */
+
+/*
+ * Parse an NFSv4 SECINFO array to an array of pseudo flavors.
+ * (Note: also works for MOUNTv3 security arrays.)
+ */
+int
+nfsm_chain_get_secinfo(struct nfsm_chain *nmc, uint32_t *sec, int *seccountp)
+{
+ int error = 0, secmax, seccount, srvcount;
+ uint32_t flavor;
+
+#if CONFIG_NFS_GSS
+ uint32_t val;
+ u_char oid[12];
+#endif
+
+ seccount = srvcount = 0;
+ secmax = *seccountp;
+ *seccountp = 0;
+
+ nfsm_chain_get_32(error, nmc, srvcount);
+ while (!error && (srvcount > 0) && (seccount < secmax)) {
+ nfsm_chain_get_32(error, nmc, flavor);
+ nfsmout_if(error);
+ switch (flavor) {
+ case RPCAUTH_NONE:
+ case RPCAUTH_SYS:
+#if CONFIG_NFS_GSS
+ case RPCAUTH_KRB5:
+ case RPCAUTH_KRB5I:
+ case RPCAUTH_KRB5P:
+#endif /* CONFIG_NFS_GSS */
+ sec[seccount++] = flavor;
+ break;
+#if CONFIG_NFS_GSS
+ case RPCSEC_GSS:
+ /* we only recognize KRB5, KRB5I, KRB5P */
+ nfsm_chain_get_32(error, nmc, val); /* OID length */
+ nfsmout_if(error);
+ if (val != sizeof(krb5_mech_oid)) {
+ nfsm_chain_adv(error, nmc, val);
+ nfsm_chain_adv(error, nmc, 2 * NFSX_UNSIGNED);
+ break;
+ }
+ nfsm_chain_get_opaque(error, nmc, val, oid); /* OID bytes */
+ nfsmout_if(error);
+ if (bcmp(oid, krb5_mech_oid, sizeof(krb5_mech_oid))) {
+ nfsm_chain_adv(error, nmc, 2 * NFSX_UNSIGNED);
+ break;
+ }
+ nfsm_chain_get_32(error, nmc, val); /* QOP */
+ nfsm_chain_get_32(error, nmc, val); /* SERVICE */
+ nfsmout_if(error);
+ switch (val) {
+ case RPCSEC_GSS_SVC_NONE:
+ sec[seccount++] = RPCAUTH_KRB5;
+ break;
+ case RPCSEC_GSS_SVC_INTEGRITY:
+ sec[seccount++] = RPCAUTH_KRB5I;
+ break;
+ case RPCSEC_GSS_SVC_PRIVACY:
+ sec[seccount++] = RPCAUTH_KRB5P;
+ break;
+ }
+ break;
+#endif /* CONFIG_NFS_GSS */
+ }
+ srvcount--;
+ }
+nfsmout:
+ if (!error) {
+ *seccountp = seccount;
+ }
+ return error;
+}
+
+#if CONFIG_NFS4
+/*
+ * Fetch the FS_LOCATIONS attribute for the node found at directory/name.
+ */
+int
+nfs4_get_fs_locations(
+ struct nfsmount *nmp,
+ nfsnode_t dnp,
+ u_char *fhp,
+ int fhsize,
+ const char *name,
+ vfs_context_t ctx,
+ struct nfs_fs_locations *nfslsp)
+{
+ int error = 0, numops, status;
+ uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
+ struct nfsreq rq, *req = &rq;
+ struct nfsreq_secinfo_args si;
+ struct nfsm_chain nmreq, nmrep;
+ uint64_t xid;
+
+ if (!fhp && dnp) {
+ fhp = dnp->n_fhp;
+ fhsize = dnp->n_fhsize;
+ }
+ if (!fhp) {
+ return EINVAL;
+ }
+
+ nfsm_chain_null(&nmreq);
+ nfsm_chain_null(&nmrep);
+
+ NFSREQ_SECINFO_SET(&si, NULL, fhp, fhsize, name, 0);
+ numops = 3;
+ nfsm_chain_build_alloc_init(error, &nmreq, 18 * NFSX_UNSIGNED);
+ nfsm_chain_add_compound_header(error, &nmreq, "fs_locations", nmp->nm_minor_vers, numops);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
+ nfsm_chain_add_fh(error, &nmreq, NFS_VER4, fhp, fhsize);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
+ nfsm_chain_add_name(error, &nmreq, name, strlen(name), nmp);
+ numops--;
+ nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_FS_LOCATIONS);
+ nfsm_chain_add_bitmap(error, &nmreq, bitmap, NFS_ATTR_BITMAP_LEN);
+ nfsm_chain_build_done(error, &nmreq);
+ nfsm_assert(error, (numops == 0), EPROTO);
+ nfsmout_if(error);
+ error = nfs_request_async(dnp, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND,
+ vfs_context_thread(ctx), vfs_context_ucred(ctx), &si, 0, NULL, &req);
+ if (!error) {
+ error = nfs_request_async_finish(req, &nmrep, &xid, &status);
+ }
+ nfsm_chain_skip_tag(error, &nmrep);
+ nfsm_chain_get_32(error, &nmrep, numops);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_LOOKUP);
+ nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
+ nfsmout_if(error);
+ error = nfs4_parsefattr(&nmrep, NULL, NULL, NULL, NULL, nfslsp);
+nfsmout:
+ nfsm_chain_cleanup(&nmrep);
+ nfsm_chain_cleanup(&nmreq);
+ return error;
+}
+
+/*
+ * Referral trigger nodes may not have many attributes provided by the
+ * server, so put some default values in place.
+ */
+void
+nfs4_default_attrs_for_referral_trigger(
+ nfsnode_t dnp,
+ char *name,
+ int namelen,
+ struct nfs_vattr *nvap,
+ fhandle_t *fhp)
+{
+ struct timespec now;
+ nanotime(&now);
+ int len;
+
+ nvap->nva_flags = NFS_FFLAG_TRIGGER | NFS_FFLAG_TRIGGER_REFERRAL;
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TYPE)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_TYPE);
+ nvap->nva_type = VDIR;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_FSID)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_FSID);
+ nvap->nva_fsid.major = 0;
+ nvap->nva_fsid.minor = 0;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_OWNER) && dnp) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_OWNER);
+ nvap->nva_uid = dnp->n_vattr.nva_uid;
+ nvap->nva_uuuid = dnp->n_vattr.nva_uuuid;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_OWNER_GROUP) && dnp) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_OWNER_GROUP);
+ nvap->nva_gid = dnp->n_vattr.nva_gid;
+ nvap->nva_guuid = dnp->n_vattr.nva_guuid;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_MODE)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_MODE);
+ nvap->nva_mode = 0777;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_SIZE)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_SIZE);
+ nvap->nva_size = 0;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_SPACE_USED)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_SPACE_USED);
+ nvap->nva_bytes = 0;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_NUMLINKS)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_NUMLINKS);
+ nvap->nva_nlink = 2;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_ACCESS)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_TIME_ACCESS);
+ nvap->nva_timesec[NFSTIME_ACCESS] = now.tv_sec;
+ nvap->nva_timensec[NFSTIME_ACCESS] = now.tv_nsec;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_MODIFY)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_TIME_MODIFY);
+ nvap->nva_timesec[NFSTIME_MODIFY] = now.tv_sec;
+ nvap->nva_timensec[NFSTIME_MODIFY] = now.tv_nsec;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_TIME_METADATA)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_TIME_METADATA);
+ nvap->nva_timesec[NFSTIME_CHANGE] = now.tv_sec;
+ nvap->nva_timensec[NFSTIME_CHANGE] = now.tv_nsec;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_FILEID)) {
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_FILEID);
+ nvap->nva_fileid = 42;
+ }
+ if (!NFS_BITMAP_ISSET(nvap->nva_bitmap, NFS_FATTR_FILEHANDLE) && dnp && name && fhp) {
+ /* Build a fake filehandle made up of parent node pointer and name */
+ NFS_BITMAP_SET(nvap->nva_bitmap, NFS_FATTR_FILEHANDLE);
+ bcopy(&dnp, &fhp->fh_data[0], sizeof(dnp));
+ len = sizeof(fhp->fh_data) - sizeof(dnp);
+ bcopy(name, &fhp->fh_data[0] + sizeof(dnp), MIN(len, namelen));
+ fhp->fh_len = sizeof(dnp) + namelen;
+ if (fhp->fh_len > (int)sizeof(fhp->fh_data)) {
+ fhp->fh_len = sizeof(fhp->fh_data);
+ }
+ }
+}
+
+/*
+ * Set NFS bitmap according to what's set in vnode_attr (and supported by the server).
+ */
+void
+nfs_vattr_set_bitmap(struct nfsmount *nmp, uint32_t *bitmap, struct vnode_attr *vap)
+{
+ int i;
+
+ NFS_CLEAR_ATTRIBUTES(bitmap);
+ if (VATTR_IS_ACTIVE(vap, va_data_size)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_SIZE);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_acl) && (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_ACL)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_ACL);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_flags)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_ARCHIVE);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_HIDDEN);
+ }
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_MIMETYPE)
+ if (VATTR_IS_ACTIVE(vap, va_mode) && !NMFLAG(nmp, ACLONLY)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_MODE);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_uuuid)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_gid) || VATTR_IS_ACTIVE(vap, va_guuid)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_OWNER_GROUP);
+ }
+ // NFS_BITMAP_SET(bitmap, NFS_FATTR_SYSTEM)
+ if (vap->va_vaflags & VA_UTIMES_NULL) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
+ } else {
+ if (VATTR_IS_ACTIVE(vap, va_access_time)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_ACCESS_SET);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_modify_time)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_MODIFY_SET);
+ }
+ }
+ if (VATTR_IS_ACTIVE(vap, va_backup_time)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_BACKUP);
+ }
+ if (VATTR_IS_ACTIVE(vap, va_create_time)) {
+ NFS_BITMAP_SET(bitmap, NFS_FATTR_TIME_CREATE);
+ }
+ /* and limit to what is supported by server */
+ for (i = 0; i < NFS_ATTR_BITMAP_LEN; i++) {
+ bitmap[i] &= nmp->nm_fsattr.nfsa_supp_attr[i];
+ }
+}
+
+/*
+ * Convert between NFSv4 and VFS ACE types
+ */
+uint32_t
+nfs4_ace_nfstype_to_vfstype(uint32_t nfsacetype, int *errorp)
+{
+ switch (nfsacetype) {
+ case NFS_ACE_ACCESS_ALLOWED_ACE_TYPE:
+ return KAUTH_ACE_PERMIT;
+ case NFS_ACE_ACCESS_DENIED_ACE_TYPE:
+ return KAUTH_ACE_DENY;
+ case NFS_ACE_SYSTEM_AUDIT_ACE_TYPE:
+ return KAUTH_ACE_AUDIT;
+ case NFS_ACE_SYSTEM_ALARM_ACE_TYPE:
+ return KAUTH_ACE_ALARM;
+ }
+ *errorp = EBADRPC;
+ return 0;
+}
+
+uint32_t
+nfs4_ace_vfstype_to_nfstype(uint32_t vfstype, int *errorp)
+{
+ switch (vfstype) {
+ case KAUTH_ACE_PERMIT:
+ return NFS_ACE_ACCESS_ALLOWED_ACE_TYPE;
+ case KAUTH_ACE_DENY:
+ return NFS_ACE_ACCESS_DENIED_ACE_TYPE;
+ case KAUTH_ACE_AUDIT:
+ return NFS_ACE_SYSTEM_AUDIT_ACE_TYPE;
+ case KAUTH_ACE_ALARM:
+ return NFS_ACE_SYSTEM_ALARM_ACE_TYPE;
+ }
+ *errorp = EINVAL;
+ return 0;
+}
+
+/*
+ * Convert between NFSv4 and VFS ACE flags
+ */
+uint32_t
+nfs4_ace_nfsflags_to_vfsflags(uint32_t nfsflags)
+{
+ uint32_t vfsflags = 0;
+
+ if (nfsflags & NFS_ACE_FILE_INHERIT_ACE) {
+ vfsflags |= KAUTH_ACE_FILE_INHERIT;
+ }
+ if (nfsflags & NFS_ACE_DIRECTORY_INHERIT_ACE) {
+ vfsflags |= KAUTH_ACE_DIRECTORY_INHERIT;
+ }
+ if (nfsflags & NFS_ACE_NO_PROPAGATE_INHERIT_ACE) {
+ vfsflags |= KAUTH_ACE_LIMIT_INHERIT;
+ }
+ if (nfsflags & NFS_ACE_INHERIT_ONLY_ACE) {
+ vfsflags |= KAUTH_ACE_ONLY_INHERIT;
+ }
+ if (nfsflags & NFS_ACE_SUCCESSFUL_ACCESS_ACE_FLAG) {
+ vfsflags |= KAUTH_ACE_SUCCESS;
+ }
+ if (nfsflags & NFS_ACE_FAILED_ACCESS_ACE_FLAG) {
+ vfsflags |= KAUTH_ACE_FAILURE;
+ }
+ if (nfsflags & NFS_ACE_INHERITED_ACE) {
+ vfsflags |= KAUTH_ACE_INHERITED;
+ }
+
+ return vfsflags;
+}
+
+uint32_t
+nfs4_ace_vfsflags_to_nfsflags(uint32_t vfsflags)
+{
+ uint32_t nfsflags = 0;
+
+ if (vfsflags & KAUTH_ACE_FILE_INHERIT) {
+ nfsflags |= NFS_ACE_FILE_INHERIT_ACE;
+ }
+ if (vfsflags & KAUTH_ACE_DIRECTORY_INHERIT) {
+ nfsflags |= NFS_ACE_DIRECTORY_INHERIT_ACE;
+ }
+ if (vfsflags & KAUTH_ACE_LIMIT_INHERIT) {
+ nfsflags |= NFS_ACE_NO_PROPAGATE_INHERIT_ACE;
+ }
+ if (vfsflags & KAUTH_ACE_ONLY_INHERIT) {
+ nfsflags |= NFS_ACE_INHERIT_ONLY_ACE;
+ }
+ if (vfsflags & KAUTH_ACE_SUCCESS) {
+ nfsflags |= NFS_ACE_SUCCESSFUL_ACCESS_ACE_FLAG;
+ }
+ if (vfsflags & KAUTH_ACE_FAILURE) {
+ nfsflags |= NFS_ACE_FAILED_ACCESS_ACE_FLAG;
+ }
+ if (vfsflags & KAUTH_ACE_INHERITED) {
+ nfsflags |= NFS_ACE_INHERITED_ACE;
+ }
+
+ return nfsflags;
+}
+
+/*
+ * Convert between NFSv4 ACE access masks and VFS access rights
+ */
+uint32_t
+nfs4_ace_nfsmask_to_vfsrights(uint32_t nfsmask)
+{
+ uint32_t vfsrights = 0;
+
+ if (nfsmask & NFS_ACE_READ_DATA) {
+ vfsrights |= KAUTH_VNODE_READ_DATA;
+ }
+ if (nfsmask & NFS_ACE_LIST_DIRECTORY) {
+ vfsrights |= KAUTH_VNODE_LIST_DIRECTORY;
+ }
+ if (nfsmask & NFS_ACE_WRITE_DATA) {
+ vfsrights |= KAUTH_VNODE_WRITE_DATA;
+ }
+ if (nfsmask & NFS_ACE_ADD_FILE) {
+ vfsrights |= KAUTH_VNODE_ADD_FILE;
+ }
+ if (nfsmask & NFS_ACE_APPEND_DATA) {
+ vfsrights |= KAUTH_VNODE_APPEND_DATA;
+ }
+ if (nfsmask & NFS_ACE_ADD_SUBDIRECTORY) {
+ vfsrights |= KAUTH_VNODE_ADD_SUBDIRECTORY;
+ }
+ if (nfsmask & NFS_ACE_READ_NAMED_ATTRS) {
+ vfsrights |= KAUTH_VNODE_READ_EXTATTRIBUTES;
+ }
+ if (nfsmask & NFS_ACE_WRITE_NAMED_ATTRS) {
+ vfsrights |= KAUTH_VNODE_WRITE_EXTATTRIBUTES;
+ }
+ if (nfsmask & NFS_ACE_EXECUTE) {
+ vfsrights |= KAUTH_VNODE_EXECUTE;
+ }
+ if (nfsmask & NFS_ACE_DELETE_CHILD) {
+ vfsrights |= KAUTH_VNODE_DELETE_CHILD;
+ }
+ if (nfsmask & NFS_ACE_READ_ATTRIBUTES) {
+ vfsrights |= KAUTH_VNODE_READ_ATTRIBUTES;
+ }
+ if (nfsmask & NFS_ACE_WRITE_ATTRIBUTES) {
+ vfsrights |= KAUTH_VNODE_WRITE_ATTRIBUTES;
+ }
+ if (nfsmask & NFS_ACE_DELETE) {
+ vfsrights |= KAUTH_VNODE_DELETE;
+ }
+ if (nfsmask & NFS_ACE_READ_ACL) {
+ vfsrights |= KAUTH_VNODE_READ_SECURITY;
+ }
+ if (nfsmask & NFS_ACE_WRITE_ACL) {
+ vfsrights |= KAUTH_VNODE_WRITE_SECURITY;
+ }
+ if (nfsmask & NFS_ACE_WRITE_OWNER) {
+ vfsrights |= KAUTH_VNODE_CHANGE_OWNER;
+ }
+ if (nfsmask & NFS_ACE_SYNCHRONIZE) {
+ vfsrights |= KAUTH_VNODE_SYNCHRONIZE;
+ }
+ if ((nfsmask & NFS_ACE_GENERIC_READ) == NFS_ACE_GENERIC_READ) {
+ vfsrights |= KAUTH_ACE_GENERIC_READ;
+ }
+ if ((nfsmask & NFS_ACE_GENERIC_WRITE) == NFS_ACE_GENERIC_WRITE) {
+ vfsrights |= KAUTH_ACE_GENERIC_WRITE;
+ }
+ if ((nfsmask & NFS_ACE_GENERIC_EXECUTE) == NFS_ACE_GENERIC_EXECUTE) {
+ vfsrights |= KAUTH_ACE_GENERIC_EXECUTE;
+ }
+
+ return vfsrights;
+}
+
+uint32_t
+nfs4_ace_vfsrights_to_nfsmask(uint32_t vfsrights)
+{
+ uint32_t nfsmask = 0;
+
+ if (vfsrights & KAUTH_VNODE_READ_DATA) {
+ nfsmask |= NFS_ACE_READ_DATA;
+ }
+ if (vfsrights & KAUTH_VNODE_LIST_DIRECTORY) {
+ nfsmask |= NFS_ACE_LIST_DIRECTORY;
+ }
+ if (vfsrights & KAUTH_VNODE_WRITE_DATA) {
+ nfsmask |= NFS_ACE_WRITE_DATA;
+ }
+ if (vfsrights & KAUTH_VNODE_ADD_FILE) {
+ nfsmask |= NFS_ACE_ADD_FILE;
+ }
+ if (vfsrights & KAUTH_VNODE_APPEND_DATA) {
+ nfsmask |= NFS_ACE_APPEND_DATA;
+ }
+ if (vfsrights & KAUTH_VNODE_ADD_SUBDIRECTORY) {
+ nfsmask |= NFS_ACE_ADD_SUBDIRECTORY;
+ }
+ if (vfsrights & KAUTH_VNODE_READ_EXTATTRIBUTES) {
+ nfsmask |= NFS_ACE_READ_NAMED_ATTRS;
+ }
+ if (vfsrights & KAUTH_VNODE_WRITE_EXTATTRIBUTES) {
+ nfsmask |= NFS_ACE_WRITE_NAMED_ATTRS;
+ }
+ if (vfsrights & KAUTH_VNODE_EXECUTE) {
+ nfsmask |= NFS_ACE_EXECUTE;
+ }
+ if (vfsrights & KAUTH_VNODE_DELETE_CHILD) {
+ nfsmask |= NFS_ACE_DELETE_CHILD;
+ }
+ if (vfsrights & KAUTH_VNODE_READ_ATTRIBUTES) {
+ nfsmask |= NFS_ACE_READ_ATTRIBUTES;
+ }
+ if (vfsrights & KAUTH_VNODE_WRITE_ATTRIBUTES) {
+ nfsmask |= NFS_ACE_WRITE_ATTRIBUTES;
+ }
+ if (vfsrights & KAUTH_VNODE_DELETE) {
+ nfsmask |= NFS_ACE_DELETE;
+ }
+ if (vfsrights & KAUTH_VNODE_READ_SECURITY) {
+ nfsmask |= NFS_ACE_READ_ACL;
+ }
+ if (vfsrights & KAUTH_VNODE_WRITE_SECURITY) {
+ nfsmask |= NFS_ACE_WRITE_ACL;
+ }
+ if (vfsrights & KAUTH_VNODE_CHANGE_OWNER) {
+ nfsmask |= NFS_ACE_WRITE_OWNER;
+ }
+ if (vfsrights & KAUTH_VNODE_SYNCHRONIZE) {
+ nfsmask |= NFS_ACE_SYNCHRONIZE;
+ }
+ if (vfsrights & KAUTH_ACE_GENERIC_READ) {
+ nfsmask |= NFS_ACE_GENERIC_READ;
+ }
+ if (vfsrights & KAUTH_ACE_GENERIC_WRITE) {
+ nfsmask |= NFS_ACE_GENERIC_WRITE;
+ }
+ if (vfsrights & KAUTH_ACE_GENERIC_EXECUTE) {
+ nfsmask |= NFS_ACE_GENERIC_EXECUTE;
+ }
+ if (vfsrights & KAUTH_ACE_GENERIC_ALL) {
+ nfsmask |= (KAUTH_ACE_GENERIC_READ | KAUTH_ACE_GENERIC_WRITE | NFS_ACE_GENERIC_EXECUTE);
+ }
+
+ return nfsmask;
+}
+
+/*
+ * nfs4_wkid2sidd::
+ * mapid a wellknown identity to guid.
+ * Return 0 on success ENOENT if id does not map and EINVAL if the id is not a well known name.
+ */
+static int
+nfs4_wkid2sid(const char *id, ntsid_t *sp)
+{
+ size_t len = strnlen(id, MAXIDNAMELEN);
+
+ if (len == MAXIDNAMELEN || id[len - 1] != '@') {
+ return EINVAL;
+ }
+
+ bzero(sp, sizeof(ntsid_t));
+ sp->sid_kind = 1;
+ sp->sid_authcount = 1;
+ if (!strcmp(id, "OWNER@")) {
+ // S-1-3-0
+ sp->sid_authority[5] = 3;
+ sp->sid_authorities[0] = 0;
+ } else if (!strcmp(id, "GROUP@")) {
+ // S-1-3-1
+ sp->sid_authority[5] = 3;
+ sp->sid_authorities[0] = 1;
+ } else if (!strcmp(id, "EVERYONE@")) {
+ // S-1-1-0
+ sp->sid_authority[5] = 1;
+ sp->sid_authorities[0] = 0;
+ } else if (!strcmp(id, "INTERACTIVE@")) {
+ // S-1-5-4
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 4;
+ } else if (!strcmp(id, "NETWORK@")) {
+ // S-1-5-2
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 2;
+ } else if (!strcmp(id, "DIALUP@")) {
+ // S-1-5-1
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 1;
+ } else if (!strcmp(id, "BATCH@")) {
+ // S-1-5-3
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 3;
+ } else if (!strcmp(id, "ANONYMOUS@")) {
+ // S-1-5-7
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 7;
+ } else if (!strcmp(id, "AUTHENTICATED@")) {
+ // S-1-5-11
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 11;
+ } else if (!strcmp(id, "SERVICE@")) {
+ // S-1-5-6
+ sp->sid_authority[5] = 5;
+ sp->sid_authorities[0] = 6;
+ } else {
+ // S-1-0-0 "NOBODY"
+ sp->sid_authority[5] = 0;
+ sp->sid_authorities[0] = 0;
+ }
+ return 0;
+}
+
+static int
+nfs4_fallback_name(const char *id, int have_at)
+{
+ if (have_at) {
+ /* must be user@domain */
+ /* try to identify some well-known IDs */
+ if (!strncmp(id, "root@", 5)) {
+ return 0;
+ } else if (!strncmp(id, "wheel@", 6)) {
+ return 0;
+ } else if (!strncmp(id, "nobody@", 7)) {
+ return -2;
+ } else if (!strncmp(id, "nfsnobody@", 10)) {
+ return -2;
+ }
+ }
+ return -2;
+}
+
+static void
+nfs4_mapid_log(int error, const char *idstr, int isgroup, guid_t *gp)
+{
+ if (error && (nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_FAILED_MAPPINGS)) {
+ printf("nfs4_id2guid: idmap failed for %s %s error %d\n", idstr, isgroup ? "G" : " ", error);
+ }
+ if (!error && (nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_SUCCESSFUL_MAPPINGS)) {
+ printf("nfs4_id2guid: idmap for %s %s got guid "
+ "%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x\n",
+ idstr, isgroup ? "G" : " ",
+ gp->g_guid[0], gp->g_guid[1], gp->g_guid[2], gp->g_guid[3],
+ gp->g_guid[4], gp->g_guid[5], gp->g_guid[6], gp->g_guid[7],
+ gp->g_guid[8], gp->g_guid[9], gp->g_guid[10], gp->g_guid[11],
+ gp->g_guid[12], gp->g_guid[13], gp->g_guid[14], gp->g_guid[15]);
+ }
+}
+
+static char *
+nfs4_map_domain(char *id, char **atp)
+{
+ char *at = *atp;
+ char *dsnode, *otw_nfs4domain;
+ char *new_id = NULL;
+ size_t otw_domain_len;
+ size_t otw_id_2_at_len;
+ int error;
+
+ if (at == NULL) {
+ at = strchr(id, '@');
+ }
+ if (at == NULL || *at != '@') {
+ return NULL;
+ }
+
+ otw_nfs4domain = at + 1;
+ otw_domain_len = strnlen(otw_nfs4domain, MAXPATHLEN);
+ otw_id_2_at_len = at - id + 1;
+
+ MALLOC_ZONE(dsnode, char*, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ /* first try to map nfs4 domain to dsnode for scoped lookups */
+ error = kauth_cred_nfs4domain2dsnode(otw_nfs4domain, dsnode);
+ if (!error) {
+ /* Success! Make new id be id@dsnode */
+ size_t dsnode_len = strnlen(dsnode, MAXPATHLEN);
+ size_t new_id_len = otw_id_2_at_len + dsnode_len + 1;
+ char tmp;
+
+ MALLOC(new_id, char*, new_id_len, M_TEMP, M_WAITOK);
+ tmp = *otw_nfs4domain;
+ *otw_nfs4domain = '\0'; /* Chop of the old domain */
+ strlcpy(new_id, id, MAXPATHLEN);
+ *otw_nfs4domain = tmp; /* Be nice and preserve callers original id */
+ strlcat(new_id, dsnode, MAXPATHLEN);
+ at = strchr(new_id, '@');
+ } else {
+ /* Bummer:-( See if default nfs4 set for unscoped lookup */
+ size_t default_domain_len = strnlen(nfs4_default_domain, MAXPATHLEN);
+
+ if ((otw_domain_len == default_domain_len) &&
+ (strncmp(otw_nfs4domain, nfs4_default_domain, otw_domain_len) == 0)) {
+ /* Woohoo! We have matching domains, do unscoped lookups */
+ *at = '\0';
+ }
+ }
+ FREE_ZONE(dsnode, MAXPATHLEN, M_NAMEI);
+
+ if (nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_SUCCESSFUL_MAPPINGS) {
+ printf("nfs4_id2guid: after domain mapping id is %s\n", id);
+ }
+
+ *atp = at;
+ return new_id;
+}
+
+/*
+ * Map an NFSv4 ID string to a VFS guid.
+ *
+ * Try to use the ID mapping service... but we may fallback to trying to do it ourselves.
+ */
+int
+nfs4_id2guid(/*const*/ char *id, guid_t *guidp, int isgroup)
+{
+ int error = 0;
+ ntsid_t sid;
+ long num;
+ char *p, *at, *new_id = NULL;
+
+ *guidp = kauth_null_guid;
+
+ /*
+ * First check if it is just a simple numeric ID string or a special "XXX@" name.
+ * If it's a number, there's no need trying to ask the IDMAP service to map it.
+ * If it's a special "XXX@" name, we want to make sure to treat it as a group.
+ */
+ num = 1;
+ at = NULL;
+ p = id;
+ while (*p) {
+ if ((*p < '0') || (*p > '9')) {
+ num = 0;
+ }
+ if (*p == '@') {
+ at = p;
+ }
+ p++;
+ }
+
+ if (num) {
+ /* must be numeric ID (or empty) */
+ num = *id ? strtol(id, NULL, 10) : -2;
+ if (isgroup) {
+ error = kauth_cred_gid2guid((gid_t)num, guidp);
+ } else {
+ error = kauth_cred_uid2guid((uid_t)num, guidp);
+ }
+ nfs4_mapid_log(error, id, isgroup, guidp);
+ return error;
+ }
+
+ /* See if this is a well known NFSv4 name */
+ error = nfs4_wkid2sid(id, &sid);
+ if (!error) {
+ error = kauth_cred_ntsid2guid(&sid, guidp);
+ nfs4_mapid_log(error, id, 1, guidp);
+ return error;
+ }
+
+ /* Handle nfs4 domain first */
+ if (at && at[1]) {
+ new_id = nfs4_map_domain(id, &at);
+ if (new_id) {
+ id = new_id;
+ }
+ }
+
+ /* Now try to do actual id mapping */
+ if (nfs_idmap_ctrl & NFS_IDMAP_CTRL_USE_IDMAP_SERVICE) {
+ /*
+ * Ask the ID mapping service to map the ID string to a GUID.
+ *
+ * [sigh] this isn't a "pwnam/grnam" it's an NFS ID string!
+ */
+ if (isgroup) {
+ error = kauth_cred_grnam2guid(id, guidp);
+ } else {
+ error = kauth_cred_pwnam2guid(id, guidp);
+ }
+ nfs4_mapid_log(error, id, isgroup, guidp);
+ } else {
+ error = ENOTSUP;
+ }
+
+ if (error) {
+ /*
+ * fallback path... see if we can come up with an answer ourselves.
+ */
+ num = nfs4_fallback_name(id, at != NULL);
+ if (isgroup) {
+ error = kauth_cred_gid2guid((gid_t)num, guidp);
+ } else {
+ error = kauth_cred_uid2guid((uid_t)num, guidp);
+ }
+ nfs4_mapid_log(error, id, isgroup, guidp);
+ }
+
+
+ /* restore @ symbol in case we clobered for unscoped lookup */
+ if (at && *at == '\0') {
+ *at = '@';
+ }
+
+ /* free mapped domain id string */
+ if (new_id) {
+ FREE(new_id, M_TEMP);
+ }
+
+ return error;
+}
+
+/*
+ * nfs4_sid2wkid:
+ * mapid a wellknown identity to guid.
+ * returns well known name for the sid or NULL if sid does not map.
+ */
+#define MAXWELLKNOWNID 18
+
+static const char*
+nfs4_sid2wkid(ntsid_t *sp)
+{
+ if ((sp->sid_kind == 1) && (sp->sid_authcount == 1)) {
+ /* check if it's one of our well-known ACE WHO names */
+ if (sp->sid_authority[5] == 0) {
+ if (sp->sid_authorities[0] == 0) { // S-1-0-0
+ return "nobody@localdomain";
+ }
+ } else if (sp->sid_authority[5] == 1) {
+ if (sp->sid_authorities[0] == 0) { // S-1-1-0
+ return "EVERYONE@";
+ }
+ } else if (sp->sid_authority[5] == 3) {
+ if (sp->sid_authorities[0] == 0) { // S-1-3-0
+ return "OWNER@";
+ } else if (sp->sid_authorities[0] == 1) { // S-1-3-1
+ return "GROUP@";
+ }
+ } else if (sp->sid_authority[5] == 5) {
+ if (sp->sid_authorities[0] == 1) { // S-1-5-1
+ return "DIALUP@";
+ } else if (sp->sid_authorities[0] == 2) { // S-1-5-2
+ return "NETWORK@";
+ } else if (sp->sid_authorities[0] == 3) { // S-1-5-3
+ return "BATCH@";
+ } else if (sp->sid_authorities[0] == 4) { // S-1-5-4
+ return "INTERACTIVE@";
+ } else if (sp->sid_authorities[0] == 6) { // S-1-5-6
+ return "SERVICE@";
+ } else if (sp->sid_authorities[0] == 7) { // S-1-5-7
+ return "ANONYMOUS@";
+ } else if (sp->sid_authorities[0] == 11) { // S-1-5-11
+ return "AUTHENTICATED@";
+ }
+ }
+ }
+ return NULL;
+}
+
+static void
+nfs4_mapguid_log(int error, const char *where, guid_t *gp, int isgroup, const char *idstr)
+{
+ if (error && (nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_FAILED_MAPPINGS)) {
+ printf("nfs4_guid2id: %s idmap failed for "
+ "%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x %s "
+ "error %d\n", where,
+ gp->g_guid[0], gp->g_guid[1], gp->g_guid[2], gp->g_guid[3],
+ gp->g_guid[4], gp->g_guid[5], gp->g_guid[6], gp->g_guid[7],
+ gp->g_guid[8], gp->g_guid[9], gp->g_guid[10], gp->g_guid[11],
+ gp->g_guid[12], gp->g_guid[13], gp->g_guid[14], gp->g_guid[15],
+ isgroup ? "G" : " ", error);
+ }
+ if (!error && (nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_SUCCESSFUL_MAPPINGS)) {
+ printf("nfs4_guid2id: %s idmap for "
+ "%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x_%02x%02x%02x%02x %s "
+ "got ID %s\n", where,
+ gp->g_guid[0], gp->g_guid[1], gp->g_guid[2], gp->g_guid[3],
+ gp->g_guid[4], gp->g_guid[5], gp->g_guid[6], gp->g_guid[7],
+ gp->g_guid[8], gp->g_guid[9], gp->g_guid[10], gp->g_guid[11],
+ gp->g_guid[12], gp->g_guid[13], gp->g_guid[14], gp->g_guid[15],
+ isgroup ? "G" : " ", idstr);
+ }
+}
+
+static int
+nfs4_addv4domain(char *id, size_t *idlen)
+{
+ char *at = NULL, *cp;
+ int have_domain;
+ int error = 0;
+ size_t idsize;
+
+
+ if (id == NULL || *id == '\0') {
+ return EINVAL;
+ }
+
+ for (cp = id; *cp != '\0'; cp++) {
+ if (*cp == '@') {
+ at = cp;
+ break;
+ }
+ }
+
+ have_domain = (at && at[1] != '\0');
+
+ if (have_domain) {
+ char *dsnode = at + 1;
+ char *nfs4domain;
+ size_t domain_len;
+ char *mapped_domain;
+
+ MALLOC_ZONE(nfs4domain, char*, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ error = kauth_cred_dsnode2nfs4domain(dsnode, nfs4domain);
+ if (!error) {
+ domain_len = strnlen(nfs4domain, MAXPATHLEN);
+ mapped_domain = nfs4domain;
+ } else {
+ error = 0;
+ domain_len = strnlen(nfs4_default_domain, MAXPATHLEN);
+ mapped_domain = nfs4_default_domain;
+ }
+ if (domain_len) {
+ /* chop off id after the '@' */
+ at[1] = '\0';
+ /* Add our mapped_domain */
+ idsize = strlcat(id, mapped_domain, *idlen);
+ if (*idlen > idsize) {
+ *idlen = idsize;
+ } else {
+ error = ENOSPC;
+ }
+ }
+ FREE_ZONE(nfs4domain, MAXPATHLEN, M_NAMEI);
+ } else if (at == NULL) {
+ /*
+ * If we didn't find an 'at' then cp points to the end of id passed in.
+ * and if we have a nfs4_default_domain set. Try to append the
+ * default domain if we have root or set ENOSPC.
+ */
+ size_t default_domain_len = strnlen(nfs4_default_domain, MAXPATHLEN);
+
+ if (default_domain_len) {
+ strlcat(id, "@", *idlen);
+ idsize = strlcat(id, nfs4_default_domain, *idlen);
+ if (*idlen > idsize) {
+ *idlen = idsize;
+ } else {
+ error = ENOSPC;
+ }
+ } else {
+ ; /* Unscoped name otw */
+ }
+ }
+
+ if (!error && nfs_idmap_ctrl & NFS_IDMAP_CTRL_LOG_SUCCESSFUL_MAPPINGS) {
+ printf("nfs4_guid2id: id after nfs4 domain map: %s[%zd].\n", id, *idlen);
+ }
+
+ return error;
+}
+
+static char *
+nfs4_fallback_id(int numid, int isgrp, char *buf, size_t size)
+{
+ const char *idp = NULL;
+
+ if (!(nfs_idmap_ctrl & NFS_IDMAP_CTRL_FALLBACK_NO_COMMON_IDS)) {
+ /* map well known uid's to strings */
+ if (numid == 0) {
+ idp = isgrp ? "wheel" : "root";
+ } else if (numid == -2) {
+ idp = "nobody";
+ }
+ }
+ if (!idp) {
+ /* or just use a decimal number string. */
+ snprintf(buf, size - 1, "%d", numid);
+ buf[size - 1] = '\0';
+ } else {
+ size_t idplen = strlcpy(buf, idp, size);
+ if (idplen >= size) {
+ return NULL;
+ }
+ }
+
+ return buf;
+}
+
+/*
+ * Map a VFS guid to an NFSv4 ID string.
+ *
+ * Try to use the ID mapping service... but we may fallback to trying to do it ourselves.
+ */
+int
+nfs4_guid2id(guid_t *guidp, char *id, size_t *idlen, int isgroup)
+{
+ int error = 0;
+ size_t id1len, len;
+ char *id1buf, *id1;
+ char numbuf[32];
+ ntsid_t sid;
+
+ id1buf = id1 = NULL;
+ id1len = 0;
+
+ /*
+ * See if our guid maps to a well known NFSv4 name
+ */
+ error = kauth_cred_guid2ntsid(guidp, &sid);
+ if (!error) {
+ const char *wkid = nfs4_sid2wkid(&sid);
+ if (wkid) {
+ len = strnlen(wkid, MAXWELLKNOWNID);
+ strlcpy(id, wkid, *idlen);
+ error = (len < *idlen) ? 0 : ENOSPC;
+ *idlen = len;
+ nfs4_mapguid_log(error, "kauth_cred_guid2ntsid", guidp, 1, id);
+ return error;
+ }
+ } else {
+ nfs4_mapguid_log(error, "kauth_cred_guid2ntsid", guidp, isgroup, NULL);
+ }
+
+ if (nfs_idmap_ctrl & NFS_IDMAP_CTRL_USE_IDMAP_SERVICE) {
+ /*
+ * Ask the ID mapping service to map the GUID to an ID string.
+ *
+ * [sigh] this isn't a "pwnam" it's an NFS id string!
+ */
+
+ /*
+ * Stupid kauth_cred_guid2pwnam() function requires that the buffer
+ * be at least MAXPATHLEN bytes long even though most if not all ID
+ * strings will be much much shorter than that.
+ */
+
+ if (*idlen < MAXPATHLEN) {
+ MALLOC_ZONE(id1buf, char*, MAXPATHLEN, M_NAMEI, M_WAITOK);
+ id1 = id1buf;
+ id1len = MAXPATHLEN;
+ } else {
+ id1 = id;
+ id1len = *idlen;
+ }
+
+ if (isgroup) {
+ error = kauth_cred_guid2grnam(guidp, id1);
+ } else {
+ error = kauth_cred_guid2pwnam(guidp, id1);
+ }
+ if (error) {
+ nfs4_mapguid_log(error, "kauth_cred2[pw|gr]nam", guidp, isgroup, id1);
+ }
+ } else {
+ error = ENOTSUP;
+ }
+
+ if (error) {
+ /*
+ * fallback path... see if we can come up with an answer ourselves.
+ */
+ uid_t uid;
+
+ /* OK, let's just try mapping it to a UID/GID */
+ if (isgroup) {
+ error = kauth_cred_guid2gid(guidp, (gid_t*)&uid);
+ } else {
+ error = kauth_cred_guid2uid(guidp, &uid);
+ }
+ if (!error) {
+ char *fbidp = nfs4_fallback_id(uid, isgroup, numbuf, sizeof(numbuf));
+ if (fbidp == NULL) {
+ error = ENOSPC;
+ } else {
+ id1 = fbidp;
+ }
+ }
+ } else {
+ error = nfs4_addv4domain(id1, &id1len);
+ }
+
+ if (!error) {
+ if (id1 != id) {
+ /* copy idmap result to output buffer */
+ len = strlcpy(id, id1, *idlen);
+ if (len >= *idlen) {
+ error = ENOSPC;
+ } else {
+ *idlen = len;
+ }
+ }
+ }
+ nfs4_mapguid_log(error, "End of routine", guidp, isgroup, id1);
+
+ if (id1buf) {
+ FREE_ZONE(id1buf, MAXPATHLEN, M_NAMEI);
+ }