Source code of Windows XP (NT5)
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.

446 lines
11 KiB

  1. // LsaStuff.cpp
  2. //
  3. // LSA-dependent code
  4. //
  5. // HISTORY
  6. // 09-Jul-97 jonn Creation.
  7. //
  8. #include "stdafx.h"
  9. #include "DynamLnk.h" // DynamicDLL
  10. extern "C"
  11. {
  12. #define NTSTATUS LONG
  13. #define PNTSTATUS NTSTATUS*
  14. #define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
  15. #define SE_SHUTDOWN_PRIVILEGE (19L)
  16. // stuff taken from ntdef.h
  17. typedef struct _UNICODE_STRING {
  18. USHORT Length;
  19. USHORT MaximumLength;
  20. #ifdef MIDL_PASS
  21. [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
  22. #else // MIDL_PASS
  23. PWSTR Buffer;
  24. #endif // MIDL_PASS
  25. } UNICODE_STRING;
  26. typedef UNICODE_STRING *PUNICODE_STRING;
  27. typedef struct _OBJECT_ATTRIBUTES {
  28. ULONG Length;
  29. HANDLE RootDirectory;
  30. PUNICODE_STRING ObjectName;
  31. ULONG Attributes;
  32. PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
  33. PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
  34. } OBJECT_ATTRIBUTES;
  35. typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
  36. #define InitializeObjectAttributes( p, n, a, r, s ) { \
  37. (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
  38. (p)->RootDirectory = r; \
  39. (p)->Attributes = a; \
  40. (p)->ObjectName = n; \
  41. (p)->SecurityDescriptor = s; \
  42. (p)->SecurityQualityOfService = NULL; \
  43. }
  44. #define _NTDEF
  45. // from ntstatus.h
  46. #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
  47. // from lmaccess.h
  48. NET_API_STATUS NET_API_FUNCTION
  49. NetUserModalsGet (
  50. IN LPCWSTR servername OPTIONAL,
  51. IN DWORD level,
  52. OUT LPBYTE *bufptr
  53. );
  54. typedef struct _USER_MODALS_INFO_1 {
  55. DWORD usrmod1_role;
  56. LPWSTR usrmod1_primary;
  57. }USER_MODALS_INFO_1, *PUSER_MODALS_INFO_1, *LPUSER_MODALS_INFO_1;
  58. //
  59. // UAS role manifests under NETLOGON
  60. //
  61. #define UAS_ROLE_STANDALONE 0
  62. #define UAS_ROLE_MEMBER 1
  63. #define UAS_ROLE_BACKUP 2
  64. #define UAS_ROLE_PRIMARY 3
  65. #include <ntlsa.h>
  66. }
  67. #ifdef _DEBUG
  68. #define new DEBUG_NEW
  69. #undef THIS_FILE
  70. static char THIS_FILE[] = __FILE__;
  71. #endif
  72. typedef enum _Netapi32ApiIndex
  73. {
  74. BUFFERFREE_ENUM = 0,
  75. USERMODALSGET_ENUM
  76. };
  77. // not subject to localization
  78. static LPCSTR g_apchNetapi32FunctionNames[] = {
  79. "NetApiBufferFree",
  80. "NetUserModalsGet",
  81. NULL
  82. };
  83. typedef NET_API_STATUS (*BUFFERFREEPROC)(LPVOID);
  84. typedef NET_API_STATUS (*USERMODALSGETPROC)(LPCWSTR, DWORD, LPBYTE*);
  85. // not subject to localization
  86. DynamicDLL g_LSASTUFF_Netapi32DLL( _T("NETAPI32.DLL"), g_apchNetapi32FunctionNames );
  87. /*******************************************************************
  88. NAME: ::FillUnicodeString
  89. SYNOPSIS: Standalone method for filling in a UNICODE_STRING
  90. ENTRY: punistr - Unicode string to be filled in.
  91. nls - Source for filling the unistr
  92. EXIT:
  93. NOTES: punistr->Buffer is allocated here and must be deallocated
  94. by the caller using FreeUnicodeString.
  95. HISTORY:
  96. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
  97. ********************************************************************/
  98. VOID FillUnicodeString( LSA_UNICODE_STRING * punistr, LPCWSTR psz )
  99. {
  100. ASSERT( NULL != punistr && NULL != psz );
  101. size_t cTchar = ::wcslen(psz);
  102. // Length and MaximumLength are counts of bytes.
  103. punistr->Length = (USHORT)(cTchar * sizeof(WCHAR));
  104. punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
  105. punistr->Buffer = new WCHAR[cTchar + 1];
  106. ASSERT( NULL != punistr->Buffer );
  107. ::wcscpy( punistr->Buffer, psz );
  108. }
  109. /*******************************************************************
  110. NAME: ::FreeUnicodeString
  111. SYNOPSIS: Standalone method for freeing in a UNICODE_STRING
  112. ENTRY: unistr - Unicode string whose Buffer is to be freed.
  113. EXIT:
  114. HISTORY:
  115. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
  116. ********************************************************************/
  117. VOID FreeUnicodeString( LSA_UNICODE_STRING * punistr )
  118. {
  119. ASSERT( punistr != NULL );
  120. delete punistr->Buffer;
  121. }
  122. /*******************************************************************
  123. NAME: InitObjectAttributes
  124. SYNOPSIS:
  125. This function initializes the given Object Attributes structure, including
  126. Security Quality Of Service. Memory must be allcated for both
  127. ObjectAttributes and Security QOS by the caller.
  128. ENTRY:
  129. poa - Pointer to Object Attributes to be initialized.
  130. psqos - Pointer to Security QOS to be initialized.
  131. EXIT:
  132. NOTES:
  133. HISTORY:
  134. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintlsa.cxx
  135. ********************************************************************/
  136. VOID InitObjectAttributes( PLSA_OBJECT_ATTRIBUTES poa,
  137. PSECURITY_QUALITY_OF_SERVICE psqos )
  138. {
  139. ASSERT( poa != NULL );
  140. ASSERT( psqos != NULL );
  141. psqos->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  142. psqos->ImpersonationLevel = SecurityImpersonation;
  143. psqos->ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  144. psqos->EffectiveOnly = FALSE;
  145. //
  146. // Set up the object attributes prior to opening the LSA.
  147. //
  148. InitializeObjectAttributes(
  149. poa,
  150. NULL,
  151. 0L,
  152. NULL,
  153. NULL );
  154. //
  155. // The InitializeObjectAttributes macro presently stores NULL for
  156. // the psqos field, so we must manually copy that
  157. // structure for now.
  158. //
  159. poa->SecurityQualityOfService = psqos;
  160. }
  161. BOOL
  162. I_CheckLSAAccount( LSA_UNICODE_STRING* punistrServerName,
  163. LPCTSTR pszLogOnAccountName,
  164. DWORD* pdwMsgID ) // *pdsMsgID is always set if this fails
  165. {
  166. ASSERT( NULL != pdwMsgID );
  167. BOOL fSuccess = FALSE;
  168. LSA_HANDLE hlsa = NULL;
  169. LSA_HANDLE hlsaAccount = NULL;
  170. PSID psidAccount = NULL;
  171. do { // false loop
  172. //
  173. // Determine whether the target machine is a BDC, and if so, get the PDC
  174. //
  175. // if an error occurs now, it is a read error
  176. *pdwMsgID = IDS_LSAERR_READ_FAILED;
  177. //
  178. // Get LSA_POLICY handle
  179. //
  180. LSA_OBJECT_ATTRIBUTES oa;
  181. SECURITY_QUALITY_OF_SERVICE sqos;
  182. InitObjectAttributes( &oa, &sqos );
  183. NTSTATUS ntstatus = ::LsaOpenPolicy(
  184. punistrServerName,
  185. &oa,
  186. POLICY_ALL_ACCESS, // CODEWORK ??
  187. &hlsa );
  188. if ( !NT_SUCCESS(ntstatus) )
  189. break;
  190. //
  191. // Remove ".\" or "thismachine\" from the head of the account name if it is present
  192. //
  193. CString strAccountName = pszLogOnAccountName;
  194. int iBackslash = strAccountName.Find( _T('\\') );
  195. if ( -1 < iBackslash )
  196. {
  197. CString strPrefix = strAccountName.Left(iBackslash);
  198. if ( !lstrcmp(strPrefix, _T(".")) || IsLocalComputername(strPrefix) )
  199. {
  200. strAccountName = strAccountName.Mid(iBackslash+1);
  201. }
  202. }
  203. //
  204. // determine the SID of the account
  205. //
  206. PLSA_REFERENCED_DOMAIN_LIST plsardl = NULL;
  207. PLSA_TRANSLATED_SID plsasid = NULL;
  208. LSA_UNICODE_STRING unistrAccountName;
  209. ::FillUnicodeString( &unistrAccountName, strAccountName );
  210. ntstatus = ::LsaLookupNames(
  211. hlsa,
  212. 1,
  213. &unistrAccountName,
  214. &plsardl,
  215. &plsasid );
  216. ::FreeUnicodeString( &unistrAccountName );
  217. if ( !NT_SUCCESS(ntstatus) )
  218. break;
  219. //
  220. // Build the SID of the account by taking the SID of the domain
  221. // and adding at the end the RID of the account
  222. //
  223. PSID psidDomain = plsardl->Domains[0].Sid;
  224. DWORD ridAccount = plsasid[0].RelativeId;
  225. DWORD cbNewSid = ::GetLengthSid(psidDomain)+sizeof(ridAccount);
  226. psidAccount = (PSID) new BYTE[cbNewSid];
  227. ASSERT( NULL != psidAccount );
  228. (void) ::CopySid( cbNewSid, psidAccount, psidDomain );
  229. UCHAR* pcSubAuthorities = ::GetSidSubAuthorityCount( psidAccount ) ;
  230. (*pcSubAuthorities)++;
  231. DWORD* pdwSubAuthority = ::GetSidSubAuthority(
  232. psidAccount, (*pcSubAuthorities)-1 );
  233. *pdwSubAuthority = ridAccount;
  234. (void) ::LsaFreeMemory( plsardl );
  235. (void) ::LsaFreeMemory( plsasid );
  236. //
  237. // Determine whether this LSA account exists, create it if not
  238. //
  239. ntstatus = ::LsaOpenAccount( hlsa,
  240. psidAccount,
  241. ACCOUNT_ALL_ACCESS | DELETE, // CODEWORK
  242. &hlsaAccount );
  243. ULONG ulSystemAccessCurrent = 0;
  244. if (STATUS_OBJECT_NAME_NOT_FOUND == ntstatus)
  245. {
  246. // handle account-not-found case
  247. // if an error occurs now, it is a write error
  248. *pdwMsgID = IDS_LSAERR_WRITE_FAILED;
  249. ntstatus = ::LsaCreateAccount( hlsa,
  250. psidAccount,
  251. ACCOUNT_ALL_ACCESS | DELETE,
  252. &hlsaAccount );
  253. // presumably the account is created without POLICY_MODE_SERVICE privilege
  254. }
  255. else
  256. {
  257. ntstatus = ::LsaGetSystemAccessAccount( hlsaAccount, &ulSystemAccessCurrent );
  258. }
  259. if ( !NT_SUCCESS(ntstatus) )
  260. break;
  261. //
  262. // Determine whether this LSA account has POLICY_MODE_SERVICE privilege,
  263. // grant it if not
  264. //
  265. if ( POLICY_MODE_SERVICE != (ulSystemAccessCurrent & POLICY_MODE_SERVICE ) )
  266. {
  267. // if an error occurs now, it is a write error
  268. *pdwMsgID = IDS_LSAERR_WRITE_FAILED;
  269. ntstatus = ::LsaSetSystemAccessAccount(
  270. hlsaAccount,
  271. ulSystemAccessCurrent | POLICY_MODE_SERVICE );
  272. if ( !NT_SUCCESS(ntstatus) )
  273. break; // CODEWORK could check for STATUS_BACKUP_CONTROLLER
  274. // display the write-succeeded message
  275. *pdwMsgID = IDS_LSAERR_WRITE_SUCCEEDED;
  276. }
  277. else
  278. {
  279. *pdwMsgID = 0;
  280. }
  281. fSuccess = TRUE;
  282. } while (FALSE); // false loop
  283. // CODEWORK should check for special error code for NT5 non-DC
  284. // using local policy object
  285. if (NULL != hlsa)
  286. {
  287. ::LsaClose( hlsa );
  288. }
  289. if (NULL != hlsaAccount)
  290. {
  291. ::LsaClose( hlsaAccount );
  292. }
  293. if (NULL != psidAccount)
  294. {
  295. delete psidAccount;
  296. }
  297. return fSuccess;
  298. } // I_CheckLSAAccount()
  299. /////////////////////////////////////////////////////////////////////
  300. // FCheckLSAAccount()
  301. //
  302. VOID
  303. CServicePropertyData::FCheckLSAAccount()
  304. {
  305. LSA_UNICODE_STRING unistrServerName;
  306. PLSA_UNICODE_STRING punistrServerName = NULL ;
  307. USER_MODALS_INFO_1* pum1 = NULL;
  308. DWORD dwMsgID = 0;
  309. TRACE1("INFO: Checking LSA permissions for account %s...\n",
  310. (LPCTSTR)m_strLogOnAccountName);
  311. if ( !m_strMachineName.IsEmpty() )
  312. {
  313. ::FillUnicodeString( &unistrServerName, m_strMachineName );
  314. punistrServerName = &unistrServerName;
  315. }
  316. do // false loop
  317. {
  318. // check on the local machine
  319. // this will always set dwMsgID if it fails
  320. if (I_CheckLSAAccount(punistrServerName, m_strLogOnAccountName, &dwMsgID))
  321. break; // this succeeded, we can stop now
  322. // check whether this is a Backup Domain Controller
  323. if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
  324. {
  325. ASSERT(FALSE);
  326. return;
  327. }
  328. DWORD err = ((USERMODALSGETPROC)g_LSASTUFF_Netapi32DLL[USERMODALSGET_ENUM])(
  329. (m_strMachineName.IsEmpty()) ? NULL : const_cast<LPTSTR>((LPCTSTR)m_strMachineName),
  330. 1,
  331. reinterpret_cast<LPBYTE*>(&pum1) );
  332. if (NERR_Success != err)
  333. break;
  334. ASSERT( NULL != pum1 );
  335. if (UAS_ROLE_BACKUP != pum1->usrmod1_role)
  336. break; // not a backup controller
  337. if (NULL == pum1->usrmod1_primary )
  338. {
  339. ASSERT(FALSE);
  340. break;
  341. }
  342. // Try it on the PDC
  343. (void) I_CheckLSAAccount(punistrServerName, pum1->usrmod1_primary, &dwMsgID);
  344. } while (FALSE); // false loop
  345. if ( NULL != punistrServerName )
  346. {
  347. ::FreeUnicodeString( punistrServerName );
  348. }
  349. if ( NULL != pum1 )
  350. {
  351. if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
  352. {
  353. ASSERT(FALSE);
  354. return;
  355. }
  356. ((BUFFERFREEPROC)g_LSASTUFF_Netapi32DLL[BUFFERFREE_ENUM])( pum1 );
  357. }
  358. if (0 != dwMsgID)
  359. {
  360. DoServicesErrMsgBox( GetActiveWindow(), MB_OK | MB_ICONEXCLAMATION, 0, dwMsgID, (LPCTSTR)m_strLogOnAccountName );
  361. }
  362. } // FCheckLSAAccount()