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.

462 lines
12 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. #include <ntlsa.h>
  28. #define _NTDEF_
  29. typedef struct _OBJECT_ATTRIBUTES {
  30. ULONG Length;
  31. HANDLE RootDirectory;
  32. PUNICODE_STRING ObjectName;
  33. ULONG Attributes;
  34. PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
  35. PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
  36. } OBJECT_ATTRIBUTES;
  37. typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
  38. #define InitializeObjectAttributes( p, n, a, r, s ) { \
  39. (p)->Length = sizeof( OBJECT_ATTRIBUTES ); \
  40. (p)->RootDirectory = r; \
  41. (p)->Attributes = a; \
  42. (p)->ObjectName = n; \
  43. (p)->SecurityDescriptor = s; \
  44. (p)->SecurityQualityOfService = NULL; \
  45. }
  46. #define _NTDEF
  47. // from ntstatus.h
  48. #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
  49. #include <lmaccess.h>
  50. }
  51. #ifdef _DEBUG
  52. #define new DEBUG_NEW
  53. #undef THIS_FILE
  54. static char THIS_FILE[] = __FILE__;
  55. #endif
  56. typedef enum _Netapi32ApiIndex
  57. {
  58. BUFFERFREE_ENUM = 0,
  59. USERMODALSGET_ENUM
  60. };
  61. // not subject to localization
  62. static LPCSTR g_apchNetapi32FunctionNames[] = {
  63. "NetApiBufferFree",
  64. "NetUserModalsGet",
  65. NULL
  66. };
  67. typedef NET_API_STATUS (*BUFFERFREEPROC)(LPVOID);
  68. typedef NET_API_STATUS (*USERMODALSGETPROC)(LPCWSTR, DWORD, LPBYTE*);
  69. // not subject to localization
  70. DynamicDLL g_LSASTUFF_Netapi32DLL( _T("NETAPI32.DLL"), g_apchNetapi32FunctionNames );
  71. /*******************************************************************
  72. NAME: ::FillUnicodeString
  73. SYNOPSIS: Standalone method for filling in a UNICODE_STRING
  74. ENTRY: punistr - Unicode string to be filled in.
  75. nls - Source for filling the unistr
  76. EXIT:
  77. NOTES: punistr->Buffer is allocated here and must be deallocated
  78. by the caller using FreeUnicodeString.
  79. HISTORY:
  80. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
  81. ********************************************************************/
  82. VOID FillUnicodeString( LSA_UNICODE_STRING * punistr, LPCWSTR psz )
  83. {
  84. if ( NULL == punistr || NULL == psz )
  85. {
  86. ASSERT(FALSE);
  87. return;
  88. }
  89. size_t cTchar = ::wcslen(psz);
  90. punistr->Buffer = new WCHAR[cTchar + 1];
  91. ASSERT( NULL != punistr->Buffer );
  92. if ( NULL != punistr->Buffer )
  93. {
  94. ::wcscpy( punistr->Buffer, psz );
  95. // Length and MaximumLength are counts of bytes.
  96. punistr->Length = (USHORT)(cTchar * sizeof(WCHAR));
  97. punistr->MaximumLength = punistr->Length + sizeof(WCHAR);
  98. }
  99. else
  100. ::ZeroMemory( punistr, sizeof(*punistr) );
  101. }
  102. /*******************************************************************
  103. NAME: ::FreeUnicodeString
  104. SYNOPSIS: Standalone method for freeing in a UNICODE_STRING
  105. ENTRY: unistr - Unicode string whose Buffer is to be freed.
  106. EXIT:
  107. HISTORY:
  108. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintmem.cxx
  109. ********************************************************************/
  110. VOID FreeUnicodeString( LSA_UNICODE_STRING * punistr )
  111. {
  112. if ( punistr && punistr->Buffer )
  113. {
  114. delete punistr->Buffer;
  115. ::ZeroMemory( punistr, sizeof(*punistr) );
  116. }
  117. }
  118. /*******************************************************************
  119. NAME: InitObjectAttributes
  120. SYNOPSIS:
  121. This function initializes the given Object Attributes structure, including
  122. Security Quality Of Service. Memory must be allcated for both
  123. ObjectAttributes and Security QOS by the caller.
  124. ENTRY:
  125. poa - Pointer to Object Attributes to be initialized.
  126. psqos - Pointer to Security QOS to be initialized.
  127. EXIT:
  128. NOTES:
  129. HISTORY:
  130. jonn 07/09/97 copied from net\ui\common\src\lmobj\lmobj\uintlsa.cxx
  131. ********************************************************************/
  132. VOID InitObjectAttributes( PLSA_OBJECT_ATTRIBUTES poa,
  133. PSECURITY_QUALITY_OF_SERVICE psqos )
  134. {
  135. ASSERT( poa != NULL );
  136. ASSERT( psqos != NULL );
  137. psqos->Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  138. psqos->ImpersonationLevel = SecurityImpersonation;
  139. psqos->ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  140. psqos->EffectiveOnly = FALSE;
  141. //
  142. // Set up the object attributes prior to opening the LSA.
  143. //
  144. InitializeObjectAttributes(
  145. poa,
  146. NULL,
  147. 0L,
  148. NULL,
  149. NULL );
  150. //
  151. // The InitializeObjectAttributes macro presently stores NULL for
  152. // the psqos field, so we must manually copy that
  153. // structure for now.
  154. //
  155. poa->SecurityQualityOfService = psqos;
  156. }
  157. BOOL
  158. I_CheckLSAAccount( LSA_UNICODE_STRING* punistrServerName,
  159. LPCTSTR pszLogOnAccountName,
  160. DWORD* pdwMsgID ) // *pdsMsgID is always set if this fails
  161. {
  162. ASSERT( NULL != pdwMsgID );
  163. BOOL fSuccess = FALSE;
  164. LSA_HANDLE hlsa = NULL;
  165. LSA_HANDLE hlsaAccount = NULL;
  166. PSID psidAccount = NULL;
  167. do { // false loop
  168. //
  169. // Determine whether the target machine is a BDC, and if so, get the PDC
  170. //
  171. // if an error occurs now, it is a read error
  172. *pdwMsgID = IDS_LSAERR_READ_FAILED;
  173. //
  174. // Get LSA_POLICY handle
  175. //
  176. LSA_OBJECT_ATTRIBUTES oa;
  177. SECURITY_QUALITY_OF_SERVICE sqos;
  178. InitObjectAttributes( &oa, &sqos );
  179. // 563140-2002/03/04 JonN POLICY_ALL_ACCESS was too much authority.
  180. // LsaLookupNames only requires POLICY_LOOKUP_NAMES.
  181. // LsaOpenAccount requres no rights (MarkPu).
  182. // LsaCreateAccount requires POLICY_CREATE_ACCOUNT (MarkPu).
  183. NTSTATUS ntstatus = ::LsaOpenPolicy(
  184. punistrServerName,
  185. &oa,
  186. POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT,
  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. if ( !plsardl || !plsasid || !plsardl->Domains[0].Sid )
  220. {
  221. ASSERT(FALSE);
  222. ntstatus = E_FAIL;
  223. break;
  224. }
  225. // ISSUE-2002-03-04-JonN There is a potential issue here
  226. // if LsaLookupNames returns an invalid SID. This is a fairly
  227. // remote risk however.
  228. //
  229. // Build the SID of the account by taking the SID of the domain
  230. // and adding at the end the RID of the account
  231. //
  232. PSID psidDomain = plsardl->Domains[0].Sid;
  233. DWORD ridAccount = plsasid[0].RelativeId;
  234. DWORD cbNewSid = ::GetLengthSid(psidDomain)+sizeof(ridAccount);
  235. psidAccount = (PSID) new BYTE[cbNewSid];
  236. ASSERT( NULL != psidAccount );
  237. (void) ::CopySid( cbNewSid, psidAccount, psidDomain );
  238. UCHAR* pcSubAuthorities = ::GetSidSubAuthorityCount( psidAccount ) ;
  239. (*pcSubAuthorities)++;
  240. DWORD* pdwSubAuthority = ::GetSidSubAuthority(
  241. psidAccount, (*pcSubAuthorities)-1 );
  242. *pdwSubAuthority = ridAccount;
  243. (void) ::LsaFreeMemory( plsardl );
  244. (void) ::LsaFreeMemory( plsasid );
  245. // 563140-2002/04/08 JonN POLICY_ALL_ACCESS | DELETE was too much authority.
  246. // CliffV says:
  247. // LsaGetSystemAccessAccount only requires ACCOUNT_VIEW.
  248. // LsaSetSystemAccessAccount only requires ACCOUNT_ADJUST_SYSTEM_ACCESS.
  249. //
  250. // Determine whether this LSA account exists, create it if not
  251. //
  252. ntstatus = ::LsaOpenAccount( hlsa,
  253. psidAccount,
  254. ACCOUNT_VIEW | ACCOUNT_ADJUST_SYSTEM_ACCESS,
  255. &hlsaAccount );
  256. ULONG ulSystemAccessCurrent = 0;
  257. if (STATUS_OBJECT_NAME_NOT_FOUND == ntstatus)
  258. {
  259. // handle account-not-found case
  260. // if an error occurs now, it is a write error
  261. *pdwMsgID = IDS_LSAERR_WRITE_FAILED;
  262. ntstatus = ::LsaCreateAccount( hlsa,
  263. psidAccount,
  264. ACCOUNT_ADJUST_SYSTEM_ACCESS,
  265. &hlsaAccount );
  266. // 4/8/02 JonN: CliffV confirms that the account is created without any privileges
  267. }
  268. else
  269. {
  270. ntstatus = ::LsaGetSystemAccessAccount( hlsaAccount, &ulSystemAccessCurrent );
  271. }
  272. if ( !NT_SUCCESS(ntstatus) )
  273. break;
  274. //
  275. // Determine whether this LSA account has POLICY_MODE_SERVICE privilege,
  276. // grant it if not
  277. //
  278. if ( POLICY_MODE_SERVICE != (ulSystemAccessCurrent & POLICY_MODE_SERVICE ) )
  279. {
  280. // if an error occurs now, it is a write error
  281. *pdwMsgID = IDS_LSAERR_WRITE_FAILED;
  282. ntstatus = ::LsaSetSystemAccessAccount(
  283. hlsaAccount,
  284. ulSystemAccessCurrent | POLICY_MODE_SERVICE );
  285. if ( !NT_SUCCESS(ntstatus) )
  286. break; // CODEWORK could check for STATUS_BACKUP_CONTROLLER
  287. // display the write-succeeded message
  288. *pdwMsgID = IDS_LSAERR_WRITE_SUCCEEDED;
  289. }
  290. else
  291. {
  292. *pdwMsgID = 0;
  293. }
  294. fSuccess = TRUE;
  295. } while (FALSE); // false loop
  296. // CODEWORK should check for special error code for NT5 non-DC
  297. // using local policy object
  298. if (NULL != hlsa)
  299. {
  300. ::LsaClose( hlsa );
  301. }
  302. if (NULL != hlsaAccount)
  303. {
  304. ::LsaClose( hlsaAccount );
  305. }
  306. if (NULL != psidAccount)
  307. {
  308. delete[] psidAccount;
  309. }
  310. return fSuccess;
  311. } // I_CheckLSAAccount()
  312. /////////////////////////////////////////////////////////////////////
  313. // FCheckLSAAccount()
  314. //
  315. VOID
  316. CServicePropertyData::FCheckLSAAccount()
  317. {
  318. LSA_UNICODE_STRING unistrServerName;
  319. PLSA_UNICODE_STRING punistrServerName = NULL ;
  320. USER_MODALS_INFO_1* pum1 = NULL;
  321. DWORD dwMsgID = 0;
  322. TRACE1("INFO: Checking LSA permissions for account %s...\n",
  323. (LPCTSTR)m_strLogOnAccountName);
  324. if ( !m_strMachineName.IsEmpty() )
  325. {
  326. ::FillUnicodeString( &unistrServerName, m_strMachineName );
  327. punistrServerName = &unistrServerName;
  328. }
  329. do // false loop
  330. {
  331. // check on the local machine
  332. // this will always set dwMsgID if it fails
  333. if (I_CheckLSAAccount(punistrServerName, m_strLogOnAccountName, &dwMsgID))
  334. break; // this succeeded, we can stop now
  335. // check whether this is a Backup Domain Controller
  336. if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
  337. {
  338. ASSERT(FALSE);
  339. return;
  340. }
  341. DWORD err = ((USERMODALSGETPROC)g_LSASTUFF_Netapi32DLL[USERMODALSGET_ENUM])(
  342. (m_strMachineName.IsEmpty()) ? NULL : const_cast<LPTSTR>((LPCTSTR)m_strMachineName),
  343. 1,
  344. reinterpret_cast<LPBYTE*>(&pum1) );
  345. if (NERR_Success != err)
  346. break;
  347. ASSERT( NULL != pum1 );
  348. if (UAS_ROLE_BACKUP != pum1->usrmod1_role)
  349. break; // not a backup controller
  350. if (NULL == pum1->usrmod1_primary )
  351. {
  352. ASSERT(FALSE);
  353. break;
  354. }
  355. // Try it on the PDC
  356. (void) I_CheckLSAAccount(punistrServerName, pum1->usrmod1_primary, &dwMsgID);
  357. } while (FALSE); // false loop
  358. if ( NULL != punistrServerName )
  359. {
  360. ::FreeUnicodeString( punistrServerName );
  361. }
  362. if ( NULL != pum1 )
  363. {
  364. if ( !g_LSASTUFF_Netapi32DLL.LoadFunctionPointers() )
  365. {
  366. ASSERT(FALSE);
  367. return;
  368. }
  369. ((BUFFERFREEPROC)g_LSASTUFF_Netapi32DLL[BUFFERFREE_ENUM])( pum1 );
  370. }
  371. if (0 != dwMsgID)
  372. {
  373. DoServicesErrMsgBox( GetActiveWindow(), MB_OK | MB_ICONEXCLAMATION, 0, dwMsgID, (LPCTSTR)m_strLogOnAccountName );
  374. }
  375. } // FCheckLSAAccount()