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

//+---------------------------------------------------------------------------
//
// 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);
}