+ 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);
+ }
+
+ return error;
+}
+
+/*
+ * Set a vnode attr's supported bits according to the given bitmap
+ */
+void
+nfs_vattr_set_supported(uint32_t *bitmap, struct vnode_attr *vap)
+{
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE)) {
+ VATTR_SET_SUPPORTED(vap, va_type);
+ }
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
+ VATTR_SET_SUPPORTED(vap, va_data_size);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FSID)) {
+ VATTR_SET_SUPPORTED(vap, va_fsid);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ACL)) {
+ VATTR_SET_SUPPORTED(vap, va_acl);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_ARCHIVE)) {
+ VATTR_SET_SUPPORTED(vap, va_flags);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FILEID)) {
+ VATTR_SET_SUPPORTED(vap, va_fileid);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_HIDDEN)) {
+ VATTR_SET_SUPPORTED(vap, va_flags);
+ }
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MIMETYPE))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_MODE)) {
+ VATTR_SET_SUPPORTED(vap, va_mode);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_NUMLINKS)) {
+ VATTR_SET_SUPPORTED(vap, va_nlink);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER)) {
+ VATTR_SET_SUPPORTED(vap, va_uid);
+ VATTR_SET_SUPPORTED(vap, va_uuuid);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_OWNER_GROUP)) {
+ VATTR_SET_SUPPORTED(vap, va_gid);
+ VATTR_SET_SUPPORTED(vap, va_guuid);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_RAWDEV)) {
+ VATTR_SET_SUPPORTED(vap, va_rdev);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SPACE_USED)) {
+ VATTR_SET_SUPPORTED(vap, va_total_alloc);
+ }
+ // if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYSTEM))
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_ACCESS)) {
+ VATTR_SET_SUPPORTED(vap, va_access_time);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_BACKUP)) {
+ VATTR_SET_SUPPORTED(vap, va_backup_time);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_CREATE)) {
+ VATTR_SET_SUPPORTED(vap, va_create_time);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_METADATA)) {
+ VATTR_SET_SUPPORTED(vap, va_change_time);
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TIME_MODIFY)) {
+ VATTR_SET_SUPPORTED(vap, va_modify_time);
+ }
+}
+
+/*
+ * Parse the attributes that are in the mbuf list and store them in
+ * the given structures.
+ */
+int
+nfs4_parsefattr(
+ struct nfsm_chain *nmc,
+ struct nfs_fsattr *nfsap,
+ struct nfs_vattr *nvap,
+ fhandle_t *fhp,
+ struct dqblk *dqbp,
+ struct nfs_fs_locations *nfslsp)
+{
+ int error = 0, error2, rderror = 0, attrbytes;
+ uint32_t val, val2, val3, i;
+ uint32_t bitmap[NFS_ATTR_BITMAP_LEN], len;
+ size_t slen;
+ char sbuf[64], *s;
+ struct nfs_fsattr nfsa_dummy;
+ struct nfs_vattr nva_dummy;
+ struct dqblk dqb_dummy;
+ kauth_acl_t acl = NULL;
+ uint32_t ace_type, ace_flags, ace_mask;
+ struct nfs_fs_locations nfsls_dummy;
+ struct sockaddr_storage ss;
+
+ /* if not interested in some values... throw 'em into a local dummy variable */
+ if (!nfsap) {
+ nfsap = &nfsa_dummy;
+ }
+ if (!nvap) {
+ nvap = &nva_dummy;
+ }
+ if (!dqbp) {
+ dqbp = &dqb_dummy;
+ }
+ if (!nfslsp) {
+ nfslsp = &nfsls_dummy;
+ }
+ bzero(nfslsp, sizeof(*nfslsp));
+
+ attrbytes = val = val2 = val3 = 0;
+ s = sbuf;
+ slen = sizeof(sbuf);
+ NVATTR_INIT(nvap);
+
+ len = NFS_ATTR_BITMAP_LEN;
+ nfsm_chain_get_bitmap(error, nmc, bitmap, len);
+ /* add bits to object/fs attr bitmaps */
+ for (i = 0; i < NFS_ATTR_BITMAP_LEN; i++) {
+ nvap->nva_bitmap[i] |= bitmap[i] & nfs_object_attr_bitmap[i];
+ nfsap->nfsa_bitmap[i] |= bitmap[i] & nfs_fs_attr_bitmap[i];
+ }
+
+ nfsm_chain_get_32(error, nmc, attrbytes);
+ nfsmout_if(error);
+
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SUPPORTED_ATTRS)) {
+ len = NFS_ATTR_BITMAP_LEN;
+ nfsm_chain_get_bitmap(error, nmc, nfsap->nfsa_supp_attr, len);
+ attrbytes -= (len + 1) * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_TYPE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nvap->nva_type = nfstov_type(val, NFS_VER4);
+ if ((val == NFATTRDIR) || (val == NFNAMEDATTR)) {
+ nvap->nva_flags |= NFS_FFLAG_IS_ATTR;
+ } else {
+ nvap->nva_flags &= ~NFS_FFLAG_IS_ATTR;
+ }
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_FH_EXPIRE_TYPE)) {
+ nfsm_chain_get_32(error, nmc, val);
+ nfsmout_if(error);
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_FHTYPE_MASK;
+ nfsap->nfsa_flags |= val << NFS_FSFLAG_FHTYPE_SHIFT;
+ if (val & ~0xff) {
+ printf("nfs: warning unknown fh type: 0x%x\n", val);
+ }
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_CHANGE)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_change);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SIZE)) {
+ nfsm_chain_get_64(error, nmc, nvap->nva_size);
+ attrbytes -= 2 * NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_LINK_SUPPORT)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val) {
+ nfsap->nfsa_flags |= NFS_FSFLAG_LINK;
+ } else {
+ nfsap->nfsa_flags &= ~NFS_FSFLAG_LINK;
+ }
+ attrbytes -= NFSX_UNSIGNED;
+ }
+ if (NFS_BITMAP_ISSET(bitmap, NFS_FATTR_SYMLINK_SUPPORT)) {
+ nfsm_chain_get_32(error, nmc, val);
+ if (val) {
+ nfsap->nfsa_flags |= NFS_FSFLAG_SYMLINK;
+ } else {