diff --git a/build.vc10/daemon.vcxproj b/build.vc10/daemon.vcxproj
index a3385e3..c1070ee 100644
--- a/build.vc10/daemon.vcxproj
+++ b/build.vc10/daemon.vcxproj
@@ -191,6 +191,7 @@
+
diff --git a/build.vc10/daemon.vcxproj.filters b/build.vc10/daemon.vcxproj.filters
index 0405d48..860e343 100644
--- a/build.vc10/daemon.vcxproj.filters
+++ b/build.vc10/daemon.vcxproj.filters
@@ -110,6 +110,9 @@
Source Files
+
+ Source Files
+
diff --git a/daemon/acl.c b/daemon/acl.c
new file mode 100644
index 0000000..54fdbf4
--- /dev/null
+++ b/daemon/acl.c
@@ -0,0 +1,232 @@
+/* Copyright (c) 2010
+ * The Regents of the University of Michigan
+ * All Rights Reserved
+ *
+ * Permission is granted to use, copy and redistribute this software
+ * for noncommercial education and research purposes, so long as no
+ * fee is charged, and so long as the name of the University of Michigan
+ * is not used in any advertising or publicity pertaining to the use
+ * or distribution of this software without specific, written prior
+ * authorization. Permission to modify or otherwise create derivative
+ * works of this software is not granted.
+ *
+ * This software is provided as is, without representation or warranty
+ * of any kind either express or implied, including without limitation
+ * the implied warranties of merchantability, fitness for a particular
+ * purpose, or noninfringement. The Regents of the University of
+ * Michigan shall not be liable for any damages, including special,
+ * indirect, incidental, or consequential damages, with respect to any
+ * claim arising out of or in connection with the use of the software,
+ * even if it has been or is hereafter advised of the possibility of
+ * such damages.
+ */
+#include
+#include
+#include
+
+#include "nfs41.h"
+#include "nfs41_ops.h"
+#include "daemon_debug.h"
+#include "util.h"
+#include "upcall.h"
+
+static int parse_getacl(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall)
+{
+ int status;
+ getacl_upcall_args *args = &upcall->args.getacl;
+
+ status = safe_read(&buffer, &length, &args->root, sizeof(HANDLE));
+ if (status) goto out;
+ upcall_root_ref(upcall, args->root);
+ status = safe_read(&buffer, &length, &args->state, sizeof(args->state));
+ if (status) goto out;
+ upcall_open_state_ref(upcall, args->state);
+ status = safe_read(&buffer, &length, &args->query, sizeof(args->query));
+ if (status) goto out;
+
+ dprintf(1, "parsing NFS41_ACL_QUERY: info_class=%d root=0x%p open_state=0x%p\n",
+ args->query, args->root, args->state);
+out:
+ return status;
+}
+
+static int create_unknownsid(WELL_KNOWN_SID_TYPE type, PSID *sid, DWORD *sid_len)
+{
+ int status;
+ *sid_len = 0;
+ *sid = NULL;
+ if (!CreateWellKnownSid(type, NULL, *sid, sid_len)) {
+ status = GetLastError();
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
+ *sid = malloc(*sid_len);
+ if (*sid == NULL) return ERROR_INSUFFICIENT_BUFFER;
+ if (!CreateWellKnownSid(type, NULL, *sid, sid_len)) {
+ free(*sid);
+ status = GetLastError();
+ dprintf(1, "CreateWellKnownSid failed with %d\n", status);
+ return status;
+ } else return 0;
+ } else return status;
+ } else return ERROR_INTERNAL_ERROR;
+}
+
+static void convert_nfs4name_2_user_domain(LPSTR nfs4name,
+ LPSTR *domain)
+{
+ LPSTR p = nfs4name;
+ for(; p[0] != '\0'; p++) {
+ if (p[0] == '@') {
+ p[0] = '\0';
+ *domain = &p[1];
+ break;
+ }
+ }
+}
+
+static int map_name_2_sid(DWORD *sid_len, PSID *sid, LPCSTR name)
+{
+ int status;
+ SID_NAME_USE sid_type;
+ LPSTR tmp_buf = NULL;
+ DWORD tmp = 0;
+
+ status = LookupAccountName(NULL, name, NULL, sid_len, NULL, &tmp, &sid_type);
+ dprintf(1, "LookupAccountName returned %d GetLastError %d owner len %d "
+ "domain len %d\n", status, GetLastError(), *sid_len, tmp);
+ if (!status) {
+ status = GetLastError();
+ switch(status) {
+ case ERROR_INSUFFICIENT_BUFFER:
+ *sid = malloc(*sid_len);
+ if (*sid == NULL) {
+ status = GetLastError();
+ goto out;
+ }
+ tmp_buf = (LPSTR) malloc(tmp);
+ if (tmp_buf == NULL) {
+ status = GetLastError();
+ free(*sid);
+ goto out;
+ }
+ status = LookupAccountName(NULL, name, *sid, sid_len, tmp_buf,
+ &tmp, &sid_type);
+ dprintf(1, "sid_type = %d\n", sid_type);
+ free(tmp_buf);
+ if (!status) {
+ status = GetLastError();
+ free(*sid);
+ dprintf(1, "handle_getacl: LookupAccountName for owner failed "
+ "with %d\n", status);
+ goto out;
+ } else {
+ LPSTR ssid = NULL;
+ if (IsValidSid(*sid))
+ if (ConvertSidToStringSidA(*sid, &ssid))
+ printf("SID %s\n", ssid);
+ else
+ printf("ConvertSidToStringSidA failed with %d\n", GetLastError());
+ else
+ printf("Invalid Sid\n");
+ if (ssid) LocalFree(ssid);
+ }
+ status = 0;
+ break;
+ case ERROR_NONE_MAPPED:
+ status = create_unknownsid(WinNullSid, sid, sid_len);
+ break;
+ }
+ } else // This shouldn't happen
+ status = ERROR_INTERNAL_ERROR;
+out:
+ return status;
+}
+
+static int handle_getacl(nfs41_upcall *upcall)
+{
+ int status = ERROR_NOT_SUPPORTED;
+ getacl_upcall_args *args = &upcall->args.getacl;
+ nfs41_open_state *state = args->state;
+ nfs41_file_info info;
+ bitmap4 attr_request;
+ LPSTR domain = NULL;
+
+ // need to cache owner/group information XX
+ ZeroMemory(&info, sizeof(info));
+ init_getattr_request(&attr_request);
+ status = nfs41_getattr(state->session, &state->file, &attr_request, &info);
+ if (status) {
+ eprintf("nfs41_cached_getattr() failed with %d\n", status);
+ goto out;
+ }
+
+ args->osid_len = args->gsid_len = 0;
+ if (args->query & OWNER_SECURITY_INFORMATION) {
+ // parse user@domain. currently ignoring domain part XX
+ convert_nfs4name_2_user_domain((LPSTR)info.owner, &domain);
+ dprintf(1, "handle_getacl: OWNER_SECURITY_INFORMATION: for user=%s domain=%s\n",
+ info.owner, domain?domain:"");
+ status = map_name_2_sid(&args->osid_len, &args->osid, (LPSTR)info.owner);
+ if (status)
+ goto out;
+ }
+ if (args->query & GROUP_SECURITY_INFORMATION) {
+ convert_nfs4name_2_user_domain((LPSTR)info.owner_group, &domain);
+ dprintf(1, "handle_getacl: GROUP_SECURITY_INFORMATION: for %s domain=%s\n",
+ info.owner_group, domain?domain:"");
+ status = map_name_2_sid(&args->gsid_len, &args->gsid, (LPSTR)info.owner_group);
+ if (status)
+ goto out;
+ }
+ if (args->query & DACL_SECURITY_INFORMATION)
+ dprintf(1, "handle_getacl: DACL_SECURITY_INFORMATION\n");
+ if (args->query & SACL_SECURITY_INFORMATION)
+ dprintf(1, "handle_getacl: SACL_SECURITY_INFORMATION\n");
+
+out:
+ return status;
+}
+
+static int marshall_acl(unsigned char **buffer, uint32_t *remaining, uint32_t sid_len, PSID sid)
+{
+ int status;
+ status = safe_write(buffer, remaining, &sid_len, sizeof(sid_len));
+ if (status) goto out;
+ if (*remaining < sid_len)
+ return ERROR_BUFFER_OVERFLOW;
+ status = CopySid(sid_len, *buffer, sid);
+ free(sid);
+ if (!status) {
+ status = GetLastError();
+ dprintf(1, "marshall_acl: CopySid failed %d\n", status);
+ goto out;
+ } else {
+ status = 0;
+ *buffer += sid_len;
+ *remaining -= sid_len;
+ }
+out:
+ return status;
+}
+
+static int marshall_getacl(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall)
+{
+ int status = ERROR_NOT_SUPPORTED;
+ getacl_upcall_args *args = &upcall->args.getacl;
+
+ if (args->query & OWNER_SECURITY_INFORMATION) {
+ status = marshall_acl(&buffer, length, args->osid_len, args->osid);
+ if (status) goto out;
+ }
+ if (args->query & GROUP_SECURITY_INFORMATION) {
+ status = marshall_acl(&buffer, length, args->gsid_len, args->gsid);
+ if (status) goto out;
+ }
+out:
+ return status;
+}
+
+const nfs41_upcall_op nfs41_op_getacl = {
+ parse_getacl,
+ handle_getacl,
+ marshall_getacl
+};
diff --git a/daemon/daemon_debug.c b/daemon/daemon_debug.c
index 1e6fae5..0cf4334 100644
--- a/daemon/daemon_debug.c
+++ b/daemon/daemon_debug.c
@@ -281,6 +281,7 @@ const char* opcode2string(DWORD opcode)
case NFS41_EA_SET: return "NFS41_EA_SET";
case NFS41_SYMLINK: return "NFS41_SYMLINK";
case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY";
+ case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY";
default: return "UNKNOWN";
}
}
diff --git a/daemon/upcall.c b/daemon/upcall.c
index 662d30c..5203c32 100644
--- a/daemon/upcall.c
+++ b/daemon/upcall.c
@@ -45,6 +45,7 @@ extern const nfs41_upcall_op nfs41_op_setattr;
extern const nfs41_upcall_op nfs41_op_setexattr;
extern const nfs41_upcall_op nfs41_op_symlink;
extern const nfs41_upcall_op nfs41_op_volume;
+extern const nfs41_upcall_op nfs41_op_getacl;
static const nfs41_upcall_op *g_upcall_op_table[] = {
&nfs41_op_mount,
@@ -61,6 +62,7 @@ static const nfs41_upcall_op *g_upcall_op_table[] = {
&nfs41_op_setexattr,
&nfs41_op_symlink,
&nfs41_op_volume,
+ &nfs41_op_getacl,
NULL,
NULL
};
diff --git a/daemon/upcall.h b/daemon/upcall.h
index f6ba892..47991c1 100644
--- a/daemon/upcall.h
+++ b/daemon/upcall.h
@@ -155,6 +155,16 @@ typedef struct __volume_upcall_args {
} info;
} volume_upcall_args;
+typedef struct __getacl_upcall_args {
+ nfs41_root *root;
+ nfs41_open_state *state;
+ SECURITY_INFORMATION query;
+ PSID osid;
+ DWORD osid_len;
+ PSID gsid;
+ DWORD gsid_len;
+} getacl_upcall_args;
+
typedef union __upcall_args {
mount_upcall_args mount;
unmount_upcall_args unmount;
@@ -169,6 +179,7 @@ typedef union __upcall_args {
readdir_upcall_args readdir;
symlink_upcall_args symlink;
volume_upcall_args volume;
+ getacl_upcall_args getacl;
} upcall_args;
typedef struct __nfs41_upcall {
diff --git a/sys/nfs41_debug.c b/sys/nfs41_debug.c
index aa629e7..bee15ad 100644
--- a/sys/nfs41_debug.c
+++ b/sys/nfs41_debug.c
@@ -613,6 +613,7 @@ const char *opcode2string(int opcode)
case NFS41_EA_SET: return "NFS41_EA_SET";
case NFS41_SYMLINK: return "NFS41_SYMLINK";
case NFS41_VOLUME_QUERY: return "NFS41_VOLUME_QUERY";
+ case NFS41_ACL_QUERY: return "NFS41_ACL_QUERY";
default: return "UNKNOWN";
}
}
diff --git a/sys/nfs41_driver.c b/sys/nfs41_driver.c
index 027c1a2..2de3979 100644
--- a/sys/nfs41_driver.c
+++ b/sys/nfs41_driver.c
@@ -216,6 +216,15 @@ typedef struct _updowncall_entry {
PVOID buf;
LONG buf_len;
} Volume;
+ struct {
+ HANDLE open_state;
+ HANDLE session;
+ SECURITY_INFORMATION query;
+ PVOID owner_buf;
+ LONG owner_buf_len;
+ PVOID owner_group_buf;
+ LONG owner_group_buf_len;
+ } QueryAcl;
} u;
} nfs41_updowncall_entry;
@@ -1088,6 +1097,41 @@ out:
return status;
}
+NTSTATUS marshal_nfs41_getacl(nfs41_updowncall_entry *entry,
+ unsigned char *buf,
+ ULONG buf_len,
+ ULONG *len)
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ ULONG header_len = 0;
+ unsigned char *tmp = buf;
+
+ DbgEn();
+ status = marshal_nfs41_header(entry, tmp, buf_len, len);
+ if (status == STATUS_INSUFFICIENT_RESOURCES)
+ goto out;
+ else
+ tmp += *len;
+ header_len = *len + 2 * sizeof(HANDLE) + sizeof(SECURITY_INFORMATION);
+ if (header_len > buf_len) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto out;
+ }
+
+ RtlCopyMemory(tmp, &entry->u.Volume.session, sizeof(HANDLE));
+ tmp += sizeof(HANDLE);
+ RtlCopyMemory(tmp, &entry->u.Volume.open_state, sizeof(HANDLE));
+ tmp += sizeof(HANDLE);
+ RtlCopyMemory(tmp, &entry->u.Volume.query, sizeof(SECURITY_INFORMATION));
+ *len = header_len;
+
+ DbgP("session=0x%x open_state=0x%x query=%d\n", entry->u.QueryAcl.session,
+ entry->u.QueryAcl.open_state, entry->u.QueryAcl.query);
+out:
+ DbgEx();
+ return status;
+}
+
NTSTATUS marshal_nfs41_shutdown(nfs41_updowncall_entry *entry,
unsigned char *buf,
ULONG buf_len,
@@ -1180,6 +1224,9 @@ handle_upcall(
case NFS41_VOLUME_QUERY:
status = marshal_nfs41_volume(entry, pbOut, cbOut, len);
break;
+ case NFS41_ACL_QUERY:
+ status = marshal_nfs41_getacl(entry, pbOut, cbOut, len);
+ break;
default:
status = STATUS_INVALID_PARAMETER;
print_error("Unknown nfs41 ops %d\n", entry->opcode);
@@ -1517,6 +1564,32 @@ nfs41_downcall (
cur->u.Volume.buf_len = tmp->u.Volume.buf_len;
RtlCopyMemory(cur->u.Volume.buf, buf, tmp->u.Volume.buf_len);
break;
+ case NFS41_ACL_QUERY:
+ if (cur->u.QueryAcl.query & OWNER_SECURITY_INFORMATION) {
+ RtlCopyMemory(&tmp->u.QueryAcl.owner_buf_len, buf, sizeof(LONG));
+ buf += sizeof(LONG);
+ if (tmp->u.QueryAcl.owner_buf_len > cur->u.QueryAcl.owner_buf_len) {
+ cur->status = STATUS_BUFFER_TOO_SMALL;
+ cur->u.QueryAcl.owner_buf_len = tmp->u.QueryAcl.owner_buf_len;
+ break;
+ }
+ cur->u.QueryAcl.owner_buf_len = tmp->u.QueryAcl.owner_buf_len;
+ RtlCopySid(cur->u.QueryAcl.owner_buf_len, cur->u.QueryAcl.owner_buf, buf);
+ buf += tmp->u.QueryAcl.owner_buf_len;
+ }
+ if (cur->u.QueryAcl.query & GROUP_SECURITY_INFORMATION) {
+ RtlCopyMemory(&tmp->u.QueryAcl.owner_group_buf_len, buf, sizeof(LONG));
+ buf += sizeof(LONG);
+ if (tmp->u.QueryAcl.owner_group_buf_len > cur->u.QueryAcl.owner_group_buf_len) {
+ cur->status = STATUS_BUFFER_TOO_SMALL;
+ cur->u.QueryAcl.owner_group_buf_len = tmp->u.QueryAcl.owner_group_buf_len;
+ break;
+ }
+ cur->u.QueryAcl.owner_group_buf_len = tmp->u.QueryAcl.owner_group_buf_len;
+ RtlCopySid(cur->u.QueryAcl.owner_group_buf_len, cur->u.QueryAcl.owner_group_buf, buf);
+ buf += tmp->u.QueryAcl.owner_group_buf_len;
+ }
+ break;
}
}
DbgP("[downcall] About to signal waiting IO thread\n");
@@ -3609,12 +3682,125 @@ out:
DbgEx();
return status;
}
-
+static NTSTATUS map_query_acl_error(DWORD error)
+{
+ switch (error) {
+ case ERROR_NOT_SUPPORTED: return STATUS_NOT_SUPPORTED;
+ case ERROR_ACCESS_DENIED: return STATUS_ACCESS_DENIED;
+ case ERROR_FILE_NOT_FOUND: return STATUS_OBJECT_NAME_NOT_FOUND;
+ case ERROR_INVALID_PARAMETER: return STATUS_INVALID_PARAMETER;
+ default:
+ print_error("failed to map windows error %d to NTSTATUS; "
+ "defaulting to STATUS_INVALID_NETWORK_RESPONSE\n", error);
+ case ERROR_BAD_NET_RESP: return STATUS_INVALID_NETWORK_RESPONSE;
+ }
+}
NTSTATUS nfs41_QuerySecurityInformation (
IN OUT PRX_CONTEXT RxContext)
{
NTSTATUS status = STATUS_NOT_SUPPORTED; //STATUS_SUCCESS;
+ nfs41_updowncall_entry *entry;
+ PNFS41_FOBX nfs41_fobx = (PNFS41_FOBX)(RxContext->pFobx)->Context;
+ __notnull PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
+ PNFS41_V_NET_ROOT_EXTENSION pVNetRootContext =
+ NFS41GetVNetRootExtension(SrvOpen->pVNetRoot);
+ PNFS41_NETROOT_EXTENSION pNetRootContext =
+ NFS41GetNetRootExtension(SrvOpen->pVNetRoot->pNetRoot);
+ BYTE owner_buf[SECURITY_MAX_SID_SIZE], group_buf[SECURITY_MAX_SID_SIZE];
+
DbgEn();
+ print_debug_header(RxContext);
+
+ status = nfs41_UpcallCreate(NFS41_ACL_QUERY, &nfs41_fobx->sec_ctx, &entry);
+ if (status)
+ goto out;
+ entry->u.QueryAcl.open_state = nfs41_fobx->nfs41_open_state;
+ entry->u.QueryAcl.session = pVNetRootContext->session;
+ entry->u.QueryAcl.query = RxContext->CurrentIrpSp->Parameters.QuerySecurity.SecurityInformation;
+ entry->u.QueryAcl.owner_buf = owner_buf;
+ entry->u.QueryAcl.owner_buf_len = SECURITY_MAX_SID_SIZE;
+ entry->u.QueryAcl.owner_group_buf = group_buf;
+ entry->u.QueryAcl.owner_group_buf_len = SECURITY_MAX_SID_SIZE;
+ entry->version = pNetRootContext->nfs41d_version;
+
+ if (nfs41_UpcallWaitForReply(entry) != STATUS_SUCCESS) {
+ status = STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ if (entry->status == STATUS_BUFFER_TOO_SMALL) {
+ DbgP("nfs41_QuerySecurityInformation: our SID buffers are %d but we need %d\n",
+ SECURITY_MAX_SID_SIZE, entry->u.QueryFile.buf_len);
+ status = STATUS_INTERNAL_ERROR;
+ } else if (entry->status == STATUS_SUCCESS) {
+ PSECURITY_DESCRIPTOR sec_desc = (PSECURITY_DESCRIPTOR)
+ RxContext->CurrentIrp->UserBuffer;
+ SECURITY_DESCRIPTOR tmp_sec_desc;
+ ULONG tmpLength = RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length;
+ status = RtlCreateSecurityDescriptor(&tmp_sec_desc, SECURITY_DESCRIPTOR_REVISION);
+ if (status) {
+ DbgP("RtlCreateSecurityDescriptor failed %x\n", status);
+ goto out_free;
+ }
+ if (entry->u.QueryAcl.query & OWNER_SECURITY_INFORMATION) {
+ PSID osid = (PSID)owner_buf;
+ if (RtlValidSid(osid)) {
+ status = RtlSetOwnerSecurityDescriptor(&tmp_sec_desc, osid, TRUE);
+ if (status) {
+ DbgP("RtlSetOwnerSecurityDescriptor returned %x\n", status);
+ goto out_free;
+ }
+ DbgP("added owner sid\n");
+ } else {
+ DbgP("INVALID OWNER SID: adding NULL sid\n");
+ status = RtlSetOwnerSecurityDescriptor(&tmp_sec_desc, NULL, TRUE);
+ if (status) {
+ DbgP("RtlSetOwnerSecurityDescriptor returned %x\n", status);
+ goto out_free;
+ }
+ }
+ }
+ if (entry->u.QueryAcl.query & GROUP_SECURITY_INFORMATION) {
+ PSID gsid = (PSID)group_buf;
+ if (RtlValidSid(gsid)) {
+ status = RtlSetGroupSecurityDescriptor(&tmp_sec_desc, gsid, TRUE);
+ if (status) {
+ DbgP("RtlSetOwnerSecurityDescriptor returned %x\n", status);
+ goto out_free;
+ }
+ DbgP("adder group sid\n");
+ } else {
+ DbgP("INVAID GROUP SID: adding NULL sid\n");
+ status = RtlSetGroupSecurityDescriptor(&tmp_sec_desc, NULL, TRUE);
+ if (status) {
+ DbgP("RtlSetOwnerSecurityDescriptor returned %x\n", status);
+ goto out_free;
+ }
+ }
+ }
+ if (entry->u.QueryAcl.query & DACL_SECURITY_INFORMATION)
+ DbgP("handle_getacl: DACL_SECURITY_INFORMATION\n");
+ if (entry->u.QueryAcl.query & SACL_SECURITY_INFORMATION)
+ DbgP("handle_getacl: SACL_SECURITY_INFORMATION\n");
+
+ status = RtlAbsoluteToSelfRelativeSD(&tmp_sec_desc, sec_desc, &tmpLength);
+ if (status) {
+ DbgP("RtlAbsoluteToSelfRelativeSD failed %x have %dbytes need %dbytes\n",
+ status, RxContext->CurrentIrpSp->Parameters.QuerySecurity.Length, tmpLength);
+ if (status == STATUS_BUFFER_TOO_SMALL)
+ RxContext->InformationToReturn = tmpLength;
+ goto out_free;
+ }
+
+ RxContext->IoStatusBlock.Information = RxContext->InformationToReturn =
+ RtlLengthSecurityDescriptor(sec_desc);
+ RxContext->IoStatusBlock.Status = status = STATUS_SUCCESS;
+ } else {
+ status = map_query_acl_error(entry->status);
+ }
+out_free:
+ RxFreePool(entry);
+out:
DbgEx();
return status;
}
diff --git a/sys/nfs41_driver.h b/sys/nfs41_driver.h
index 4f8d363..80cf0af 100644
--- a/sys/nfs41_driver.h
+++ b/sys/nfs41_driver.h
@@ -66,6 +66,8 @@ typedef enum _nfs41_opcodes {
NFS41_EA_SET,
NFS41_SYMLINK,
NFS41_VOLUME_QUERY,
+ NFS41_ACL_QUERY,
+ NFS41_ACL_SET,
NFS41_SHUTDOWN,
INVALID_OPCODE
} nfs41_opcodes;