windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/wkssvc/server/wsconfig.c

1405 lines
40 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1991-1992 Microsoft Corporation
Module Name:
wsconfig.c
Abstract:
This module contains the Workstation service configuration routines.
Author:
Rita Wong (ritaw) 22-May-1991
Revision History:
08-May-1992 JohnRo
Wksta transports are just an array of values for one key,
not an entire section. Ditto for other domains for the browser.
13-May-1992 JohnRo
Reworked to share code with registry watch code.
--*/
#include "ws.h"
#include <ntlsa.h> // LsaQueryInformationPolicy
#include "wsdevice.h"
#include "wsconfig.h"
#include "wsbind.h"
#include "wsutil.h"
#include "wsmain.h"
#include <config.h> // NT config file helpers in netlib
#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
#include <confname.h> // Section and keyword equates.
#include <lmapibuf.h> // NetApiBufferFree().
#include <lmsname.h> // WORKSTATION_DISPLAY_NAME
#include <prefix.h> // PREFIX_ equates.
#include <strarray.h> // LPTSTR_ARRAY, etc.
#include <stdlib.h> // wcscpy().
#include <apperr.h> // Eventlog message IDs
#include <lmerrlog.h> // Eventlog message IDs
#define WS_LINKAGE_REGISTRY_PATH L"LanmanWorkstation\\Linkage"
#define WS_BIND_VALUE_NAME L"Bind"
//-------------------------------------------------------------------//
// //
// Local Function Prototypes //
// //
//-------------------------------------------------------------------//
STATIC
NTSTATUS
WsBindATransport(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
);
//-------------------------------------------------------------------//
// //
// Global variables //
// //
//-------------------------------------------------------------------//
//
// Workstation configuration information structure which holds the
// computername, primary domain, wksta config buffer, and a resource
// to serialize access to the whole thing.
//
WSCONFIGURATION_INFO WsInfo;
STATIC WS_REDIR_FIELDS WsFields[] = {
{WKSTA_KEYWORD_CHARWAIT,
(LPDWORD) &WSBUF.wki502_char_wait,
3600, 0, 65535, DWordType, WKSTA_CHARWAIT_PARMNUM},
{WKSTA_KEYWORD_MAXCOLLECTIONCOUNT,
(LPDWORD) &WSBUF.wki502_maximum_collection_count,
16, 0, 65535, DWordType, WKSTA_CHARCOUNT_PARMNUM},
{WKSTA_KEYWORD_COLLECTIONTIME,
(LPDWORD) &WSBUF.wki502_collection_time,
250, 0, 65535000, DWordType, WKSTA_CHARTIME_PARMNUM},
{WKSTA_KEYWORD_KEEPCONN,
(LPDWORD) &WSBUF.wki502_keep_conn,
600, 1, 65535, DWordType, WKSTA_KEEPCONN_PARMNUM},
{WKSTA_KEYWORD_MAXCMDS,
(LPDWORD) &WSBUF.wki502_max_cmds,
50, 50, 65535, DWordType, PARMNUM_ALL}, // Not settable dynamically
{WKSTA_KEYWORD_SESSTIMEOUT,
(LPDWORD) &WSBUF.wki502_sess_timeout,
60, 60, 65535, DWordType, WKSTA_SESSTIMEOUT_PARMNUM},
{WKSTA_KEYWORD_SIZCHARBUF,
(LPDWORD) &WSBUF.wki502_siz_char_buf,
512, 64, 4096, DWordType, WKSTA_SIZCHARBUF_PARMNUM},
{WKSTA_KEYWORD_MAXTHREADS,
(LPDWORD) &WSBUF.wki502_max_threads,
17, 1, 256, DWordType, WKSTA_MAXTHREADS_PARMNUM},
{WKSTA_KEYWORD_LOCKQUOTA,
(LPDWORD) &WSBUF.wki502_lock_quota,
6144, 0, MAXULONG, DWordType, WKSTA_LOCKQUOTA_PARMNUM},
{WKSTA_KEYWORD_LOCKINCREMENT,
(LPDWORD) &WSBUF.wki502_lock_increment,
10, 0, MAXULONG, DWordType, WKSTA_LOCKINCREMENT_PARMNUM},
{WKSTA_KEYWORD_LOCKMAXIMUM,
(LPDWORD) &WSBUF.wki502_lock_maximum,
500, 0, MAXULONG, DWordType, WKSTA_LOCKMAXIMUM_PARMNUM},
{WKSTA_KEYWORD_PIPEINCREMENT,
(LPDWORD) &WSBUF.wki502_pipe_increment,
10, 0, MAXULONG, DWordType, WKSTA_PIPEINCREMENT_PARMNUM},
{WKSTA_KEYWORD_PIPEMAXIMUM,
(LPDWORD) &WSBUF.wki502_pipe_maximum,
500, 0, MAXULONG, DWordType, WKSTA_PIPEMAXIMUM_PARMNUM},
{WKSTA_KEYWORD_CACHEFILETIMEOUT,
(LPDWORD) &WSBUF.wki502_cache_file_timeout,
40, 0, MAXULONG, DWordType, WKSTA_CACHEFILETIMEOUT_PARMNUM},
{WKSTA_KEYWORD_DORMANTFILELIMIT,
(LPDWORD) &WSBUF.wki502_dormant_file_limit,
45, 1, MAXULONG, DWordType, WKSTA_DORMANTFILELIMIT_PARMNUM},
{WKSTA_KEYWORD_READAHEADTHRUPUT,
(LPDWORD) &WSBUF.wki502_read_ahead_throughput,
MAXULONG,0, MAXULONG, DWordType, WKSTA_READAHEADTHRUPUT_PARMNUM},
{WKSTA_KEYWORD_MAILSLOTBUFFERS,
(LPDWORD) &WSBUF.wki502_num_mailslot_buffers,
3, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
{WKSTA_KEYWORD_SERVERANNOUNCEBUFS,
(LPDWORD) &WSBUF.wki502_num_srv_announce_buffers,
20, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
{WKSTA_KEYWORD_NUM_ILLEGAL_DG_EVENTS,
(LPDWORD) &WSBUF.wki502_max_illegal_datagram_events,
5, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
{WKSTA_KEYWORD_ILLEGAL_DG_RESET_TIME,
(LPDWORD) &WSBUF.wki502_illegal_datagram_event_reset_frequency,
3600, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
{WKSTA_KEYWORD_LOG_ELECTION_PACKETS,
(LPDWORD) &WSBUF.wki502_log_election_packets,
FALSE, 0, MAXULONG, BooleanType, PARMNUM_ALL}, // Not settable
{WKSTA_KEYWORD_USEOPLOCKING,
(LPDWORD) &WSBUF.wki502_use_opportunistic_locking,
TRUE, 0, 0, BooleanType, WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM},
{WKSTA_KEYWORD_USEUNLOCKBEHIND,
(LPDWORD) &WSBUF.wki502_use_unlock_behind,
TRUE, 0, 0, BooleanType, WKSTA_USEUNLOCKBEHIND_PARMNUM},
{WKSTA_KEYWORD_USECLOSEBEHIND,
(LPDWORD) &WSBUF.wki502_use_close_behind,
TRUE, 0, 0, BooleanType, WKSTA_USECLOSEBEHIND_PARMNUM},
{WKSTA_KEYWORD_BUFNAMEDPIPES,
(LPDWORD) &WSBUF.wki502_buf_named_pipes,
TRUE, 0, 0, BooleanType, WKSTA_BUFFERNAMEDPIPES_PARMNUM},
{WKSTA_KEYWORD_USELOCKREADUNLOCK,
(LPDWORD) &WSBUF.wki502_use_lock_read_unlock,
TRUE, 0, 0, BooleanType, WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM},
{WKSTA_KEYWORD_UTILIZENTCACHING,
(LPDWORD) &WSBUF.wki502_utilize_nt_caching,
TRUE, 0, 0, BooleanType, WKSTA_UTILIZENTCACHING_PARMNUM},
{WKSTA_KEYWORD_USERAWREAD,
(LPDWORD) &WSBUF.wki502_use_raw_read,
TRUE, 0, 0, BooleanType, WKSTA_USERAWREAD_PARMNUM},
{WKSTA_KEYWORD_USERAWWRITE,
(LPDWORD) &WSBUF.wki502_use_raw_write,
TRUE, 0, 0, BooleanType, WKSTA_USERAWWRITE_PARMNUM},
{WKSTA_KEYWORD_USEWRITERAWDATA,
(LPDWORD) &WSBUF.wki502_use_write_raw_data,
TRUE, 0, 0, BooleanType, WKSTA_USEWRITERAWWITHDATA_PARMNUM},
{WKSTA_KEYWORD_USEENCRYPTION,
(LPDWORD) &WSBUF.wki502_use_encryption,
TRUE, 0, 0, BooleanType, WKSTA_USEENCRYPTION_PARMNUM},
{WKSTA_KEYWORD_BUFFILESDENYWRITE,
(LPDWORD) &WSBUF.wki502_buf_files_deny_write,
TRUE, 0, 0, BooleanType, WKSTA_BUFFILESWITHDENYWRITE_PARMNUM},
{WKSTA_KEYWORD_BUFREADONLYFILES,
(LPDWORD) &WSBUF.wki502_buf_read_only_files,
TRUE, 0, 0, BooleanType, WKSTA_BUFFERREADONLYFILES_PARMNUM},
{WKSTA_KEYWORD_FORCECORECREATE,
(LPDWORD) &WSBUF.wki502_force_core_create_mode,
TRUE, 0, 0, BooleanType, WKSTA_FORCECORECREATEMODE_PARMNUM},
{WKSTA_KEYWORD_USE512BYTEMAXTRANS,
(LPDWORD) &WSBUF.wki502_use_512_byte_max_transfer,
FALSE, 0, 0, BooleanType, WKSTA_USE512BYTESMAXTRANSFER_PARMNUM},
{NULL, NULL, 0, 0, BooleanType}
};
//
// For specifying the importance of a transport when binding to it.
// The higher the number means that the transport will be searched
// first.
//
STATIC DWORD QualityOfService = 65536;
DWORD
WsInAWorkgroup(
VOID
)
/*++
Routine Description:
This function determines whether we are a member of a domain, or of
a workgroup. First it checks to make sure we're running on a Windows NT
system (otherwise we're obviously in a domain) and if so, queries LSA
to get the Primary domain SID, if this is NULL, we're in a workgroup.
If we fail for some random unexpected reason, we'll pretend we're in a
domain (it's more restrictive).
Arguments:
None
Return Value:
TRUE - We're in a workgroup
FALSE - We're in a domain
--*/
{
NT_PRODUCT_TYPE ProductType;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE Handle;
NTSTATUS Status;
PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
DWORD Result = FALSE;
Status = RtlGetNtProductType(&ProductType);
if (!NT_SUCCESS(Status)) {
NetpKdPrint((
PREFIX_WKSTA "Could not get Product type\n"));
return FALSE;
}
if (ProductType == NtProductLanManNt) {
return(FALSE);
}
InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
Status = LsaOpenPolicy(NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&Handle);
if (!NT_SUCCESS(Status)) {
NetpKdPrint((
PREFIX_WKSTA "Could not open LSA Policy Database\n"));
return FALSE;
}
Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
(PVOID *) &PolicyPrimaryDomainInfo);
if (NT_SUCCESS(Status)) {
if (PolicyPrimaryDomainInfo->Sid == NULL) {
Result = TRUE;
}
else {
Result = FALSE;
}
}
if (PolicyPrimaryDomainInfo) {
LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
}
LsaClose(Handle);
return(Result);
}
NET_API_STATUS
WsGetWorkstationConfiguration(
VOID
)
{
NET_API_STATUS status;
LPNET_CONFIG_HANDLE WorkstationSection;
LPTSTR ComputerName;
LPTSTR DomainNameT;
DWORD version;
NT_PRODUCT_TYPE NtProductType;
BYTE Buffer[max(sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
(DNLEN + 1) * sizeof(WCHAR),
sizeof(LMDR_REQUEST_PACKET))];
PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer;
PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
//
// Lock config information structure for write access since we are
// initializing the data in the structure.
//
if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
//IF_DEBUG(START) {
DbgPrint("WKSSVC Acquire ConfigResource failed\n");
//}
return NERR_InternalError;
}
//
// Set pointer to configuration fields structure
//
WsInfo.WsConfigFields = WsFields;
//
// Get the version name.
//
version = GetVersion( );
WsInfo.MajorVersion = version & 0xff;
WsInfo.MinorVersion = (version >> 8) & 0xff;
WsInfo.RedirectorPlatform = PLATFORM_ID_NT;
//
// Get the configured computer name. NetpGetComputerName allocates
// the memory to hold the computername string using NetApiBufferAllocate().
//
if ((status = NetpGetComputerName(
&ComputerName
)) != NERR_Success) {
RtlReleaseResource(&WsInfo.ConfigResource);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Get computer name failed %lx\n", status);
//}
return status;
}
if ((status = I_NetNameCanonicalize(
NULL,
ComputerName,
(LPTSTR) WsInfo.WsComputerName,
sizeof( WsInfo.WsComputerName ),
NAMETYPE_COMPUTER,
0
)) != NERR_Success) {
LPWSTR SubString[1];
NetpKdPrint((
PREFIX_WKSTA FORMAT_LPTSTR " is an invalid computername.\n",
ComputerName
));
SubString[0] = ComputerName;
WsLogEvent(
APE_BAD_COMPNAME,
EVENTLOG_ERROR_TYPE,
1,
SubString,
NERR_Success
);
(void) NetApiBufferFree((PVOID) ComputerName);
RtlReleaseResource(&WsInfo.ConfigResource);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Invalid computer name failed %lx\n", status);
//}
return status;
}
//
// Free memory allocated by NetpGetComputerName.
//
(void) NetApiBufferFree(ComputerName);
WsInfo.WsComputerNameLength = STRLEN((LPWSTR) WsInfo.WsComputerName);
//
// Open config file and get handle to the [LanmanWorkstation] section
//
if ((status = NetpOpenConfigData(
&WorkstationSection,
NULL, // local (no server name)
SECT_NT_WKSTA,
TRUE // want read-only access
)) != NERR_Success) {
RtlReleaseResource(&WsInfo.ConfigResource);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Open config file failed %lx\n", status);
//}
return status;
}
IF_DEBUG(CONFIG) {
NetpKdPrint((PREFIX_WKSTA "ComputerName " FORMAT_LPTSTR ", length %lu\n",
WsInfo.WsComputerName, WsInfo.WsComputerNameLength));
}
//
// Get the primary domain name from the configuration file
//
if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) {
//IF_DEBUG(START) {
DbgPrint("WKSSVC Get the primary domain name failed %lx\n", status);
//}
goto CloseConfigFile;
}
NetpAssert( DomainNameT != NULL );
if ( *DomainNameT != 0 ) {
if ((status = I_NetNameCanonicalize(
NULL,
DomainNameT,
(LPWSTR) WsInfo.WsPrimaryDomainName,
(DNLEN + 1) * sizeof(TCHAR),
WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
0
)) != NERR_Success) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
" is an invalid primary domain name.\n", DomainNameT));
SubString[0] = DomainNameT;
WsLogEvent(
APE_CS_InvalidDomain,
EVENTLOG_ERROR_TYPE,
1,
SubString,
NERR_Success
);
(void) NetApiBufferFree(DomainNameT);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Invalid domain name failed %lx\n", status);
//}
goto CloseConfigFile;
}
} else {
WsInfo.WsPrimaryDomainName[0] = 0;
}
//
// Free memory allocated by NetpGetDomainName.
//
(void) NetApiBufferFree(DomainNameT);
WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName);
//
// Read the redirector configuration fields
//
WsUpdateWkstaToMatchRegistry(WorkstationSection, TRUE);
//
// Initialize redirector configuration
//
Rrp->Type = ConfigInformation;
Rrp->Version = REQUEST_PACKET_VERSION;
STRCPY((LPWSTR) Rrp->Parameters.Start.RedirectorName,
(LPWSTR) WsInfo.WsComputerName);
Rrp->Parameters.Start.RedirectorNameLength =
WsInfo.WsComputerNameLength*sizeof(TCHAR);
Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR);
STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName+WsInfo.WsComputerNameLength),
(LPWSTR) WsInfo.WsPrimaryDomainName
);
status = WsRedirFsControl(
WsRedirDeviceHandle,
FSCTL_LMR_START,
Rrp,
sizeof(LMR_REQUEST_PACKET) +
Rrp->Parameters.Start.RedirectorNameLength+
Rrp->Parameters.Start.DomainNameLength,
(LPBYTE) &WSBUF,
sizeof(WKSTA_INFO_502),
NULL
);
if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA "Start redirector failed %lu\n", status));
SubString[0] = L"redirector";
WsLogEvent(
NELOG_Service_Fail,
EVENTLOG_ERROR_TYPE,
1,
SubString,
status
);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Start redirector failed %lx\n", status);
//}
goto CloseConfigFile;
}
//
// If we still have the default value for number of mailslot buffers,
// pick a "reasonable" value based on the amount of physical memory
// available in the system.
//
if (WSBUF.wki502_num_mailslot_buffers == MAXULONG) {
MEMORYSTATUS MemoryStatus;
GlobalMemoryStatus(&MemoryStatus);
//
// Lets take up 1/40th of 1% of physical memory for mailslot buffers
//
WSBUF.wki502_num_mailslot_buffers =
(DWORD)(MemoryStatus.dwTotalPhys / (100 * 40 * 512));
}
//
// Initialize datagram receiver configuration
//
Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
Drrp->Parameters.Start.NumberOfMailslotBuffers =
WSBUF.wki502_num_mailslot_buffers;
Drrp->Parameters.Start.NumberOfServerAnnounceBuffers =
WSBUF.wki502_num_srv_announce_buffers;
Drrp->Parameters.Start.IllegalDatagramThreshold =
WSBUF.wki502_max_illegal_datagram_events;
Drrp->Parameters.Start.EventLogResetFrequency =
WSBUF.wki502_illegal_datagram_event_reset_frequency;
Drrp->Parameters.Start.LogElectionPackets =
(WSBUF.wki502_log_election_packets != FALSE);
RtlGetNtProductType(&NtProductType);
Drrp->Parameters.Start.IsLanManNt = (NtProductType == NtProductLanManNt);
status = WsDgReceiverIoControl(
WsDgReceiverDeviceHandle,
IOCTL_LMDR_START,
Drrp,
sizeof(LMDR_REQUEST_PACKET),
NULL,
0,
NULL
);
if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA "Start datagram receiver failed %lu\n", status));
SubString[0] = L"datagram receiver";
WsLogEvent(
NELOG_Service_Fail,
EVENTLOG_ERROR_TYPE,
1,
SubString,
status
);
//IF_DEBUG(START) {
DbgPrint("WKSSVC Start Datagram recevier failed %lx\n", status);
//}
goto CloseConfigFile;
}
// do all error reporting in the routine
// don't check any errors here
WsCSCReportStartRedir();
status = NERR_Success;
CloseConfigFile:
(void) NetpCloseConfigData(WorkstationSection);
RtlReleaseResource(&WsInfo.ConfigResource);
return status;
}
VOID
WsUpdateWkstaToMatchRegistry(
IN LPNET_CONFIG_HANDLE WorkstationSection,
IN BOOL IsWkstaInit
)
/*++
Routine Description:
This function reads each redirector configuration field into the
WsInfo.WsConfigBuf (WSBUF) buffer so that the values are ready to be
set in the redirector. If a field is not found or is invalid, the
default value is set.
Arguments:
WorkstationSection - Supplies a handle to read the Workstation
configuration parameters.
IsWkstaInit - Supplies a flag which if TRUE indicates that this
routine is called at workstation init time so the non-settable
fields are acceptable and set in WSBUF. If FALSE, the
non-settable fields are ignored.
Return Value:
None.
--*/
{
DWORD i;
NET_API_STATUS status;
DWORD TempDwordValue;
//
// NTRAID-70687-2/6/2000 davey Invalid keyword value. How to report error?
//
#define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
{ \
NetpKdPrint(( \
PREFIX_WKSTA "*ERROR* Tried to set keyword '" FORMAT_LPTSTR \
"' with invalid value.\n" \
"This error is ignored.\n", \
lptstrKeyword )); \
}
for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
//
// This is to handle fields that are not settable via
// NetWkstaSetInfo. However, these non-settable fields,
// designated by Parmnum == PARMNUM_ALL, can be assigned when
// the workstation is starting up.
//
if ((WsInfo.WsConfigFields[i].Parmnum != PARMNUM_ALL) ||
IsWkstaInit) {
//
// Depending on data type, get the appropriate kind of value.
//
switch (WsInfo.WsConfigFields[i].DataType) {
case BooleanType:
status = NetpGetConfigBool(
WorkstationSection,
WsInfo.WsConfigFields[i].Keyword,
WsInfo.WsConfigFields[i].Default,
(LPBOOL) (WsInfo.WsConfigFields[i].FieldPtr)
);
if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
}
break;
case DWordType:
status = NetpGetConfigDword(
WorkstationSection,
WsInfo.WsConfigFields[i].Keyword,
WsInfo.WsConfigFields[i].Default,
&TempDwordValue
);
if ((status == NO_ERROR) || (status == NERR_CfgParamNotFound)) {
//
// Make sure keyword is in range.
//
if (TempDwordValue < WsInfo.WsConfigFields[i].Minimum ||
TempDwordValue > WsInfo.WsConfigFields[i].Maximum) {
//
// NTRAID-70689-2/6/2000 davey Better way to report error?
//
NetpKdPrint((
PREFIX_WKSTA FORMAT_LPTSTR
" value out of range %lu (%lu-%lu)\n",
WsInfo.WsConfigFields[i].Keyword,
TempDwordValue,
WsInfo.WsConfigFields[i].Minimum,
WsInfo.WsConfigFields[i].Maximum
));
//
// Set back to default.
//
*(WsInfo.WsConfigFields[i].FieldPtr)
= WsInfo.WsConfigFields[i].Default;
}
else {
*(WsInfo.WsConfigFields[i].FieldPtr) = TempDwordValue;
}
}
else {
REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
}
break;
default:
NetpAssert(FALSE);
} // switch
}
}
}
NET_API_STATUS
WsBindToTransports(
VOID
)
/*++
Routine Description:
This function binds the transports specified in the registry to the
redirector. The order of priority for the transports follows the order
they are listed by the "Bind=" valuename.
Arguments:
None.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
--*/
{
NET_API_STATUS status;
NET_API_STATUS tempStatus;
NTSTATUS ntstatus;
DWORD transportsBound;
PRTL_QUERY_REGISTRY_TABLE queryTable;
LIST_ENTRY header;
PLIST_ENTRY pListEntry;
PWS_BIND pBind;
//
// Ask the RTL to call us back for each subvalue in the MULTI_SZ
// value \LanmanWorkstation\Linkage\Bind.
//
queryTable = (PVOID)LocalAlloc(
0,
sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
);
if (queryTable == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
InitializeListHead( &header);
queryTable[0].QueryRoutine = WsBindATransport;
queryTable[0].Flags = 0;
queryTable[0].Name = WS_BIND_VALUE_NAME;
queryTable[0].EntryContext = NULL;
queryTable[0].DefaultType = REG_NONE;
queryTable[0].DefaultData = NULL;
queryTable[0].DefaultLength = 0;
queryTable[1].QueryRoutine = NULL;
queryTable[1].Flags = 0;
queryTable[1].Name = NULL;
ntstatus = RtlQueryRegistryValues(
RTL_REGISTRY_SERVICES, // path relative to ...
WS_LINKAGE_REGISTRY_PATH,
queryTable,
(PVOID) &header, // context
NULL
);
if ( !NT_SUCCESS( ntstatus)) {
NetpKdPrint((
PREFIX_WKSTA "WsBindToTransports: RtlQueryRegistryValues Failed:"
FORMAT_NTSTATUS "\n",
ntstatus
));
status = NetpNtStatusToApiStatus( ntstatus);
} else {
status = NO_ERROR;
}
//
// First process all the data, then clean up.
//
for ( pListEntry = header.Flink;
pListEntry != &header;
pListEntry = pListEntry->Flink) {
pBind = CONTAINING_RECORD(
pListEntry,
WS_BIND,
ListEntry
);
tempStatus = NO_ERROR;
if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
WaitForSingleObject(
pBind->Redir->EventHandle,
INFINITE
);
tempStatus = WsMapStatus( pBind->Redir->IoStatusBlock.Status);
pBind->Redir->Bound = (tempStatus == NO_ERROR);
if (tempStatus == ERROR_DUP_NAME) {
status = tempStatus;
}
}
if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
WaitForSingleObject(
pBind->Dgrec->EventHandle,
INFINITE
);
tempStatus = WsMapStatus( pBind->Dgrec->IoStatusBlock.Status);
pBind->Dgrec->Bound = (tempStatus == NO_ERROR);
if (tempStatus == ERROR_DUP_NAME) {
status = tempStatus;
}
}
if ( tempStatus == ERROR_DUP_NAME) {
NetpKdPrint((
PREFIX_WKSTA "Computername " FORMAT_LPTSTR
" already exists on network " FORMAT_LPTSTR "\n",
WsInfo.WsComputerName,
pBind->TransportName
));
}
//
// If one is installed but the other is not, clean up the other.
//
if ( pBind->Dgrec->Bound != pBind->Redir->Bound) {
WsUnbindTransport2( pBind);
}
}
if ( status != NO_ERROR) {
if (status == ERROR_DUP_NAME) {
WsLogEvent(
NERR_DupNameReboot,
EVENTLOG_ERROR_TYPE,
0,
NULL,
NERR_Success
);
}
for ( pListEntry = header.Flink;
pListEntry != &header;
pListEntry = pListEntry->Flink) {
pBind = CONTAINING_RECORD(
pListEntry,
WS_BIND,
ListEntry
);
WsUnbindTransport2( pBind);
}
}
for ( transportsBound = 0;
IsListEmpty( &header) == FALSE;
LocalFree((HLOCAL) pBind)) {
pListEntry = RemoveHeadList( &header);
pBind = CONTAINING_RECORD(
pListEntry,
WS_BIND,
ListEntry
);
if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
CloseHandle( pBind->Redir->EventHandle);
}
if ( pBind->Redir->Bound == TRUE) {
transportsBound++;
}
if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
CloseHandle( pBind->Dgrec->EventHandle);
}
}
(void) LocalFree((HLOCAL) queryTable);
if ( WsRedirAsyncDeviceHandle != NULL) {
(VOID)NtClose( WsRedirAsyncDeviceHandle);
WsRedirAsyncDeviceHandle = NULL;
}
if ( WsDgrecAsyncDeviceHandle != NULL) {
(VOID)NtClose( WsDgrecAsyncDeviceHandle);
WsDgrecAsyncDeviceHandle = NULL;
}
if (transportsBound == 0) {
NetpKdPrint((
PREFIX_WKSTA "WsBindToTransports: Failed to bind to any"
" transports" FORMAT_API_STATUS "\n",
status
));
if ( status != ERROR_DUP_NAME) {
WsLogEvent(
NELOG_NoTranportLoaded,
EVENTLOG_ERROR_TYPE,
0,
NULL,
NERR_Success
);
status = NO_ERROR;
}
}
return status;
}
STATIC
NTSTATUS
WsBindATransport(
IN PWSTR ValueName,
IN ULONG ValueType,
IN PVOID ValueData,
IN ULONG ValueLength,
IN PVOID Context,
IN PVOID EntryContext
)
/*++
This routine always returns SUCCESS because we want all transports
to be processed fully.
--*/
{
NET_API_STATUS status;
DBG_UNREFERENCED_PARAMETER( ValueName);
DBG_UNREFERENCED_PARAMETER( ValueLength);
DBG_UNREFERENCED_PARAMETER( EntryContext);
//
// The value type must be REG_SZ (translated from REG_MULTI_SZ by the RTL).
//
if (ValueType != REG_SZ) {
NetpKdPrint((
PREFIX_WKSTA "WsBindATransport: ignored invalid value "
FORMAT_LPWSTR "\n",
ValueName
));
return STATUS_SUCCESS;
}
//
// Bind transport
//
status = WsAsyncBindTransport(
ValueData, // name of transport device object
--QualityOfService,
(PLIST_ENTRY)Context
);
if ( status != NERR_Success) {
NetpKdPrint((
PREFIX_WKSTA "WsAsyncBindTransport " FORMAT_LPTSTR
" returns " FORMAT_API_STATUS "\n",
ValueData,
status
));
}
return STATUS_SUCCESS;
}
NET_API_STATUS
WsAddDomains(
VOID
)
/*++
Routine Description:
This function tells the datagram receiver the names to listen on for
datagrams. The names include the computer name, the primary domain,
name and the other domains. The logon domain is not added here; it is
made known to the datagram receiver whenever a user logs on.
Arguments:
None.
Return Value:
NET_API_STATUS - NERR_Success or reason for failure.
Warning:
This routine is UNICODE only.
--*/
{
NET_API_STATUS status;
LPNET_CONFIG_HANDLE SectionHandle = NULL;
LPTSTR OtherDomainName = NULL;
LPTSTR_ARRAY ArrayStart = NULL;
BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
(max(MAX_PATH, DNLEN) + 1) * sizeof(WCHAR)];
PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
//
// Now loop through and add all the other domains.
//
//
// Open registry section listing the other domains. Note that this
// is workstation servive parameter, NOT the browser service parameter.
//
if ((status = NetpOpenConfigData(
&SectionHandle,
NULL, // no server name
SECT_NT_WKSTA,
TRUE // read-only
)) != NERR_Success) {
//
// Ignore the error if the config section couldn't be found.
//
status = NERR_Success;
goto DomainsCleanup;
}
//
// Get value for OtherDomains keyword in the wksta section.
// This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
//
status = NetpGetConfigTStrArray(
SectionHandle,
WKSTA_KEYWORD_OTHERDOMAINS,
&ArrayStart // Must be freed by NetApiBufferFree().
);
if (status != NERR_Success) {
status = NERR_Success; // other domain is optional
goto DomainsCleanup;
}
NetpAssert(ArrayStart != NULL);
if (NetpIsTStrArrayEmpty(ArrayStart)) {
goto DomainsCleanup;
}
OtherDomainName = ArrayStart;
while (! NetpIsTStrArrayEmpty(OtherDomainName)) {
if ((status = I_NetNameCanonicalize(
NULL,
OtherDomainName,
(LPWSTR) Drrp->Parameters.AddDelName.Name,
(DNLEN + 1) * sizeof(TCHAR),
NAMETYPE_DOMAIN,
0
)) != NERR_Success) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
" is an invalid other domain name.\n", OtherDomainName));
SubString[0] = OtherDomainName;
WsLogEvent(
APE_CS_InvalidDomain,
EVENTLOG_ERROR_TYPE,
1,
SubString,
NERR_Success
);
status = NERR_Success; // loading other domains is optional
goto NextOtherDomain;
}
//
// Tell the datagram receiver about an other domain name
//
Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
Drrp->Parameters.AddDelName.Type = OtherDomain;
Drrp->Parameters.AddDelName.DgReceiverNameLength =
STRLEN(OtherDomainName) * sizeof(TCHAR);
status = WsDgReceiverIoControl(
WsDgReceiverDeviceHandle,
IOCTL_LMDR_ADD_NAME,
Drrp,
sizeof(LMDR_REQUEST_PACKET) +
Drrp->Parameters.AddDelName.DgReceiverNameLength,
NULL,
0,
NULL
);
//
// Service install still pending. Update checkpoint counter and the
// status with the Service Controller.
//
WsGlobalData.Status.dwCheckPoint++;
WsUpdateStatus();
if (status != NERR_Success) {
LPWSTR SubString[1];
NetpKdPrint((
PREFIX_WKSTA "Add Other domain name " FORMAT_LPTSTR
" failed with error code %lu\n",
OtherDomainName,
status
));
SubString[0] = OtherDomainName;
WsLogEvent(
APE_CS_InvalidDomain,
EVENTLOG_ERROR_TYPE,
1,
SubString,
status
);
status = NERR_Success; // loading other domains is optional
}
NextOtherDomain:
OtherDomainName = NetpNextTStrArrayEntry(OtherDomainName);
}
DomainsCleanup:
//
// Done with reading from config file. Close file, free memory, etc.
//
if (ArrayStart != NULL) {
(VOID) NetApiBufferFree(ArrayStart);
}
if (SectionHandle != NULL) {
(VOID) NetpCloseConfigData(SectionHandle);
}
return status;
}
VOID
WsLogEvent(
DWORD MessageId,
WORD EventType,
DWORD NumberOfSubStrings,
LPWSTR *SubStrings,
DWORD ErrorCode
)
{
HANDLE LogHandle;
PSID UserSid = NULL;
LogHandle = RegisterEventSourceW (
NULL,
WORKSTATION_DISPLAY_NAME
);
if (LogHandle == NULL) {
NetpKdPrint((PREFIX_WKSTA "RegisterEventSourceW failed %lu\n",
GetLastError()));
return;
}
if (ErrorCode == NERR_Success) {
//
// No error codes were specified
//
(void) ReportEventW(
LogHandle,
EventType,
0, // event category
MessageId,
UserSid,
(WORD)NumberOfSubStrings,
0,
SubStrings,
(PVOID) NULL
);
}
else {
//
// Log the error code specified
//
(void) ReportEventW(
LogHandle,
EventType,
0, // event category
MessageId,
UserSid,
(WORD)NumberOfSubStrings,
sizeof(DWORD),
SubStrings,
(PVOID) &ErrorCode
);
}
DeregisterEventSource(LogHandle);
}
NET_API_STATUS
WsSetWorkStationDomainName(
VOID
)
{
NET_API_STATUS status;
LPNET_CONFIG_HANDLE WorkstationSection;
LPTSTR ComputerName;
LPTSTR DomainNameT;
DWORD version;
NT_PRODUCT_TYPE NtProductType;
BYTE Buffer[sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
(DNLEN + 1) * sizeof(WCHAR)];
PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer;
NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName start.\n"));
//
// Lock config information structure for write access since we are
// modifying the data in the WsInfo.
//
if (!RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
return NERR_InternalError;
}
//
// Get the primary domain name from the configuration file
//
if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) {
goto CloseConfigFile;
}
NetpAssert( DomainNameT != NULL );
if ( *DomainNameT != 0 ) {
if ((status = I_NetNameCanonicalize(
NULL,
DomainNameT,
(LPWSTR) WsInfo.WsPrimaryDomainName,
(DNLEN + 1) * sizeof(TCHAR),
WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
0
)) != NERR_Success) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
" is an invalid primary domain name.\n", DomainNameT));
SubString[0] = DomainNameT;
WsLogEvent(
APE_CS_InvalidDomain,
EVENTLOG_ERROR_TYPE,
1,
SubString,
NERR_Success
);
(void) NetApiBufferFree(DomainNameT);
goto CloseConfigFile;
}
} else {
WsInfo.WsPrimaryDomainName[0] = 0;
}
//
// Free memory allocated by NetpGetDomainName.
//
(void) NetApiBufferFree(DomainNameT);
WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName);
//
// Initialize redirector configuration
//
Rrp->Type = ConfigInformation;
Rrp->Version = REQUEST_PACKET_VERSION;
Rrp->Parameters.Start.RedirectorNameLength = 0;
Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR);
STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName),
(LPWSTR) WsInfo.WsPrimaryDomainName);
NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName call rdr.\n"));
status = WsRedirFsControl(
WsRedirDeviceHandle,
FSCTL_LMR_SET_DOMAIN_NAME,
Rrp,
sizeof(LMR_REQUEST_PACKET) + Rrp->Parameters.Start.DomainNameLength,
NULL,
0,
NULL
);
if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
LPWSTR SubString[1];
NetpKdPrint((PREFIX_WKSTA "Set domain name failed %lu\n", status));
SubString[0] = L"redirector";
WsLogEvent(
NELOG_Service_Fail,
EVENTLOG_ERROR_TYPE,
1,
SubString,
status
);
}
CloseConfigFile:
RtlReleaseResource(&WsInfo.ConfigResource);
return status;
}