676 lines
16 KiB
C
676 lines
16 KiB
C
/*++
|
||
|
||
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 <netlibnt.h>
|
||
|
||
#include <tstr.h>
|
||
#include <lmerr.h>
|
||
|
||
|
||
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;
|
||
|
||
//
|
||
// 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
|