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.

1266 lines
32 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. LlsUtil.c
  5. Abstract:
  6. Author:
  7. Arthur Hanson (arth) Dec 07, 1994
  8. Environment:
  9. Revision History:
  10. Jeff Parham (jeffparh) 12-Jan-1996
  11. o Added WinNtBuildNumberGet() to ascertain the Windows NT build number
  12. running on a given machine.
  13. o Enhanced output of TimeToString().
  14. --*/
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <ntlsa.h>
  19. #include <windows.h>
  20. #include <stdlib.h>
  21. #include <crypt.h>
  22. #include <wchar.h>
  23. #include <dsgetdc.h>
  24. #include <sddl.h>
  25. #include "debug.h"
  26. #include "llssrv.h"
  27. #include "llsevent.h"
  28. #include <strsafe.h>
  29. //
  30. // NB : Keep this define in sync with client\llsrpc.rc.
  31. //
  32. #define IDS_LICENSEWARNING 1501
  33. const char HeaderString[] = "License Logging System Data File\x01A";
  34. #define HEADER_SIZE 34
  35. typedef struct _LLS_FILE_HEADER {
  36. char Header[HEADER_SIZE];
  37. DWORD Version;
  38. DWORD DataSize;
  39. } LLS_FILE_HEADER, *PLLS_FILE_HEADER;
  40. extern HANDLE gLlsDllHandle;
  41. static HANDLE ghWarningDlgThreadHandle = NULL;
  42. VOID WarningDlgThread( PVOID ThreadParameter );
  43. /////////////////////////////////////////////////////////////////////////
  44. NTSTATUS
  45. EBlock(
  46. PVOID Data,
  47. ULONG DataSize
  48. )
  49. /*++
  50. Routine Description:
  51. Arguments:
  52. Return Value:
  53. --*/
  54. {
  55. NTSTATUS Status = STATUS_SUCCESS;
  56. DATA_KEY PublicKey;
  57. CRYPT_BUFFER CryptBuffer;
  58. //
  59. // Init our public key
  60. //
  61. PublicKey.Length = 4;
  62. PublicKey.MaximumLength = 4;
  63. PublicKey.Buffer = LocalAlloc(LPTR, 4);
  64. if (PublicKey.Buffer != NULL) {
  65. ((char *) (PublicKey.Buffer))[0] = '7';
  66. ((char *) (PublicKey.Buffer))[1] = '7';
  67. ((char *) (PublicKey.Buffer))[2] = '7';
  68. ((char *) (PublicKey.Buffer))[3] = '7';
  69. CryptBuffer.Length = DataSize;
  70. CryptBuffer.MaximumLength = DataSize;
  71. CryptBuffer.Buffer = (PVOID) Data;
  72. Status = RtlEncryptData2(&CryptBuffer, &PublicKey);
  73. LocalFree(PublicKey.Buffer);
  74. } else
  75. Status = STATUS_NO_MEMORY;
  76. return Status;
  77. } // EBlock
  78. /////////////////////////////////////////////////////////////////////////
  79. NTSTATUS
  80. DeBlock(
  81. PVOID Data,
  82. ULONG DataSize
  83. )
  84. /*++
  85. Routine Description:
  86. Arguments:
  87. Return Value:
  88. --*/
  89. {
  90. NTSTATUS Status = STATUS_SUCCESS;
  91. DATA_KEY PublicKey;
  92. CRYPT_BUFFER CryptBuffer;
  93. //
  94. // Init our public key
  95. //
  96. PublicKey.Length = 4;
  97. PublicKey.MaximumLength = 4;
  98. PublicKey.Buffer = LocalAlloc(LPTR, 4);
  99. if (PublicKey.Buffer != NULL) {
  100. ((char *) (PublicKey.Buffer))[0] = '7';
  101. ((char *) (PublicKey.Buffer))[1] = '7';
  102. ((char *) (PublicKey.Buffer))[2] = '7';
  103. ((char *) (PublicKey.Buffer))[3] = '7';
  104. CryptBuffer.Length = DataSize;
  105. CryptBuffer.MaximumLength = DataSize;
  106. CryptBuffer.Buffer = (PVOID) Data;
  107. Status = RtlDecryptData2(&CryptBuffer, &PublicKey);
  108. LocalFree(PublicKey.Buffer);
  109. } else
  110. Status = STATUS_NO_MEMORY;
  111. return Status;
  112. } // DeBlock
  113. /////////////////////////////////////////////////////////////////////////
  114. BOOL
  115. FileExists(
  116. LPTSTR FileName
  117. )
  118. /*++
  119. Routine Description:
  120. Arguments:
  121. Return Value:
  122. --*/
  123. {
  124. return (BOOL) RtlDoesFileExists_U(FileName);
  125. } // FileExists
  126. /////////////////////////////////////////////////////////////////////////
  127. VOID
  128. lsplitpath(
  129. const TCHAR *path,
  130. TCHAR *drive,
  131. TCHAR *dir,
  132. TCHAR *fname,
  133. TCHAR *ext
  134. )
  135. /*++
  136. Routine Description:
  137. Splits a path name into its individual components
  138. Took the _splitpath and _makepath routines and converted them to
  139. be NT (long file name) and Unicode friendly.
  140. Arguments:
  141. Entry:
  142. path - pointer to path name to be parsed
  143. drive - pointer to buffer for drive component, if any
  144. dir - pointer to buffer for subdirectory component, if any
  145. fname - pointer to buffer for file base name component, if any
  146. ext - pointer to buffer for file name extension component, if any
  147. Exit:
  148. drive - pointer to drive string. Includes ':' if a drive was given.
  149. dir - pointer to subdirectory string. Includes leading and
  150. trailing '/' or '\', if any.
  151. fname - pointer to file base name
  152. ext - pointer to file extension, if any. Includes leading '.'.
  153. Return Value:
  154. --*/
  155. {
  156. TCHAR *p;
  157. TCHAR *last_slash = NULL, *dot = NULL;
  158. SIZE_T len;
  159. ASSERT(NULL != path &&
  160. NULL != drive &&
  161. NULL != dir &&
  162. NULL != fname &&
  163. NULL != ext);
  164. // init these so we don't exit with bogus values
  165. drive[0] = TEXT('\0');
  166. dir[0] = TEXT('\0');
  167. fname[0] = TEXT('\0');
  168. ext[0] = TEXT('\0');
  169. if (path[0] == TEXT('\0'))
  170. return;
  171. /*+---------------------------------------------------------------------+
  172. | Assume that the path argument has the following form, where any or |
  173. | all of the components may be missing. |
  174. | |
  175. | <drive><dir><fname><ext> |
  176. | |
  177. | drive: |
  178. | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a |
  179. | ':' or a '\' in the case of a UNC path. |
  180. | dir: |
  181. | 0 to _MAX_DIR-1 characters in the form of an absolute path |
  182. | (leading '/' or '\') or relative path, the last of which, if |
  183. | any, must be a '/' or '\'. E.g - |
  184. | |
  185. | absolute path: |
  186. | \top\next\last\ ; or |
  187. | /top/next/last/ |
  188. | relative path: |
  189. | top\next\last\ ; or |
  190. | top/next/last/ |
  191. | Mixed use of '/' and '\' within a path is also tolerated |
  192. | fname: |
  193. | 0 to _MAX_FNAME-1 characters not including the '.' character |
  194. | ext: |
  195. | 0 to _MAX_EXT-1 characters where, if any, the first must be a |
  196. | '.' |
  197. +---------------------------------------------------------------------+*/
  198. // extract drive letter and :, if any
  199. if ( path[0] && (path[1] == TEXT(':')) ) {
  200. if (drive) {
  201. drive[0] = path[0];
  202. drive[1] = path[1];
  203. drive[2] = TEXT('\0');
  204. }
  205. path += 2;
  206. }
  207. // if no drive then check for UNC pathname
  208. if (drive[0] == TEXT('\0'))
  209. if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
  210. // got a UNC path so put server-sharename into drive
  211. drive[0] = path[0];
  212. drive[1] = path[1];
  213. path += 2;
  214. p = &drive[2];
  215. while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
  216. *p++ = *path++;
  217. if (*path == TEXT('\0'))
  218. return;
  219. // now sitting at the share - copy this as well (copy slash first)
  220. *p++ = *path++;
  221. while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
  222. *p++ = *path++;
  223. // tack on terminating NULL
  224. *p = TEXT('\0');
  225. }
  226. /*+---------------------------------------------------------------------+
  227. | extract path string, if any. Path now points to the first character|
  228. | of the path, if any, or the filename or extension, if no path was |
  229. | specified. Scan ahead for the last occurence, if any, of a '/' or |
  230. | '\' path separator character. If none is found, there is no path. |
  231. | We will also note the last '.' character found, if any, to aid in |
  232. | handling the extension. |
  233. +---------------------------------------------------------------------+*/
  234. for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
  235. if (*p == TEXT('/') || *p == TEXT('\\'))
  236. // point to one beyond for later copy
  237. last_slash = p + 1;
  238. else if (*p == TEXT('.'))
  239. dot = p;
  240. }
  241. if (last_slash) {
  242. // found a path - copy up through last_slash or max. characters allowed,
  243. // whichever is smaller
  244. if (dir) {
  245. len = __min((last_slash - path), (_MAX_DIR - 1));
  246. lstrcpyn(dir, path, (int)len + 1);
  247. dir[len] = TEXT('\0');
  248. }
  249. path = last_slash;
  250. }
  251. /*+---------------------------------------------------------------------+
  252. | extract file name and extension, if any. Path now points to the |
  253. | first character of the file name, if any, or the extension if no |
  254. | file name was given. Dot points to the '.' beginning the extension,|
  255. | if any. |
  256. +---------------------------------------------------------------------+*/
  257. if (dot && (dot >= path)) {
  258. // found the marker for an extension - copy the file name up to the
  259. // '.'.
  260. if (fname) {
  261. len = __min((dot - path), (_MAX_FNAME - 1));
  262. lstrcpyn(fname, path, (int)len + 1);
  263. *(fname + len) = TEXT('\0');
  264. }
  265. // now we can get the extension - remember that p still points to the
  266. // terminating nul character of path.
  267. if (ext) {
  268. len = __min((p - dot), (_MAX_EXT - 1));
  269. lstrcpyn(ext, dot, (int)len + 1);
  270. ext[len] = TEXT('\0');
  271. }
  272. }
  273. else {
  274. // found no extension, give empty extension and copy rest of string
  275. // into fname.
  276. if (fname) {
  277. len = __min((p - path), (_MAX_FNAME - 1));
  278. lstrcpyn(fname, path, (int)len + 1);
  279. fname[len] = TEXT('\0');
  280. }
  281. if (ext) {
  282. *ext = TEXT('\0');
  283. }
  284. }
  285. } // lsplitpath
  286. /////////////////////////////////////////////////////////////////////////
  287. VOID
  288. lmakepath(
  289. TCHAR *path,
  290. const TCHAR *drive,
  291. const TCHAR *dir,
  292. const TCHAR *fname,
  293. const TCHAR *ext
  294. )
  295. /*++
  296. Routine Description:
  297. Create a path name from its individual components.
  298. Arguments:
  299. Entry:
  300. char *path - pointer to buffer for constructed path
  301. char *drive - pointer to drive component, may or may not contain
  302. trailing ':'
  303. char *dir - pointer to subdirectory component, may or may not include
  304. leading and/or trailing '/' or '\' characters
  305. char *fname - pointer to file base name component
  306. char *ext - pointer to extension component, may or may not contain
  307. a leading '.'.
  308. Exit:
  309. path - pointer to constructed path name
  310. Return Value:
  311. --*/
  312. {
  313. const TCHAR *p;
  314. /*+---------------------------------------------------------------------+
  315. | we assume that the arguments are in the following form (although we |
  316. | do not diagnose invalid arguments or illegal filenames (such as |
  317. | names longer than 8.3 or with illegal characters in them) |
  318. | |
  319. | drive: |
  320. | A or A: |
  321. | dir: |
  322. | \top\next\last\ ; or |
  323. | /top/next/last/ ; or |
  324. | |
  325. | either of the above forms with either/both the leading and |
  326. | trailing / or \ removed. Mixed use of '/' and '\' is also |
  327. | tolerated |
  328. | fname: |
  329. | any valid file name |
  330. | ext: |
  331. | any valid extension (none if empty or null ) |
  332. +---------------------------------------------------------------------+*/
  333. ASSERT(NULL != path &&
  334. NULL != drive &&
  335. NULL != dir &&
  336. NULL != fname &&
  337. NULL != ext);
  338. // copy drive
  339. if (drive && *drive)
  340. while (*drive)
  341. *path++ = *drive++;
  342. // copy dir
  343. if (NULL != (p = dir) && *p) {
  344. do {
  345. *path++ = *p++;
  346. }
  347. while (*p);
  348. if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
  349. *path++ = TEXT('\\');
  350. }
  351. }
  352. // copy fname
  353. if (NULL != (p = fname)) {
  354. while (*p) {
  355. *path++ = *p++;
  356. }
  357. }
  358. // copy ext, including 0-terminator - check to see if a '.' needs to be
  359. // inserted.
  360. if (NULL != (p = ext)) {
  361. if (*p && *p != TEXT('.')) {
  362. *path++ = TEXT('.');
  363. }
  364. while (0 != (*path++ = *p++))
  365. ;
  366. }
  367. else {
  368. // better add the 0-terminator
  369. *path = TEXT('\0');
  370. }
  371. } // lmakepath
  372. /////////////////////////////////////////////////////////////////////////
  373. VOID
  374. FileBackupCreate(
  375. LPTSTR Path
  376. )
  377. /*++
  378. Routine Description:
  379. Arguments:
  380. Return Value:
  381. --*/
  382. {
  383. DWORD dwFileNumber = 0;
  384. TCHAR Drive[_MAX_DRIVE + 1];
  385. TCHAR Dir[_MAX_DIR + 1];
  386. TCHAR FileName[_MAX_FNAME + 1];
  387. TCHAR Ext[_MAX_EXT + 1];
  388. TCHAR NewExt[_MAX_EXT + 1];
  389. TCHAR NewPath[MAX_PATH + 1];
  390. HRESULT hr;
  391. size_t cb;
  392. //
  393. // Make sure file exists
  394. //
  395. if (!FileExists(FileName))
  396. return;
  397. //
  398. // Split name into constituent parts...
  399. //
  400. lsplitpath(Path, Drive, Dir, FileName, Ext);
  401. // Find next backup number...
  402. // Files are backed up as .xxx where xxx is a number in the form .001,
  403. // the first backup is stored as .001, second as .002, etc...
  404. cb = sizeof(NewExt);
  405. do {
  406. //
  407. // Create new file name with backup extension
  408. //
  409. dwFileNumber++;
  410. hr = StringCbPrintf(NewExt, cb, TEXT("%03u"), dwFileNumber);
  411. ASSERT(SUCCEEDED(hr));
  412. lmakepath(NewPath, Drive, Dir, FileName, NewExt);
  413. } while ( FileExists(NewPath) );
  414. MoveFile( Path, NewPath );
  415. } // FileBackupCreate
  416. DWORD
  417. SetLlsFileAcl(WCHAR const *pwszFileName)
  418. /*
  419. Description: set file protected ACL with BA/SY/NS full control
  420. Arguments:
  421. pwszFileName - the file name
  422. Return:
  423. if any win error
  424. */
  425. {
  426. // this is lls file acl in sddl string format
  427. #define LLS_FILE_ACL L"D:PAR(A;OICI;FA;;;BA)(A;OICI;FA;;;NS)(A;OICI;FA;;;SY)"
  428. DWORD dwErr;
  429. PSECURITY_DESCRIPTOR pSD = NULL;
  430. if (NULL == pwszFileName)
  431. {
  432. dwErr = ERROR_INVALID_PARAMETER;
  433. goto error;
  434. }
  435. if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
  436. LLS_FILE_ACL, //lls file string acl
  437. SDDL_REVISION_1,
  438. &pSD,
  439. NULL)) // no interest on size
  440. {
  441. dwErr = GetLastError();
  442. goto error;
  443. }
  444. if (NULL == pSD)
  445. {
  446. // might be on file system doesn't support acl
  447. goto done;
  448. }
  449. if (0 == SetFileSecurity(
  450. pwszFileName,
  451. DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
  452. pSD))
  453. {
  454. dwErr = GetLastError();
  455. goto error;
  456. }
  457. done:
  458. dwErr = ERROR_SUCCESS;
  459. error:
  460. if (NULL != pSD)
  461. {
  462. LocalFree(pSD);
  463. }
  464. return dwErr;
  465. }
  466. /////////////////////////////////////////////////////////////////////////
  467. HANDLE
  468. LlsFileInit(
  469. LPTSTR FileName,
  470. DWORD Version,
  471. DWORD DataSize
  472. )
  473. /*++
  474. Routine Description:
  475. Arguments:
  476. Return Value:
  477. --*/
  478. {
  479. HANDLE hFile = NULL;
  480. LLS_FILE_HEADER Header;
  481. DWORD BytesWritten;
  482. HRESULT hr;
  483. BOOL bFileExists;
  484. #ifdef DEBUG
  485. if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
  486. dprintf(TEXT("LLS TRACE: LlsFileInit\n"));
  487. #endif
  488. if (FileName == NULL)
  489. return NULL;
  490. hr = StringCbCopyA(Header.Header, sizeof(Header.Header), HeaderString);
  491. ASSERT(SUCCEEDED(hr));
  492. Header.Version = Version;
  493. Header.DataSize = DataSize;
  494. bFileExists = FileExists(FileName);
  495. if (bFileExists)
  496. {
  497. SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
  498. }
  499. hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  500. if (hFile != INVALID_HANDLE_VALUE) {
  501. if (!WriteFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesWritten, NULL)) {
  502. CloseHandle(hFile);
  503. hFile = NULL;
  504. }
  505. } else {
  506. hFile = NULL;
  507. }
  508. if (NULL != hFile && !bFileExists)
  509. {
  510. // no error check, if acl apply failed, go on
  511. SetLlsFileAcl(FileName);
  512. }
  513. return hFile;
  514. } // LlsFileInit
  515. /////////////////////////////////////////////////////////////////////////
  516. HANDLE
  517. LlsFileCheck(
  518. LPTSTR FileName,
  519. LPDWORD Version,
  520. LPDWORD DataSize
  521. )
  522. /*++
  523. Routine Description:
  524. Arguments:
  525. Return Value:
  526. --*/
  527. {
  528. BOOL FileOK = FALSE;
  529. HANDLE hFile = NULL;
  530. LLS_FILE_HEADER Header;
  531. DWORD FileSize;
  532. DWORD BytesRead;
  533. #ifdef DEBUG
  534. if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE))
  535. dprintf(TEXT("LLS TRACE: LlsFileCheck\n"));
  536. #endif
  537. if (FileName == NULL)
  538. return NULL;
  539. //
  540. // We are assuming the file exists
  541. //
  542. SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL);
  543. hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  544. if (hFile != INVALID_HANDLE_VALUE) {
  545. FileSize = GetFileSize(hFile, NULL);
  546. //
  547. // Make sure there is enough data there to read
  548. //
  549. if (FileSize > (sizeof(LLS_FILE_HEADER) + 1)) {
  550. if (ReadFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesRead, NULL)) {
  551. if ( !_strcmpi(Header.Header, HeaderString) ) {
  552. //
  553. // Data checks out - so return datalength
  554. //
  555. *Version = Header.Version;
  556. *DataSize = Header.DataSize;
  557. FileOK = TRUE;
  558. }
  559. }
  560. }
  561. //
  562. // If we opened the file and something was wrong - close it.
  563. //
  564. if (!FileOK) {
  565. CloseHandle(hFile);
  566. hFile = NULL;
  567. }
  568. } else {
  569. return NULL;
  570. }
  571. return hFile;
  572. } // LlsFileCheck
  573. /////////////////////////////////////////////////////////////////////////
  574. DWORD
  575. DateSystemGet(
  576. )
  577. /*++
  578. Routine Description:
  579. Arguments:
  580. Return Value:
  581. Seconds since midnight.
  582. --*/
  583. {
  584. DWORD Seconds = 0;
  585. LARGE_INTEGER SysTime;
  586. NtQuerySystemTime(&SysTime);
  587. RtlTimeToSecondsSince1980(&SysTime, &Seconds);
  588. return Seconds;
  589. } // DateSystemGet
  590. /////////////////////////////////////////////////////////////////////////
  591. DWORD
  592. DateLocalGet(
  593. )
  594. /*++
  595. Routine Description:
  596. Arguments:
  597. Return Value:
  598. Seconds since midnight.
  599. --*/
  600. {
  601. DWORD Seconds = 0;
  602. LARGE_INTEGER SysTime, LocalTime;
  603. NtQuerySystemTime(&SysTime);
  604. RtlSystemTimeToLocalTime(&SysTime, &LocalTime);
  605. RtlTimeToSecondsSince1980(&LocalTime, &Seconds);
  606. return Seconds;
  607. } // DateLocalGet
  608. /////////////////////////////////////////////////////////////////////////
  609. DWORD
  610. InAWorkgroup(
  611. VOID
  612. )
  613. /*++
  614. Routine Description:
  615. This function determines whether we are a member of a domain, or of
  616. a workgroup. First it checks to make sure we're running on a Windows NT
  617. system (otherwise we're obviously in a domain) and if so, queries LSA
  618. to get the Primary domain SID, if this is NULL, we're in a workgroup.
  619. If we fail for some random unexpected reason, we'll pretend we're in a
  620. workgroup (it's more restrictive).
  621. Arguments:
  622. None
  623. Return Value:
  624. TRUE - We're in a workgroup
  625. FALSE - We're in a domain
  626. --*/
  627. {
  628. NT_PRODUCT_TYPE ProductType;
  629. OBJECT_ATTRIBUTES ObjectAttributes;
  630. LSA_HANDLE Handle;
  631. NTSTATUS Status;
  632. PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL;
  633. DWORD Result = FALSE;
  634. Status = RtlGetNtProductType(&ProductType);
  635. if (!NT_SUCCESS(Status)) {
  636. #if DBG
  637. dprintf(TEXT("ERROR LLS Could not get Product type\n"));
  638. #endif
  639. return TRUE;
  640. }
  641. if (ProductType == NtProductLanManNt) {
  642. return(FALSE);
  643. }
  644. InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL);
  645. Status = LsaOpenPolicy(NULL,
  646. &ObjectAttributes,
  647. POLICY_VIEW_LOCAL_INFORMATION,
  648. &Handle);
  649. if (!NT_SUCCESS(Status)) {
  650. #if DBG
  651. dprintf(TEXT("ERROR LLS: Could not open LSA Policy Database\n"));
  652. #endif
  653. return TRUE;
  654. }
  655. Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation,
  656. (PVOID *) &PolicyPrimaryDomainInfo);
  657. if (NT_SUCCESS(Status)) {
  658. if (PolicyPrimaryDomainInfo->Sid == NULL) {
  659. Result = TRUE;
  660. }
  661. else {
  662. Result = FALSE;
  663. }
  664. }
  665. if (PolicyPrimaryDomainInfo) {
  666. LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo);
  667. }
  668. LsaClose(Handle);
  669. return(Result);
  670. } // InAWorkgroup
  671. /////////////////////////////////////////////////////////////////////////
  672. VOID
  673. LogEvent(
  674. DWORD MessageId,
  675. DWORD NumberOfSubStrings,
  676. LPWSTR *SubStrings,
  677. DWORD ErrorCode
  678. )
  679. {
  680. HANDLE LogHandle;
  681. WORD wEventType;
  682. LogHandle = RegisterEventSourceW (
  683. NULL,
  684. TEXT("LicenseService")
  685. );
  686. if (LogHandle == NULL) {
  687. #if DBG
  688. dprintf(TEXT("LLS RegisterEventSourceW failed %lu\n"), GetLastError());
  689. #endif
  690. return;
  691. }
  692. switch ( MessageId >> 30 )
  693. {
  694. case STATUS_SEVERITY_INFORMATIONAL:
  695. case STATUS_SEVERITY_SUCCESS:
  696. wEventType = EVENTLOG_INFORMATION_TYPE;
  697. break;
  698. case STATUS_SEVERITY_WARNING:
  699. wEventType = EVENTLOG_WARNING_TYPE;
  700. break;
  701. case STATUS_SEVERITY_ERROR:
  702. default:
  703. wEventType = EVENTLOG_ERROR_TYPE;
  704. break;
  705. }
  706. if (ErrorCode == ERROR_SUCCESS) {
  707. //
  708. // No error codes were specified
  709. //
  710. (void) ReportEventW(
  711. LogHandle,
  712. wEventType,
  713. 0, // event category
  714. MessageId,
  715. NULL,
  716. (WORD)NumberOfSubStrings,
  717. 0,
  718. SubStrings,
  719. (PVOID) NULL
  720. );
  721. }
  722. else {
  723. //
  724. // Log the error code specified
  725. //
  726. (void) ReportEventW(
  727. LogHandle,
  728. wEventType,
  729. 0, // event category
  730. MessageId,
  731. NULL,
  732. (WORD)NumberOfSubStrings,
  733. sizeof(DWORD),
  734. SubStrings,
  735. (PVOID) &ErrorCode
  736. );
  737. }
  738. DeregisterEventSource(LogHandle);
  739. } // LogEvent
  740. #define THROTTLE_WRAPAROUND 24
  741. //
  742. // Reduce the frequency of logging
  743. // No need for the limit to be exact
  744. //
  745. /////////////////////////////////////////////////////////////////////////
  746. VOID
  747. ThrottleLogEvent(
  748. DWORD MessageId,
  749. DWORD NumberOfSubStrings,
  750. LPWSTR *SubStrings,
  751. DWORD ErrorCode
  752. )
  753. {
  754. static LONG lLogged = THROTTLE_WRAPAROUND;
  755. LONG lResult;
  756. lResult = InterlockedIncrement(&lLogged);
  757. if (THROTTLE_WRAPAROUND <= lResult)
  758. {
  759. LogEvent(
  760. MessageId,
  761. NumberOfSubStrings,
  762. SubStrings,
  763. ErrorCode );
  764. lLogged = 0;
  765. }
  766. }
  767. /////////////////////////////////////////////////////////////////////////
  768. VOID
  769. LicenseCapacityWarningDlg(DWORD dwCapacityState)
  770. {
  771. //
  772. // NB : The ServiceLock critical section is entered for the duration
  773. // of this routine. No serialization issues.
  774. //
  775. if (ghWarningDlgThreadHandle == NULL) {
  776. //
  777. // No action necessary if this fails.
  778. //
  779. DWORD Ignore;
  780. DWORD * pWarningMessageID;
  781. pWarningMessageID = LocalAlloc(LPTR, sizeof(DWORD));
  782. if ( pWarningMessageID != NULL ) {
  783. switch( dwCapacityState ) {
  784. case LICENSE_CAPACITY_NEAR_MAXIMUM:
  785. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_NEAR_MAX;
  786. break;
  787. case LICENSE_CAPACITY_AT_MAXIMUM:
  788. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_AT_MAX;
  789. break;
  790. case LICENSE_CAPACITY_EXCEEDED:
  791. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_EXCEEDED;
  792. break;
  793. default:
  794. *pWarningMessageID = LLS_EVENT_NOTIFY_LICENSES_EXCEEDED;
  795. };
  796. ghWarningDlgThreadHandle = CreateThread(NULL,
  797. 0L,
  798. (LPTHREAD_START_ROUTINE)
  799. WarningDlgThread,
  800. pWarningMessageID,
  801. 0L,
  802. &Ignore);
  803. if (ghWarningDlgThreadHandle == NULL)
  804. {
  805. //
  806. // CreateThread failed
  807. //
  808. LocalFree(pWarningMessageID);
  809. }
  810. }
  811. }
  812. }
  813. /////////////////////////////////////////////////////////////////////////
  814. VOID
  815. WarningDlgThread(
  816. PVOID ThreadParameter)
  817. {
  818. LPTSTR pszWarningMessage = NULL;
  819. DWORD * pWarningMessageID;
  820. HANDLE hThread;
  821. TCHAR szWarningTitle[256] = TEXT("");
  822. ASSERT(ThreadParameter != NULL);
  823. pWarningMessageID = (DWORD *)ThreadParameter;
  824. //
  825. // NB : The .dll should already have been loaded in MasterServiceListInit
  826. // on service startup. This logic exists here for the case where
  827. // the code invoked on initialization should fail.
  828. //
  829. // It is OK if another thread should simulataneously initialize
  830. // gLlsDllHandle. Worst case, there will be an orphaned handle
  831. // to the .dll. But the .dll is loaded for the lifetime of this
  832. // .exe, so no big deal.
  833. //
  834. if ( gLlsDllHandle == NULL ) {
  835. gLlsDllHandle = LoadLibrary(TEXT("LLSRPC.DLL"));
  836. }
  837. if ( gLlsDllHandle != NULL) {
  838. DWORD ccWarningMessage;
  839. //
  840. // Fetch the dialog title.
  841. //
  842. LoadString(gLlsDllHandle,
  843. IDS_LICENSEWARNING,
  844. szWarningTitle,
  845. sizeof(szWarningTitle)/sizeof(TCHAR));
  846. //
  847. // Fetch the dialog message.
  848. //
  849. ccWarningMessage = FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
  850. FORMAT_MESSAGE_FROM_HMODULE |
  851. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  852. gLlsDllHandle,
  853. *pWarningMessageID,
  854. 0,
  855. (LPTSTR)&pszWarningMessage,
  856. 0,
  857. NULL);
  858. if ( ccWarningMessage > 2 ) {
  859. //
  860. // Strip the trailing <CR><LF> format message always adds.
  861. //
  862. pszWarningMessage[ccWarningMessage - 2] = TEXT('\0');
  863. MessageBox(NULL,
  864. pszWarningMessage,
  865. szWarningTitle,
  866. MB_ICONWARNING | MB_OK | MB_SYSTEMMODAL |
  867. MB_SERVICE_NOTIFICATION);
  868. }
  869. if ( pszWarningMessage != NULL) {
  870. LocalFree(pszWarningMessage);
  871. }
  872. }
  873. LocalFree(pWarningMessageID);
  874. //
  875. // By closing the handle, we allow the system to remove all remaining
  876. // traces of this thread.
  877. //
  878. hThread = ghWarningDlgThreadHandle;
  879. ghWarningDlgThreadHandle = NULL;
  880. CloseHandle(hThread);
  881. }
  882. /////////////////////////////////////////////////////////////////////////
  883. DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber )
  884. /*++
  885. Routine Description:
  886. Retrieve the build number of Windows NT running on a given machine.
  887. Arguments:
  888. pszServerName (LPTSTR)
  889. Name of the server to check.
  890. pdwBuildNumber (LPDWORD)
  891. On return, holds the build number of the server (e.g., 1057 for the
  892. release version of Windows NT 3.51).
  893. Return Value:
  894. ERROR_SUCCESS or Win error code.
  895. --*/
  896. {
  897. LONG lError;
  898. HKEY hKeyLocalMachine;
  899. lError = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine );
  900. if ( ERROR_SUCCESS != lError )
  901. {
  902. #if DBG
  903. dprintf( TEXT("WinNtBuildNumberGet(): Could not connect to remote registry, error %ld.\n"), lError );
  904. #endif
  905. }
  906. else
  907. {
  908. HKEY hKeyCurrentVersion;
  909. lError = RegOpenKeyEx( hKeyLocalMachine,
  910. TEXT( "Software\\Microsoft\\Windows NT\\CurrentVersion" ),
  911. 0,
  912. KEY_READ,
  913. &hKeyCurrentVersion );
  914. if ( ERROR_SUCCESS != lError )
  915. {
  916. #if DBG
  917. dprintf( TEXT("WinNtBuildNumberGet(): Could not open key, error %ld.\n"), lError );
  918. #endif
  919. }
  920. else
  921. {
  922. DWORD dwType;
  923. TCHAR szWinNtBuildNumber[ 16 ];
  924. DWORD cbWinNtBuildNumber = sizeof( szWinNtBuildNumber );
  925. lError = RegQueryValueEx( hKeyCurrentVersion,
  926. TEXT( "CurrentBuildNumber" ),
  927. NULL,
  928. &dwType,
  929. (LPBYTE)szWinNtBuildNumber,
  930. &cbWinNtBuildNumber );
  931. if ( ERROR_SUCCESS != lError )
  932. {
  933. #if DBG
  934. dprintf( TEXT("WinNtBuildNumberGet(): Could not query value, error %ld.\n"), lError );
  935. #endif
  936. }
  937. else
  938. {
  939. ASSERT(NULL != pdwBuildNumber);
  940. *pdwBuildNumber = (DWORD) _wtol( szWinNtBuildNumber );
  941. }
  942. RegCloseKey( hKeyCurrentVersion );
  943. }
  944. RegCloseKey( hKeyLocalMachine );
  945. }
  946. return (DWORD) lError;
  947. }
  948. #if DBG
  949. /////////////////////////////////////////////////////////////////////////
  950. LPTSTR
  951. TimeToString(
  952. ULONG Seconds
  953. )
  954. {
  955. TIME_FIELDS tf;
  956. LARGE_INTEGER Time, LTime;
  957. static TCHAR TimeString[100];
  958. HRESULT hr;
  959. if ( 0 == Seconds )
  960. {
  961. hr = StringCbCopy(TimeString, sizeof(TimeString), TEXT("None"));
  962. ASSERT(SUCCEEDED(hr));
  963. }
  964. else
  965. {
  966. RtlSecondsSince1980ToTime(Seconds, &Time);
  967. RtlSystemTimeToLocalTime(&Time, &LTime);
  968. RtlTimeToTimeFields(&LTime, &tf);
  969. hr = StringCbPrintf(TimeString, sizeof(TimeString), TEXT("%02hd/%02hd/%04hd @ %02hd:%02hd:%02hd"), tf.Month, tf.Day, tf.Year, tf.Hour, tf.Minute, tf.Second);
  970. ASSERT(SUCCEEDED(hr));
  971. }
  972. return TimeString;
  973. } // TimeToString
  974. #endif //DBG