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
771 lines
20 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1997.
|
|
//
|
|
// File: setspn.c
|
|
//
|
|
// Contents:
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 7-30-98 RichardW Created
|
|
// 8-10-99 JBrezak Turned into setspn added list capability
|
|
// 09-22-99 Jaroslad support for adding/removing arbitrary SPNs
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#define SECURITY_WIN32
|
|
#include <rpc.h>
|
|
#include <sspi.h>
|
|
#include <secext.h>
|
|
#include <lm.h>
|
|
#include <winsock2.h>
|
|
#include <dsgetdc.h>
|
|
#include <dsgetdcp.h>
|
|
#include <ntdsapi.h>
|
|
#include <ntdsapip.h> // private DS_NAME_FORMATS
|
|
#include <stdio.h>
|
|
#include <winldap.h>
|
|
#include <shlwapi.h>
|
|
|
|
//
|
|
// General arrary count.
|
|
//
|
|
|
|
#ifndef COUNTOF
|
|
#define COUNTOF(s) ( sizeof( (s) ) / sizeof( *(s) ) )
|
|
#endif // COUNTOF
|
|
|
|
DWORD debug = 0;
|
|
|
|
BOOL
|
|
AddSpn(
|
|
PUNICODE_STRING Service,
|
|
PUNICODE_STRING Server
|
|
);
|
|
|
|
BOOL
|
|
FindDomainForAccount(
|
|
PUNICODE_STRING Server,
|
|
PUNICODE_STRING DomainToCheck,
|
|
PUNICODE_STRING Domain,
|
|
PUNICODE_STRING DC
|
|
)
|
|
{
|
|
ULONG NetStatus ;
|
|
PDOMAIN_CONTROLLER_INFO DcInfo ;
|
|
ULONG DomainNameLength;
|
|
|
|
if ( Server->MaximumLength - Server->Length < 2 * sizeof( WCHAR ))
|
|
{
|
|
fprintf( stderr, "FindDomainForAccount: Server name too long\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
Server->Buffer[Server->Length / sizeof( WCHAR )] = L'$';
|
|
Server->Length += sizeof( WCHAR );
|
|
Server->Buffer[Server->Length / sizeof( WCHAR )] = L'\0';
|
|
|
|
NetStatus = DsGetDcNameWithAccountW(
|
|
NULL,
|
|
Server->Buffer,
|
|
UF_ACCOUNT_TYPE_MASK,
|
|
DomainToCheck->Buffer,
|
|
NULL,
|
|
NULL,
|
|
DS_DIRECTORY_SERVICE_REQUIRED |
|
|
DS_RETURN_FLAT_NAME,
|
|
&DcInfo );
|
|
|
|
if ( NetStatus != 0 )
|
|
{
|
|
Server->Length -= sizeof( WCHAR );
|
|
Server->Buffer[Server->Length / sizeof( WCHAR )] = L'\0';
|
|
|
|
NetStatus = DsGetDcNameWithAccountW(
|
|
NULL,
|
|
Server->Buffer,
|
|
UF_ACCOUNT_TYPE_MASK,
|
|
DomainToCheck->Buffer,
|
|
NULL,
|
|
NULL,
|
|
DS_DIRECTORY_SERVICE_REQUIRED |
|
|
DS_RETURN_FLAT_NAME,
|
|
&DcInfo );
|
|
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf( stderr, "FindDomainForAccount: DsGetDcNameWithAccountW failed!\n" );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
DomainNameLength = wcslen( DcInfo->DomainName );
|
|
|
|
if ( DomainNameLength * sizeof( WCHAR ) >= Domain->MaximumLength )
|
|
{
|
|
fprintf( stderr, "FindDomainForAccount: Domain name too short\n" );
|
|
NetApiBufferFree( DcInfo );
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( Domain->Buffer, DcInfo->DomainName );
|
|
Domain->Length = (USHORT)DomainNameLength * sizeof ( WCHAR );
|
|
|
|
if (DC)
|
|
{
|
|
ULONG DcLength = wcslen( &DcInfo->DomainControllerName[2] );
|
|
|
|
if ( DcLength * sizeof( WCHAR ) >= DC->MaximumLength )
|
|
{
|
|
fprintf( stderr, "FindDomainForAccount: DC name too short\n" );
|
|
NetApiBufferFree( DcInfo );
|
|
return FALSE;
|
|
}
|
|
|
|
wcscpy( DC->Buffer, &DcInfo->DomainControllerName[2] );
|
|
DC->Length = (USHORT)DcLength * sizeof( WCHAR );
|
|
}
|
|
|
|
NetApiBufferFree( DcInfo );
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
BOOL
|
|
AddSpn(
|
|
PUNICODE_STRING Service,
|
|
PUNICODE_STRING Server
|
|
)
|
|
{
|
|
WCHAR DomainBuffer[ MAX_PATH ];
|
|
UNICODE_STRING Domain;
|
|
WCHAR FlatName[ 2 * MAX_PATH + 2 ];
|
|
WCHAR HostSpn[ 3 * MAX_PATH + 3 ];
|
|
WCHAR FlatSpn[ 2 * MAX_PATH + 2 ];
|
|
HANDLE hDs ;
|
|
ULONG NetStatus ;
|
|
PDS_NAME_RESULT Result ;
|
|
LPWSTR Spns[2];
|
|
UNICODE_STRING EmptyString;
|
|
LPWSTR Flat = FlatName;
|
|
|
|
RtlInitUnicodeString( &EmptyString, L"" );
|
|
|
|
Domain.Length = 0;
|
|
Domain.MaximumLength = sizeof( DomainBuffer );
|
|
Domain.Buffer = DomainBuffer;
|
|
|
|
if ( !FindDomainForAccount( Server, &EmptyString, &Domain, NULL ))
|
|
{
|
|
fprintf( stderr, "Could not find account %ws\n", Server->Buffer );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( Domain.Length + sizeof( WCHAR ) + Server->Length >= sizeof( FlatName ))
|
|
{
|
|
fprintf( stderr, "AddSpn: FlatName too short\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
_snwprintf(
|
|
FlatName,
|
|
sizeof( FlatName ) / sizeof( WCHAR ),
|
|
L"%s\\%s",
|
|
Domain.Buffer, Server->Buffer
|
|
);
|
|
|
|
// _snwprintf does not necessarily NULL-terminate its output
|
|
FlatName[ sizeof( FlatName ) / sizeof( WCHAR ) - 1] = L'\0';
|
|
|
|
NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
|
|
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf( stderr, "Failed to bind to DC of domain %ws, %#x\n", Domain.Buffer, NetStatus );
|
|
return FALSE ;
|
|
}
|
|
|
|
NetStatus = DsCrackNames(
|
|
hDs,
|
|
0,
|
|
DS_NT4_ACCOUNT_NAME,
|
|
DS_FQDN_1779_NAME,
|
|
1,
|
|
&Flat,
|
|
&Result );
|
|
|
|
if ( NetStatus != 0 ||
|
|
Result->rItems[0].status != DS_NAME_NO_ERROR ||
|
|
Result->cItems != 1)
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
|
|
FlatName, NetStatus, Result->cItems,
|
|
(Result->cItems==1)?Result->rItems[0].status:-1 );
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
if ( Service->Length + Server->Length + Domain.Length + 4 >= sizeof( HostSpn ))
|
|
{
|
|
fprintf( stderr, "AddSpn: HostSpn too short\n" );
|
|
DsUnBind( &hDs );
|
|
return FALSE;
|
|
}
|
|
|
|
_snwprintf(
|
|
HostSpn,
|
|
sizeof( HostSpn ) / sizeof( WCHAR ),
|
|
L"%s/%s.%s",
|
|
Service->Buffer,
|
|
Server->Buffer,
|
|
Domain.Buffer
|
|
);
|
|
|
|
// _snwprintf does not necessarily NULL-terminate its output
|
|
HostSpn[sizeof( HostSpn ) / sizeof( WCHAR ) - 1] = L'\0';
|
|
|
|
if ( Service->Length + 2 + Server->Length >= sizeof( FlatSpn ))
|
|
{
|
|
fprintf( stderr, "AddSpn: FlatSpn too short\n" );
|
|
DsUnBind( &hDs );
|
|
return FALSE;
|
|
}
|
|
|
|
_snwprintf(
|
|
FlatSpn,
|
|
sizeof( FlatSpn ) / sizeof( WCHAR ),
|
|
L"%s/%s",
|
|
Service->Buffer,
|
|
Server->Buffer
|
|
);
|
|
|
|
// _snwprintf does not necessarily NULL-terminate its output
|
|
FlatSpn[sizeof( FlatSpn ) / sizeof( WCHAR ) - 1] = L'\0';
|
|
|
|
Spns[0] = HostSpn;
|
|
Spns[1] = FlatSpn;
|
|
|
|
printf("Registering ServicePrincipalNames for %ws\n", Result->rItems[0].pName);
|
|
printf("\t%ws\n", HostSpn);
|
|
printf("\t%ws\n", FlatSpn);
|
|
|
|
#if 0
|
|
printf("DsWriteAccountSpn: Commented out\n");
|
|
#else
|
|
NetStatus = DsWriteAccountSpn(
|
|
hDs,
|
|
DS_SPN_ADD_SPN_OP,
|
|
Result->rItems[0].pName,
|
|
2,
|
|
Spns );
|
|
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to assign SPN to account '%ws', %#x\n",
|
|
Result->rItems[0].pName, NetStatus );
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
DsFreeNameResult( Result );
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
return NetStatus == 0 ;
|
|
}
|
|
|
|
// added by jaroslad on 09/22/99
|
|
BOOL
|
|
AddRemoveSpn(
|
|
PUNICODE_STRING HostSpn,
|
|
PUNICODE_STRING HostDomain,
|
|
PUNICODE_STRING Server,
|
|
DS_SPN_WRITE_OP Operation
|
|
|
|
)
|
|
{
|
|
WCHAR DomainBuffer[ MAX_PATH ];
|
|
UNICODE_STRING Domain;
|
|
WCHAR FlatName[ 2 * MAX_PATH + 2 ];
|
|
HANDLE hDs ;
|
|
ULONG NetStatus ;
|
|
PDS_NAME_RESULT Result ;
|
|
LPWSTR Spns[2];
|
|
LPWSTR Flat = FlatName;
|
|
|
|
Domain.Length = 0;
|
|
Domain.MaximumLength = sizeof( DomainBuffer );
|
|
Domain.Buffer = DomainBuffer;
|
|
|
|
if ( !FindDomainForAccount( Server, HostDomain, &Domain, NULL ))
|
|
{
|
|
fprintf(stderr,
|
|
"Unable to locate account %ws\n", Server->Buffer);
|
|
return FALSE ;
|
|
}
|
|
|
|
if ( Domain.Length + Server->Length + sizeof( WCHAR ) >= sizeof( FlatName ))
|
|
{
|
|
fprintf( stderr, "AddRemoveSpn: FlatName too short\n" );
|
|
return FALSE;
|
|
}
|
|
|
|
_snwprintf(
|
|
FlatName,
|
|
sizeof( FlatName ) / sizeof( WCHAR ),
|
|
L"%s\\%s",
|
|
Domain.Buffer,
|
|
Server->Buffer
|
|
);
|
|
|
|
NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to bind to DC of domain %ws, %#x\n",
|
|
Domain.Buffer, NetStatus );
|
|
return FALSE ;
|
|
}
|
|
|
|
NetStatus = DsCrackNames(
|
|
hDs,
|
|
0,
|
|
DS_NT4_ACCOUNT_NAME,
|
|
DS_FQDN_1779_NAME,
|
|
1,
|
|
&Flat,
|
|
&Result );
|
|
|
|
if ( NetStatus != 0 ||
|
|
Result->rItems[0].status != DS_NAME_NO_ERROR ||
|
|
Result->cItems != 1)
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
|
|
FlatName, NetStatus, Result->cItems,
|
|
(Result->cItems==1)?Result->rItems[0].status:-1 );
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
Spns[0] = HostSpn->Buffer;
|
|
|
|
printf("%s ServicePrincipalNames for %ws\n",
|
|
(Operation==DS_SPN_DELETE_SPN_OP)?"Unregistering":"Registering", Result->rItems[0].pName);
|
|
printf("\t%ws\n", HostSpn->Buffer);
|
|
|
|
#if 0
|
|
printf("DsWriteAccountSpn: Commented out\n");
|
|
#else
|
|
NetStatus = DsWriteAccountSpn(
|
|
hDs,
|
|
Operation,
|
|
Result->rItems[0].pName,
|
|
1,
|
|
Spns );
|
|
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf(stderr,
|
|
"Failed to %s SPN on account '%ws', %#x\n",
|
|
(Operation==DS_SPN_DELETE_SPN_OP)?"remove":"assign",
|
|
Result->rItems[0].pName, NetStatus );
|
|
DsUnBind( &hDs );
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
DsFreeNameResult( Result );
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
return NetStatus == 0 ;
|
|
}
|
|
|
|
|
|
BOOL
|
|
LookupHostSpn(
|
|
PUNICODE_STRING ServerDomain,
|
|
PUNICODE_STRING Server
|
|
)
|
|
{
|
|
WCHAR FlatName[ MAX_PATH + 1 ] = {0};
|
|
HANDLE hDs ;
|
|
ULONG NetStatus ;
|
|
PDS_NAME_RESULT Result ;
|
|
LDAP *ld;
|
|
int rc;
|
|
LDAPMessage *e, *res = NULL;
|
|
WCHAR *base_dn;
|
|
WCHAR *search_dn, search_ava[256];
|
|
WCHAR DomainBuffer[ MAX_PATH ];
|
|
UNICODE_STRING Domain;
|
|
WCHAR DcBuffer[ MAX_PATH ];
|
|
UNICODE_STRING DC;
|
|
LPWSTR Flat = FlatName;
|
|
|
|
Domain.Length = 0;
|
|
Domain.MaximumLength = sizeof( DomainBuffer );
|
|
Domain.Buffer = DomainBuffer;
|
|
|
|
DC.Length = 0;
|
|
DC.MaximumLength = sizeof( DcBuffer );
|
|
DC.Buffer = DcBuffer;
|
|
|
|
if ( !FindDomainForAccount( Server, ServerDomain, &Domain, &DC ))
|
|
{
|
|
fprintf(stderr, "Cannot find account %ws\n", Server->Buffer);
|
|
return FALSE ;
|
|
}
|
|
|
|
if (debug)
|
|
printf("Domain=%ws DC=%ws\n", Domain.Buffer, DC.Buffer);
|
|
|
|
ld = ldap_open(DC.Buffer, LDAP_PORT);
|
|
if (ld == NULL) {
|
|
fprintf(stderr, "ldap_init failed = %x", LdapGetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
rc = ldap_bind_s(ld, NULL, NULL, LDAP_AUTH_NEGOTIATE);
|
|
if (rc != LDAP_SUCCESS) {
|
|
fprintf(stderr, "ldap_bind failed = %x", LdapGetLastError());
|
|
ldap_unbind(ld);
|
|
return FALSE;
|
|
}
|
|
|
|
NetStatus = DsBind( NULL, Domain.Buffer, &hDs );
|
|
if ( NetStatus != 0 )
|
|
{
|
|
fprintf(stderr, "Failed to bind to DC of domain %ws, %#x\n",
|
|
Domain.Buffer, NetStatus );
|
|
ldap_unbind(ld);
|
|
return FALSE ;
|
|
}
|
|
|
|
if ( Domain.Length + sizeof( WCHAR ) + Server->Length >= sizeof( FlatName ))
|
|
{
|
|
fprintf( stderr, "LookupHostSpn: FlatName too short\n" );
|
|
ldap_unbind(ld);
|
|
return FALSE;
|
|
}
|
|
|
|
_snwprintf(
|
|
FlatName,
|
|
sizeof( FlatName ) / sizeof( WCHAR ),
|
|
L"%s\\%s",
|
|
Domain.Buffer,
|
|
Server->Buffer
|
|
);
|
|
|
|
// _snwprintf does not necessarily NULL-terminate its output
|
|
FlatName[sizeof( FlatName ) / sizeof( WCHAR ) - 1] = L'\0';
|
|
|
|
NetStatus = DsCrackNames(
|
|
hDs,
|
|
0,
|
|
DS_NT4_ACCOUNT_NAME,
|
|
DS_FQDN_1779_NAME,
|
|
1,
|
|
&Flat,
|
|
&Result );
|
|
|
|
if ( NetStatus != 0 ||
|
|
Result->rItems[0].status != DS_NAME_NO_ERROR ||
|
|
Result->cItems != 1)
|
|
{
|
|
if (Result->rItems[0].status == DS_NAME_ERROR_NOT_FOUND)
|
|
{
|
|
fprintf(stderr,
|
|
"Requested name \"%ws\" not found in directory.\n",
|
|
FlatName);
|
|
}
|
|
else if (Result->rItems[0].status == DS_NAME_ERROR_NOT_UNIQUE)
|
|
{
|
|
fprintf(stderr,
|
|
"Requested name \"%ws\" not unique in directory.\n",
|
|
FlatName);
|
|
}
|
|
else
|
|
fprintf(stderr,
|
|
"Failed to crack name %ws into the FQDN, (%d) %d %#x\n",
|
|
FlatName, NetStatus, Result->cItems,
|
|
(Result->cItems==1)?Result->rItems[0].status:-1 );
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
return FALSE ;
|
|
}
|
|
|
|
search_dn = Server->Buffer;
|
|
|
|
base_dn = StrChr(Result->rItems[0].pName, L',');
|
|
if (!base_dn)
|
|
base_dn = Result->rItems[0].pName;
|
|
else
|
|
base_dn++;
|
|
|
|
if (debug) {
|
|
printf("BASE_DN=%S\n", base_dn);
|
|
printf("SEARCH_DN=%S\n", search_dn);
|
|
}
|
|
|
|
DsUnBind( &hDs );
|
|
|
|
_snwprintf(
|
|
search_ava,
|
|
sizeof( search_ava ) / sizeof( WCHAR ),
|
|
L"(sAMAccountName=%s)",
|
|
search_dn);
|
|
|
|
// _snwprintf does not necessarily NULL-terminate its output
|
|
search_ava[sizeof( search_ava ) / sizeof( WCHAR ) - 1] = L'\0';
|
|
|
|
if (debug)
|
|
printf("FILTER=\"%S\"\n", search_ava);
|
|
|
|
rc = ldap_search_s(ld, base_dn, LDAP_SCOPE_SUBTREE,
|
|
search_ava, NULL, 0, &res);
|
|
|
|
DsFreeNameResult( Result );
|
|
|
|
if (rc != LDAP_SUCCESS) {
|
|
fprintf(stderr, "ldap_search_s failed: %S", ldap_err2string(rc));
|
|
if ( res ) {
|
|
ldap_msgfree(res);
|
|
}
|
|
ldap_unbind(ld);
|
|
return 1;
|
|
}
|
|
|
|
for (e = ldap_first_entry(ld, res);
|
|
e;
|
|
e = ldap_next_entry(ld, e)) {
|
|
|
|
BerElement *b;
|
|
WCHAR *attr;
|
|
WCHAR *dn = ldap_get_dn(ld, res);
|
|
|
|
printf("Registered ServicePrincipalNames");
|
|
if (dn)
|
|
printf(" for %S", dn);
|
|
|
|
printf(":\n");
|
|
|
|
ldap_memfree(dn);
|
|
|
|
for (attr = ldap_first_attribute(ld, e, &b);
|
|
attr;
|
|
attr = ldap_next_attribute(ld, e, b)) {
|
|
|
|
WCHAR **values, **p;
|
|
values = ldap_get_values(ld, e, attr);
|
|
for (p = values; *p; p++) {
|
|
|
|
if (StrCmp(attr, L"servicePrincipalName") == 0)
|
|
printf(" %S\n", *p);
|
|
}
|
|
|
|
ldap_value_free(values);
|
|
ldap_memfree(attr);
|
|
}
|
|
|
|
//ber_free(b, 1);
|
|
}
|
|
|
|
ldap_msgfree(res);
|
|
ldap_unbind(ld);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void Usage( PWSTR name)
|
|
{
|
|
printf("\
|
|
Usage: %S [switches data] computername \n\
|
|
Where \"computername\" can be the name or domain\\name\n\
|
|
\n\
|
|
Switches:\n\
|
|
-R = reset HOST ServicePrincipalName\n\
|
|
Usage: setspn -R computername\n\
|
|
-A = add arbitrary SPN \n\
|
|
Usage: setspn -A SPN computername\n\
|
|
-D = delete arbitrary SPN \n\
|
|
Usage: setspn -D SPN computername\n\
|
|
-L = list registered SPNs \n\
|
|
Usage: setspn [-L] computername \n\
|
|
Examples: \n\
|
|
setspn -R daserver1 \n\
|
|
It will register SPN \"HOST/daserver1\" and \"HOST/{DNS of daserver1}\" \n\
|
|
setspn -A http/daserver daserver1 \n\
|
|
It will register SPN \"http/daserver\" for computer \"daserver1\" \n\
|
|
setspn -D http/daserver daserver1 \n\
|
|
It will delete SPN \"http/daserver\" for computer \"daserver1\" \n\
|
|
", name);
|
|
ExitProcess(0);
|
|
}
|
|
|
|
void __cdecl wmain (int argc, wchar_t *argv[])
|
|
{
|
|
int resetSPN = FALSE, addSPN = FALSE, deleteSPN = FALSE, listSPN = TRUE;
|
|
UNICODE_STRING Service, Host,HostSpn, HostDomain ;
|
|
wchar_t *ptr;
|
|
int i;
|
|
DS_SPN_WRITE_OP Operation;
|
|
PWSTR Scan;
|
|
DWORD Status = 1;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if ((argv[i][0] == L'-') || (argv[i][0] == L'/'))
|
|
{
|
|
for (ptr = (argv[i] + 1); *ptr; ptr++)
|
|
{
|
|
switch(towupper(*ptr))
|
|
{
|
|
case L'R':
|
|
resetSPN = TRUE;
|
|
break;
|
|
case L'A':
|
|
addSPN = TRUE;
|
|
break;
|
|
case L'D':
|
|
deleteSPN = TRUE;
|
|
break;
|
|
case L'L':
|
|
listSPN = TRUE;
|
|
break;
|
|
case L'V':
|
|
debug = TRUE;
|
|
break;
|
|
case L'?':
|
|
default:
|
|
Usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if ( resetSPN )
|
|
{
|
|
UNICODE_STRING Service, Server;
|
|
WCHAR ServerBuffer[MAX_PATH];
|
|
|
|
if ( ( argc - i ) != 1 )
|
|
{
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
wcsncpy( ServerBuffer, argv[i], MAX_PATH-1 );
|
|
ServerBuffer[MAX_PATH-2] = L'\0'; // leave space for trailing $
|
|
|
|
RtlInitUnicodeString( &Service, L"HOST" );
|
|
RtlInitUnicodeString( &Server, ServerBuffer );
|
|
Server.MaximumLength = MAX_PATH;
|
|
|
|
if ( AddSpn( &Service, &Server ))
|
|
{
|
|
printf("Updated object\n");
|
|
Status = 0;
|
|
}
|
|
}
|
|
else if ( addSPN || deleteSPN )
|
|
{
|
|
WCHAR HostBuffer[MAX_PATH];
|
|
|
|
if ( ( argc - i ) != 2 )
|
|
{
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
RtlInitUnicodeString( &HostSpn, argv[i] );
|
|
|
|
Scan = argv[ i + 1 ];
|
|
|
|
if ( Scan = wcschr( Scan, L'\\' ) )
|
|
{
|
|
*Scan++ = L'\0';
|
|
RtlInitUnicodeString( &HostDomain, argv[i+1] );
|
|
wcsncpy( HostBuffer, Scan, MAX_PATH-1 );
|
|
HostBuffer[MAX_PATH-2] = L'\0';
|
|
RtlInitUnicodeString( &Host, HostBuffer );
|
|
Host.MaximumLength = MAX_PATH;
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString( &HostDomain, L"" );
|
|
wcsncpy( HostBuffer, argv[i+1], MAX_PATH-1 );
|
|
HostBuffer[MAX_PATH-2] = L'\0';
|
|
RtlInitUnicodeString( &Host, HostBuffer );
|
|
Host.MaximumLength = MAX_PATH;
|
|
}
|
|
|
|
if ( addSPN )
|
|
{
|
|
Operation = DS_SPN_ADD_SPN_OP;
|
|
}
|
|
|
|
if ( deleteSPN )
|
|
{
|
|
Operation = DS_SPN_DELETE_SPN_OP;
|
|
}
|
|
|
|
if ( AddRemoveSpn( &HostSpn, &HostDomain, &Host, Operation) )
|
|
{
|
|
printf("Updated object\n");
|
|
Status = 0;
|
|
}
|
|
}
|
|
else if ( listSPN )
|
|
{
|
|
WCHAR HostBuffer[MAX_PATH];
|
|
|
|
if ( ( argc - i ) != 1 )
|
|
{
|
|
Usage( argv[0] );
|
|
}
|
|
|
|
Scan = argv[ i ];
|
|
|
|
if ( Scan = wcschr( Scan, L'\\' ) )
|
|
{
|
|
*Scan++ = L'\0';
|
|
RtlInitUnicodeString( &HostDomain, argv[i] );
|
|
wcsncpy( HostBuffer, Scan, MAX_PATH-1 );
|
|
HostBuffer[MAX_PATH-2] = L'\0';
|
|
RtlInitUnicodeString( &Host, HostBuffer );
|
|
Host.MaximumLength = MAX_PATH;
|
|
}
|
|
else
|
|
{
|
|
RtlInitUnicodeString( &HostDomain, L"" );
|
|
wcsncpy( HostBuffer, argv[i], MAX_PATH-1 );
|
|
HostBuffer[MAX_PATH-2] = L'\0';
|
|
RtlInitUnicodeString( &Host, HostBuffer );
|
|
Host.MaximumLength = MAX_PATH;
|
|
}
|
|
|
|
if (LookupHostSpn( &HostDomain, &Host ))
|
|
{
|
|
Status = 0;
|
|
}
|
|
}
|
|
|
|
ExitProcess(Status);
|
|
}
|