/*++ Copyright (c) 1998-2002 Microsoft Corporation Module Name: create.c Abstract: This module contains code for opening a handle to UL. Author: Keith Moore (keithmo) 10-Jun-1998 Revision History: --*/ #include "precomp.h" #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, UlCreate ) #endif // ALLOC_PRAGMA #define IS_NAMED_FILE_OBJECT(pFileObject) \ ((pFileObject)->FileName.Length != 0) // // Public functions. // /***************************************************************************++ Routine Description: This is the routine that handles Create IRPs in Http.sys. Create IRPs are issued when the file object is created. Control Channel (\Device\Http\Control) - unnamed only - open only, create to fail. - Open will be allowed by any user. - EA must have proper major/minorversion, everything else must be NULL/0 AppPool (\Device\Http\AppPool) - can be unnamed or named - unnamed --> anyone can create, no one can open (server API customers) - named --> admin only can create, anyone with correct SD can open (IIS WAS + Worker process) - EA must have proper major/minorversion, everything else must be NULL/0 Filter (\Device\Http\Filter) - only named and MUST be either SSLFilterChannel or SSLClientFilterChannel. - SSLFilterChannel can be created only by admin/local system, opened with correct SD. - SSLClientFilterChannel can be created by anyone, opened by anyone with correct SD, but only if EnableHttpClient is set - EA must have proper major/minorversion, everything else must be NULL/0 Server (\Device\Http\Server\) - only unnamed - Create only, open to fail. - can be done by anyone. - Should be allowed only if EnableHttpClient is present. - EA must have major/minorversion, server & TRANSPORT_ADDRESS structure. Proxy is optional. Arguments: pDeviceObject - Supplies a pointer to the target device object. pIrp - Supplies a pointer to IO request packet. Return Value: NTSTATUS - Completion status. --***************************************************************************/ NTSTATUS UlCreate( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp ) { NTSTATUS status; PIO_STACK_LOCATION pIrpSp; PFILE_OBJECT pFileObject = NULL; PFILE_FULL_EA_INFORMATION pEaBuffer; PHTTP_OPEN_PACKET pOpenPacket; UCHAR createDisposition; PWSTR pName = NULL; USHORT nameLength; PIO_SECURITY_CONTEXT pSecurityContext; STRING CompareVersionName; STRING EaName; PWSTR pSafeName = NULL; // // Sanity check. // PAGED_CODE(); UL_ENTER_DRIVER( "UlCreate", pIrp ); #if defined(_WIN64) // // We do not support 32-bit processes on 64-bit platforms. // if (IoIs32bitProcess(pIrp)) { status = STATUS_NOT_SUPPORTED; goto complete; } #endif // // Find and validate the open packet. // pEaBuffer = (PFILE_FULL_EA_INFORMATION)(pIrp->AssociatedIrp.SystemBuffer); if (pEaBuffer == NULL) { status = STATUS_INVALID_PARAMETER; goto complete; } RtlInitString(&CompareVersionName, HTTP_OPEN_PACKET_NAME); EaName.MaximumLength = pEaBuffer->EaNameLength + 1; EaName.Length = pEaBuffer->EaNameLength; EaName.Buffer = pEaBuffer->EaName; if ( RtlEqualString(&CompareVersionName, &EaName, FALSE) ) { // // Found the version information in the EA // if( pEaBuffer->EaValueLength != sizeof(*pOpenPacket) ) { status = STATUS_INVALID_PARAMETER; goto complete; } if(pEaBuffer->NextEntryOffset != 0) { status = STATUS_INVALID_PARAMETER; goto complete; } pOpenPacket = (PHTTP_OPEN_PACKET) (pEaBuffer->EaName + pEaBuffer->EaNameLength + 1 ); ASSERT(pOpenPacket == ALIGN_UP_POINTER(pOpenPacket, PVOID)); // // For now, we'll fail if the incoming version doesn't EXACTLY match // the expected version. In future, we may need to be a bit more // flexible to allow down-level clients. // if (pOpenPacket->MajorVersion != HTTP_INTERFACE_VERSION_MAJOR || pOpenPacket->MinorVersion != HTTP_INTERFACE_VERSION_MINOR) { status = STATUS_REVISION_MISMATCH; goto complete; } if(pDeviceObject != g_pUcServerDeviceObject && (pOpenPacket->ProxyNameLength != 0 || pOpenPacket->ServerNameLength != 0 || pOpenPacket->TransportAddressLength != 0 || pOpenPacket->pProxyName != NULL || pOpenPacket->pServerName != NULL || pOpenPacket->pTransportAddress != NULL)) { status = STATUS_INVALID_PARAMETER; goto complete; } } else { status = STATUS_INVALID_PARAMETER; goto complete; } // // Snag the current IRP stack pointer, then extract the creation // disposition. IO stores this as the high byte of the Options field. // Also snag the file object; we'll need it often. // pIrpSp = IoGetCurrentIrpStackLocation( pIrp ); createDisposition = (UCHAR)( pIrpSp->Parameters.Create.Options >> 24 ); pFileObject = pIrpSp->FileObject; pSecurityContext = pIrpSp->Parameters.Create.SecurityContext; ASSERT( pSecurityContext != NULL ); // // Determine if this is a request to open a control channel or // open/create an app pool. // if (pDeviceObject == g_pUlControlDeviceObject) { // // It's a control channel. // // Validate the creation disposition. We allow open only. // if (createDisposition != FILE_OPEN) { status = STATUS_INVALID_PARAMETER; goto complete; } // These things can't be named if (IS_NAMED_FILE_OBJECT(pFileObject)) { status = STATUS_INVALID_PARAMETER; goto complete; } ASSERT(pFileObject->FileName.Buffer == NULL); UlTrace(OPEN_CLOSE, ( "UlCreate: opening a control channel: %p\n", pFileObject )); // // Open the control channel. // status = UlCreateControlChannel(GET_PP_CONTROL_CHANNEL(pFileObject)); if (NT_SUCCESS(status)) { ASSERT( GET_CONTROL_CHANNEL(pFileObject) != NULL ); MARK_VALID_CONTROL_CHANNEL( pFileObject ); } } else if (pDeviceObject == g_pUlFilterDeviceObject) { // // It's a filter channel - It has to be named and has to be either // a client or a server filter channel. // if (!IS_NAMED_FILE_OBJECT(pFileObject)) { status = STATUS_INVALID_PARAMETER; goto complete; } ASSERT(L'\\' == pFileObject->FileName.Buffer[0]); pName = pFileObject->FileName.Buffer + 1; nameLength = pFileObject->FileName.Length - sizeof(WCHAR); pSafeName = UL_ALLOCATE_POOL( PagedPool, nameLength + sizeof(WCHAR), UL_STRING_LOG_BUFFER_POOL_TAG ); if(pSafeName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } RtlCopyMemory(pSafeName, pName, nameLength); pSafeName[nameLength/sizeof(WCHAR)] = L'\0'; pName = pSafeName; if(IsServerFilterChannel(pName, nameLength)) { // Yes - it's a filter channel. We'll allow Create or Open but // both have to be admin only. // // If it's create, we'll do an access check // if(createDisposition == FILE_CREATE) { status = UlAccessCheck( g_pAdminAllSystemAll, pSecurityContext->AccessState, pSecurityContext->DesiredAccess, pIrp->RequestorMode, pName ); if(!NT_SUCCESS(status)) { goto complete; } } else if(createDisposition == FILE_OPEN) { // We are opening an existing channel - the access check // will be done inside UlAttachFilterProcess } else { // Neither FILE_CREATE nor FILE_OPEN. Bail! status = STATUS_INVALID_PARAMETER; goto complete; } UlTrace(OPEN_CLOSE, ( "UlCreate: opening a server filter channel: %p, %.*ls\n", pFileObject, nameLength / sizeof(WCHAR), pName )); } else if(IsClientFilterChannel(pName, nameLength)) { // It's a client - Only Create. Also, make sure that the client // code is really enabled. if (createDisposition != FILE_CREATE || !g_HttpClientEnabled) { status = STATUS_INVALID_PARAMETER; goto complete; } UlTrace(OPEN_CLOSE, ( "UlCreate: opening a client filter channel: %p, %.*ls\n", pFileObject, nameLength / sizeof(WCHAR), pName )); // // No access checks for the client! // } else { // // If it is neither server nor client filter channel, fail the // call. // status = STATUS_INVALID_PARAMETER; goto complete; } status = UlAttachFilterProcess( pName, nameLength, (BOOLEAN)(createDisposition == FILE_CREATE), pSecurityContext->AccessState, pSecurityContext->DesiredAccess, pIrp->RequestorMode, GET_PP_FILTER_PROCESS(pFileObject) ); if (NT_SUCCESS(status)) { ASSERT( GET_FILTER_PROCESS(pFileObject) != NULL ); MARK_VALID_FILTER_CHANNEL( pFileObject ); } } else if(pDeviceObject == g_pUlAppPoolDeviceObject ) { // // It's an app pool. // // // Bind to the specified app pool. // if (!IS_NAMED_FILE_OBJECT(pFileObject)) { ASSERT(pFileObject->FileName.Buffer == NULL); pName = NULL; nameLength = 0; // Validate the creation disposition. We allow create only // for unnamed app-pools. if(createDisposition != FILE_CREATE) { status = STATUS_INVALID_PARAMETER; goto complete; } UlTrace(OPEN_CLOSE, ( "UlCreate: opening an unnamed AppPool: %p\n", pFileObject )); } else { if (pFileObject->FileName.Length > UL_MAX_APP_POOL_NAME_SIZE) { status = STATUS_OBJECT_NAME_INVALID; goto complete; } // Skip the preceding '\' in the FileName added by iomgr. // ASSERT(L'\\' == pFileObject->FileName.Buffer[0]); pName = pFileObject->FileName.Buffer + 1; nameLength = pFileObject->FileName.Length - sizeof(WCHAR); pSafeName = UL_ALLOCATE_POOL( PagedPool, nameLength + sizeof(WCHAR), UL_STRING_LOG_BUFFER_POOL_TAG ); if(pSafeName == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; goto complete; } RtlCopyMemory(pSafeName, pName, nameLength); pSafeName[nameLength/sizeof(WCHAR)] = L'\0'; pName = pSafeName; if(createDisposition == FILE_CREATE) { // // Creation of named app-pools must be only for admins. // // The filter object is has FileAll for Admin/LocalSystem only // so, we'll piggy back on that security descriptor. // status = UlAccessCheck( g_pAdminAllSystemAll, pSecurityContext->AccessState, pSecurityContext->DesiredAccess, pIrp->RequestorMode, pName ); if(!NT_SUCCESS(status)) { goto complete; } } else if(createDisposition == FILE_OPEN) { // UlAttachProcessToAppPool will do the appropriate checks // to ensure that the security descriptors match. } else { // Neither FILE_CREATE nor FILE_OPEN. status = STATUS_INVALID_PARAMETER; goto complete; } UlTrace(OPEN_CLOSE, ( "UlCreate: opening an AppPool: %p, %.*ls\n", pFileObject, nameLength / sizeof(WCHAR), pName )); } status = UlAttachProcessToAppPool( pName, nameLength, (BOOLEAN)(createDisposition == FILE_CREATE), pSecurityContext->AccessState, pSecurityContext->DesiredAccess, pIrp->RequestorMode, GET_PP_APP_POOL_PROCESS(pFileObject) ); if (NT_SUCCESS(status)) { ASSERT( GET_APP_POOL_PROCESS(pFileObject) != NULL ); MARK_VALID_APP_POOL( pFileObject ); } } else { ASSERT(pDeviceObject == g_pUcServerDeviceObject ); ASSERT(g_HttpClientEnabled); // // It is mandatory for the application to pass in a valid version // and a valid URI. If either of this is missing, we bail. // // if(pOpenPacket->ServerNameLength == 0 || pOpenPacket->pServerName == NULL || pOpenPacket->pTransportAddress == NULL || pOpenPacket->TransportAddressLength == 0 || IS_NAMED_FILE_OBJECT(pFileObject) ) { status = STATUS_INVALID_PARAMETER; goto complete; } if(createDisposition != FILE_CREATE) { status = STATUS_INVALID_PARAMETER; goto complete; } UlTrace(OPEN_CLOSE, ( "UlCreate: opening a ServInfo: %p\n", pFileObject )); // // Create our context here and store it in // pIrpSp->FileObject->FsContext // status = UcCreateServerInformation( (PUC_PROCESS_SERVER_INFORMATION *) &pFileObject->FsContext, pOpenPacket->pServerName, pOpenPacket->ServerNameLength, pOpenPacket->pProxyName, pOpenPacket->ProxyNameLength, pOpenPacket->pTransportAddress, pOpenPacket->TransportAddressLength, pIrp->RequestorMode ); // // UC_BUGBUG (INVESTIGATE) // // Setting this field to non-NULL value enable fast IO code path // for reads and writes. // // pIrpSp->FileObject->PrivateCacheMap = (PVOID)-1; MARK_VALID_SERVER( pFileObject ); } // // Complete the request. // complete: if(pSafeName) { ASSERT(pSafeName == pName); UL_FREE_POOL(pSafeName, UL_STRING_LOG_BUFFER_POOL_TAG); } UlTrace(OPEN_CLOSE, ( "UlCreate: %s file object = %p, %s\n", (NT_SUCCESS(status) ? "opened" : "did not open"), pFileObject, HttpStatusToString(status) )); pIrp->IoStatus.Status = status; UlCompleteRequest( pIrp, IO_NO_INCREMENT ); UL_LEAVE_DRIVER( "UlCreate" ); RETURN(status); } // UlCreate