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.

671 lines
17 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997.
  5. //
  6. // File: whackspn.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 7-30-98 RichardW Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #define LDAP_UNICODE 1
  18. #include <nt.h>
  19. #include <ntrtl.h>
  20. #include <nturtl.h>
  21. #include <windows.h>
  22. #define SECURITY_WIN32
  23. #include <rpc.h>
  24. #include <sspi.h>
  25. #include <secext.h>
  26. #include <lm.h>
  27. #include <winsock2.h>
  28. #include <winldap.h>
  29. #include <ntldap.h>
  30. #include <dsgetdc.h>
  31. #include <dsgetdcp.h>
  32. #include <ntdsapi.h>
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #define NlPrint(x) DPrint x;
  36. #define NL_CRITICAL 0
  37. #define NL_MISC 0
  38. HANDLE hGC ;
  39. BOOL AddDollar = TRUE;
  40. VOID
  41. DPrint(
  42. DWORD Level,
  43. PCHAR FormatString,
  44. ...
  45. )
  46. {
  47. va_list ArgList;
  48. va_start(ArgList, FormatString);
  49. vprintf( FormatString, ArgList );
  50. va_end( ArgList );
  51. }
  52. BOOL
  53. FindDomainForServer(
  54. PWSTR Server,
  55. PWSTR Domain
  56. )
  57. {
  58. ULONG NetStatus ;
  59. WCHAR LocalServerName[ 64 ];
  60. PDOMAIN_CONTROLLER_INFOW DcInfo ;
  61. wcsncpy( LocalServerName, Server, 63 );
  62. if ( AddDollar ) {
  63. wcscat( LocalServerName, L"$" );
  64. }
  65. NetStatus = DsGetDcNameWithAccountW(
  66. NULL,
  67. LocalServerName,
  68. ( AddDollar ? UF_MACHINE_ACCOUNT_MASK : UF_NORMAL_ACCOUNT ),
  69. L"",
  70. NULL,
  71. NULL,
  72. DS_DIRECTORY_SERVICE_REQUIRED |
  73. DS_RETURN_DNS_NAME,
  74. &DcInfo );
  75. if ( NetStatus == 0 ) {
  76. wcscpy( Domain, DcInfo->DomainName );
  77. NetApiBufferFree( DcInfo );
  78. return TRUE ;
  79. } else {
  80. printf("can't find DC for %ws - %u\n", LocalServerName, NetStatus );
  81. }
  82. return FALSE ;
  83. }
  84. DWORD
  85. AddDnsHostNameAttribute(
  86. HANDLE hDs,
  87. PWCHAR Server,
  88. PWCHAR DnsDomain
  89. )
  90. {
  91. NET_API_STATUS NetStatus = NO_ERROR;
  92. ULONG CrackStatus = DS_NAME_NO_ERROR;
  93. LPWSTR DnsHostNameValues[2];
  94. LPWSTR SpnArray[3];
  95. LPWSTR DnsSpn = NULL;
  96. LPWSTR NetbiosSpn = NULL;
  97. LDAPModW DnsHostNameAttr;
  98. LDAPModW SpnAttr;
  99. LDAPModW *Mods[3] = {NULL};
  100. LDAP *LdapHandle = NULL;
  101. LDAPMessage *LdapMessage = NULL;
  102. PDS_NAME_RESULTW CrackedName = NULL;
  103. LPWSTR DnOfAccount = NULL;
  104. LPWSTR NameToCrack;
  105. DWORD SamNameSize;
  106. WCHAR SamName[ DNLEN + 1 + CNLEN + 1 + 1];
  107. WCHAR nameBuffer[ 256 ];
  108. ULONG LdapStatus;
  109. LONG LdapOption;
  110. LDAP_TIMEVAL LdapTimeout;
  111. ULONG MessageNumber;
  112. //
  113. // Ldap modify control needed to indicate that the
  114. // existing values of the modified attributes should
  115. // be left intact and the missing ones should be added.
  116. // Without this control, a modification of an attribute
  117. // that results in an addition of a value that already
  118. // exists will fail.
  119. //
  120. LDAPControl ModifyControl = {
  121. LDAP_SERVER_PERMISSIVE_MODIFY_OID_W,
  122. {
  123. 0, NULL
  124. },
  125. FALSE
  126. };
  127. PLDAPControl ModifyControlArray[2] =
  128. {
  129. &ModifyControl,
  130. NULL
  131. };
  132. //
  133. // Prepare DnsHostName modification entry
  134. //
  135. wcsncpy( nameBuffer, Server, sizeof( nameBuffer ) / sizeof( WCHAR ));
  136. wcscat( nameBuffer, L"." );
  137. wcscat( nameBuffer, DnsDomain );
  138. DnsHostNameValues[0] = nameBuffer;
  139. DnsHostNameValues[1] = NULL;
  140. //
  141. // If we set both DnsHostName and SPN, then DnsHostName is
  142. // missing, so add it. If we set DnsHostName only, then
  143. // DnsHostName already exists (but incorrect), so replace it.
  144. //
  145. if ( TRUE ) {
  146. DnsHostNameAttr.mod_op = LDAP_MOD_ADD;
  147. } else {
  148. DnsHostNameAttr.mod_op = LDAP_MOD_REPLACE;
  149. }
  150. DnsHostNameAttr.mod_type = L"DnsHostName";
  151. DnsHostNameAttr.mod_values = DnsHostNameValues;
  152. Mods[0] = &DnsHostNameAttr;
  153. Mods[1] = NULL;
  154. //
  155. // The name of the computer object is
  156. // <NetbiosDomainName>\<NetbiosComputerName>$
  157. //
  158. wcscpy( SamName, DnsDomain );
  159. {
  160. PWCHAR p;
  161. p = wcschr( SamName, L'.' );
  162. *p = UNICODE_NULL;
  163. }
  164. wcscat( SamName, L"\\" );
  165. wcscat( SamName, Server );
  166. wcscat( SamName, L"$" );
  167. //
  168. // Crack the sam account name into a DN:
  169. //
  170. NameToCrack = SamName;
  171. NetStatus = DsCrackNamesW(
  172. hDs,
  173. 0,
  174. DS_NT4_ACCOUNT_NAME,
  175. DS_FQDN_1779_NAME,
  176. 1,
  177. &NameToCrack,
  178. &CrackedName );
  179. if ( NetStatus != NO_ERROR ) {
  180. NlPrint(( NL_CRITICAL,
  181. "SPN: CrackNames failed on %ws for %ws: %ld\n",
  182. DnsDomain,
  183. SamName,
  184. NetStatus ));
  185. goto Cleanup ;
  186. }
  187. if ( CrackedName->cItems != 1 ) {
  188. CrackStatus = DS_NAME_ERROR_NOT_UNIQUE;
  189. NlPrint(( NL_CRITICAL,
  190. "SPN: Cracked Name is not unique on %ws for %ws: %ld\n",
  191. DnsDomain,
  192. SamName,
  193. NetStatus ));
  194. goto Cleanup ;
  195. }
  196. if ( CrackedName->rItems[ 0 ].status != DS_NAME_NO_ERROR ) {
  197. NlPrint(( NL_CRITICAL,
  198. "SPN: CrackNames failed on %ws for %ws: substatus %ld\n",
  199. DnsDomain,
  200. SamName,
  201. CrackedName->rItems[ 0 ].status ));
  202. CrackStatus = CrackedName->rItems[ 0 ].status;
  203. goto Cleanup ;
  204. }
  205. DnOfAccount = CrackedName->rItems[0].pName;
  206. //
  207. // Open an LDAP connection to the DC and set useful options
  208. //
  209. LdapHandle = ldap_init( DnsDomain, LDAP_PORT );
  210. if ( LdapHandle == NULL ) {
  211. NetStatus = GetLastError();
  212. NlPrint(( NL_CRITICAL,
  213. "SPN: ldap_init failed on %ws for %ws: %ld\n",
  214. DnsDomain,
  215. SamName,
  216. NetStatus ));
  217. goto Cleanup;
  218. }
  219. // 30 second timeout
  220. LdapOption = 30;
  221. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_TIMELIMIT, &LdapOption );
  222. if ( LdapStatus != LDAP_SUCCESS ) {
  223. NlPrint(( NL_CRITICAL,
  224. "SPN: ldap_set_option LDAP_OPT_TIMELIMIT failed on %ws for %ws: %ld: %s\n",
  225. DnsDomain,
  226. SamName,
  227. LdapStatus,
  228. ldap_err2stringA( LdapStatus )));
  229. NetStatus = LdapMapErrorToWin32(LdapStatus);
  230. goto Cleanup;
  231. }
  232. // Don't chase referals
  233. LdapOption = PtrToLong(LDAP_OPT_OFF);
  234. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_REFERRALS, &LdapOption );
  235. if ( LdapStatus != LDAP_SUCCESS ) {
  236. NlPrint(( NL_CRITICAL,
  237. "SPN: ldap_set_option LDAP_OPT_REFERRALS failed on %ws for %ws: %ld: %s\n",
  238. DnsDomain,
  239. SamName,
  240. LdapStatus,
  241. ldap_err2stringA( LdapStatus )));
  242. NetStatus = LdapMapErrorToWin32(LdapStatus);
  243. goto Cleanup;
  244. }
  245. #if 0
  246. // Set the option telling LDAP that I passed it an explicit DC name and
  247. // that it can avoid the DsGetDcName.
  248. LdapOption = PtrToLong(LDAP_OPT_ON);
  249. LdapStatus = ldap_set_optionW( LdapHandle, LDAP_OPT_AREC_EXCLUSIVE, &LdapOption );
  250. if ( LdapStatus != LDAP_SUCCESS ) {
  251. NlPrint(( NL_CRITICAL,
  252. "SPN: ldap_set_option LDAP_OPT_AREC_EXCLUSIVE failed on %ws for %ws: %ld: %s\n",
  253. DnsDomain,
  254. SamName,
  255. LdapStatus,
  256. ldap_err2stringA( LdapStatus )));
  257. NetStatus = LdapMapErrorToWin32(LdapStatus);
  258. goto Cleanup;
  259. }
  260. #endif
  261. //
  262. // Bind to the DC
  263. //
  264. LdapStatus = ldap_bind_s( LdapHandle,
  265. NULL, // No DN of account to authenticate as
  266. NULL, // Default credentials
  267. LDAP_AUTH_NEGOTIATE );
  268. if ( LdapStatus != LDAP_SUCCESS ) {
  269. NlPrint(( NL_CRITICAL,
  270. "SPN: Cannot ldap_bind to %ws for %ws: %ld: %s\n",
  271. DnsDomain,
  272. SamName,
  273. LdapStatus,
  274. ldap_err2stringA( LdapStatus )));
  275. NetStatus = LdapMapErrorToWin32(LdapStatus);
  276. goto Cleanup;
  277. }
  278. //
  279. // Write the modifications
  280. //
  281. LdapStatus = ldap_modify_extW( LdapHandle,
  282. DnOfAccount,
  283. Mods,
  284. (PLDAPControl *) &ModifyControlArray,
  285. NULL, // No client controls
  286. &MessageNumber );
  287. if ( LdapStatus != LDAP_SUCCESS ) {
  288. NlPrint(( NL_CRITICAL,
  289. "SPN: Cannot ldap_modify on %ws for %ws: %ld: %s\n",
  290. DnsDomain,
  291. DnOfAccount,
  292. LdapStatus,
  293. ldap_err2stringA( LdapStatus )));
  294. NetStatus = LdapMapErrorToWin32(LdapStatus);
  295. goto Cleanup;
  296. }
  297. // Wait for the modify to complete
  298. LdapTimeout.tv_sec = 30;
  299. LdapTimeout.tv_usec = 0;
  300. LdapStatus = ldap_result( LdapHandle,
  301. MessageNumber,
  302. LDAP_MSG_ALL,
  303. &LdapTimeout,
  304. &LdapMessage );
  305. switch ( LdapStatus ) {
  306. case -1:
  307. NlPrint(( NL_CRITICAL,
  308. "SPN: Cannot ldap_result on %ws for %ws: %ld: %s\n",
  309. DnsDomain,
  310. SamName,
  311. LdapHandle->ld_errno,
  312. ldap_err2stringA( LdapHandle->ld_errno )));
  313. NetStatus = LdapMapErrorToWin32(LdapStatus);
  314. goto Cleanup;
  315. case 0:
  316. NlPrint(( NL_CRITICAL,
  317. "SPN: ldap_result timeout on %ws for %ws.\n",
  318. DnsDomain,
  319. SamName ));
  320. NetStatus = LdapMapErrorToWin32(LdapStatus);
  321. goto Cleanup;
  322. case LDAP_RES_MODIFY:
  323. if ( LdapMessage->lm_returncode != 0 ) {
  324. NlPrint(( NL_CRITICAL,
  325. "SPN: Cannot ldap_result on %ws for %ws: %ld: %s\n",
  326. DnsDomain,
  327. SamName,
  328. LdapMessage->lm_returncode,
  329. ldap_err2stringA( LdapMessage->lm_returncode )));
  330. NetStatus = LdapMapErrorToWin32(LdapMessage->lm_returncode);
  331. goto Cleanup;
  332. }
  333. NlPrint(( NL_MISC,
  334. "SPN: Set successfully on DC %ws\n",
  335. DnsDomain ));
  336. break; // This is what we expect
  337. default:
  338. NlPrint(( NL_CRITICAL,
  339. "SPN: ldap_result unexpected result on %ws for %ws: %ld\n",
  340. DnsDomain,
  341. SamName,
  342. LdapStatus ));
  343. NetStatus = LdapMapErrorToWin32(LdapStatus);
  344. goto Cleanup;
  345. }
  346. Cleanup:
  347. //
  348. // Log the failure in the event log, if requested.
  349. // Try to output the most specific error.
  350. //
  351. if ( CrackedName ) {
  352. DsFreeNameResultW( CrackedName );
  353. }
  354. if ( LdapMessage != NULL ) {
  355. ldap_msgfree( LdapMessage );
  356. }
  357. if ( LdapHandle != NULL ) {
  358. ldap_unbind_s( LdapHandle );
  359. }
  360. if ( DnsSpn ) {
  361. LocalFree( DnsSpn );
  362. }
  363. if ( NetbiosSpn ) {
  364. LocalFree( NetbiosSpn );
  365. }
  366. return NetStatus;
  367. }
  368. BOOL
  369. AddHostSpn(
  370. PWSTR Server
  371. )
  372. {
  373. WCHAR HostSpn[ 64 ];
  374. WCHAR Domain[ MAX_PATH ];
  375. WCHAR FlatName[ 64 ];
  376. HANDLE hDs ;
  377. ULONG NetStatus ;
  378. PWSTR Dot ;
  379. PDS_NAME_RESULTW Result ;
  380. LPWSTR Flat = FlatName;
  381. LPWSTR Spn = HostSpn ;
  382. wcscpy( HostSpn, L"HOST/" );
  383. wcscat( HostSpn, Server );
  384. #if 1
  385. if ( !FindDomainForServer( Server, Domain )) {
  386. printf(" No domain controller for %ws found\n", Server );
  387. return FALSE ;
  388. }
  389. #else
  390. wcscpy( Domain, L"ntdev.microsoft.com" );
  391. #endif
  392. Dot = wcschr( Domain, L'.' );
  393. if ( Dot ) {
  394. *Dot = L'\0';
  395. }
  396. wcscpy( FlatName, Domain );
  397. if ( Dot ) {
  398. *Dot = L'.' ;
  399. }
  400. wcscat( FlatName, L"\\" );
  401. wcscat( FlatName, Server );
  402. if ( AddDollar ) {
  403. wcscat( FlatName, L"$" );
  404. }
  405. NetStatus = DsBindW( NULL, Domain, &hDs );
  406. if ( NetStatus != 0 ) {
  407. printf("Failed to bind to DC of domain %ws, %#x\n",
  408. Domain, NetStatus );
  409. return FALSE ;
  410. }
  411. NetStatus = DsCrackNamesW(
  412. hDs,
  413. 0,
  414. DS_NT4_ACCOUNT_NAME,
  415. DS_FQDN_1779_NAME,
  416. 1,
  417. &Flat,
  418. &Result );
  419. if ( NetStatus != 0 ) {
  420. printf("Failed to crack name %ws into the FQDN, %#x\n",
  421. FlatName, NetStatus );
  422. DsUnBind( &hDs );
  423. return FALSE ;
  424. } else {
  425. DWORD i;
  426. BOOL foundFailure = FALSE;
  427. //
  428. // check the stat-i inside the struct
  429. //
  430. for ( i = 0; i < Result->cItems; ++i ) {
  431. if ( Result->rItems[i].status == DS_NAME_NO_ERROR ) {
  432. printf("Found domain\\name '%ws\\%ws' for Flatname '%ws' \n",
  433. Result->rItems[i].pDomain,
  434. Result->rItems[i].pName,
  435. FlatName);
  436. } else {
  437. printf("Couldn't crack name for '%ws' - %u\n",
  438. FlatName, Result->rItems[i].status);
  439. foundFailure = TRUE;
  440. }
  441. }
  442. if ( foundFailure ) {
  443. return FALSE;
  444. }
  445. }
  446. //
  447. // add DnsHostName attribute to this object
  448. //
  449. NetStatus = AddDnsHostNameAttribute( hDs, Server, Domain );
  450. if ( NetStatus != 0 ) {
  451. printf( "Failed to add DnsHostName attrib to '%ws.%ws', %#x\n",
  452. Server, Domain, NetStatus );
  453. return FALSE;
  454. }
  455. //
  456. // write the netbios based SPN
  457. //
  458. NetStatus = DsServerRegisterSpnW(
  459. DS_SPN_ADD_SPN_OP,
  460. L"HOST",
  461. Result->rItems[0].pName);
  462. if ( NetStatus != 0 ) {
  463. printf("DsServerRegisterSpn Failed to assign SPN to '%ws', %#x\n",
  464. Result->rItems[0].pName, NetStatus );
  465. }
  466. NetStatus = DsWriteAccountSpnW(
  467. hDs,
  468. DS_SPN_ADD_SPN_OP,
  469. Result->rItems[0].pName,
  470. 1,
  471. &Spn );
  472. if ( NetStatus != 0 ) {
  473. printf("Failed to assign SPN '%ws' to account '%ws', %#x\n",
  474. HostSpn, Result->rItems[0].pName, NetStatus );
  475. }
  476. else {
  477. DWORD i;
  478. BOOL foundFailure = FALSE;
  479. //
  480. // check the stat-i inside the struct
  481. //
  482. for ( i = 0; i < Result->cItems; ++i ) {
  483. if ( Result->rItems[i].status == DS_NAME_NO_ERROR ) {
  484. printf("Assigned SPN '%ws' to account '%ws' \n",
  485. HostSpn, Result->rItems[i].pName );
  486. } else {
  487. printf("WriteAccountSpn failed for '%ws' - %u\n",
  488. HostSpn, Result->rItems[i].status);
  489. foundFailure = TRUE;
  490. }
  491. }
  492. if ( foundFailure ) {
  493. return FALSE;
  494. }
  495. }
  496. //
  497. // do it again with the DNS domain as well
  498. //
  499. wcscat( HostSpn, L"." );
  500. wcscat( HostSpn, Domain );
  501. NetStatus = DsWriteAccountSpnW(hDs,
  502. DS_SPN_ADD_SPN_OP,
  503. Result->rItems[0].pName,
  504. 1,
  505. &Spn );
  506. if ( NetStatus != 0 ) {
  507. printf("Failed to assign SPN '%ws' to account '%ws', %#x\n",
  508. HostSpn, Result->rItems[0].pName, NetStatus );
  509. }
  510. else {
  511. DWORD i;
  512. BOOL foundFailure = FALSE;
  513. //
  514. // check the stat-i inside the struct
  515. //
  516. for ( i = 0; i < Result->cItems; ++i ) {
  517. if ( Result->rItems[i].status == DS_NAME_NO_ERROR ) {
  518. printf("Assigned SPN '%ws' to account '%ws' \n",
  519. HostSpn, Result->rItems[i].pName );
  520. } else {
  521. printf("WriteAccountSpn failed for '%ws' - %u\n",
  522. HostSpn, Result->rItems[i].status);
  523. foundFailure = TRUE;
  524. }
  525. }
  526. if ( foundFailure ) {
  527. return FALSE;
  528. }
  529. }
  530. DsFreeNameResultW( Result );
  531. DsUnBind( &hDs );
  532. return NetStatus == 0 ;
  533. }
  534. void __cdecl wmain (int argc, wchar_t *argv[])
  535. {
  536. PWCHAR machineName;
  537. if ( argc < 2 ) {
  538. printf("usage: %s [-$] machinename\n", argv[0] );
  539. exit(0);
  540. }
  541. if ( argv[1][0] == '-') {
  542. switch (argv[1][1]) {
  543. case '$':
  544. AddDollar = FALSE ;
  545. break;
  546. default:
  547. printf(" unrecognized option, '%s'\n", argv[1] );
  548. exit(0);
  549. }
  550. machineName = argv[2];
  551. }
  552. else {
  553. machineName = argv[1];
  554. }
  555. if ( AddHostSpn( machineName ) ) {
  556. printf("Updated object\n" );
  557. }
  558. }