Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1404 lines
41 KiB

  1. /*++
  2. Copyright (c) 1991-1992 Microsoft Corporation
  3. Module Name:
  4. wsconfig.c
  5. Abstract:
  6. This module contains the Workstation service configuration routines.
  7. Author:
  8. Rita Wong (ritaw) 22-May-1991
  9. Revision History:
  10. 08-May-1992 JohnRo
  11. Wksta transports are just an array of values for one key,
  12. not an entire section. Ditto for other domains for the browser.
  13. 13-May-1992 JohnRo
  14. Reworked to share code with registry watch code.
  15. --*/
  16. #include "ws.h"
  17. #include <ntlsa.h> // LsaQueryInformationPolicy
  18. #include "wsdevice.h"
  19. #include "wsconfig.h"
  20. #include "wsbind.h"
  21. #include "wsutil.h"
  22. #include "wsmain.h"
  23. #include <config.h> // NT config file helpers in netlib
  24. #include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
  25. #include <confname.h> // Section and keyword equates.
  26. #include <lmapibuf.h> // NetApiBufferFree().
  27. #include <lmsname.h> // WORKSTATION_DISPLAY_NAME
  28. #include <prefix.h> // PREFIX_ equates.
  29. #include <strarray.h> // LPTSTR_ARRAY, etc.
  30. #include <stdlib.h> // wcscpy().
  31. #include <apperr.h> // Eventlog message IDs
  32. #include <lmerrlog.h> // Eventlog message IDs
  33. #define WS_LINKAGE_REGISTRY_PATH L"LanmanWorkstation\\Linkage"
  34. #define WS_BIND_VALUE_NAME L"Bind"
  35. //-------------------------------------------------------------------//
  36. // //
  37. // Local Function Prototypes //
  38. // //
  39. //-------------------------------------------------------------------//
  40. STATIC
  41. NTSTATUS
  42. WsBindATransport(
  43. IN PWSTR ValueName,
  44. IN ULONG ValueType,
  45. IN PVOID ValueData,
  46. IN ULONG ValueLength,
  47. IN PVOID Context,
  48. IN PVOID EntryContext
  49. );
  50. //-------------------------------------------------------------------//
  51. // //
  52. // Global variables //
  53. // //
  54. //-------------------------------------------------------------------//
  55. //
  56. // Workstation configuration information structure which holds the
  57. // computername, primary domain, wksta config buffer, and a resource
  58. // to serialize access to the whole thing.
  59. //
  60. WSCONFIGURATION_INFO WsInfo;
  61. STATIC WS_REDIR_FIELDS WsFields[] = {
  62. {WKSTA_KEYWORD_CHARWAIT,
  63. (LPDWORD) &WSBUF.wki502_char_wait,
  64. 3600, 0, 65535, DWordType, WKSTA_CHARWAIT_PARMNUM},
  65. {WKSTA_KEYWORD_MAXCOLLECTIONCOUNT,
  66. (LPDWORD) &WSBUF.wki502_maximum_collection_count,
  67. 16, 0, 65535, DWordType, WKSTA_CHARCOUNT_PARMNUM},
  68. {WKSTA_KEYWORD_COLLECTIONTIME,
  69. (LPDWORD) &WSBUF.wki502_collection_time,
  70. 250, 0, 65535000, DWordType, WKSTA_CHARTIME_PARMNUM},
  71. {WKSTA_KEYWORD_KEEPCONN,
  72. (LPDWORD) &WSBUF.wki502_keep_conn,
  73. 600, 1, 65535, DWordType, WKSTA_KEEPCONN_PARMNUM},
  74. {WKSTA_KEYWORD_MAXCMDS,
  75. (LPDWORD) &WSBUF.wki502_max_cmds,
  76. 50, 50, 65535, DWordType, PARMNUM_ALL}, // Not settable dynamically
  77. {WKSTA_KEYWORD_SESSTIMEOUT,
  78. (LPDWORD) &WSBUF.wki502_sess_timeout,
  79. 60, 60, 65535, DWordType, WKSTA_SESSTIMEOUT_PARMNUM},
  80. {WKSTA_KEYWORD_SIZCHARBUF,
  81. (LPDWORD) &WSBUF.wki502_siz_char_buf,
  82. 512, 64, 4096, DWordType, WKSTA_SIZCHARBUF_PARMNUM},
  83. {WKSTA_KEYWORD_MAXTHREADS,
  84. (LPDWORD) &WSBUF.wki502_max_threads,
  85. 17, 1, 256, DWordType, WKSTA_MAXTHREADS_PARMNUM},
  86. {WKSTA_KEYWORD_LOCKQUOTA,
  87. (LPDWORD) &WSBUF.wki502_lock_quota,
  88. 6144, 0, MAXULONG, DWordType, WKSTA_LOCKQUOTA_PARMNUM},
  89. {WKSTA_KEYWORD_LOCKINCREMENT,
  90. (LPDWORD) &WSBUF.wki502_lock_increment,
  91. 10, 0, MAXULONG, DWordType, WKSTA_LOCKINCREMENT_PARMNUM},
  92. {WKSTA_KEYWORD_LOCKMAXIMUM,
  93. (LPDWORD) &WSBUF.wki502_lock_maximum,
  94. 500, 0, MAXULONG, DWordType, WKSTA_LOCKMAXIMUM_PARMNUM},
  95. {WKSTA_KEYWORD_PIPEINCREMENT,
  96. (LPDWORD) &WSBUF.wki502_pipe_increment,
  97. 10, 0, MAXULONG, DWordType, WKSTA_PIPEINCREMENT_PARMNUM},
  98. {WKSTA_KEYWORD_PIPEMAXIMUM,
  99. (LPDWORD) &WSBUF.wki502_pipe_maximum,
  100. 500, 0, MAXULONG, DWordType, WKSTA_PIPEMAXIMUM_PARMNUM},
  101. {WKSTA_KEYWORD_CACHEFILETIMEOUT,
  102. (LPDWORD) &WSBUF.wki502_cache_file_timeout,
  103. 40, 0, MAXULONG, DWordType, WKSTA_CACHEFILETIMEOUT_PARMNUM},
  104. {WKSTA_KEYWORD_DORMANTFILELIMIT,
  105. (LPDWORD) &WSBUF.wki502_dormant_file_limit,
  106. 45, 1, MAXULONG, DWordType, WKSTA_DORMANTFILELIMIT_PARMNUM},
  107. {WKSTA_KEYWORD_READAHEADTHRUPUT,
  108. (LPDWORD) &WSBUF.wki502_read_ahead_throughput,
  109. MAXULONG,0, MAXULONG, DWordType, WKSTA_READAHEADTHRUPUT_PARMNUM},
  110. {WKSTA_KEYWORD_MAILSLOTBUFFERS,
  111. (LPDWORD) &WSBUF.wki502_num_mailslot_buffers,
  112. 3, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
  113. {WKSTA_KEYWORD_SERVERANNOUNCEBUFS,
  114. (LPDWORD) &WSBUF.wki502_num_srv_announce_buffers,
  115. 20, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
  116. {WKSTA_KEYWORD_NUM_ILLEGAL_DG_EVENTS,
  117. (LPDWORD) &WSBUF.wki502_max_illegal_datagram_events,
  118. 5, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
  119. {WKSTA_KEYWORD_ILLEGAL_DG_RESET_TIME,
  120. (LPDWORD) &WSBUF.wki502_illegal_datagram_event_reset_frequency,
  121. 3600, 0, MAXULONG, DWordType, PARMNUM_ALL}, // Not settable
  122. {WKSTA_KEYWORD_LOG_ELECTION_PACKETS,
  123. (LPDWORD) &WSBUF.wki502_log_election_packets,
  124. FALSE, 0, MAXULONG, BooleanType, PARMNUM_ALL}, // Not settable
  125. {WKSTA_KEYWORD_USEOPLOCKING,
  126. (LPDWORD) &WSBUF.wki502_use_opportunistic_locking,
  127. TRUE, 0, 0, BooleanType, WKSTA_USEOPPORTUNISTICLOCKING_PARMNUM},
  128. {WKSTA_KEYWORD_USEUNLOCKBEHIND,
  129. (LPDWORD) &WSBUF.wki502_use_unlock_behind,
  130. TRUE, 0, 0, BooleanType, WKSTA_USEUNLOCKBEHIND_PARMNUM},
  131. {WKSTA_KEYWORD_USECLOSEBEHIND,
  132. (LPDWORD) &WSBUF.wki502_use_close_behind,
  133. TRUE, 0, 0, BooleanType, WKSTA_USECLOSEBEHIND_PARMNUM},
  134. {WKSTA_KEYWORD_BUFNAMEDPIPES,
  135. (LPDWORD) &WSBUF.wki502_buf_named_pipes,
  136. TRUE, 0, 0, BooleanType, WKSTA_BUFFERNAMEDPIPES_PARMNUM},
  137. {WKSTA_KEYWORD_USELOCKREADUNLOCK,
  138. (LPDWORD) &WSBUF.wki502_use_lock_read_unlock,
  139. TRUE, 0, 0, BooleanType, WKSTA_USELOCKANDREADANDUNLOCK_PARMNUM},
  140. {WKSTA_KEYWORD_UTILIZENTCACHING,
  141. (LPDWORD) &WSBUF.wki502_utilize_nt_caching,
  142. TRUE, 0, 0, BooleanType, WKSTA_UTILIZENTCACHING_PARMNUM},
  143. {WKSTA_KEYWORD_USERAWREAD,
  144. (LPDWORD) &WSBUF.wki502_use_raw_read,
  145. TRUE, 0, 0, BooleanType, WKSTA_USERAWREAD_PARMNUM},
  146. {WKSTA_KEYWORD_USERAWWRITE,
  147. (LPDWORD) &WSBUF.wki502_use_raw_write,
  148. TRUE, 0, 0, BooleanType, WKSTA_USERAWWRITE_PARMNUM},
  149. {WKSTA_KEYWORD_USEWRITERAWDATA,
  150. (LPDWORD) &WSBUF.wki502_use_write_raw_data,
  151. TRUE, 0, 0, BooleanType, WKSTA_USEWRITERAWWITHDATA_PARMNUM},
  152. {WKSTA_KEYWORD_USEENCRYPTION,
  153. (LPDWORD) &WSBUF.wki502_use_encryption,
  154. TRUE, 0, 0, BooleanType, WKSTA_USEENCRYPTION_PARMNUM},
  155. {WKSTA_KEYWORD_BUFFILESDENYWRITE,
  156. (LPDWORD) &WSBUF.wki502_buf_files_deny_write,
  157. TRUE, 0, 0, BooleanType, WKSTA_BUFFILESWITHDENYWRITE_PARMNUM},
  158. {WKSTA_KEYWORD_BUFREADONLYFILES,
  159. (LPDWORD) &WSBUF.wki502_buf_read_only_files,
  160. TRUE, 0, 0, BooleanType, WKSTA_BUFFERREADONLYFILES_PARMNUM},
  161. {WKSTA_KEYWORD_FORCECORECREATE,
  162. (LPDWORD) &WSBUF.wki502_force_core_create_mode,
  163. TRUE, 0, 0, BooleanType, WKSTA_FORCECORECREATEMODE_PARMNUM},
  164. {WKSTA_KEYWORD_USE512BYTEMAXTRANS,
  165. (LPDWORD) &WSBUF.wki502_use_512_byte_max_transfer,
  166. FALSE, 0, 0, BooleanType, WKSTA_USE512BYTESMAXTRANSFER_PARMNUM},
  167. {NULL, NULL, 0, 0, BooleanType}
  168. };
  169. //
  170. // For specifying the importance of a transport when binding to it.
  171. // The higher the number means that the transport will be searched
  172. // first.
  173. //
  174. STATIC DWORD QualityOfService = 65536;
  175. DWORD
  176. WsInAWorkgroup(
  177. VOID
  178. )
  179. /*++
  180. Routine Description:
  181. This function determines whether we are a member of a domain, or of
  182. a workgroup. First it checks to make sure we're running on a Windows NT
  183. system (otherwise we're obviously in a domain) and if so, queries LSA
  184. to get the Primary domain SID, if this is NULL, we're in a workgroup.
  185. If we fail for some random unexpected reason, we'll pretend we're in a
  186. domain (it's more restrictive).
  187. Arguments:
  188. None
  189. Return Value:
  190. TRUE - We're in a workgroup
  191. FALSE - We're in a domain
  192. --*/
  193. {
  194. NT_PRODUCT_TYPE ProductType;
  195. OBJECT_ATTRIBUTES ObjectAttributes;
  196. LSA_HANDLE Handle;
  197. NTSTATUS Status;
  198. PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
  199. DWORD Result = FALSE;
  200. Status = RtlGetNtProductType(&ProductType);
  201. if (!NT_SUCCESS(Status)) {
  202. NetpKdPrint((
  203. PREFIX_WKSTA "Could not get Product type\n"));
  204. return FALSE;
  205. }
  206. if (ProductType == NtProductLanManNt) {
  207. return(FALSE);
  208. }
  209. InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
  210. Status = LsaOpenPolicy(NULL,
  211. &ObjectAttributes,
  212. POLICY_VIEW_LOCAL_INFORMATION,
  213. &Handle);
  214. if (!NT_SUCCESS(Status)) {
  215. NetpKdPrint((
  216. PREFIX_WKSTA "Could not open LSA Policy Database\n"));
  217. return FALSE;
  218. }
  219. Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
  220. (PVOID *) &PolicyPrimaryDomainInfo);
  221. if (NT_SUCCESS(Status)) {
  222. if (PolicyPrimaryDomainInfo->Sid == NULL) {
  223. Result = TRUE;
  224. }
  225. else {
  226. Result = FALSE;
  227. }
  228. }
  229. if (PolicyPrimaryDomainInfo) {
  230. LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
  231. }
  232. LsaClose(Handle);
  233. return(Result);
  234. }
  235. NET_API_STATUS
  236. WsGetWorkstationConfiguration(
  237. VOID
  238. )
  239. {
  240. NET_API_STATUS status;
  241. LPNET_CONFIG_HANDLE WorkstationSection;
  242. LPTSTR ComputerName;
  243. LPTSTR DomainNameT;
  244. DWORD version;
  245. NT_PRODUCT_TYPE NtProductType;
  246. BYTE Buffer[max(sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
  247. (DNLEN + 1) * sizeof(WCHAR),
  248. sizeof(LMDR_REQUEST_PACKET))];
  249. PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer;
  250. PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
  251. //
  252. // Lock config information structure for write access since we are
  253. // initializing the data in the structure.
  254. //
  255. if (! RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
  256. //IF_DEBUG(START) {
  257. DbgPrint("WKSSVC Acquire ConfigResource failed\n");
  258. //}
  259. return NERR_InternalError;
  260. }
  261. //
  262. // Set pointer to configuration fields structure
  263. //
  264. WsInfo.WsConfigFields = WsFields;
  265. //
  266. // Get the version name.
  267. //
  268. version = GetVersion( );
  269. WsInfo.MajorVersion = version & 0xff;
  270. WsInfo.MinorVersion = (version >> 8) & 0xff;
  271. WsInfo.RedirectorPlatform = PLATFORM_ID_NT;
  272. //
  273. // Get the configured computer name. NetpGetComputerName allocates
  274. // the memory to hold the computername string using NetApiBufferAllocate().
  275. //
  276. if ((status = NetpGetComputerName(
  277. &ComputerName
  278. )) != NERR_Success) {
  279. RtlReleaseResource(&WsInfo.ConfigResource);
  280. //IF_DEBUG(START) {
  281. DbgPrint("WKSSVC Get computer name failed %lx\n", status);
  282. //}
  283. return status;
  284. }
  285. if ((status = I_NetNameCanonicalize(
  286. NULL,
  287. ComputerName,
  288. (LPTSTR) WsInfo.WsComputerName,
  289. sizeof( WsInfo.WsComputerName ),
  290. NAMETYPE_COMPUTER,
  291. 0
  292. )) != NERR_Success) {
  293. LPWSTR SubString[1];
  294. NetpKdPrint((
  295. PREFIX_WKSTA FORMAT_LPTSTR " is an invalid computername.\n",
  296. ComputerName
  297. ));
  298. SubString[0] = ComputerName;
  299. WsLogEvent(
  300. APE_BAD_COMPNAME,
  301. EVENTLOG_ERROR_TYPE,
  302. 1,
  303. SubString,
  304. NERR_Success
  305. );
  306. (void) NetApiBufferFree((PVOID) ComputerName);
  307. RtlReleaseResource(&WsInfo.ConfigResource);
  308. //IF_DEBUG(START) {
  309. DbgPrint("WKSSVC Invalid computer name failed %lx\n", status);
  310. //}
  311. return status;
  312. }
  313. //
  314. // Free memory allocated by NetpGetComputerName.
  315. //
  316. (void) NetApiBufferFree(ComputerName);
  317. WsInfo.WsComputerNameLength = STRLEN((LPWSTR) WsInfo.WsComputerName);
  318. //
  319. // Open config file and get handle to the [LanmanWorkstation] section
  320. //
  321. if ((status = NetpOpenConfigData(
  322. &WorkstationSection,
  323. NULL, // local (no server name)
  324. SECT_NT_WKSTA,
  325. TRUE // want read-only access
  326. )) != NERR_Success) {
  327. RtlReleaseResource(&WsInfo.ConfigResource);
  328. //IF_DEBUG(START) {
  329. DbgPrint("WKSSVC Open config file failed %lx\n", status);
  330. //}
  331. return status;
  332. }
  333. IF_DEBUG(CONFIG) {
  334. NetpKdPrint((PREFIX_WKSTA "ComputerName " FORMAT_LPTSTR ", length %lu\n",
  335. WsInfo.WsComputerName, WsInfo.WsComputerNameLength));
  336. }
  337. //
  338. // Get the primary domain name from the configuration file
  339. //
  340. if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) {
  341. //IF_DEBUG(START) {
  342. DbgPrint("WKSSVC Get the primary domain name failed %lx\n", status);
  343. //}
  344. goto CloseConfigFile;
  345. }
  346. NetpAssert( DomainNameT != NULL );
  347. if ( *DomainNameT != 0 ) {
  348. if ((status = I_NetNameCanonicalize(
  349. NULL,
  350. DomainNameT,
  351. (LPWSTR) WsInfo.WsPrimaryDomainName,
  352. (DNLEN + 1) * sizeof(TCHAR),
  353. WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
  354. 0
  355. )) != NERR_Success) {
  356. LPWSTR SubString[1];
  357. NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
  358. " is an invalid primary domain name.\n", DomainNameT));
  359. SubString[0] = DomainNameT;
  360. WsLogEvent(
  361. APE_CS_InvalidDomain,
  362. EVENTLOG_ERROR_TYPE,
  363. 1,
  364. SubString,
  365. NERR_Success
  366. );
  367. (void) NetApiBufferFree(DomainNameT);
  368. //IF_DEBUG(START) {
  369. DbgPrint("WKSSVC Invalid domain name failed %lx\n", status);
  370. //}
  371. goto CloseConfigFile;
  372. }
  373. } else {
  374. WsInfo.WsPrimaryDomainName[0] = 0;
  375. }
  376. //
  377. // Free memory allocated by NetpGetDomainName.
  378. //
  379. (void) NetApiBufferFree(DomainNameT);
  380. WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName);
  381. //
  382. // Read the redirector configuration fields
  383. //
  384. WsUpdateWkstaToMatchRegistry(WorkstationSection, TRUE);
  385. //
  386. // Initialize redirector configuration
  387. //
  388. Rrp->Type = ConfigInformation;
  389. Rrp->Version = REQUEST_PACKET_VERSION;
  390. STRCPY((LPWSTR) Rrp->Parameters.Start.RedirectorName,
  391. (LPWSTR) WsInfo.WsComputerName);
  392. Rrp->Parameters.Start.RedirectorNameLength =
  393. WsInfo.WsComputerNameLength*sizeof(TCHAR);
  394. Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR);
  395. STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName+WsInfo.WsComputerNameLength),
  396. (LPWSTR) WsInfo.WsPrimaryDomainName
  397. );
  398. status = WsRedirFsControl(
  399. WsRedirDeviceHandle,
  400. FSCTL_LMR_START,
  401. Rrp,
  402. sizeof(LMR_REQUEST_PACKET) +
  403. Rrp->Parameters.Start.RedirectorNameLength+
  404. Rrp->Parameters.Start.DomainNameLength,
  405. (LPBYTE) &WSBUF,
  406. sizeof(WKSTA_INFO_502),
  407. NULL
  408. );
  409. if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
  410. LPWSTR SubString[1];
  411. NetpKdPrint((PREFIX_WKSTA "Start redirector failed %lu\n", status));
  412. SubString[0] = L"redirector";
  413. WsLogEvent(
  414. NELOG_Service_Fail,
  415. EVENTLOG_ERROR_TYPE,
  416. 1,
  417. SubString,
  418. status
  419. );
  420. //IF_DEBUG(START) {
  421. DbgPrint("WKSSVC Start redirector failed %lx\n", status);
  422. //}
  423. goto CloseConfigFile;
  424. }
  425. //
  426. // If we still have the default value for number of mailslot buffers,
  427. // pick a "reasonable" value based on the amount of physical memory
  428. // available in the system.
  429. //
  430. if (WSBUF.wki502_num_mailslot_buffers == MAXULONG) {
  431. MEMORYSTATUS MemoryStatus;
  432. GlobalMemoryStatus(&MemoryStatus);
  433. //
  434. // Lets take up 1/40th of 1% of physical memory for mailslot buffers
  435. //
  436. WSBUF.wki502_num_mailslot_buffers =
  437. (DWORD)(MemoryStatus.dwTotalPhys / (100 * 40 * 512));
  438. }
  439. //
  440. // Initialize datagram receiver configuration
  441. //
  442. Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
  443. Drrp->Parameters.Start.NumberOfMailslotBuffers =
  444. WSBUF.wki502_num_mailslot_buffers;
  445. Drrp->Parameters.Start.NumberOfServerAnnounceBuffers =
  446. WSBUF.wki502_num_srv_announce_buffers;
  447. Drrp->Parameters.Start.IllegalDatagramThreshold =
  448. WSBUF.wki502_max_illegal_datagram_events;
  449. Drrp->Parameters.Start.EventLogResetFrequency =
  450. WSBUF.wki502_illegal_datagram_event_reset_frequency;
  451. Drrp->Parameters.Start.LogElectionPackets =
  452. (WSBUF.wki502_log_election_packets != FALSE);
  453. RtlGetNtProductType(&NtProductType);
  454. Drrp->Parameters.Start.IsLanManNt = (NtProductType == NtProductLanManNt);
  455. status = WsDgReceiverIoControl(
  456. WsDgReceiverDeviceHandle,
  457. IOCTL_LMDR_START,
  458. Drrp,
  459. sizeof(LMDR_REQUEST_PACKET),
  460. NULL,
  461. 0,
  462. NULL
  463. );
  464. if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
  465. LPWSTR SubString[1];
  466. NetpKdPrint((PREFIX_WKSTA "Start datagram receiver failed %lu\n", status));
  467. SubString[0] = L"datagram receiver";
  468. WsLogEvent(
  469. NELOG_Service_Fail,
  470. EVENTLOG_ERROR_TYPE,
  471. 1,
  472. SubString,
  473. status
  474. );
  475. //IF_DEBUG(START) {
  476. DbgPrint("WKSSVC Start Datagram recevier failed %lx\n", status);
  477. //}
  478. goto CloseConfigFile;
  479. }
  480. // do all error reporting in the routine
  481. // don't check any errors here
  482. WsCSCReportStartRedir();
  483. status = NERR_Success;
  484. CloseConfigFile:
  485. (void) NetpCloseConfigData(WorkstationSection);
  486. RtlReleaseResource(&WsInfo.ConfigResource);
  487. return status;
  488. }
  489. VOID
  490. WsUpdateWkstaToMatchRegistry(
  491. IN LPNET_CONFIG_HANDLE WorkstationSection,
  492. IN BOOL IsWkstaInit
  493. )
  494. /*++
  495. Routine Description:
  496. This function reads each redirector configuration field into the
  497. WsInfo.WsConfigBuf (WSBUF) buffer so that the values are ready to be
  498. set in the redirector. If a field is not found or is invalid, the
  499. default value is set.
  500. Arguments:
  501. WorkstationSection - Supplies a handle to read the Workstation
  502. configuration parameters.
  503. IsWkstaInit - Supplies a flag which if TRUE indicates that this
  504. routine is called at workstation init time so the non-settable
  505. fields are acceptable and set in WSBUF. If FALSE, the
  506. non-settable fields are ignored.
  507. Return Value:
  508. None.
  509. --*/
  510. {
  511. DWORD i;
  512. NET_API_STATUS status;
  513. DWORD TempDwordValue;
  514. //
  515. // NTRAID-70687-2/6/2000 davey Invalid keyword value. How to report error?
  516. //
  517. #define REPORT_KEYWORD_IGNORED( lptstrKeyword ) \
  518. { \
  519. NetpKdPrint(( \
  520. PREFIX_WKSTA "*ERROR* Tried to set keyword '" FORMAT_LPTSTR \
  521. "' with invalid value.\n" \
  522. "This error is ignored.\n", \
  523. lptstrKeyword )); \
  524. }
  525. for (i = 0; WsInfo.WsConfigFields[i].Keyword != NULL; i++) {
  526. //
  527. // This is to handle fields that are not settable via
  528. // NetWkstaSetInfo. However, these non-settable fields,
  529. // designated by Parmnum == PARMNUM_ALL, can be assigned when
  530. // the workstation is starting up.
  531. //
  532. if ((WsInfo.WsConfigFields[i].Parmnum != PARMNUM_ALL) ||
  533. IsWkstaInit) {
  534. //
  535. // Depending on data type, get the appropriate kind of value.
  536. //
  537. switch (WsInfo.WsConfigFields[i].DataType) {
  538. case BooleanType:
  539. status = NetpGetConfigBool(
  540. WorkstationSection,
  541. WsInfo.WsConfigFields[i].Keyword,
  542. WsInfo.WsConfigFields[i].Default,
  543. (LPBOOL) (WsInfo.WsConfigFields[i].FieldPtr)
  544. );
  545. if ((status != NO_ERROR) && (status != NERR_CfgParamNotFound)) {
  546. REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
  547. }
  548. break;
  549. case DWordType:
  550. status = NetpGetConfigDword(
  551. WorkstationSection,
  552. WsInfo.WsConfigFields[i].Keyword,
  553. WsInfo.WsConfigFields[i].Default,
  554. &TempDwordValue
  555. );
  556. if ((status == NO_ERROR) || (status == NERR_CfgParamNotFound)) {
  557. //
  558. // Make sure keyword is in range.
  559. //
  560. if (TempDwordValue < WsInfo.WsConfigFields[i].Minimum ||
  561. TempDwordValue > WsInfo.WsConfigFields[i].Maximum) {
  562. //
  563. // NTRAID-70689-2/6/2000 davey Better way to report error?
  564. //
  565. NetpKdPrint((
  566. PREFIX_WKSTA FORMAT_LPTSTR
  567. " value out of range %lu (%lu-%lu)\n",
  568. WsInfo.WsConfigFields[i].Keyword,
  569. TempDwordValue,
  570. WsInfo.WsConfigFields[i].Minimum,
  571. WsInfo.WsConfigFields[i].Maximum
  572. ));
  573. //
  574. // Set back to default.
  575. //
  576. *(WsInfo.WsConfigFields[i].FieldPtr)
  577. = WsInfo.WsConfigFields[i].Default;
  578. }
  579. else {
  580. *(WsInfo.WsConfigFields[i].FieldPtr) = TempDwordValue;
  581. }
  582. }
  583. else {
  584. REPORT_KEYWORD_IGNORED( WsInfo.WsConfigFields[i].Keyword );
  585. }
  586. break;
  587. default:
  588. NetpAssert(FALSE);
  589. } // switch
  590. }
  591. }
  592. }
  593. NET_API_STATUS
  594. WsBindToTransports(
  595. VOID
  596. )
  597. /*++
  598. Routine Description:
  599. This function binds the transports specified in the registry to the
  600. redirector. The order of priority for the transports follows the order
  601. they are listed by the "Bind=" valuename.
  602. Arguments:
  603. None.
  604. Return Value:
  605. NET_API_STATUS - NERR_Success or reason for failure.
  606. --*/
  607. {
  608. NET_API_STATUS status;
  609. NET_API_STATUS tempStatus;
  610. NTSTATUS ntstatus;
  611. DWORD transportsBound;
  612. PRTL_QUERY_REGISTRY_TABLE queryTable;
  613. LIST_ENTRY header;
  614. PLIST_ENTRY pListEntry;
  615. PWS_BIND pBind;
  616. //
  617. // Ask the RTL to call us back for each subvalue in the MULTI_SZ
  618. // value \LanmanWorkstation\Linkage\Bind.
  619. //
  620. queryTable = (PVOID)LocalAlloc(
  621. 0,
  622. sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
  623. );
  624. if (queryTable == NULL) {
  625. return ERROR_NOT_ENOUGH_MEMORY;
  626. }
  627. InitializeListHead( &header);
  628. queryTable[0].QueryRoutine = WsBindATransport;
  629. queryTable[0].Flags = 0;
  630. queryTable[0].Name = WS_BIND_VALUE_NAME;
  631. queryTable[0].EntryContext = NULL;
  632. queryTable[0].DefaultType = REG_NONE;
  633. queryTable[0].DefaultData = NULL;
  634. queryTable[0].DefaultLength = 0;
  635. queryTable[1].QueryRoutine = NULL;
  636. queryTable[1].Flags = 0;
  637. queryTable[1].Name = NULL;
  638. ntstatus = RtlQueryRegistryValues(
  639. RTL_REGISTRY_SERVICES, // path relative to ...
  640. WS_LINKAGE_REGISTRY_PATH,
  641. queryTable,
  642. (PVOID) &header, // context
  643. NULL
  644. );
  645. if ( !NT_SUCCESS( ntstatus)) {
  646. NetpKdPrint((
  647. PREFIX_WKSTA "WsBindToTransports: RtlQueryRegistryValues Failed:"
  648. FORMAT_NTSTATUS "\n",
  649. ntstatus
  650. ));
  651. status = NetpNtStatusToApiStatus( ntstatus);
  652. } else {
  653. status = NO_ERROR;
  654. }
  655. //
  656. // First process all the data, then clean up.
  657. //
  658. for ( pListEntry = header.Flink;
  659. pListEntry != &header;
  660. pListEntry = pListEntry->Flink) {
  661. pBind = CONTAINING_RECORD(
  662. pListEntry,
  663. WS_BIND,
  664. ListEntry
  665. );
  666. tempStatus = NO_ERROR;
  667. if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
  668. WaitForSingleObject(
  669. pBind->Redir->EventHandle,
  670. INFINITE
  671. );
  672. tempStatus = WsMapStatus( pBind->Redir->IoStatusBlock.Status);
  673. pBind->Redir->Bound = (tempStatus == NO_ERROR);
  674. if (tempStatus == ERROR_DUP_NAME) {
  675. status = tempStatus;
  676. }
  677. }
  678. if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
  679. WaitForSingleObject(
  680. pBind->Dgrec->EventHandle,
  681. INFINITE
  682. );
  683. tempStatus = WsMapStatus( pBind->Dgrec->IoStatusBlock.Status);
  684. pBind->Dgrec->Bound = (tempStatus == NO_ERROR);
  685. if (tempStatus == ERROR_DUP_NAME) {
  686. status = tempStatus;
  687. }
  688. }
  689. if ( tempStatus == ERROR_DUP_NAME) {
  690. NetpKdPrint((
  691. PREFIX_WKSTA "Computername " FORMAT_LPTSTR
  692. " already exists on network " FORMAT_LPTSTR "\n",
  693. WsInfo.WsComputerName,
  694. pBind->TransportName
  695. ));
  696. }
  697. //
  698. // If one is installed but the other is not, clean up the other.
  699. //
  700. if ( pBind->Dgrec->Bound != pBind->Redir->Bound) {
  701. WsUnbindTransport2( pBind);
  702. }
  703. }
  704. if ( status != NO_ERROR) {
  705. if (status == ERROR_DUP_NAME) {
  706. WsLogEvent(
  707. NERR_DupNameReboot,
  708. EVENTLOG_ERROR_TYPE,
  709. 0,
  710. NULL,
  711. NERR_Success
  712. );
  713. }
  714. for ( pListEntry = header.Flink;
  715. pListEntry != &header;
  716. pListEntry = pListEntry->Flink) {
  717. pBind = CONTAINING_RECORD(
  718. pListEntry,
  719. WS_BIND,
  720. ListEntry
  721. );
  722. WsUnbindTransport2( pBind);
  723. }
  724. }
  725. for ( transportsBound = 0;
  726. IsListEmpty( &header) == FALSE;
  727. LocalFree((HLOCAL) pBind)) {
  728. pListEntry = RemoveHeadList( &header);
  729. pBind = CONTAINING_RECORD(
  730. pListEntry,
  731. WS_BIND,
  732. ListEntry
  733. );
  734. if ( pBind->Redir->EventHandle != INVALID_HANDLE_VALUE) {
  735. CloseHandle( pBind->Redir->EventHandle);
  736. }
  737. if ( pBind->Redir->Bound == TRUE) {
  738. transportsBound++;
  739. }
  740. if ( pBind->Dgrec->EventHandle != INVALID_HANDLE_VALUE) {
  741. CloseHandle( pBind->Dgrec->EventHandle);
  742. }
  743. }
  744. (void) LocalFree((HLOCAL) queryTable);
  745. if ( WsRedirAsyncDeviceHandle != NULL) {
  746. (VOID)NtClose( WsRedirAsyncDeviceHandle);
  747. WsRedirAsyncDeviceHandle = NULL;
  748. }
  749. if ( WsDgrecAsyncDeviceHandle != NULL) {
  750. (VOID)NtClose( WsDgrecAsyncDeviceHandle);
  751. WsDgrecAsyncDeviceHandle = NULL;
  752. }
  753. if (transportsBound == 0) {
  754. NetpKdPrint((
  755. PREFIX_WKSTA "WsBindToTransports: Failed to bind to any"
  756. " transports" FORMAT_API_STATUS "\n",
  757. status
  758. ));
  759. if ( status != ERROR_DUP_NAME) {
  760. WsLogEvent(
  761. NELOG_NoTranportLoaded,
  762. EVENTLOG_ERROR_TYPE,
  763. 0,
  764. NULL,
  765. NERR_Success
  766. );
  767. status = NO_ERROR;
  768. }
  769. }
  770. return status;
  771. }
  772. STATIC
  773. NTSTATUS
  774. WsBindATransport(
  775. IN PWSTR ValueName,
  776. IN ULONG ValueType,
  777. IN PVOID ValueData,
  778. IN ULONG ValueLength,
  779. IN PVOID Context,
  780. IN PVOID EntryContext
  781. )
  782. /*++
  783. This routine always returns SUCCESS because we want all transports
  784. to be processed fully.
  785. --*/
  786. {
  787. NET_API_STATUS status;
  788. DBG_UNREFERENCED_PARAMETER( ValueName);
  789. DBG_UNREFERENCED_PARAMETER( ValueLength);
  790. DBG_UNREFERENCED_PARAMETER( EntryContext);
  791. //
  792. // The value type must be REG_SZ (translated from REG_MULTI_SZ by the RTL).
  793. //
  794. if (ValueType != REG_SZ) {
  795. NetpKdPrint((
  796. PREFIX_WKSTA "WsBindATransport: ignored invalid value "
  797. FORMAT_LPWSTR "\n",
  798. ValueName
  799. ));
  800. return STATUS_SUCCESS;
  801. }
  802. //
  803. // Bind transport
  804. //
  805. status = WsAsyncBindTransport(
  806. ValueData, // name of transport device object
  807. --QualityOfService,
  808. (PLIST_ENTRY)Context
  809. );
  810. if ( status != NERR_Success) {
  811. NetpKdPrint((
  812. PREFIX_WKSTA "WsAsyncBindTransport " FORMAT_LPTSTR
  813. " returns " FORMAT_API_STATUS "\n",
  814. ValueData,
  815. status
  816. ));
  817. }
  818. return STATUS_SUCCESS;
  819. }
  820. NET_API_STATUS
  821. WsAddDomains(
  822. VOID
  823. )
  824. /*++
  825. Routine Description:
  826. This function tells the datagram receiver the names to listen on for
  827. datagrams. The names include the computer name, the primary domain,
  828. name and the other domains. The logon domain is not added here; it is
  829. made known to the datagram receiver whenever a user logs on.
  830. Arguments:
  831. None.
  832. Return Value:
  833. NET_API_STATUS - NERR_Success or reason for failure.
  834. Warning:
  835. This routine is UNICODE only.
  836. --*/
  837. {
  838. NET_API_STATUS status;
  839. LPNET_CONFIG_HANDLE SectionHandle = NULL;
  840. LPTSTR OtherDomainName = NULL;
  841. LPTSTR_ARRAY ArrayStart = NULL;
  842. BYTE Buffer[sizeof(LMDR_REQUEST_PACKET) +
  843. (max(MAX_PATH, DNLEN) + 1) * sizeof(WCHAR)];
  844. PLMDR_REQUEST_PACKET Drrp = (PLMDR_REQUEST_PACKET) Buffer;
  845. //
  846. // Now loop through and add all the other domains.
  847. //
  848. //
  849. // Open registry section listing the other domains. Note that this
  850. // is workstation servive parameter, NOT the browser service parameter.
  851. //
  852. if ((status = NetpOpenConfigData(
  853. &SectionHandle,
  854. NULL, // no server name
  855. SECT_NT_WKSTA,
  856. TRUE // read-only
  857. )) != NERR_Success) {
  858. //
  859. // Ignore the error if the config section couldn't be found.
  860. //
  861. status = NERR_Success;
  862. goto DomainsCleanup;
  863. }
  864. //
  865. // Get value for OtherDomains keyword in the wksta section.
  866. // This is a "NULL-NULL" array (which corresponds to REG_MULTI_SZ).
  867. //
  868. status = NetpGetConfigTStrArray(
  869. SectionHandle,
  870. WKSTA_KEYWORD_OTHERDOMAINS,
  871. &ArrayStart // Must be freed by NetApiBufferFree().
  872. );
  873. if (status != NERR_Success) {
  874. status = NERR_Success; // other domain is optional
  875. goto DomainsCleanup;
  876. }
  877. NetpAssert(ArrayStart != NULL);
  878. if (NetpIsTStrArrayEmpty(ArrayStart)) {
  879. goto DomainsCleanup;
  880. }
  881. OtherDomainName = ArrayStart;
  882. while (! NetpIsTStrArrayEmpty(OtherDomainName)) {
  883. if ((status = I_NetNameCanonicalize(
  884. NULL,
  885. OtherDomainName,
  886. (LPWSTR) Drrp->Parameters.AddDelName.Name,
  887. (DNLEN + 1) * sizeof(TCHAR),
  888. NAMETYPE_DOMAIN,
  889. 0
  890. )) != NERR_Success) {
  891. LPWSTR SubString[1];
  892. NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
  893. " is an invalid other domain name.\n", OtherDomainName));
  894. SubString[0] = OtherDomainName;
  895. WsLogEvent(
  896. APE_CS_InvalidDomain,
  897. EVENTLOG_ERROR_TYPE,
  898. 1,
  899. SubString,
  900. NERR_Success
  901. );
  902. status = NERR_Success; // loading other domains is optional
  903. goto NextOtherDomain;
  904. }
  905. //
  906. // Tell the datagram receiver about an other domain name
  907. //
  908. Drrp->Version = LMDR_REQUEST_PACKET_VERSION;
  909. Drrp->Parameters.AddDelName.Type = OtherDomain;
  910. Drrp->Parameters.AddDelName.DgReceiverNameLength =
  911. STRLEN(OtherDomainName) * sizeof(TCHAR);
  912. status = WsDgReceiverIoControl(
  913. WsDgReceiverDeviceHandle,
  914. IOCTL_LMDR_ADD_NAME,
  915. Drrp,
  916. sizeof(LMDR_REQUEST_PACKET) +
  917. Drrp->Parameters.AddDelName.DgReceiverNameLength,
  918. NULL,
  919. 0,
  920. NULL
  921. );
  922. //
  923. // Service install still pending. Update checkpoint counter and the
  924. // status with the Service Controller.
  925. //
  926. WsGlobalData.Status.dwCheckPoint++;
  927. WsUpdateStatus();
  928. if (status != NERR_Success) {
  929. LPWSTR SubString[1];
  930. NetpKdPrint((
  931. PREFIX_WKSTA "Add Other domain name " FORMAT_LPTSTR
  932. " failed with error code %lu\n",
  933. OtherDomainName,
  934. status
  935. ));
  936. SubString[0] = OtherDomainName;
  937. WsLogEvent(
  938. APE_CS_InvalidDomain,
  939. EVENTLOG_ERROR_TYPE,
  940. 1,
  941. SubString,
  942. status
  943. );
  944. status = NERR_Success; // loading other domains is optional
  945. }
  946. NextOtherDomain:
  947. OtherDomainName = NetpNextTStrArrayEntry(OtherDomainName);
  948. }
  949. DomainsCleanup:
  950. //
  951. // Done with reading from config file. Close file, free memory, etc.
  952. //
  953. if (ArrayStart != NULL) {
  954. (VOID) NetApiBufferFree(ArrayStart);
  955. }
  956. if (SectionHandle != NULL) {
  957. (VOID) NetpCloseConfigData(SectionHandle);
  958. }
  959. return status;
  960. }
  961. VOID
  962. WsLogEvent(
  963. DWORD MessageId,
  964. WORD EventType,
  965. DWORD NumberOfSubStrings,
  966. LPWSTR *SubStrings,
  967. DWORD ErrorCode
  968. )
  969. {
  970. HANDLE LogHandle;
  971. PSID UserSid = NULL;
  972. LogHandle = RegisterEventSourceW (
  973. NULL,
  974. WORKSTATION_DISPLAY_NAME
  975. );
  976. if (LogHandle == NULL) {
  977. NetpKdPrint((PREFIX_WKSTA "RegisterEventSourceW failed %lu\n",
  978. GetLastError()));
  979. return;
  980. }
  981. if (ErrorCode == NERR_Success) {
  982. //
  983. // No error codes were specified
  984. //
  985. (void) ReportEventW(
  986. LogHandle,
  987. EventType,
  988. 0, // event category
  989. MessageId,
  990. UserSid,
  991. (WORD)NumberOfSubStrings,
  992. 0,
  993. SubStrings,
  994. (PVOID) NULL
  995. );
  996. }
  997. else {
  998. //
  999. // Log the error code specified
  1000. //
  1001. (void) ReportEventW(
  1002. LogHandle,
  1003. EventType,
  1004. 0, // event category
  1005. MessageId,
  1006. UserSid,
  1007. (WORD)NumberOfSubStrings,
  1008. sizeof(DWORD),
  1009. SubStrings,
  1010. (PVOID) &ErrorCode
  1011. );
  1012. }
  1013. DeregisterEventSource(LogHandle);
  1014. }
  1015. NET_API_STATUS
  1016. WsSetWorkStationDomainName(
  1017. VOID
  1018. )
  1019. {
  1020. NET_API_STATUS status;
  1021. LPNET_CONFIG_HANDLE WorkstationSection;
  1022. LPTSTR ComputerName;
  1023. LPTSTR DomainNameT;
  1024. DWORD version;
  1025. NT_PRODUCT_TYPE NtProductType;
  1026. BYTE Buffer[sizeof(LMR_REQUEST_PACKET) + (MAX_PATH + 1) * sizeof(WCHAR) +
  1027. (DNLEN + 1) * sizeof(WCHAR)];
  1028. PLMR_REQUEST_PACKET Rrp = (PLMR_REQUEST_PACKET) Buffer;
  1029. NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName start.\n"));
  1030. //
  1031. // Lock config information structure for write access since we are
  1032. // modifying the data in the WsInfo.
  1033. //
  1034. if (!RtlAcquireResourceExclusive(&WsInfo.ConfigResource, TRUE)) {
  1035. return NERR_InternalError;
  1036. }
  1037. //
  1038. // Get the primary domain name from the configuration file
  1039. //
  1040. if ((status = NetpGetDomainName(&DomainNameT)) != NERR_Success) {
  1041. goto CloseConfigFile;
  1042. }
  1043. NetpAssert( DomainNameT != NULL );
  1044. if ( *DomainNameT != 0 ) {
  1045. if ((status = I_NetNameCanonicalize(
  1046. NULL,
  1047. DomainNameT,
  1048. (LPWSTR) WsInfo.WsPrimaryDomainName,
  1049. (DNLEN + 1) * sizeof(TCHAR),
  1050. WsInAWorkgroup() ? NAMETYPE_WORKGROUP : NAMETYPE_DOMAIN,
  1051. 0
  1052. )) != NERR_Success) {
  1053. LPWSTR SubString[1];
  1054. NetpKdPrint((PREFIX_WKSTA FORMAT_LPTSTR
  1055. " is an invalid primary domain name.\n", DomainNameT));
  1056. SubString[0] = DomainNameT;
  1057. WsLogEvent(
  1058. APE_CS_InvalidDomain,
  1059. EVENTLOG_ERROR_TYPE,
  1060. 1,
  1061. SubString,
  1062. NERR_Success
  1063. );
  1064. (void) NetApiBufferFree(DomainNameT);
  1065. goto CloseConfigFile;
  1066. }
  1067. } else {
  1068. WsInfo.WsPrimaryDomainName[0] = 0;
  1069. }
  1070. //
  1071. // Free memory allocated by NetpGetDomainName.
  1072. //
  1073. (void) NetApiBufferFree(DomainNameT);
  1074. WsInfo.WsPrimaryDomainNameLength = STRLEN((LPWSTR) WsInfo.WsPrimaryDomainName);
  1075. //
  1076. // Initialize redirector configuration
  1077. //
  1078. Rrp->Type = ConfigInformation;
  1079. Rrp->Version = REQUEST_PACKET_VERSION;
  1080. Rrp->Parameters.Start.RedirectorNameLength = 0;
  1081. Rrp->Parameters.Start.DomainNameLength = WsInfo.WsPrimaryDomainNameLength*sizeof(TCHAR);
  1082. STRCPY((LPWSTR) (Rrp->Parameters.Start.RedirectorName),
  1083. (LPWSTR) WsInfo.WsPrimaryDomainName);
  1084. NetpKdPrint((PREFIX_WKSTA "WsSetWorkStationDomainName call rdr.\n"));
  1085. status = WsRedirFsControl(
  1086. WsRedirDeviceHandle,
  1087. FSCTL_LMR_SET_DOMAIN_NAME,
  1088. Rrp,
  1089. sizeof(LMR_REQUEST_PACKET) + Rrp->Parameters.Start.DomainNameLength,
  1090. NULL,
  1091. 0,
  1092. NULL
  1093. );
  1094. if ((status != NERR_Success) && (status != ERROR_SERVICE_ALREADY_RUNNING)) {
  1095. LPWSTR SubString[1];
  1096. NetpKdPrint((PREFIX_WKSTA "Set domain name failed %lu\n", status));
  1097. SubString[0] = L"redirector";
  1098. WsLogEvent(
  1099. NELOG_Service_Fail,
  1100. EVENTLOG_ERROR_TYPE,
  1101. 1,
  1102. SubString,
  1103. status
  1104. );
  1105. }
  1106. CloseConfigFile:
  1107. RtlReleaseResource(&WsInfo.ConfigResource);
  1108. return status;
  1109. }