support for multiuser environment

on a mount request, search a list of existing mounts for a given netroot
based on LUID entries (login ids). if an entry is found, then check the
current request rpcsec flavor against existing sessions/mounts. If no
match found, do another upcall for a mount and store a session of this
flavor for this LUID. if no entry found for this LUID, then do a mount
upcall, create a new entry and add it to the list.
This commit is contained in:
Olga Kornievskaia 2011-09-29 18:45:21 -04:00
parent 436df23cba
commit 52081e3175

View file

@ -235,6 +235,19 @@ typedef struct _updowncall_list {
} nfs41_updowncall_list; } nfs41_updowncall_list;
nfs41_updowncall_list *upcall = NULL, *downcall = NULL; nfs41_updowncall_list *upcall = NULL, *downcall = NULL;
typedef struct _nfs41_mount_entry {
LIST_ENTRY next;
LUID login_id;
HANDLE authsys_session;
HANDLE gss_session;
HANDLE gssi_session;
HANDLE gssp_session;
} nfs41_mount_entry;
typedef struct _nfs41_mount_list {
LIST_ENTRY head;
} nfs41_mount_list;
#define nfs41_AddEntry(lock,pList,pEntry) \ #define nfs41_AddEntry(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \ ExAcquireFastMutex(&lock); \
InsertTailList(&pList->head, &(pEntry)->next); \ InsertTailList(&pList->head, &(pEntry)->next); \
@ -266,6 +279,15 @@ nfs41_updowncall_list *upcall = NULL, *downcall = NULL;
nfs41_updowncall_entry, \ nfs41_updowncall_entry, \
next))); \ next))); \
ExReleaseFastMutex(&lock); ExReleaseFastMutex(&lock);
#define nfs41_GetFirstMountEntry(lock,pList,pEntry) \
ExAcquireFastMutex(&lock); \
pEntry = (IsListEmpty(&pList->head) \
? NULL \
: (nfs41_mount_entry *) \
(CONTAINING_RECORD(pList->head.Flink, \
nfs41_mount_entry, \
next))); \
ExReleaseFastMutex(&lock);
#define nfs41_GetNextEntry(pList,pEntry) \ #define nfs41_GetNextEntry(pList,pEntry) \
((pEntry->next.Flink == &pList->head) \ ((pEntry->next.Flink == &pList->head) \
? NULL \ ? NULL \
@ -304,10 +326,10 @@ typedef struct _NFS41_MOUNT_CONFIG {
typedef struct _NFS41_NETROOT_EXTENSION { typedef struct _NFS41_NETROOT_EXTENSION {
NODE_TYPE_CODE NodeTypeCode; NODE_TYPE_CODE NodeTypeCode;
NODE_BYTE_SIZE NodeByteSize; NODE_BYTE_SIZE NodeByteSize;
HANDLE auth_sys_session;
HANDLE gss_session;
DWORD nfs41d_version; DWORD nfs41d_version;
BOOLEAN do_umount; BOOLEAN mounts_init;
FAST_MUTEX mountLock;
nfs41_mount_list *mounts;
} NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION; } NFS41_NETROOT_EXTENSION, *PNFS41_NETROOT_EXTENSION;
#define NFS41GetNetRootExtension(pNetRoot) \ #define NFS41GetNetRootExtension(pNetRoot) \
(((pNetRoot) == NULL) ? NULL : (PNFS41_NETROOT_EXTENSION)((pNetRoot)->Context)) (((pNetRoot) == NULL) ? NULL : (PNFS41_NETROOT_EXTENSION)((pNetRoot)->Context))
@ -2546,6 +2568,36 @@ static NTSTATUS map_sec_flavor(
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
NTSTATUS nfs41_GetLUID(PLUID id)
{
NTSTATUS status = STATUS_SUCCESS;
SECURITY_SUBJECT_CONTEXT sec_ctx;
SECURITY_QUALITY_OF_SERVICE sec_qos;
SECURITY_CLIENT_CONTEXT clnt_sec_ctx;
SeCaptureSubjectContext(&sec_ctx);
sec_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
sec_qos.ImpersonationLevel = SecurityIdentification/*SecurityImpersonation*/;
sec_qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
sec_qos.EffectiveOnly = 0;
status = SeCreateClientSecurityFromSubjectContext(&sec_ctx, &sec_qos, 1, &clnt_sec_ctx);
if (status) {
DbgP("SeCreateClientSecurityFromSubjectContext failed %x\n", status);
goto release_sec_ctx;
}
status = SeQueryAuthenticationIdToken(clnt_sec_ctx.ClientToken, id);
if (status) {
DbgP("SeQueryAuthenticationIdToken failed %x\n", status);
goto release_clnt_sec_ctx;
}
release_clnt_sec_ctx:
SeDeleteClientSecurity(&clnt_sec_ctx);
release_sec_ctx:
SeReleaseSubjectContext(&sec_ctx);
return status;
}
NTSTATUS nfs41_CreateVNetRoot( NTSTATUS nfs41_CreateVNetRoot(
IN OUT PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext) IN OUT PMRX_CREATENETROOT_CONTEXT pCreateNetRootContext)
{ {
@ -2560,6 +2612,10 @@ NTSTATUS nfs41_CreateVNetRoot(
NFS41GetNetRootExtension(pNetRoot); NFS41GetNetRootExtension(pNetRoot);
NFS41GetDeviceExtension(pCreateNetRootContext->RxContext,DevExt); NFS41GetDeviceExtension(pCreateNetRootContext->RxContext,DevExt);
DWORD nfs41d_version = DevExt->nfs41d_version; DWORD nfs41d_version = DevExt->nfs41d_version;
nfs41_mount_entry *existing_mount = NULL;
LUID luid;
BOOLEAN found_existing_mount = FALSE, found_matching_flavor = FALSE;
ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) && ASSERT((NodeType(pNetRoot) == RDBSS_NTC_NETROOT) &&
(NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL)); (NodeType(pNetRoot->pSrvCall) == RDBSS_NTC_SRVCALL));
@ -2617,32 +2673,125 @@ NTSTATUS nfs41_CreateVNetRoot(
goto out; goto out;
} }
if (pVNetRootContext->sec_flavor == RPCSEC_AUTH_SYS && status = nfs41_GetLUID(&luid);
pNetRootContext->auth_sys_session) { if (status) {
pVNetRootContext->session = pNetRootContext->auth_sys_session;
DbgP("Using existing AUTH_SYS session 0x%x\n", pVNetRootContext->session);
goto out;
} else if ((pVNetRootContext->sec_flavor != RPCSEC_AUTH_SYS ||
pNetRoot->Type != NET_ROOT_WILD) &&
pNetRootContext->gss_session) {
pVNetRootContext->session = pNetRootContext->gss_session;
DbgP("Using existing AUTHGSS session 0x%x\n", pVNetRootContext->session);
goto out; goto out;
} }
/* send the mount upcall */ if (!pNetRootContext->mounts_init) {
DbgP("Server Name %wZ Mount Point %wZ SecFlavor %wZ\n", DbgP("Initializing mount array\n");
&Config.SrvName, &Config.MntPt, &Config.SecFlavor); ExInitializeFastMutex(&pNetRootContext->mountLock);
status = nfs41_mount(&Config.SrvName, &Config.MntPt, pVNetRootContext->sec_flavor, pNetRootContext->mounts = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_mount_list),
&pVNetRootContext->session, &nfs41d_version); NFS41_MM_POOLTAG);
if (status != STATUS_SUCCESS) if (pNetRootContext->mounts == NULL) {
goto out; status = STATUS_INSUFFICIENT_RESOURCES;
goto out;
}
InitializeListHead(&pNetRootContext->mounts->head);
pNetRootContext->mounts_init = TRUE;
} else {
PLIST_ENTRY pEntry;
ExAcquireFastMutex(&pNetRootContext->mountLock);
pEntry = &pNetRootContext->mounts->head;
while (pEntry != NULL) {
existing_mount = (nfs41_mount_entry *)CONTAINING_RECORD(pEntry,
nfs41_mount_entry, next);
DbgP("comparing %x.%x with %x.%x\n", luid.HighPart, luid.LowPart,
existing_mount->login_id.HighPart, existing_mount->login_id.LowPart);
if (RtlEqualLuid(&luid, &existing_mount->login_id)) {
DbgP("Found a matching LUID entry\n");
found_existing_mount = TRUE;
switch(pVNetRootContext->sec_flavor) {
case RPCSEC_AUTH_SYS:
if (existing_mount->authsys_session != INVALID_HANDLE_VALUE)
pVNetRootContext->session = existing_mount->authsys_session;
break;
case RPCSEC_AUTHGSS_KRB5:
if (existing_mount->gssi_session != INVALID_HANDLE_VALUE)
pVNetRootContext->session = existing_mount->gss_session;
break;
case RPCSEC_AUTHGSS_KRB5I:
if (existing_mount->gss_session != INVALID_HANDLE_VALUE)
pVNetRootContext->session = existing_mount->gssi_session;
break;
case RPCSEC_AUTHGSS_KRB5P:
if (existing_mount->gssp_session != INVALID_HANDLE_VALUE)
pVNetRootContext->session = existing_mount->gssp_session;
break;
}
if (pVNetRootContext->session)
found_matching_flavor = 1;
break;
}
if (pEntry->Flink == &pNetRootContext->mounts->head) {
DbgP("reached end of the list\n");
break;
}
pEntry = pEntry->Flink;
}
ExReleaseFastMutex(&pNetRootContext->mountLock);
if (!found_matching_flavor)
DbgP("Didn't find matching security flavor\n");
}
if (!found_existing_mount || !found_matching_flavor) {
/* send the mount upcall */
DbgP("Server Name %wZ Mount Point %wZ SecFlavor %wZ\n",
&Config.SrvName, &Config.MntPt, &Config.SecFlavor);
status = nfs41_mount(&Config.SrvName, &Config.MntPt, pVNetRootContext->sec_flavor,
&pVNetRootContext->session, &nfs41d_version);
if (status != STATUS_SUCCESS) {
if (!found_existing_mount) {
RxFreePool(pNetRootContext->mounts);
pNetRootContext->mounts_init = FALSE;
pVNetRootContext->session = INVALID_HANDLE_VALUE;
}
goto out;
}
}
if (!found_existing_mount) {
/* create a new mount entry and add it to the list */
nfs41_mount_entry *entry;
entry = RxAllocatePoolWithTag(NonPagedPool, sizeof(nfs41_mount_entry),
NFS41_MM_POOLTAG);
if (entry == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
RxFreePool(pNetRootContext->mounts);
goto out;
}
entry->authsys_session = entry->gss_session =
entry->gssi_session = entry->gssp_session = INVALID_HANDLE_VALUE;
switch (pVNetRootContext->sec_flavor) {
case RPCSEC_AUTH_SYS:
entry->authsys_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5:
entry->gss_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5I:
entry->gssi_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5P:
entry->gssp_session = pVNetRootContext->session; break;
}
RtlCopyLuid(&entry->login_id, &luid);
nfs41_AddEntry(pNetRootContext->mountLock, pNetRootContext->mounts, entry);
} else if (!found_matching_flavor) {
ASSERT(existing_mount != NULL);
/* modify existing mount entry */
DbgP("Using existing %d flavor session 0x%x\n",
pVNetRootContext->sec_flavor);
switch (pVNetRootContext->sec_flavor) {
case RPCSEC_AUTH_SYS:
existing_mount->authsys_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5:
existing_mount->gss_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5I:
existing_mount->gssi_session = pVNetRootContext->session; break;
case RPCSEC_AUTHGSS_KRB5P:
existing_mount->gssp_session = pVNetRootContext->session; break;
}
}
pNetRootContext->nfs41d_version = nfs41d_version; pNetRootContext->nfs41d_version = nfs41d_version;
pNetRootContext->do_umount = TRUE;
if (pVNetRootContext->sec_flavor == RPCSEC_AUTH_SYS)
pNetRootContext->auth_sys_session = pVNetRootContext->session;
else
pNetRootContext->gss_session = pVNetRootContext->session;
DbgP("Saving new session 0x%x\n", pVNetRootContext->session); DbgP("Saving new session 0x%x\n", pVNetRootContext->session);
out: out:
@ -2730,6 +2879,7 @@ NTSTATUS nfs41_FinalizeNetRoot(
PNFS41_NETROOT_EXTENSION pNetRootContext = PNFS41_NETROOT_EXTENSION pNetRootContext =
NFS41GetNetRootExtension((PMRX_NET_ROOT)pNetRoot); NFS41GetNetRootExtension((PMRX_NET_ROOT)pNetRoot);
nfs41_updowncall_entry *tmp; nfs41_updowncall_entry *tmp;
nfs41_mount_entry *mount_tmp;
DbgEn(); DbgEn();
print_net_root(1, pNetRoot); print_net_root(1, pNetRoot);
@ -2739,8 +2889,7 @@ NTSTATUS nfs41_FinalizeNetRoot(
goto out; goto out;
} }
if (pNetRootContext == NULL || (pNetRootContext->auth_sys_session == NULL && if (pNetRootContext == NULL || !pNetRootContext->mounts_init) {
pNetRootContext->gss_session == NULL)) {
print_error("No valid session has been established\n"); print_error("No valid session has been established\n");
goto out; goto out;
} }
@ -2751,20 +2900,41 @@ NTSTATUS nfs41_FinalizeNetRoot(
goto out; goto out;
} }
if (pNetRootContext->auth_sys_session && pNetRootContext->do_umount) { do {
status = nfs41_unmount(pNetRootContext->auth_sys_session, pNetRootContext->nfs41d_version); nfs41_GetFirstMountEntry(pNetRootContext->mountLock,
if (status) { pNetRootContext->mounts, mount_tmp);
print_error("nfs41_mount AUTH_SYS failed with %d\n", status); if (mount_tmp == NULL)
goto out; break;
DbgP("Removing entry luid %x.%x from mount list\n", mount_tmp->login_id.HighPart,
mount_tmp->login_id.LowPart);
if (mount_tmp->authsys_session != INVALID_HANDLE_VALUE) {
status = nfs41_unmount(mount_tmp->authsys_session, pNetRootContext->nfs41d_version);
if (status)
print_error("nfs41_unmount AUTH_SYS failed with %d\n", status);
} }
} if (mount_tmp->gss_session != INVALID_HANDLE_VALUE) {
if (pNetRootContext->gss_session) { status = nfs41_unmount(mount_tmp->gss_session, pNetRootContext->nfs41d_version);
status = nfs41_unmount(pNetRootContext->gss_session, pNetRootContext->nfs41d_version); if (status)
if (status) { print_error("nfs41_unmount RPCSEC_GSS_KRB5 failed with %d\n", status);
print_error("nfs41_mount AUTHGSS failed with %d\n", status);
goto out;
} }
} if (mount_tmp->gssi_session != INVALID_HANDLE_VALUE) {
status = nfs41_unmount(mount_tmp->gssi_session, pNetRootContext->nfs41d_version);
if (status)
print_error("nfs41_unmount RPCSEC_GSS_KRB5I failed with %d\n", status);
}
if (mount_tmp->gssp_session != INVALID_HANDLE_VALUE) {
status = nfs41_unmount(mount_tmp->gssp_session, pNetRootContext->nfs41d_version);
if (status)
print_error("nfs41_unmount RPCSEC_GSS_KRB5P failed with %d\n", status);
}
nfs41_RemoveEntry(pNetRootContext->mountLock, pNetRootContext->mounts, mount_tmp);
RxFreePool(mount_tmp);
} while (1);
/* ignore any errors from unmount */
status = STATUS_SUCCESS;
RxFreePool(pNetRootContext->mounts);
// check if there is anything waiting in the upcall or downcall queue // check if there is anything waiting in the upcall or downcall queue
do { do {
nfs41_GetFirstEntry(upcallLock, upcall, tmp); nfs41_GetFirstEntry(upcallLock, upcall, tmp);
@ -2925,7 +3095,7 @@ NTSTATUS nfs41_Create(
goto out; goto out;
} }
if (pNetRootContext->auth_sys_session == NULL && pNetRootContext->gss_session == NULL) { if (!pNetRootContext->mounts_init) {
print_error("No valid session established\n"); print_error("No valid session established\n");
goto out; goto out;
} }