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.

348 lines
9.5 KiB

  1. #ifndef _WIN32_WINNT
  2. #define _WIN32_WINNT 0x0400
  3. #endif
  4. #include <nt.h>
  5. #include <ntrtl.h>
  6. #include <nturtl.h>
  7. #include <rpc.h>
  8. #include <ntsam.h>
  9. #include <String.h>
  10. #include <ntdsapi.h>
  11. //#pragma comment(lib, "samlib.lib")
  12. #ifdef __cplusplus
  13. extern "C" {
  14. #endif
  15. NTSTATUS
  16. SamConnectWithCreds(
  17. IN PUNICODE_STRING ServerName,
  18. OUT PSAM_HANDLE ServerHandle,
  19. IN ACCESS_MASK DesiredAccess,
  20. IN POBJECT_ATTRIBUTES ObjectAttributes,
  21. IN RPC_AUTH_IDENTITY_HANDLE Creds,
  22. IN PWCHAR Spn,
  23. OUT BOOL *pfDstIsW2K
  24. );
  25. #ifdef __cplusplus
  26. }
  27. #endif
  28. namespace
  29. {
  30. //------------------------------------------------------------------------------
  31. // ConnectToSam Function
  32. //
  33. // Synopsis
  34. // Connects to Sam Server on specified domain controller using specified
  35. // credentials.
  36. //
  37. // Arguments
  38. // IN pszDomain - the domain name
  39. // IN pszDomainController - the domain controller to connect to
  40. // IN pszUserName - the credentials user
  41. // IN pszPassword - the credentials password
  42. // IN pszUserDomain - the credentials domain
  43. // OUT phSam - the handle to the SAM server
  44. //
  45. // Return
  46. // Win32 error code
  47. //------------------------------------------------------------------------------
  48. DWORD __stdcall ConnectToSam
  49. (
  50. PCWSTR pszDomain,
  51. PCWSTR pszDomainController,
  52. PCWSTR pszUserName,
  53. PCWSTR pszPassword,
  54. PCWSTR pszUserDomain,
  55. SAM_HANDLE* phSam
  56. )
  57. {
  58. DWORD dwError = ERROR_SUCCESS;
  59. *phSam = NULL;
  60. // SAM server name
  61. UNICODE_STRING usServerName;
  62. usServerName.Length = wcslen(pszDomainController) * sizeof(WCHAR);
  63. usServerName.MaximumLength = usServerName.Length + sizeof(WCHAR);
  64. usServerName.Buffer = const_cast<PWSTR>(pszDomainController);
  65. // object attributes
  66. OBJECT_ATTRIBUTES oa = { sizeof(OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
  67. // authorization identity
  68. SEC_WINNT_AUTH_IDENTITY_W swaiAuthIdentity;
  69. swaiAuthIdentity.User = const_cast<PWSTR>(pszUserName);
  70. swaiAuthIdentity.UserLength = wcslen(pszUserName);
  71. swaiAuthIdentity.Domain = const_cast<PWSTR>(pszUserDomain);
  72. swaiAuthIdentity.DomainLength = wcslen(pszUserDomain);
  73. swaiAuthIdentity.Password = const_cast<PWSTR>(pszPassword);
  74. swaiAuthIdentity.PasswordLength = wcslen(pszPassword);
  75. swaiAuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
  76. // service principal name ?
  77. const WCHAR c_szA[] = L"cifs/";
  78. const WCHAR c_szC[] = L"@";
  79. PWSTR pszSpn = new WCHAR[wcslen(c_szA) + wcslen(pszDomainController) + wcslen(c_szC) + wcslen(pszDomain) + 1];
  80. if (pszSpn)
  81. {
  82. wcscpy(pszSpn, c_szA);
  83. wcscat(pszSpn, pszDomainController);
  84. wcscat(pszSpn, c_szC);
  85. wcscat(pszSpn, pszDomain);
  86. }
  87. else {
  88. return ERROR_OUTOFMEMORY;
  89. }
  90. BOOL fSrcIsW2K;
  91. NTSTATUS ntsStatus = SamConnectWithCreds(
  92. &usServerName,
  93. phSam,
  94. MAXIMUM_ALLOWED,
  95. &oa,
  96. &swaiAuthIdentity,
  97. pszSpn,
  98. &fSrcIsW2K
  99. );
  100. if (ntsStatus == RPC_NT_UNKNOWN_AUTHN_SERVICE)
  101. {
  102. // Note that following comment is from the DsAddSidHistory
  103. // implementation.
  104. //
  105. // It might be that the SrcDc is NT4 and the client is
  106. // running locally. This config is supported so try the
  107. // binding with a NULL SrcSpn to force the underlying code
  108. // to use AUTH_WINNT instead of AUTH_NEGOTIATE.
  109. ntsStatus = SamConnectWithCreds(
  110. &usServerName,
  111. phSam,
  112. MAXIMUM_ALLOWED,
  113. &oa,
  114. &swaiAuthIdentity,
  115. NULL,
  116. &fSrcIsW2K
  117. );
  118. }
  119. delete [] pszSpn;
  120. if (ntsStatus != STATUS_SUCCESS)
  121. {
  122. //
  123. // SamConnectWithCreds sometimes returns Win32 errors instead of an
  124. // NT status. Therefore assume that if the severity is success that a
  125. // Win32 error has been returned.
  126. //
  127. if (NT_SUCCESS(ntsStatus))
  128. {
  129. dwError = ntsStatus;
  130. }
  131. else
  132. {
  133. dwError = RtlNtStatusToDosError(ntsStatus);
  134. }
  135. }
  136. return dwError;
  137. }
  138. //------------------------------------------------------------------------------
  139. // OpenDomain Function
  140. //
  141. // Synopsis
  142. // Opens the specified domain and returns a handle that may be used to open
  143. // objects in the domain.
  144. //
  145. // Arguments
  146. // IN hSam - SAM handle
  147. // IN pszDomain - domain to open
  148. // OUT phDomain - domain handle
  149. //
  150. // Return
  151. // Win32 error code
  152. //------------------------------------------------------------------------------
  153. DWORD __stdcall OpenDomain(SAM_HANDLE hSam, PCWSTR pszDomain, SAM_HANDLE* phDomain)
  154. {
  155. DWORD dwError = ERROR_SUCCESS;
  156. *phDomain = NULL;
  157. //
  158. // Retrieve domain SID from SAM server.
  159. //
  160. PSID pSid;
  161. UNICODE_STRING usDomain;
  162. usDomain.Length = wcslen(pszDomain) * sizeof(WCHAR);
  163. usDomain.MaximumLength = usDomain.Length + sizeof(WCHAR);
  164. usDomain.Buffer = const_cast<PWSTR>(pszDomain);
  165. NTSTATUS ntsStatus = SamLookupDomainInSamServer(hSam, &usDomain, &pSid);
  166. if (ntsStatus == STATUS_SUCCESS)
  167. {
  168. //
  169. // Open domain object in SAM server.
  170. //
  171. ntsStatus = SamOpenDomain(hSam, DOMAIN_LOOKUP, pSid, phDomain);
  172. if (ntsStatus != STATUS_SUCCESS)
  173. {
  174. dwError = RtlNtStatusToDosError(ntsStatus);
  175. }
  176. SamFreeMemory(pSid);
  177. }
  178. else
  179. {
  180. dwError = RtlNtStatusToDosError(ntsStatus);
  181. }
  182. return dwError;
  183. }
  184. //------------------------------------------------------------------------------
  185. // OpenDomain Function
  186. //
  187. // Synopsis
  188. // Verifies that the principal which obtained the domain handle has domain
  189. // admin rights in the domain.
  190. //
  191. // Note that the comments and logic were borrowed from the DsAddSidHistory code.
  192. // //depot/Lab03_N/ds/ds/src/ntdsa/dra/addsid.c
  193. //
  194. // Arguments
  195. // IN hDomain - domain handle
  196. //
  197. // Return
  198. // Win32 error code
  199. //------------------------------------------------------------------------------
  200. DWORD __stdcall VerifyDomainAdminRights(SAM_HANDLE hDomain)
  201. {
  202. DWORD dwError = ERROR_SUCCESS;
  203. // We need to verify that the credentials used to get hSam have domain
  204. // admin rights in the source domain. RichardW observes that we can
  205. // do this easily for both NT4 and NT5 cases by checking whether we
  206. // can open the domain admins object for write. On NT4, the principal
  207. // would have to be an immediate member of domain admins. On NT5 the
  208. // principal may transitively be a member of domain admins. But rather
  209. // than checking memberships per se, the ability to open domain admins
  210. // for write proves that the principal could add himself if he wanted
  211. // to, thus he/she is essentially a domain admin. I.e. The premise is
  212. // that security is set up such that only domain admins can modify the
  213. // domain admins group. If that's not the case, the customer has far
  214. // worse security issues to deal with than someone stealing a SID.
  215. // You'd think we should ask for GROUP_ALL_ACCESS. But it turns out
  216. // that in 2000.3 DELETE is not given by default to domain admins.
  217. // So we modify the access required accordingly. PraeritG has been
  218. // notified of this phenomena.
  219. SAM_HANDLE hGroup;
  220. NTSTATUS ntsStatus = SamOpenGroup(hDomain, GROUP_ALL_ACCESS & ~DELETE, DOMAIN_GROUP_RID_ADMINS, &hGroup);
  221. if (ntsStatus == STATUS_SUCCESS)
  222. {
  223. SamCloseHandle(hGroup);
  224. }
  225. else
  226. {
  227. dwError = RtlNtStatusToDosError(ntsStatus);
  228. }
  229. return dwError;
  230. }
  231. } // namespace
  232. //------------------------------------------------------------------------------
  233. // VerifyAdminCredentials Function
  234. //
  235. // Synopsis
  236. // Verifies that the given credentials are valid in the specified domain and
  237. // that the account has domain admin rights in the domain.
  238. //
  239. // Arguments
  240. // IN pszDomain
  241. // IN pszDomainController
  242. // IN pszUserName
  243. // IN pszPassword
  244. // IN pszUserDomain
  245. //
  246. // Return
  247. // Win32 error code
  248. // ERROR_SUCCESS - credentials are valid and the account does have domain
  249. // admin rights in the domain
  250. // ERROR_ACCESS_DENIED - either the credentials are invalid or the account does
  251. // not have domain admin rights in the domain
  252. //------------------------------------------------------------------------------
  253. DWORD __stdcall VerifyAdminCredentials
  254. (
  255. PCWSTR pszDomain,
  256. PCWSTR pszDomainController,
  257. PCWSTR pszUserName,
  258. PCWSTR pszPassword,
  259. PCWSTR pszUserDomain
  260. )
  261. {
  262. //
  263. // Connect to SAM server.
  264. //
  265. SAM_HANDLE hSam;
  266. DWORD dwError = ConnectToSam(pszDomain, pszDomainController, pszUserName, pszPassword, pszUserDomain, &hSam);
  267. if (dwError == ERROR_SUCCESS)
  268. {
  269. //
  270. // Open domain object on SAM server.
  271. //
  272. SAM_HANDLE hDomain;
  273. dwError = OpenDomain(hSam, pszDomain, &hDomain);
  274. if (dwError == ERROR_SUCCESS)
  275. {
  276. //
  277. // Verify credentials have administrative rights in domain.
  278. //
  279. dwError = VerifyDomainAdminRights(hDomain);
  280. SamCloseHandle(hDomain);
  281. }
  282. SamCloseHandle(hSam);
  283. }
  284. return dwError;
  285. }