/*++ Copyright (c) 1991-1992 Microsoft Corporation Module Name: SrvInfo.c Abstract: This module contains support for the server get and set info APIs in the server service. Author: David Treadwell (davidtr) 7-Mar-1991 Revision History: --*/ #include "srvsvcp.h" #include "ssreg.h" #include #include #include NET_API_STATUS NET_API_FUNCTION NetrServerGetInfo ( IN LPWSTR ServerName, IN DWORD Level, OUT LPSERVER_INFO InfoStruct ) /*++ Routine Description: This routine uses the server parameters stored in the server service to return the server information. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { ULONG outputBufferLength; NET_API_STATUS error; ACCESS_MASK desiredAccess; LPWSTR DomainName; PNAME_LIST_ENTRY service; PTRANSPORT_LIST_ENTRY transport; UCHAR serverNameBuf[ MAX_PATH ]; UNICODE_STRING ServerNameUnicode; NTSTATUS status; ULONG namelen; // // validate incomming string lengths // if(ServerName!=NULL && StringCchLength(ServerName,1024,NULL) != S_OK) { return ERROR_INVALID_PARAMETER; } // // Determine the access required for the requested level of // information. // switch ( Level ) { case 100: case 101: desiredAccess = SRVSVC_CONFIG_USER_INFO_GET; break; case 102: case 502: desiredAccess = SRVSVC_CONFIG_POWER_INFO_GET; break; case 503: desiredAccess = SRVSVC_CONFIG_ADMIN_INFO_GET; break; default: return ERROR_INVALID_LEVEL; } // // Make sure that the caller has that level of access. // error = SsCheckAccess( &SsConfigInfoSecurityObject, desiredAccess ); if ( error != NO_ERROR ) { return ERROR_ACCESS_DENIED; } // // Acquire the resource that protects server information. Since // we'll only read the information, get shared access to the // resource. // (VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE ); if( ServerName == NULL ) { ServerName = SsData.ServerNameBuffer; } // // Convert the server name // if( ServerName[0] == L'\\' && ServerName[1] == L'\\' ) { ServerName += 2; } RtlInitUnicodeString( &ServerNameUnicode, ServerName ); error = ConvertStringToTransportAddress( &ServerNameUnicode, serverNameBuf, &namelen ); if( error != NERR_Success ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return error; } // // Look for the NAME_LIST_ENTRY entry that represents the name of the server // the client referred to. // DomainName = SsData.DomainNameBuffer; for( service = SsData.SsServerNameList; service != NULL; service = service->Next ) { if( service->TransportAddressLength != namelen ) { continue; } if( RtlEqualMemory( serverNameBuf, service->TransportAddress, namelen ) ) { DomainName = service->DomainName; break; } } // // If we didn't find an entry, find and use the primary entry // if( service == NULL ) { for( service = SsData.SsServerNameList; service != NULL; service = service->Next ) { if( service->PrimaryName ) { DomainName = service->DomainName; break; } } } // // Use the level parameter to determine how much space to allocate // and how to fill it in. // switch ( Level ) { case 100: { PSERVER_INFO_100 sv100; // // All we copy is the server name. // outputBufferLength = sizeof(SERVER_INFO_100) + STRSIZE( ServerName); sv100 = MIDL_user_allocate( outputBufferLength ); if ( sv100 == NULL ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy over the fixed portion of the buffer. // RtlCopyMemory( sv100, &SsData.ServerInfo102, sizeof(SERVER_INFO_100) ); // // Set up the name string. // sv100->sv100_name = (LPWSTR)( sv100 + 1 ); STRCPY( sv100->sv100_name, ServerName ); // // Set up the output buffer pointer. // InfoStruct->ServerInfo100 = sv100; break; } case 101: { PSERVER_INFO_101 sv101; // // All we copy is the server name. // outputBufferLength = sizeof(SERVER_INFO_101) + STRSIZE( ServerName ) + STRSIZE( SsData.ServerCommentBuffer ) ; sv101 = MIDL_user_allocate( outputBufferLength ); if ( sv101 == NULL ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy over the fixed portion of the buffer. // RtlCopyMemory( sv101, &SsData.ServerInfo102, sizeof(SERVER_INFO_101) ); if( service != NULL ) { sv101->sv101_type = service->ServiceBits; for( transport = service->Transports; transport; transport = transport->Next ) { sv101->sv101_type |= transport->ServiceBits; } } else { // // If there are no transports, // return the global information. // sv101->sv101_type = SsGetServerType(); } // // Set up the variable portion of the buffer. // sv101->sv101_name = (LPWSTR)( sv101 + 1 ); STRCPY( sv101->sv101_name, ServerName ); sv101->sv101_comment = (LPWSTR)( (PCHAR)sv101->sv101_name + STRSIZE( ServerName )); STRCPY( sv101->sv101_comment, SsData.ServerCommentBuffer ); // // Set up the output buffer pointer. // InfoStruct->ServerInfo101 = sv101; break; } case 102: { PSERVER_INFO_102 sv102; // // We copy the server name, server comment, and user path // buffer. // outputBufferLength = sizeof(SERVER_INFO_102) + STRSIZE( ServerName ) + STRSIZE( SsData.ServerCommentBuffer ) + STRSIZE( SsData.UserPathBuffer ) ; sv102 = MIDL_user_allocate( outputBufferLength ); if ( sv102 == NULL ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy over the fixed portion of the buffer. // RtlCopyMemory( sv102, &SsData.ServerInfo102, sizeof(SERVER_INFO_102) ); if( service != NULL ) { sv102->sv102_type = service->ServiceBits; for( transport = service->Transports; transport; transport = transport->Next ) { sv102->sv102_type |= transport->ServiceBits; } } else { // // If there are no transports, // return the global information. // sv102->sv102_type = SsGetServerType(); } // // Set up the server name. // sv102->sv102_name = (LPWSTR)( sv102 + 1 ); STRCPY( sv102->sv102_name, ServerName ); // // Set up the server comment. // sv102->sv102_comment = (LPWSTR)( (PCHAR)sv102->sv102_name + STRSIZE( ServerName )); STRCPY( sv102->sv102_comment, SsData.ServerCommentBuffer ); // // Set up the user path. // sv102->sv102_userpath = (LPWSTR)( (PCHAR)sv102->sv102_comment + STRSIZE( sv102->sv102_comment ) ); STRCPY( sv102->sv102_userpath, SsData.UserPathBuffer ); // // Set up the output buffer pointer. // InfoStruct->ServerInfo102 = sv102; break; } case 502: // // Allocate enough space to hold the fixed structure. This level has // no variable structure. // InfoStruct->ServerInfo502 = MIDL_user_allocate( sizeof(SERVER_INFO_502) ); if ( InfoStruct->ServerInfo502 == NULL ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy the data from the server service buffer to the user buffer. // RtlCopyMemory( InfoStruct->ServerInfo502, &SsData.ServerInfo599, sizeof(SERVER_INFO_502) ); break; case 503: { PSERVER_INFO_503 sv503; outputBufferLength = sizeof( *sv503 ) + STRSIZE( DomainName ); sv503 = MIDL_user_allocate( outputBufferLength ); if ( sv503 == NULL ) { RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_NOT_ENOUGH_MEMORY; } // // Copy the data from the server service buffer to the user buffer. // RtlCopyMemory( sv503, &SsData.ServerInfo599, sizeof( *sv503 ) ); // // Copy the domain name // sv503->sv503_domain = (LPWSTR)( sv503 + 1 ); STRCPY( sv503->sv503_domain, DomainName ); InfoStruct->ServerInfo503 = sv503; break; } default: RtlReleaseResource( &SsData.SsServerInfoResource ); return ERROR_INVALID_LEVEL; } RtlReleaseResource( &SsData.SsServerInfoResource ); return NO_ERROR; } // NetrServerGetInfo NET_API_STATUS NET_API_FUNCTION NetrServerSetInfo ( IN LPWSTR ServerName, IN DWORD Level, IN LPSERVER_INFO InfoStruct, OUT LPDWORD ErrorParameter OPTIONAL ) /*++ Routine Description: This routine sets information in the server service and server. Arguments: None. Return Value: NET_API_STATUS - NO_ERROR or reason for failure. --*/ { NET_API_STATUS error; ULONG i; LONG parmnum; BOOLEAN validLevel = FALSE; PSERVER_REQUEST_PACKET srp; LPBYTE buffer = (LPBYTE)InfoStruct->ServerInfo100; BOOLEAN announcementInformationChanged = FALSE; ServerName; // // Check that user input buffer is not NULL // if (buffer == NULL) { if ( ARGUMENT_PRESENT( ErrorParameter ) ) { *ErrorParameter = PARM_ERROR_UNKNOWN; } return ERROR_INVALID_PARAMETER; } parmnum = (LONG)(Level - PARMNUM_BASE_INFOLEVEL); if ( ARGUMENT_PRESENT( ErrorParameter ) ) { *ErrorParameter = parmnum; } // // Make sure that the caller is allowed to set information in the // server. // error = SsCheckAccess( &SsConfigInfoSecurityObject, SRVSVC_CONFIG_INFO_SET ); if ( error != NO_ERROR ) { return ERROR_ACCESS_DENIED; } // // Acquire the resource that protects server information. Since // we're going to be writing to the information, we need exclusive // access to the reqource. // // // If a parameter number was specified, set that one field. // if ( parmnum >= 0 ) { // // Walk through the field descriptors looking for an // equivalent parameter number. // for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) { if ( (ULONG)parmnum == SsServerInfoFields[i].ParameterNumber ) { // // Verify that the field is settable. // // !!! We should also reject levels above 502? // if ( SsServerInfoFields[i].Settable != ALWAYS_SETTABLE ) { return ERROR_INVALID_LEVEL; } (VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE ); // // Set the field. // error = SsSetField( &SsServerInfoFields[i], buffer, TRUE, &announcementInformationChanged ); RtlReleaseResource( &SsData.SsServerInfoResource ); // // If a relevant parameter changed, call // SsSetExportedServerType. This will cause an // announcement to be sent. // if ( announcementInformationChanged ) { SsSetExportedServerType( NULL, TRUE, TRUE ); } return error; } } // // If a match had been found we would have returned by now. // Indicate that the parameter number was illegal. // return ERROR_INVALID_LEVEL; } // // A full input structure was specified. Walk through all the // server data field descriptors, looking for fields that should be // set. // for ( i = 0; SsServerInfoFields[i].FieldName != NULL; i++ ) { ULONG fieldLevel; // // We need to set this field if: // // o the level specified on input is the same order as the // level of the field. They have the same order if // they are in the same century (e.g. 101 and 102 are // in the same order); AND // // o the specified level is greater than or equal to the // level of the field. For example, if the input // level is 101 and the field level is 102, don't set // the field. If the input level is 102 and the field // level is 101, set it; AND // // o the field is settable. If the field is not settable // by NetServerSetInfo, just ignore the value in the // input structure. // // Note that level 598 doesn't follow the first rule above. It // is NOT a superset of 50x, and it is NOT a subset of 599. // fieldLevel = SsServerInfoFields[i].Level; if ( Level / 100 == fieldLevel / 100 && ((fieldLevel != 598) && (Level >= fieldLevel) || (fieldLevel == 598) && (Level == 598)) && SsServerInfoFields[i].Settable == ALWAYS_SETTABLE ) { // // We found a match, so the specified level number must have // been valid. // // !!! Reject levels above 502? validLevel = TRUE; // // Set this field. // (VOID)RtlAcquireResourceExclusive( &SsData.SsServerInfoResource, TRUE ); error = SsSetField( &SsServerInfoFields[i], buffer + SsServerInfoFields[i].FieldOffset, TRUE, &announcementInformationChanged ); RtlReleaseResource( &SsData.SsServerInfoResource ); if ( error != NO_ERROR ) { // // Set the parameter in error if we need to. // if ( ARGUMENT_PRESENT(ErrorParameter) ) { *ErrorParameter = SsServerInfoFields[i].ParameterNumber; } return error; } } } // // If no match was ever found, then an invalid level was passed in. // if ( !validLevel ) { return ERROR_INVALID_LEVEL; } // // Get an SRP and set it up with the appropriate level. // srp = SsAllocateSrp( ); if ( srp == NULL ) { return ERROR_NOT_ENOUGH_MEMORY; } srp->Level = 0xFFFFFFFF; (VOID)RtlAcquireResourceShared( &SsData.SsServerInfoResource, TRUE ); // // Send the request on to the server. // error = SsServerFsControl( FSCTL_SRV_NET_SERVER_SET_INFO, srp, &SsData.ServerInfo102, sizeof(SERVER_INFO_102) + sizeof(SERVER_INFO_599) + sizeof(SERVER_INFO_598) ); // // Release the resource and free the SRP. // RtlReleaseResource( &SsData.SsServerInfoResource ); SsFreeSrp( srp ); // // If a relevant parameter changed, call SsSetExportedServerType. // This will cause an announcement to be sent. // if ( announcementInformationChanged ) { SsSetExportedServerType( NULL, TRUE, TRUE ); } return error; } // NetrServerSetInfo