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;