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.

463 lines
12 KiB

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