/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: SsSubs.c Abstract: This module contains support routines for the NT server service. Author: David Treadwell (davidtr) 10-Jan-1991 Revision History: --*/ #include "srvsvcp.h" #include "ssreg.h" #include #include #include #include #include #include #include #include #define MRXSMB_DEVICE_NAME TEXT("\\Device\\LanmanRedirector") PSERVER_REQUEST_PACKET SsAllocateSrp ( VOID ) /*++ Routine Description: This routine allocates a serer request packet so that an API can communicate with the kernel-mode server. Any general initialization in performed here. Arguments: None. Return Value: PSERVER_REQUEST_PACKET - a pointer to the allocated SRP. --*/ { PSERVER_REQUEST_PACKET srp; srp = MIDL_user_allocate( sizeof(SERVER_REQUEST_PACKET) ); if ( srp != NULL ) { RtlZeroMemory( srp, sizeof(SERVER_REQUEST_PACKET) ); } return srp; } // SsAllocateSrp #if DBG VOID SsAssert( IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber ) { BOOL ok; CHAR choice[16]; DWORD bytes; DWORD error; SsPrintf( "\nAssertion failed: %s\n at line %ld of %s\n", FailedAssertion, LineNumber, FileName ); do { HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); SsPrintf( "Break or Ignore [bi]? " ); if (hStdIn && (hStdIn != INVALID_HANDLE_VALUE)) { bytes = sizeof(choice); ok = ReadFile( hStdIn, &choice, bytes, &bytes, NULL ); } else { // default to "break" ok = TRUE; choice[0] = TEXT('B'); } if ( ok ) { if ( toupper(choice[0]) == 'I' ) { break; } if ( toupper(choice[0]) == 'B' ) { DbgUserBreakPoint( ); } } else { error = GetLastError( ); } } while ( TRUE ); return; } // SsAssert #endif VOID SsCloseServer ( VOID ) /*++ Routine Description: This routine closes the server file system device, if it has been opened. Arguments: None. Return Value: None. --*/ { // // Close the server device, if it has been opened. // if ( SsData.SsServerDeviceHandle != NULL ) { NtClose( SsData.SsServerDeviceHandle ); SsData.SsServerDeviceHandle = NULL; } } // SsCloseServer VOID SsControlCHandler ( IN ULONG CtrlType ) /*++ Routine Description: Captures and ignores a kill signal. Without this, any ^C pressed in the window that started the server service will result in this process being killed, and then the server can't function properly. Arguments: None. Return Value: None. --*/ { CtrlType; return; } // SsControlCHandler VOID SsFreeSrp ( IN PSERVER_REQUEST_PACKET Srp ) /*++ Routine Description: Frees an SRP allocated by SsAllocateSrp. Arguments: Srp - a pointer to the SRP to free. Return Value: None. --*/ { MIDL_user_free( Srp ); } // SsFreeSrp VOID SsLogEvent( IN DWORD MessageId, IN DWORD NumberOfSubStrings, IN LPWSTR *SubStrings, IN DWORD ErrorCode ) { HANDLE logHandle; DWORD dataSize = 0; LPVOID rawData = NULL; USHORT eventType = EVENTLOG_ERROR_TYPE; logHandle = RegisterEventSource( NULL, SERVER_DISPLAY_NAME ); if ( logHandle == NULL ) { SS_PRINT(( "SRVSVC: RegisterEventSource failed: %lu\n", GetLastError() )); return; } if ( ErrorCode != NERR_Success ) { // // An error code was specified. // dataSize = sizeof(ErrorCode); rawData = (LPVOID)&ErrorCode; } // // If the message is is only a warning, then set the event type as such. // This is doc'd in netevent.h. // if ((ULONG)(MessageId & 0xC0000000) == (ULONG) 0x80000000 ) { eventType = EVENTLOG_WARNING_TYPE; } // // Log the error. // if ( !ReportEventW( logHandle, eventType, 0, // event category MessageId, NULL, // user SID (WORD)NumberOfSubStrings, dataSize, SubStrings, rawData ) ) { SS_PRINT(( "SRVSVC: ReportEvent failed: %lu\n", GetLastError() )); } if ( !DeregisterEventSource( logHandle ) ) { SS_PRINT(( "SRVSVC: DeregisterEventSource failed: %lu\n", GetLastError() )); } return; } // SsLogEvent NET_API_STATUS SsOpenServer () /*++ Routine Description: This routine opens the server file system device, allowing the server service to send FS controls to it. Arguments: None. Return Value: NET_API_STATUS - results of operation. --*/ { NTSTATUS status; UNICODE_STRING unicodeServerName; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; // // Open the server device. // RtlInitUnicodeString( &unicodeServerName, SERVER_DEVICE_NAME ); InitializeObjectAttributes( &objectAttributes, &unicodeServerName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Opening the server with desired access = SYNCHRONIZE and open // options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have // to worry about waiting for the NtFsControlFile to complete--this // makes all IO system calls that use this handle synchronous. // status = NtOpenFile( &SsData.SsServerDeviceHandle, FILE_ALL_ACCESS & ~SYNCHRONIZE, &objectAttributes, &ioStatusBlock, 0, 0 ); if ( NT_SUCCESS(status) ) { status = ioStatusBlock.Status; } if ( !NT_SUCCESS(status) ) { IF_DEBUG(INITIALIZATION_ERRORS) { SS_PRINT(( "SsOpenServer: NtOpenFile (server device object) " "failed: %X\n", status )); } return NetpNtStatusToApiStatus( status ); } // // We're now ready to talk to the server. // return NO_ERROR; } // SsOpenServer #if DBG VOID SsPrintf ( char *Format, ... ) { va_list arglist; char OutputBuffer[1024]; ULONG length; HANDLE hStdOut; va_start( arglist, Format ); vsprintf( OutputBuffer, Format, arglist ); va_end( arglist ); length = strlen( OutputBuffer ); hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdOut && (hStdOut != INVALID_HANDLE_VALUE)) { WriteFile(hStdOut, (LPVOID )OutputBuffer, length, &length, NULL ); } } // SsPrintf #endif NET_API_STATUS SsServerFsControlGetInfo ( IN ULONG ServerControlCode, IN PSERVER_REQUEST_PACKET Srp, IN OUT PVOID *OutputBuffer, IN ULONG PreferredMaximumLength ) /*++ Routine Description: This routine sends an SRP to the server for an API that retrieves information from the server and takes a PreferredMaximumLength parameter. Arguments: ServerControlCode - the FSCTL code for the operation. Srp - a pointer to the SRP for the operation. OutputBuffer - a pointer to receive a pointer to the buffer allocated by this routine to hold the output information. PreferredMaximumLength - the PreferredMaximumLength parameter. Return Value: NET_API_STATUS - results of operation. --*/ { NET_API_STATUS status = STATUS_SUCCESS; ULONG resumeHandle = Srp->Parameters.Get.ResumeHandle; ULONG BufLen = min( INITIAL_BUFFER_SIZE, PreferredMaximumLength ); ULONG i; *OutputBuffer = NULL; // // Normally, we should only go through this loop at most 2 times. But // if the amount of data the server needs to return is growing, then // we might be forced to run through it a couple of more times. '5' // is an arbitrary number just to ensure we don't get stuck. // for( i=0; i < 5; i++ ) { if( *OutputBuffer ) { MIDL_user_free( *OutputBuffer ); } *OutputBuffer = MIDL_user_allocate( BufLen ); if( *OutputBuffer == NULL ) { status = ERROR_NOT_ENOUGH_MEMORY; break; } // // Make the request of the server. // Srp->Parameters.Get.ResumeHandle = resumeHandle; status = SsServerFsControl( ServerControlCode, Srp, *OutputBuffer, BufLen ); // // If we were successful, or we got an error other than our buffer // being too small, break out. // if ( status != ERROR_MORE_DATA && status != NERR_BufTooSmall ) { break; } // // We've been told that our buffer isn't big enough. But if we've hit // the caller's PreferredMaximumLength, break out. // if( BufLen >= PreferredMaximumLength ) { break; } // // Let's try again. EXTRA_ALLOCATION is here to cover the case where the // amount of space required grows between the previous FsControl call and // the next one. // BufLen = min( Srp->Parameters.Get.TotalBytesNeeded + EXTRA_ALLOCATION, PreferredMaximumLength ); } if ( *OutputBuffer && Srp->Parameters.Get.EntriesRead == 0 ) { MIDL_user_free( *OutputBuffer ); *OutputBuffer = NULL; } return status; } // SsServerFsControlGetInfo NET_API_STATUS SsServerFsControl ( IN ULONG ServerControlCode, IN PSERVER_REQUEST_PACKET Srp OPTIONAL, IN PVOID Buffer, IN ULONG BufferLength ) /*++ Routine Description: This routine sends an FSCTL to the server using the previously opened server handle Arguments: ServerControlCode - the FSCTL code to send to the server. Srp - a pointer to the SRP for the operation. Buffer - a pointer to the buffer to pass to the server as the "OutputBuffer" parameter of NtFsControlFile. BufferLength - the size of this buffer. Return Value: NET_API_STATUS - results of the operation. --*/ { NTSTATUS status; NET_API_STATUS error; IO_STATUS_BLOCK ioStatusBlock; PSERVER_REQUEST_PACKET sendSrp; ULONG sendSrpLength; PWCH name1Buffer, name2Buffer; HANDLE eventHandle; if( SsData.SsServerDeviceHandle == NULL ) { DbgPrint( "SRVSVC: SsData.SsServerDeviceHandle == NULL\n" ); return ERROR_BAD_NET_RESP; } // // If a name was specified, we must capture the SRP along with the // name in order to avoid sending embedded input pointers. // if ( Srp != NULL ) { name1Buffer = Srp->Name1.Buffer; name2Buffer = Srp->Name2.Buffer; if ( Srp->Name1.Buffer != NULL || Srp->Name2.Buffer != NULL ) { PCHAR nextStringLocation; // // Allocate enough space to hold the SRP + name. // sendSrpLength = sizeof(SERVER_REQUEST_PACKET); if ( Srp->Name1.Buffer != NULL ) { sendSrpLength += Srp->Name1.MaximumLength; } if ( Srp->Name2.Buffer != NULL ) { sendSrpLength += Srp->Name2.MaximumLength; } sendSrp = MIDL_user_allocate( sendSrpLength ); if ( sendSrp == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } // // Copy over the SRP. // RtlCopyMemory( sendSrp, Srp, sizeof(SERVER_REQUEST_PACKET) ); // // Set up the names in the new SRP. // nextStringLocation = (PCHAR)( sendSrp + 1 ); if ( Srp->Name1.Buffer != NULL ) { sendSrp->Name1.Length = Srp->Name1.Length; sendSrp->Name1.MaximumLength = Srp->Name1.MaximumLength; sendSrp->Name1.Buffer = (PWCH)nextStringLocation; RtlCopyMemory( sendSrp->Name1.Buffer, Srp->Name1.Buffer, Srp->Name1.MaximumLength ); nextStringLocation += Srp->Name1.MaximumLength; POINTER_TO_OFFSET( sendSrp->Name1.Buffer, sendSrp ); } if ( Srp->Name2.Buffer != NULL ) { sendSrp->Name2.Length = Srp->Name2.Length; sendSrp->Name2.MaximumLength = Srp->Name2.MaximumLength; sendSrp->Name2.Buffer = (PWCH)nextStringLocation; RtlCopyMemory( sendSrp->Name2.Buffer, Srp->Name2.Buffer, Srp->Name2.MaximumLength ); POINTER_TO_OFFSET( sendSrp->Name2.Buffer, sendSrp ); } } else { // // There was no name in the SRP, so just send the SRP that was // passed in. // sendSrp = Srp; sendSrpLength = sizeof(SERVER_REQUEST_PACKET); } } else { // // This request has no SRP. // sendSrp = NULL; sendSrpLength = 0; } // // Create an event to synchronize with the driver // status = NtCreateEvent( &eventHandle, FILE_ALL_ACCESS, NULL, NotificationEvent, FALSE ); // // Send the request to the server FSD. // if( NT_SUCCESS( status ) ) { status = NtFsControlFile( SsData.SsServerDeviceHandle, eventHandle, NULL, NULL, &ioStatusBlock, ServerControlCode, sendSrp, sendSrpLength, Buffer, BufferLength ); if( status == STATUS_PENDING ) { NtWaitForSingleObject( eventHandle, FALSE, NULL ); } NtClose( eventHandle ); } // // If an error code was set in the SRP, use it. Otherwise, if // an error was returned or set in the IO status block, use that. // if ( (sendSrp != NULL) && (sendSrp->ErrorCode != NO_ERROR) ) { error = sendSrp->ErrorCode; IF_DEBUG(API_ERRORS) { SS_PRINT(( "SsServerFsControl: (1) API call %lx to srv failed, " "err = %ld\n", ServerControlCode, error )); } } else { if ( NT_SUCCESS(status) ) { status = ioStatusBlock.Status; } if ( status == STATUS_SERVER_HAS_OPEN_HANDLES ) { error = ERROR_SERVER_HAS_OPEN_HANDLES; } else { error = NetpNtStatusToApiStatus( status ); } if ( error != NO_ERROR ) { IF_DEBUG(API_ERRORS) { SS_PRINT(( "SsServerFsControl: (2) API call %lx to srv " "failed, err = %ld, status = %X\n", ServerControlCode, error, status )); } } } // // If a separate buffer was allocated to capture the name, copy // over the new SRP and free it. // if ( sendSrp != Srp ) { RtlCopyMemory( Srp, sendSrp, sizeof(SERVER_REQUEST_PACKET) ); Srp->Name1.Buffer = name1Buffer; Srp->Name2.Buffer = name2Buffer; MIDL_user_free( sendSrp ); } return error; } // SsServerFsControl DWORD SsGetServerType ( VOID ) /*++ Routine Description: Return the ServiceBits of all the services implemented by this service. Enter with SsData.SsServerInfoResource locked. Arguments: None Return Value: SV_TYPE service bits. --*/ { DWORD serviceBits; serviceBits = SV_TYPE_SERVER | SV_TYPE_NT | SsData.ServiceBits; if( SsData.IsDfsRoot ) { serviceBits |= SV_TYPE_DFS; } if ( SsData.ServerInfo599.sv599_timesource ) { serviceBits |= SV_TYPE_TIME_SOURCE; } if ( SsData.ServerInfo598.sv598_producttype == NtProductServer ) { serviceBits |= SV_TYPE_SERVER_NT; } if ( SsData.NumberOfPrintShares != 0 ) { serviceBits |= SV_TYPE_PRINTQ_SERVER; } return serviceBits; } VOID SsSetExportedServerType ( IN PNAME_LIST_ENTRY service OPTIONAL, IN BOOL ExternalBitsAlreadyChanged, IN BOOL UpdateImmediately ) { DWORD serviceBits; DWORD newServiceBits; BOOL changed = ExternalBitsAlreadyChanged; // // The value returned in the sv102_type field is an amalgam of the // following: // // 1) The internal server type bits SV_TYPE_SERVER (always set), // SV_TYPE_NT (always set), SV_TYPE_TIME_SOURCE (set if the // parameter TimeSource is TRUE), and SV_TYPE_PRINTQ_SERVER (set // if there are any print shares). // // 2) SV_TYPE_DFS if this machine is the root of a DFS tree // // 3) The bits set by the service controller calling I_NetServerSetServiceBits. // SV_TYPE_TIME_SOURCE is a pseudo internal bit. It can be set // internally or it can be set by the w32time service. // // 4) The logical OR of all per-transport server type bits set by // the Browser calling I_NetServerSetServiceBits. // (VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE ); serviceBits = SsGetServerType(); if( ARGUMENT_PRESENT( service ) ) { // // Change the bits for the passed-in NAME_LIST_ENTRY only // newServiceBits = service->ServiceBits; newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS); newServiceBits |= serviceBits; if( service->ServiceBits != newServiceBits ) { service->ServiceBits |= newServiceBits; changed = TRUE; } } else { // // Change the bits for each NAME_LIST_ENTRY // for ( service = SsData.SsServerNameList; service != NULL; service = service->Next ) { newServiceBits = service->ServiceBits; newServiceBits &= ~(SV_TYPE_SERVER_NT | SV_TYPE_PRINTQ_SERVER | SV_TYPE_DFS ); newServiceBits |= serviceBits; if( service->ServiceBits != newServiceBits ) { service->ServiceBits |= newServiceBits; changed = TRUE; } } } RtlReleaseResource( &SsData.SsServerInfoResource ); if ( changed && UpdateImmediately ) { if( SsData.SsStatusChangedEvent ) { if ( !SetEvent( SsData.SsStatusChangedEvent ) ) { SS_PRINT(( "SsSetExportedServerType: SetEvent failed: %ld\n", GetLastError( ) )); } } } return; } // SsSetExportedServerType NET_API_STATUS SsSetField ( IN PFIELD_DESCRIPTOR Field, IN PVOID Value, IN BOOLEAN WriteToRegistry, OUT BOOLEAN *AnnouncementInformationChanged OPTIONAL ) { PCHAR structure; // // *** We do not initialize *AnnouncementInformationChanged to // FALSE! We leave it alone, unless interesting information is // changed, in which case we set it to TRUE. This is to allow a // caller to initialize it itself, then call this function // multiple times, with the resulting value in the parameter // being TRUE if at least one of the calls changed an // interesting parameter. // // // Determine the structure that will be set. // if ( Field->Level / 100 == 5 ) { if ( Field->Level != 598 ) { structure = (PCHAR)&SsData.ServerInfo599; } else { structure = (PCHAR)&SsData.ServerInfo598; } } else { structure = (PCHAR)&SsData.ServerInfo102; } // // Set the value in the field based on the field type. // switch ( Field->FieldType ) { case BOOLEAN_FIELD: { BOOLEAN value = *(PBOOLEAN)Value; PBOOLEAN valueLocation; // // BOOLEANs may only be TRUE (1) or FALSE (0). // if ( value != TRUE && value != FALSE ) { return ERROR_INVALID_PARAMETER; } valueLocation = (PBOOLEAN)( structure + Field->FieldOffset ); // // If we're turning off Hidden (i.e., making the server public), // indicate that an announcment-related parameter has changed. // This will cause an announcement to be sent immediately. // if ( (Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_102, sv102_hidden )) && (value && !(*valueLocation)) && (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) { *AnnouncementInformationChanged = TRUE; } *valueLocation = value; break; } case DWORD_FIELD: { DWORD value = *(PDWORD)Value; PDWORD valueLocation; // // Make sure that the specified value is in the range of // legal values for the Field. // if ( value > Field->MaximumValue || value < Field->MinimumValue ) { return ERROR_INVALID_PARAMETER; } valueLocation = (PDWORD)( structure + Field->FieldOffset ); *valueLocation = value; break; } case LPSTR_FIELD: { LPWCH value = *(LPWCH *)Value; LPWSTR valueLocation; ULONG maxLength; // // We are setting the name, comment, or userpath for the server. // Use the field offset to determine which. // if ( Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_102, sv102_name ) ) { valueLocation = SsData.ServerNameBuffer; maxLength = sizeof( SsData.SsServerTransportAddress ); } else if ( Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_102, sv102_comment ) ) { valueLocation = SsData.ServerCommentBuffer; maxLength = MAXCOMMENTSZ; } else if ( Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_102, sv102_userpath ) ) { valueLocation = SsData.UserPathBuffer; maxLength = MAX_PATH; } else if ( Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_599, sv599_domain ) ) { valueLocation = SsData.DomainNameBuffer; maxLength = DNLEN; } else { SS_ASSERT( FALSE ); return ERROR_INVALID_PARAMETER; } // // If the string is too long, return an error. // if ( (value != NULL) && (STRLEN(value) > maxLength) ) { return ERROR_INVALID_PARAMETER; } // // If we're changing the server comment, indicate that an // announcment-related parameter has changed. This will cause // an announcement to be sent immediately. // if ( (Field->FieldOffset == FIELD_OFFSET( SERVER_INFO_102, sv102_comment )) && ( ((value == NULL) && (*valueLocation != '\0')) || ((value != NULL) && (wcscmp(value,valueLocation) != 0)) ) && (ARGUMENT_PRESENT(AnnouncementInformationChanged)) ) { *AnnouncementInformationChanged = TRUE; } // // If the input is NULL, make the string zero length. // if ( value == NULL ) { *valueLocation = '\0'; *(valueLocation+1) = '\0'; } else { wcscpy( valueLocation, value ); } break; } } // end switch // // The change worked. If requested, add the parameter to the // registry, thus effecting a sticky change. Don't write it // to the registry if this is xxx_comment or xxx_disc since // we already write out their more well known aliases // srvcomment and autodisconnect. Changes here should also be // made to SetStickyParameters(). // if ( WriteToRegistry && (_wcsicmp( Field->FieldName, DISC_VALUE_NAME ) != 0) && (_wcsicmp( Field->FieldName, COMMENT_VALUE_NAME ) != 0) ) { SsAddParameterToRegistry( Field, Value ); } return NO_ERROR; } // SsSetField UINT SsGetDriveType ( IN LPWSTR path ) /*++ Routine Description: This routine calls GetDriveType, attempting to eliminate the DRIVE_NO_ROOT_DIR type Arguments: A path Return Value: The drive type --*/ { UINT driveType = GetDriveType( path ); if( driveType == DRIVE_NO_ROOT_DIR ) { if( path[0] != UNICODE_NULL && path[1] == L':' ) { WCHAR shortPath[ 4 ]; shortPath[0] = path[0]; shortPath[1] = L':'; shortPath[2] = L'\\'; shortPath[3] = L'\0'; driveType = GetDriveType( shortPath ); } else { ULONG len = wcslen( path ); LPWSTR pathWithSlash = MIDL_user_allocate( (len + 2) * sizeof( *path ) ); if( pathWithSlash != NULL ) { RtlCopyMemory( pathWithSlash, path, len * sizeof( *path ) ); pathWithSlash[ len ] = L'\\'; pathWithSlash[ len+1 ] = L'\0'; driveType = GetDriveType( pathWithSlash ); MIDL_user_free( pathWithSlash ); } } } return driveType; } VOID SsNotifyRdrOfGuid( LPGUID Guid ) { NTSTATUS status; HANDLE hMrxSmbHandle; UNICODE_STRING unicodeServerName; OBJECT_ATTRIBUTES objectAttributes; IO_STATUS_BLOCK ioStatusBlock; // // Open the server device. // RtlInitUnicodeString( &unicodeServerName, MRXSMB_DEVICE_NAME ); InitializeObjectAttributes( &objectAttributes, &unicodeServerName, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Opening the server with desired access = SYNCHRONIZE and open // options = FILE_SYNCHRONOUS_IO_NONALERT means that we don't have // to worry about waiting for the NtFsControlFile to complete--this // makes all IO system calls that use this handle synchronous. // status = NtOpenFile( &hMrxSmbHandle, FILE_ALL_ACCESS & ~SYNCHRONIZE, &objectAttributes, &ioStatusBlock, 0, 0 ); if ( NT_SUCCESS(status) ) { status = NtFsControlFile( hMrxSmbHandle, NULL, NULL, NULL, &ioStatusBlock, FSCTL_LMR_SET_SERVER_GUID, Guid, sizeof(GUID), NULL, 0 ); NtClose( hMrxSmbHandle ); } }