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.

771 lines
20 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. //
  37. // General arrary count.
  38. //
  39. #ifndef COUNTOF
  40. #define COUNTOF(s) ( sizeof( (s) ) / sizeof( *(s) ) )
  41. #endif // COUNTOF
  42. DWORD debug = 0;
  43. BOOL
  44. AddSpn(
  45. PUNICODE_STRING Service,
  46. PUNICODE_STRING Server
  47. );
  48. BOOL
  49. FindDomainForAccount(
  50. PUNICODE_STRING Server,
  51. PUNICODE_STRING DomainToCheck,
  52. PUNICODE_STRING Domain,
  53. PUNICODE_STRING DC
  54. )
  55. {
  56. ULONG NetStatus ;
  57. PDOMAIN_CONTROLLER_INFO DcInfo ;
  58. ULONG DomainNameLength;
  59. if ( Server->MaximumLength - Server->Length < 2 * sizeof( WCHAR ))
  60. {
  61. fprintf( stderr, "FindDomainForAccount: Server name too long\n" );
  62. return FALSE;
  63. }
  64. Server->Buffer[Server->Length / sizeof( WCHAR )] = L'$';
  65. Server->Length += sizeof( WCHAR );
  66. Server->Buffer[Server->Length / sizeof( WCHAR )] = L'\0';
  67. NetStatus = DsGetDcNameWithAccountW(
  68. NULL,
  69. Server->Buffer,
  70. UF_ACCOUNT_TYPE_MASK,
  71. DomainToCheck->Buffer,
  72. NULL,
  73. NULL,
  74. DS_DIRECTORY_SERVICE_REQUIRED |
  75. DS_RETURN_FLAT_NAME,
  76. &DcInfo );
  77. if ( NetStatus != 0 )
  78. {
  79. Server->Length -= sizeof( WCHAR );
  80. Server->Buffer[Server->Length / sizeof( WCHAR )] = L'\0';
  81. NetStatus = DsGetDcNameWithAccountW(
  82. NULL,
  83. Server->Buffer,
  84. UF_ACCOUNT_TYPE_MASK,
  85. DomainToCheck->Buffer,
  86. NULL,
  87. NULL,
  88. DS_DIRECTORY_SERVICE_REQUIRED |
  89. DS_RETURN_FLAT_NAME,
  90. &DcInfo );
  91. if ( NetStatus != 0 )
  92. {
  93. fprintf( stderr, "FindDomainForAccount: DsGetDcNameWithAccountW failed!\n" );
  94. return FALSE;
  95. }
  96. }
  97. DomainNameLength = wcslen( DcInfo->DomainName );
  98. if ( DomainNameLength * sizeof( WCHAR ) >= Domain->MaximumLength )
  99. {
  100. fprintf( stderr, "FindDomainForAccount: Domain name too short\n" );
  101. NetApiBufferFree( DcInfo );
  102. return FALSE;
  103. }
  104. wcscpy( Domain->Buffer, DcInfo->DomainName );
  105. Domain->Length = (USHORT)DomainNameLength * sizeof ( WCHAR );
  106. if (DC)
  107. {
  108. ULONG DcLength = wcslen( &DcInfo->DomainControllerName[2] );
  109. if ( DcLength * sizeof( WCHAR ) >= DC->MaximumLength )
  110. {
  111. fprintf( stderr, "FindDomainForAccount: DC name too short\n" );
  112. NetApiBufferFree( DcInfo );
  113. return FALSE;
  114. }
  115. wcscpy( DC->Buffer, &DcInfo->DomainControllerName[2] );
  116. DC->Length = (USHORT)DcLength * sizeof( WCHAR );
  117. }
  118. NetApiBufferFree( DcInfo );
  119. return TRUE ;
  120. }
  121. BOOL
  122. AddSpn(
  123. PUNICODE_STRING Service,
  124. PUNICODE_STRING Server
  125. )
  126. {
  127. WCHAR DomainBuffer[ MAX_PATH ];
  128. UNICODE_STRING Domain;
  129. WCHAR FlatName[ 2 * MAX_PATH + 2 ];
  130. WCHAR HostSpn[ 3 * MAX_PATH + 3 ];
  131. WCHAR FlatSpn[ 2 * MAX_PATH + 2 ];
  132. HANDLE hDs ;
  133. ULONG NetStatus ;
  134. PDS_NAME_RESULT Result ;
  135. LPWSTR Spns[2];
  136. UNICODE_STRING EmptyString;
  137. LPWSTR Flat = FlatName;
  138. RtlInitUnicodeString( &EmptyString, L"" );
  139. Domain.Length = 0;
  140. Domain.MaximumLength = sizeof( DomainBuffer );
  141. Domain.Buffer = DomainBuffer;
  142. if ( !FindDomainForAccount( Server, &EmptyString, &Domain, NULL ))
  143. {
  144. fprintf( stderr, "Could not find account %ws\n", Server->Buffer );
  145. return FALSE;
  146. }
  147. if ( Domain.Length + sizeof( WCHAR ) + Server->Length >= sizeof( FlatName ))
  148. {
  149. fprintf( stderr, "AddSpn: FlatName too short\n" );
  150. return FALSE;
  151. }
  152. _snwprintf(
  153. FlatName,
  154. sizeof( FlatName ) / sizeof( WCHAR ),
  155. L"%s\\%s",
  156. Domain.Buffer, Server->Buffer
  157. );
  158. // _snwprintf does not necessarily NULL-terminate its output
  159. FlatName[ sizeof( FlatName ) / sizeof( WCHAR ) - 1] = L'\0';
  160. NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
  161. if ( NetStatus != 0 )
  162. {
  163. fprintf( stderr, "Failed to bind to DC of domain %ws, %#x\n", Domain.Buffer, NetStatus );
  164. return FALSE ;
  165. }
  166. NetStatus = DsCrackNames(
  167. hDs,
  168. 0,
  169. DS_NT4_ACCOUNT_NAME,
  170. DS_FQDN_1779_NAME,
  171. 1,
  172. &Flat,
  173. &Result );
  174. if ( NetStatus != 0 ||
  175. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  176. Result->cItems != 1)
  177. {
  178. fprintf(stderr,
  179. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  180. FlatName, NetStatus, Result->cItems,
  181. (Result->cItems==1)?Result->rItems[0].status:-1 );
  182. DsUnBind( &hDs );
  183. return FALSE ;
  184. }
  185. if ( Service->Length + Server->Length + Domain.Length + 4 >= sizeof( HostSpn ))
  186. {
  187. fprintf( stderr, "AddSpn: HostSpn too short\n" );
  188. DsUnBind( &hDs );
  189. return FALSE;
  190. }
  191. _snwprintf(
  192. HostSpn,
  193. sizeof( HostSpn ) / sizeof( WCHAR ),
  194. L"%s/%s.%s",
  195. Service->Buffer,
  196. Server->Buffer,
  197. Domain.Buffer
  198. );
  199. // _snwprintf does not necessarily NULL-terminate its output
  200. HostSpn[sizeof( HostSpn ) / sizeof( WCHAR ) - 1] = L'\0';
  201. if ( Service->Length + 2 + Server->Length >= sizeof( FlatSpn ))
  202. {
  203. fprintf( stderr, "AddSpn: FlatSpn too short\n" );
  204. DsUnBind( &hDs );
  205. return FALSE;
  206. }
  207. _snwprintf(
  208. FlatSpn,
  209. sizeof( FlatSpn ) / sizeof( WCHAR ),
  210. L"%s/%s",
  211. Service->Buffer,
  212. Server->Buffer
  213. );
  214. // _snwprintf does not necessarily NULL-terminate its output
  215. FlatSpn[sizeof( FlatSpn ) / sizeof( WCHAR ) - 1] = L'\0';
  216. Spns[0] = HostSpn;
  217. Spns[1] = FlatSpn;
  218. printf("Registering ServicePrincipalNames for %ws\n", Result->rItems[0].pName);
  219. printf("\t%ws\n", HostSpn);
  220. printf("\t%ws\n", FlatSpn);
  221. #if 0
  222. printf("DsWriteAccountSpn: Commented out\n");
  223. #else
  224. NetStatus = DsWriteAccountSpn(
  225. hDs,
  226. DS_SPN_ADD_SPN_OP,
  227. Result->rItems[0].pName,
  228. 2,
  229. Spns );
  230. if ( NetStatus != 0 )
  231. {
  232. fprintf(stderr,
  233. "Failed to assign SPN to account '%ws', %#x\n",
  234. Result->rItems[0].pName, NetStatus );
  235. return FALSE;
  236. }
  237. #endif
  238. DsFreeNameResult( Result );
  239. DsUnBind( &hDs );
  240. return NetStatus == 0 ;
  241. }
  242. // added by jaroslad on 09/22/99
  243. BOOL
  244. AddRemoveSpn(
  245. PUNICODE_STRING HostSpn,
  246. PUNICODE_STRING HostDomain,
  247. PUNICODE_STRING Server,
  248. DS_SPN_WRITE_OP Operation
  249. )
  250. {
  251. WCHAR DomainBuffer[ MAX_PATH ];
  252. UNICODE_STRING Domain;
  253. WCHAR FlatName[ 2 * MAX_PATH + 2 ];
  254. HANDLE hDs ;
  255. ULONG NetStatus ;
  256. PDS_NAME_RESULT Result ;
  257. LPWSTR Spns[2];
  258. LPWSTR Flat = FlatName;
  259. Domain.Length = 0;
  260. Domain.MaximumLength = sizeof( DomainBuffer );
  261. Domain.Buffer = DomainBuffer;
  262. if ( !FindDomainForAccount( Server, HostDomain, &Domain, NULL ))
  263. {
  264. fprintf(stderr,
  265. "Unable to locate account %ws\n", Server->Buffer);
  266. return FALSE ;
  267. }
  268. if ( Domain.Length + Server->Length + sizeof( WCHAR ) >= sizeof( FlatName ))
  269. {
  270. fprintf( stderr, "AddRemoveSpn: FlatName too short\n" );
  271. return FALSE;
  272. }
  273. _snwprintf(
  274. FlatName,
  275. sizeof( FlatName ) / sizeof( WCHAR ),
  276. L"%s\\%s",
  277. Domain.Buffer,
  278. Server->Buffer
  279. );
  280. NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
  281. if ( NetStatus != 0 )
  282. {
  283. fprintf(stderr,
  284. "Failed to bind to DC of domain %ws, %#x\n",
  285. Domain.Buffer, NetStatus );
  286. return FALSE ;
  287. }
  288. NetStatus = DsCrackNames(
  289. hDs,
  290. 0,
  291. DS_NT4_ACCOUNT_NAME,
  292. DS_FQDN_1779_NAME,
  293. 1,
  294. &Flat,
  295. &Result );
  296. if ( NetStatus != 0 ||
  297. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  298. Result->cItems != 1)
  299. {
  300. fprintf(stderr,
  301. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  302. FlatName, NetStatus, Result->cItems,
  303. (Result->cItems==1)?Result->rItems[0].status:-1 );
  304. DsUnBind( &hDs );
  305. return FALSE ;
  306. }
  307. Spns[0] = HostSpn->Buffer;
  308. printf("%s ServicePrincipalNames for %ws\n",
  309. (Operation==DS_SPN_DELETE_SPN_OP)?"Unregistering":"Registering", Result->rItems[0].pName);
  310. printf("\t%ws\n", HostSpn->Buffer);
  311. #if 0
  312. printf("DsWriteAccountSpn: Commented out\n");
  313. #else
  314. NetStatus = DsWriteAccountSpn(
  315. hDs,
  316. Operation,
  317. Result->rItems[0].pName,
  318. 1,
  319. Spns );
  320. if ( NetStatus != 0 )
  321. {
  322. fprintf(stderr,
  323. "Failed to %s SPN on account '%ws', %#x\n",
  324. (Operation==DS_SPN_DELETE_SPN_OP)?"remove":"assign",
  325. Result->rItems[0].pName, NetStatus );
  326. DsUnBind( &hDs );
  327. return FALSE;
  328. }
  329. #endif
  330. DsFreeNameResult( Result );
  331. DsUnBind( &hDs );
  332. return NetStatus == 0 ;
  333. }
  334. BOOL
  335. LookupHostSpn(
  336. PUNICODE_STRING ServerDomain,
  337. PUNICODE_STRING Server
  338. )
  339. {
  340. WCHAR FlatName[ MAX_PATH + 1 ] = {0};
  341. HANDLE hDs ;
  342. ULONG NetStatus ;
  343. PDS_NAME_RESULT Result ;
  344. LDAP *ld;
  345. int rc;
  346. LDAPMessage *e, *res = NULL;
  347. WCHAR *base_dn;
  348. WCHAR *search_dn, search_ava[256];
  349. WCHAR DomainBuffer[ MAX_PATH ];
  350. UNICODE_STRING Domain;
  351. WCHAR DcBuffer[ MAX_PATH ];
  352. UNICODE_STRING DC;
  353. LPWSTR Flat = FlatName;
  354. Domain.Length = 0;
  355. Domain.MaximumLength = sizeof( DomainBuffer );
  356. Domain.Buffer = DomainBuffer;
  357. DC.Length = 0;
  358. DC.MaximumLength = sizeof( DcBuffer );
  359. DC.Buffer = DcBuffer;
  360. if ( !FindDomainForAccount( Server, ServerDomain, &Domain, &DC ))
  361. {
  362. fprintf(stderr, "Cannot find account %ws\n", Server->Buffer);
  363. return FALSE ;
  364. }
  365. if (debug)
  366. printf("Domain=%ws DC=%ws\n", Domain.Buffer, DC.Buffer);
  367. ld = ldap_open(DC.Buffer, LDAP_PORT);
  368. if (ld == NULL) {
  369. fprintf(stderr, "ldap_init failed = %x", LdapGetLastError());
  370. return FALSE;
  371. }
  372. rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
  373. if (rc != LDAP_SUCCESS) {
  374. fprintf(stderr, "ldap_bind failed = %x", LdapGetLastError());
  375. ldap_unbind(ld);
  376. return FALSE;
  377. }
  378. NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
  379. if ( NetStatus != 0 )
  380. {
  381. fprintf(stderr, "Failed to bind to DC of domain %ws, %#x\n",
  382. Domain.Buffer, NetStatus );
  383. ldap_unbind(ld);
  384. return FALSE ;
  385. }
  386. if ( Domain.Length + sizeof( WCHAR ) + Server->Length >= sizeof( FlatName ))
  387. {
  388. fprintf( stderr, "LookupHostSpn: FlatName too short\n" );
  389. ldap_unbind(ld);
  390. return FALSE;
  391. }
  392. _snwprintf(
  393. FlatName,
  394. sizeof( FlatName ) / sizeof( WCHAR ),
  395. L"%s\\%s",
  396. Domain.Buffer,
  397. Server->Buffer
  398. );
  399. // _snwprintf does not necessarily NULL-terminate its output
  400. FlatName[sizeof( FlatName ) / sizeof( WCHAR ) - 1] = L'\0';
  401. NetStatus = DsCrackNames(
  402. hDs,
  403. 0,
  404. DS_NT4_ACCOUNT_NAME,
  405. DS_FQDN_1779_NAME,
  406. 1,
  407. &Flat,
  408. &Result );
  409. if ( NetStatus != 0 ||
  410. Result->rItems[0].status != DS_NAME_NO_ERROR ||
  411. Result->cItems != 1)
  412. {
  413. if (Result->rItems[0].status == DS_NAME_ERROR_NOT_FOUND)
  414. {
  415. fprintf(stderr,
  416. "Requested name \"%ws\" not found in directory.\n",
  417. FlatName);
  418. }
  419. else if (Result->rItems[0].status == DS_NAME_ERROR_NOT_UNIQUE)
  420. {
  421. fprintf(stderr,
  422. "Requested name \"%ws\" not unique in directory.\n",
  423. FlatName);
  424. }
  425. else
  426. fprintf(stderr,
  427. "Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
  428. FlatName, NetStatus, Result->cItems,
  429. (Result->cItems==1)?Result->rItems[0].status:-1 );
  430. DsUnBind( &hDs );
  431. return FALSE ;
  432. }
  433. search_dn = Server->Buffer;
  434. base_dn = StrChr(Result->rItems[0].pName, L',');
  435. if (!base_dn)
  436. base_dn = Result->rItems[0].pName;
  437. else
  438. base_dn++;
  439. if (debug) {
  440. printf("BASE_DN=%S\n", base_dn);
  441. printf("SEARCH_DN=%S\n", search_dn);
  442. }
  443. DsUnBind( &hDs );
  444. _snwprintf(
  445. search_ava,
  446. sizeof( search_ava ) / sizeof( WCHAR ),
  447. L"(sAMAccountName=%s)",
  448. search_dn);
  449. // _snwprintf does not necessarily NULL-terminate its output
  450. search_ava[sizeof( search_ava ) / sizeof( WCHAR ) - 1] = L'\0';
  451. if (debug)
  452. printf("FILTER=\"%S\"\n", search_ava);
  453. rc = ldap_search_s(ld, base_dn, LDAP_SCOPE_SUBTREE,
  454. search_ava, NULL, 0, &res);
  455. DsFreeNameResult( Result );
  456. if (rc != LDAP_SUCCESS) {
  457. fprintf(stderr, "ldap_search_s failed: %S", ldap_err2string(rc));
  458. if ( res ) {
  459. ldap_msgfree(res);
  460. }
  461. ldap_unbind(ld);
  462. return 1;
  463. }
  464. for (e = ldap_first_entry(ld, res);
  465. e;
  466. e = ldap_next_entry(ld, e)) {
  467. BerElement *b;
  468. WCHAR *attr;
  469. WCHAR *dn = ldap_get_dn(ld, res);
  470. printf("Registered ServicePrincipalNames");
  471. if (dn)
  472. printf(" for %S", dn);
  473. printf(":\n");
  474. ldap_memfree(dn);
  475. for (attr = ldap_first_attribute(ld, e, &b);
  476. attr;
  477. attr = ldap_next_attribute(ld, e, b)) {
  478. WCHAR **values, **p;
  479. values = ldap_get_values(ld, e, attr);
  480. for (p = values; *p; p++) {
  481. if (StrCmp(attr, L"servicePrincipalName") == 0)
  482. printf(" %S\n", *p);
  483. }
  484. ldap_value_free(values);
  485. ldap_memfree(attr);
  486. }
  487. //ber_free(b, 1);
  488. }
  489. ldap_msgfree(res);
  490. ldap_unbind(ld);
  491. return TRUE;
  492. }
  493. void Usage( PWSTR name)
  494. {
  495. printf("\
  496. Usage: %S [switches data] computername \n\
  497. Where \"computername\" can be the name or domain\\name\n\
  498. \n\
  499. Switches:\n\
  500. -R = reset HOST ServicePrincipalName\n\
  501. Usage: setspn -R computername\n\
  502. -A = add arbitrary SPN \n\
  503. Usage: setspn -A SPN computername\n\
  504. -D = delete arbitrary SPN \n\
  505. Usage: setspn -D SPN computername\n\
  506. -L = list registered SPNs \n\
  507. Usage: setspn [-L] computername \n\
  508. Examples: \n\
  509. setspn -R daserver1 \n\
  510. It will register SPN \"HOST/daserver1\" and \"HOST/{DNS of daserver1}\" \n\
  511. setspn -A http/daserver daserver1 \n\
  512. It will register SPN \"http/daserver\" for computer \"daserver1\" \n\
  513. setspn -D http/daserver daserver1 \n\
  514. It will delete SPN \"http/daserver\" for computer \"daserver1\" \n\
  515. ", name);
  516. ExitProcess(0);
  517. }
  518. void __cdecl wmain (int argc, wchar_t *argv[])
  519. {
  520. int resetSPN = FALSE, addSPN = FALSE, deleteSPN = FALSE, listSPN = TRUE;
  521. UNICODE_STRING Service, Host,HostSpn, HostDomain ;
  522. wchar_t *ptr;
  523. int i;
  524. DS_SPN_WRITE_OP Operation;
  525. PWSTR Scan;
  526. DWORD Status = 1;
  527. for (i = 1; i < argc; i++)
  528. {
  529. if ((argv[i][0] == L'-') || (argv[i][0] == L'/'))
  530. {
  531. for (ptr = (argv[i] + 1); *ptr; ptr++)
  532. {
  533. switch(towupper(*ptr))
  534. {
  535. case L'R':
  536. resetSPN = TRUE;
  537. break;
  538. case L'A':
  539. addSPN = TRUE;
  540. break;
  541. case L'D':
  542. deleteSPN = TRUE;
  543. break;
  544. case L'L':
  545. listSPN = TRUE;
  546. break;
  547. case L'V':
  548. debug = TRUE;
  549. break;
  550. case L'?':
  551. default:
  552. Usage(argv[0]);
  553. break;
  554. }
  555. }
  556. }
  557. else
  558. break;
  559. }
  560. if ( resetSPN )
  561. {
  562. UNICODE_STRING Service, Server;
  563. WCHAR ServerBuffer[MAX_PATH];
  564. if ( ( argc - i ) != 1 )
  565. {
  566. Usage( argv[0] );
  567. }
  568. wcsncpy( ServerBuffer, argv[i], MAX_PATH-1 );
  569. ServerBuffer[MAX_PATH-2] = L'\0'; // leave space for trailing $
  570. RtlInitUnicodeString( &Service, L"HOST" );
  571. RtlInitUnicodeString( &Server, ServerBuffer );
  572. Server.MaximumLength = MAX_PATH;
  573. if ( AddSpn( &Service, &Server ))
  574. {
  575. printf("Updated object\n");
  576. Status = 0;
  577. }
  578. }
  579. else if ( addSPN || deleteSPN )
  580. {
  581. WCHAR HostBuffer[MAX_PATH];
  582. if ( ( argc - i ) != 2 )
  583. {
  584. Usage( argv[0] );
  585. }
  586. RtlInitUnicodeString( &HostSpn, argv[i] );
  587. Scan = argv[ i + 1 ];
  588. if ( Scan = wcschr( Scan, L'\\' ) )
  589. {
  590. *Scan++ = L'\0';
  591. RtlInitUnicodeString( &HostDomain, argv[i+1] );
  592. wcsncpy( HostBuffer, Scan, MAX_PATH-1 );
  593. HostBuffer[MAX_PATH-2] = L'\0';
  594. RtlInitUnicodeString( &Host, HostBuffer );
  595. Host.MaximumLength = MAX_PATH;
  596. }
  597. else
  598. {
  599. RtlInitUnicodeString( &HostDomain, L"" );
  600. wcsncpy( HostBuffer, argv[i+1], MAX_PATH-1 );
  601. HostBuffer[MAX_PATH-2] = L'\0';
  602. RtlInitUnicodeString( &Host, HostBuffer );
  603. Host.MaximumLength = MAX_PATH;
  604. }
  605. if ( addSPN )
  606. {
  607. Operation = DS_SPN_ADD_SPN_OP;
  608. }
  609. if ( deleteSPN )
  610. {
  611. Operation = DS_SPN_DELETE_SPN_OP;
  612. }
  613. if ( AddRemoveSpn( &HostSpn, &HostDomain, &Host, Operation) )
  614. {
  615. printf("Updated object\n");
  616. Status = 0;
  617. }
  618. }
  619. else if ( listSPN )
  620. {
  621. WCHAR HostBuffer[MAX_PATH];
  622. if ( ( argc - i ) != 1 )
  623. {
  624. Usage( argv[0] );
  625. }
  626. Scan = argv[ i ];
  627. if ( Scan = wcschr( Scan, L'\\' ) )
  628. {
  629. *Scan++ = L'\0';
  630. RtlInitUnicodeString( &HostDomain, argv[i] );
  631. wcsncpy( HostBuffer, Scan, MAX_PATH-1 );
  632. HostBuffer[MAX_PATH-2] = L'\0';
  633. RtlInitUnicodeString( &Host, HostBuffer );
  634. Host.MaximumLength = MAX_PATH;
  635. }
  636. else
  637. {
  638. RtlInitUnicodeString( &HostDomain, L"" );
  639. wcsncpy( HostBuffer, argv[i], MAX_PATH-1 );
  640. HostBuffer[MAX_PATH-2] = L'\0';
  641. RtlInitUnicodeString( &Host, HostBuffer );
  642. Host.MaximumLength = MAX_PATH;
  643. }
  644. if (LookupHostSpn( &HostDomain, &Host ))
  645. {
  646. Status = 0;
  647. }
  648. }
  649. ExitProcess(Status);
  650. }