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.

622 lines
14 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997.
  5. //
  6. // File: setspn.c
  7. //
  8. // Contents:
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 7-30-98 RichardW Created
  15. // 8-10-99 JBrezak Turned into setspn added list capability
  16. // 09-22-99 Jaroslad support for adding/removing arbitrary SPNs
  17. //
  18. //----------------------------------------------------------------------------
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #include <windows.h>
  23. #define SECURITY_WIN32
  24. #include <rpc.h>
  25. #include <sspi.h>
  26. #include <secext.h>
  27. #include <lm.h>
  28. #include <winsock2.h>
  29. #include <dsgetdc.h>
  30. #include <dsgetdcp.h>
  31. #include <ntdsapi.h>
  32. #include <ntdsapip.h> // private DS_NAME_FORMATS
  33. #include <stdio.h>
  34. #include <winldap.h>
  35. #include <shlwapi.h>
  36. DWORD debug = 0;
  37. BOOL AddSpn(PWSTR Service, PWSTR Server);
  38. BOOL
  39. FindDomainForAccount(
  40. PWSTR Server,
  41. PWSTR DomainToCheck,
  42. PWSTR Domain,
  43. PWSTR DC
  44. )
  45. {
  46. ULONG NetStatus ;
  47. PDOMAIN_CONTROLLER_INFO DcInfo ;
  48. WCHAR LocalServerName[ 64 ];
  49. wcsncpy( LocalServerName, Server, 63 );
  50. wcscat( LocalServerName, L"$" );
  51. NetStatus = DsGetDcNameWithAccountW(
  52. NULL,
  53. LocalServerName,
  54. UF_ACCOUNT_TYPE_MASK,
  55. DomainToCheck,
  56. NULL,
  57. NULL,
  58. DS_DIRECTORY_SERVICE_REQUIRED |
  59. DS_RETURN_FLAT_NAME,
  60. &DcInfo );
  61. if ( NetStatus == 0 )
  62. {
  63. wcscat( Server, L"$" );
  64. wcscpy( Domain, DcInfo->DomainName );
  65. if (DC)
  66. {
  67. wcscpy( DC, &DcInfo->DomainControllerName[2] );
  68. }
  69. NetApiBufferFree( DcInfo );
  70. return TRUE ;
  71. }
  72. NetStatus = DsGetDcNameWithAccountW(
  73. NULL,
  74. Server,
  75. UF_ACCOUNT_TYPE_MASK,
  76. DomainToCheck,
  77. NULL,
  78. NULL,
  79. DS_DIRECTORY_SERVICE_REQUIRED |
  80. DS_RETURN_FLAT_NAME,
  81. &DcInfo );
  82. if ( NetStatus == 0 )
  83. {
  84. wcscpy( Domain, DcInfo->DomainName );
  85. if (DC)
  86. {
  87. wcscpy( DC, &DcInfo->DomainControllerName[2] );
  88. }
  89. NetApiBufferFree( DcInfo );
  90. return TRUE ;
  91. }
  92. return FALSE ;
  93. }
  94. BOOL
  95. AddHostSpn(
  96. PWSTR Server
  97. )
  98. {
  99. return (AddSpn(L"HOST", Server));
  100. }
  101. BOOL
  102. AddSpn(
  103. PWSTR Service,
  104. PWSTR Server
  105. )
  106. {
  107. WCHAR HostSpn[ 64 ];
  108. WCHAR FlatSpn[ 64 ];
  109. WCHAR Domain[ MAX_PATH ];
  110. WCHAR FlatName[ 64 ];
  111. HANDLE hDs ;
  112. ULONG NetStatus ;
  113. PDS_NAME_RESULT Result ;
  114. LPWSTR Flat = FlatName;
  115. LPWSTR Spns[2];
  116. WCHAR LocalServerName[ 64 ];
  117. wcsncpy( LocalServerName, Server, sizeof(LocalServerName)/sizeof(LocalServerName[0]) );
  118. if ( !FindDomainForAccount( LocalServerName, L"", Domain, NULL ))
  119. {
  120. fprintf(stderr,
  121. "Could not find account %ws\n", LocalServerName );
  122. return FALSE;
  123. }
  124. wcscpy( FlatName, Domain );
  125. wcscat( FlatName, L"\\" );
  126. wcscat( FlatName, LocalServerName );
  127. NetStatus = DsBind( NULL, Domain, &hDs );
  128. if ( NetStatus != 0 )
  129. {
  130. fprintf(stderr,
  131. "Failed to bind to DC of domain %ws, %#x\n",
  132. Domain, NetStatus );
  133. return FALSE ;
  134. }
  135. NetStatus = DsCrackNames(
  136. hDs,
  137. 0,
  138. DS_NT4_ACCOUNT_NAME,
  139. DS_FQDN_1779_NAME,
  140. 1,
  141. &Flat,
  142. &Result );
  143. if ( NetStatus != 0 ||
  144. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  145. Result->cItems != 1)
  146. {
  147. fprintf(stderr,
  148. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  149. FlatName, NetStatus, Result->cItems,
  150. (Result->cItems==1)?Result->rItems[0].status:-1 );
  151. DsUnBind( &hDs );
  152. return FALSE ;
  153. }
  154. wsprintf( HostSpn, L"%s/%s.%s", Service, Server, Domain );
  155. wsprintf( FlatSpn, L"%s/%s", Service, Server );
  156. Spns[0] = HostSpn;
  157. Spns[1] = FlatSpn;
  158. printf("Registering ServicePrincipalNames for %ws\n",
  159. Result->rItems[0].pName);
  160. printf("\t%ws\n", HostSpn);
  161. printf("\t%ws\n", FlatSpn);
  162. #if 0
  163. printf("DsWriteAccountSpn: Commented out\n");
  164. #else
  165. NetStatus = DsWriteAccountSpn(
  166. hDs,
  167. DS_SPN_ADD_SPN_OP,
  168. Result->rItems[0].pName,
  169. 2,
  170. Spns );
  171. if ( NetStatus != 0 )
  172. {
  173. fprintf(stderr,
  174. "Failed to assign SPN to account '%ws', %#x\n",
  175. Result->rItems[0].pName, NetStatus );
  176. return FALSE;
  177. }
  178. #endif
  179. DsFreeNameResult( Result );
  180. DsUnBind( &hDs );
  181. return NetStatus == 0 ;
  182. }
  183. // added by jaroslad on 09/22/99
  184. BOOL
  185. AddRemoveSpn(
  186. PWSTR HostSpn,
  187. PWSTR HostDomain,
  188. PWSTR Server,
  189. DS_SPN_WRITE_OP Operation
  190. )
  191. {
  192. WCHAR FlatSpn[ MAX_PATH ];
  193. WCHAR Domain[ MAX_PATH ];
  194. WCHAR FlatName[ MAX_PATH ];
  195. HANDLE hDs ;
  196. ULONG NetStatus ;
  197. PDS_NAME_RESULT Result ;
  198. LPWSTR Flat = FlatName;
  199. LPWSTR Spns[2];
  200. if ( !FindDomainForAccount( Server, HostDomain, Domain, NULL ))
  201. {
  202. fprintf(stderr,
  203. "Unable to locate account %ws\n", Server);
  204. return FALSE ;
  205. }
  206. wcscpy( FlatName, Domain );
  207. wcscat( FlatName, L"\\" );
  208. wcscat( FlatName, Server );
  209. NetStatus = DsBind( NULL, Domain, &hDs );
  210. if ( NetStatus != 0 )
  211. {
  212. fprintf(stderr,
  213. "Failed to bind to DC of domain %ws, %#x\n",
  214. Domain, NetStatus );
  215. return FALSE ;
  216. }
  217. NetStatus = DsCrackNames(
  218. hDs,
  219. 0,
  220. DS_NT4_ACCOUNT_NAME,
  221. DS_FQDN_1779_NAME,
  222. 1,
  223. &Flat,
  224. &Result );
  225. if ( NetStatus != 0 ||
  226. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  227. Result->cItems != 1)
  228. {
  229. fprintf(stderr,
  230. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  231. FlatName, NetStatus, Result->cItems,
  232. (Result->cItems==1)?Result->rItems[0].status:-1 );
  233. DsUnBind( &hDs );
  234. return FALSE ;
  235. }
  236. Spns[0] = HostSpn;
  237. printf("%s ServicePrincipalNames for %ws\n",
  238. (Operation==DS_SPN_DELETE_SPN_OP)?"Unregistering":"Registering", Result->rItems[0].pName);
  239. printf("\t%ws\n", HostSpn);
  240. #if 0
  241. printf("DsWriteAccountSpn: Commented out\n");
  242. #else
  243. NetStatus = DsWriteAccountSpn(
  244. hDs,
  245. Operation,
  246. Result->rItems[0].pName,
  247. 1,
  248. Spns );
  249. if ( NetStatus != 0 )
  250. {
  251. fprintf(stderr,
  252. "Failed to %s SPN on account '%ws', %#x\n",
  253. (Operation==DS_SPN_DELETE_SPN_OP)?"remove":"assign",
  254. Result->rItems[0].pName, NetStatus );
  255. return FALSE;
  256. }
  257. #endif
  258. DsFreeNameResult( Result );
  259. DsUnBind( &hDs );
  260. return NetStatus == 0 ;
  261. }
  262. BOOL
  263. LookupHostSpn(
  264. PWSTR ServerDomain,
  265. PWSTR Server
  266. )
  267. {
  268. WCHAR FlatName[ 128 ];
  269. HANDLE hDs ;
  270. ULONG NetStatus ;
  271. PDS_NAME_RESULT Result ;
  272. LPWSTR Flat = FlatName;
  273. LDAP *ld;
  274. int rc;
  275. LDAPMessage *e, *res;
  276. WCHAR *base_dn;
  277. WCHAR *search_dn, search_ava[256];
  278. WCHAR Domain[ MAX_PATH ];
  279. WCHAR DC[ MAX_PATH ];
  280. if ( !FindDomainForAccount( Server, ServerDomain, Domain, DC ))
  281. {
  282. fprintf(stderr, "Cannot find account %S\n", Server);
  283. return FALSE ;
  284. }
  285. if (debug)
  286. printf("Domain=%S DC=%S\n", Domain, DC);
  287. ld = ldap_open(DC, LDAP_PORT);
  288. if (ld == NULL) {
  289. fprintf(stderr, "ldap_init failed = %x", LdapGetLastError());
  290. return FALSE;
  291. }
  292. rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  293. if (rc != LDAP_SUCCESS) {
  294. fprintf(stderr, "ldap_bind failed = %x", LdapGetLastError());
  295. ldap_unbind(ld);
  296. return FALSE;
  297. }
  298. NetStatus = DsBind( NULL, Domain, &hDs );
  299. if ( NetStatus != 0 )
  300. {
  301. fprintf(stderr, "Failed to bind to DC of domain %ws, %#x\n",
  302. Domain, NetStatus );
  303. return FALSE ;
  304. }
  305. wcscpy( FlatName, Domain );
  306. wcscat( FlatName, L"\\" );
  307. wcscat( FlatName, Server );
  308. NetStatus = DsCrackNames(
  309. hDs,
  310. 0,
  311. DS_NT4_ACCOUNT_NAME,
  312. DS_FQDN_1779_NAME,
  313. 1,
  314. &Flat,
  315. &Result );
  316. if ( NetStatus != 0 ||
  317. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  318. Result->cItems != 1)
  319. {
  320. if (Result->rItems[0].status == DS_NAME_ERROR_NOT_FOUND)
  321. {
  322. fprintf(stderr,
  323. "Requested name \"%ws\" not found in directory.\n",
  324. FlatName);
  325. }
  326. else if (Result->rItems[0].status == DS_NAME_ERROR_NOT_UNIQUE)
  327. {
  328. fprintf(stderr,
  329. "Requested name \"%ws\" not unique in directory.\n",
  330. FlatName);
  331. }
  332. else
  333. fprintf(stderr,
  334. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  335. FlatName, NetStatus, Result->cItems,
  336. (Result->cItems==1)?Result->rItems[0].status:-1 );
  337. DsUnBind( &hDs );
  338. return FALSE ;
  339. }
  340. search_dn = Server;
  341. base_dn = StrChr(Result->rItems[0].pName, L',');
  342. if (!base_dn)
  343. base_dn = Result->rItems[0].pName;
  344. else
  345. base_dn++;
  346. if (debug) {
  347. printf("BASE_DN=%S\n", base_dn);
  348. printf("SEARCH_DN=%S\n", search_dn);
  349. }
  350. DsUnBind( &hDs );
  351. wsprintf(search_ava, L"(sAMAccountName=%s)", search_dn);
  352. if (debug)
  353. printf("FILTER=\"%S\"\n", search_ava);
  354. rc = ldap_search_s(ld, base_dn, LDAP_SCOPE_SUBTREE,
  355. search_ava, NULL, 0, &res);
  356. DsFreeNameResult( Result );
  357. if (rc != LDAP_SUCCESS) {
  358. fprintf(stderr, "ldap_search_s failed: %S", ldap_err2string(rc));
  359. ldap_unbind(ld);
  360. return 1;
  361. }
  362. for (e = ldap_first_entry(ld, res);
  363. e;
  364. e = ldap_next_entry(ld, e)) {
  365. BerElement *b;
  366. WCHAR *attr;
  367. WCHAR *dn = ldap_get_dn(ld, res);
  368. printf("Registered ServicePrincipalNames");
  369. if (dn)
  370. printf(" for %S", dn);
  371. printf(":\n");
  372. ldap_memfree(dn);
  373. for (attr = ldap_first_attribute(ld, e, &b);
  374. attr;
  375. attr = ldap_next_attribute(ld, e, b)) {
  376. WCHAR **values, **p;
  377. values = ldap_get_values(ld, e, attr);
  378. for (p = values; *p; p++) {
  379. if (StrCmp(attr, L"servicePrincipalName") == 0)
  380. printf(" %S\n", *p);
  381. }
  382. ldap_value_free(values);
  383. ldap_memfree(attr);
  384. }
  385. //ber_free(b, 1);
  386. }
  387. ldap_msgfree(res);
  388. ldap_unbind(ld);
  389. return TRUE;
  390. }
  391. void Usage( PWSTR name)
  392. {
  393. printf("\
  394. Usage: %S [switches data] computername \n\
  395. Where \"computername\" can be the name or domain\\name\n\
  396. \n\
  397. Switches:\n\
  398. -R = reset HOST ServicePrincipalName\n\
  399. Usage: setspn -R computername\n\
  400. -A = add arbitrary SPN \n\
  401. Usage: setspn -A SPN computername\n\
  402. -D = delete arbitrary SPN \n\
  403. Usage: setspn -D SPN computername\n\
  404. -L = list registered SPNs \n\
  405. Usage: setspn [-L] computername \n\
  406. Examples: \n\
  407. setspn -R daserver1 \n\
  408. It will register SPN \"HOST/daserver1\" and \"HOST/{DNS of daserver1}\" \n\
  409. setspn -A http/daserver daserver1 \n\
  410. It will register SPN \"http/daserver\" for computer \"daserver1\" \n\
  411. setspn -D http/daserver daserver1 \n\
  412. It will delete SPN \"http/daserver\" for computer \"daserver1\" \n\
  413. ", name);
  414. ExitProcess(0);
  415. }
  416. void __cdecl wmain (int argc, wchar_t *argv[])
  417. {
  418. int resetSPN = FALSE, addSPN = FALSE, deleteSPN = FALSE, listSPN = TRUE;
  419. UNICODE_STRING Service, Host,HostSpn, HostDomain ;
  420. wchar_t *ptr;
  421. int i;
  422. DS_SPN_WRITE_OP Operation;
  423. PWSTR Scan;
  424. DWORD Status = 1;
  425. for (i = 1; i < argc; i++)
  426. {
  427. if ((argv[i][0] == L'-') || (argv[i][0] == L'/'))
  428. {
  429. for (ptr = (argv[i] + 1); *ptr; ptr++)
  430. {
  431. switch(towupper(*ptr))
  432. {
  433. case L'R':
  434. resetSPN = TRUE;
  435. break;
  436. case L'A':
  437. addSPN = TRUE;
  438. break;
  439. case L'D':
  440. deleteSPN = TRUE;
  441. break;
  442. case L'L':
  443. listSPN = TRUE;
  444. break;
  445. case L'V':
  446. debug = TRUE;
  447. break;
  448. case L'?':
  449. default:
  450. Usage(argv[0]);
  451. break;
  452. }
  453. }
  454. }
  455. else
  456. break;
  457. }
  458. if ( resetSPN )
  459. {
  460. if ( ( argc - i ) != 1 )
  461. {
  462. Usage( argv[0] );
  463. }
  464. RtlInitUnicodeString( &Host, argv[i] );
  465. if ( AddHostSpn( Host.Buffer ) )
  466. {
  467. printf("Updated object\n");
  468. Status = 0;
  469. }
  470. }
  471. else if ( addSPN || deleteSPN )
  472. {
  473. if ( ( argc - i ) != 2 )
  474. {
  475. Usage( argv[0] );
  476. }
  477. RtlInitUnicodeString( &HostSpn, argv[i] );
  478. Scan = argv[ i + 1 ];
  479. if ( Scan = wcschr( Scan, L'\\' ) )
  480. {
  481. *Scan++ = L'\0';
  482. RtlInitUnicodeString( &HostDomain, argv[i+1] );
  483. RtlInitUnicodeString( &Host, Scan );
  484. }
  485. else
  486. {
  487. RtlInitUnicodeString( &HostDomain, L"" );
  488. RtlInitUnicodeString( &Host, argv[ i + 1 ] );
  489. }
  490. if ( addSPN )
  491. Operation = DS_SPN_ADD_SPN_OP;
  492. if ( deleteSPN )
  493. Operation = DS_SPN_DELETE_SPN_OP;
  494. if ( AddRemoveSpn( HostSpn.Buffer, HostDomain.Buffer, Host.Buffer, Operation) )
  495. {
  496. printf("Updated object\n");
  497. Status = 0;
  498. }
  499. }
  500. else if ( listSPN )
  501. {
  502. if ( ( argc - i ) != 1 )
  503. {
  504. Usage( argv[0] );
  505. }
  506. Scan = argv[ i ];
  507. if ( Scan = wcschr( Scan, L'\\' ) )
  508. {
  509. *Scan++ = L'\0';
  510. RtlInitUnicodeString( &HostDomain, argv[ i ] );
  511. RtlInitUnicodeString( &Host, Scan );
  512. }
  513. else
  514. {
  515. RtlInitUnicodeString( &HostDomain, L"" );
  516. RtlInitUnicodeString( &Host, argv[ i ] );
  517. }
  518. if (LookupHostSpn( HostDomain.Buffer, Host.Buffer ))
  519. {
  520. Status = 0;
  521. }
  522. }
  523. ExitProcess(Status);
  524. }