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.
 
 
 
 
 
 

610 lines
19 KiB

/*++
The original filename was created in RuiM's EFS common library.
I have since changed it severely.
* FileName: delegation.c
* Author: RuiM
* Copyright (c) 1998 Microsoft Corp.
*
CONTENTS: U(QueryAccountControlFlags)
U(SetAccountControlFlags)
U(LdapFindAttributeInMessage)
U(LdapSearchForUniqueDn)
--*/
#pragma warning(disable:4057) /* indirection to slightly different
base types. Useless warning that hits
thousands of times in this file. */
#pragma warning(disable:4221) /* allow nonstandard extension (automatic
initialization of a variable with
address of another automatic variable) */
#include "unimacro.h"
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntdef.h> // required to keep winbase.h from breaking
#include <ntpoapi.h> // required to keep winbase.h from breaking
#include <windows.h>
#include <winbase.h>
#include <lmaccess.h>
#include <winldap.h>
#include <tchar.h>
#include <stdlib.h>
#include <stdio.h>
#include "delegation.h"
#include "delegtools.h"
// These constants are required for queries below.
TCHAR U(SamAccountAttribute) [] = TEXT("samAccountName");
TCHAR U(UserAccountAttribute) [] = TEXT("userAccountControl");
TCHAR U(NamingContextAttribute)[] = TEXT("defaultNamingContext");
/*++**************************************************************
NAME: U(LdapFindAttributeInMessage)
This searches for a given attribute in a message (via
ldap_get_values_len) and returns the value. Note that this function
will fail if the attribute has multiple values.
MODIFIES: pcbData -- receives length of the data (in bytes)
ppvData -- receives pointer to the data
TAKES: pLdap -- ldap connection handle
pMessage -- message to search
PropertyName -- property to find in the message
RETURNS: TRUE when the function succeeds.
FALSE otherwise.
LASTERROR: not set
LOGGING: printf on error
CALLED BY: anyone
FREE WITH: ppvdata should be freed with free()
**************************************************************--*/
BOOL
U(LdapFindAttributeInMessage)( IN PLDAP pLdap,
IN PLDAPMessage pMessage,
IN LPTSTR PropertyName,
OUT OPTIONAL PULONG pcbData,
OUT OPTIONAL PVOID *ppvData ) {
PLDAP_BERVAL *ppBerVals;
BOOL ret = FALSE;
ppBerVals = ldap_get_values_len( pLdap,
pMessage,
PropertyName );
if ( ppBerVals ) {
if ( ppBerVals[ 0 ] == NULL ) {
printf( "ERROR: empty berval structure returned when parsing "
STRING_FMTA " attribute.\n",
PropertyName );
SetLastError( ERROR_INVALID_DATA );
} else if ( ppBerVals[ 1 ] != NULL ) {
printf( "ERROR: nonunique berval structure returned "
"when parsing " STRING_FMTA " attribute.\n",
PropertyName );
SetLastError( ERROR_DS_NAME_ERROR_NOT_UNIQUE );
} else {
/* this sequence is arranged in such a way that
the important stuff comes last, keeping us
from having to free ppvData after we've alloc'd it. */
ret = TRUE;
if ( pcbData ) {
*pcbData = ppBerVals[ 0 ]->bv_len;
}
if ( ppvData ) {
*ppvData = malloc( ppBerVals[ 0 ]->bv_len );
if ( *ppvData ) {
memcpy( *ppvData,
ppBerVals[ 0 ]->bv_val,
ppBerVals[ 0 ]->bv_len );
} else {
printf( "Failed to allocate %ld bytes.\n",
ppBerVals[ 0 ]->bv_len );
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
ret = FALSE;
}
}
}
ldap_value_free_len( ppBerVals );
} else {
printf( "Failed to retrieve values for property " STRING_FMTA
": 0x%x.\n",
PropertyName,
pLdap->ld_errno );
SetLastError( pLdap->ld_errno );
}
return ret;
}
/*++**************************************************************
NAME: U(LdapSearchForUniqueDn)
Searches the DS for a DN with a match for the given search term.
MODIFIES: pDnOfObject -- if requested, receives the object's DN
ppMessage -- if requested, receives the message data
TAKES: pLdap -- ldap handle returned by ldap_open
SearchTerm -- what to search, e.g. "(foo=bar)"
rzRequestedAttributes -- attributes to return in ppMessage
RETURNS: TRUE when the function succeeds.
FALSE otherwise or if the result is nonunique (WASBUG 73899).
LASTERROR: not set
LOGGING: printf on failure
CALLED BY: anyone
FREE WITH: free pDnOfObject with ldap_memfree
free ppMessage with ldap_msgfree
**************************************************************--*/
BOOL
U(LdapSearchForUniqueDn)( IN PLDAP pLdap,
IN LPTSTR SearchTerm,
IN LPTSTR *rzRequestedAttributes,
OUT OPTIONAL LPTSTR *pDnOfObject,
OUT OPTIONAL PLDAPMessage *ppMessage ) {
DWORD dwErr;
PLDAPMessage pMessage = NULL;
PLDAPMessage pResult = NULL;
LPTSTR pDn = NULL;
LPTSTR *ppAttrs = NULL;
BOOL ret = FALSE;
LPTSTR Attrs[] = { U(NamingContextAttribute), NULL };
/* First, determine the default naming context property for the base
of the DSA. */
dwErr = ldap_search_s( pLdap,
NULL,
LDAP_SCOPE_BASE,
TEXT("objectClass=*"),
Attrs,
FALSE,
&pResult );
if ( dwErr == LDAP_SUCCESS ) {
ppAttrs = ldap_get_values( pLdap,
pResult,
U(NamingContextAttribute) );
if ( ppAttrs ) {
dwErr = ldap_search_s( pLdap,
ppAttrs[ 0 ],
LDAP_SCOPE_SUBTREE, // search the whole tree
SearchTerm,
rzRequestedAttributes,
FALSE, // don't only return attr names
&pMessage );
/* ldap_search_s can return a whole bunch of potential
"success" errors. So, I'll check to see that pMessage
is nonnull. This may or may not be a good thing to do,
but it's bound to be safer than checking the error output. */
if ( pMessage != NULL ) {
// make sure the response is unique
if ( !ldap_first_entry( pLdap,
pMessage ) ) {
printf( "WARNING: search term \"" STRING_FMTA "\" "
"produced no results.\n",
SearchTerm );
} else if ( ldap_next_entry( pLdap,
ldap_first_entry( pLdap,
pMessage ) ) ) {
/* Nonunique search result. Warn the user and
drop out. */
PLDAPMessage p = pMessage;
ULONG i = 1;
printf( "WARNING: search term \"" STRING_FMTA "\" returns "
"multiple results (should be unique).\n"
"\n"
"The results follow:\n",
SearchTerm );
for ( p = ldap_first_entry( pLdap,
pMessage );
p != NULL ;
p = ldap_next_entry( pLdap,
p ),
i++ ) {
pDn = ldap_get_dn( pLdap,
p );
if ( !pDn ) {
printf( "%2ld. <Unknown DN: 0x%x>\n",
i,
pLdap->ld_errno );
} else {
printf( "%2ld. %hs\n",
i,
pDn );
ldap_memfreeA( pDn );
}
}
} else {
ret = TRUE; // go optimistic
if ( pDnOfObject ) {
pDn = ldap_get_dn( pLdap,
pMessage );
if ( pDn ) {
*pDnOfObject = pDn;
} else {
printf( "Failed to get DN from search result: 0x%x\n",
pLdap->ld_errno );
SetLastError( pLdap->ld_errno );
ret = FALSE;
}
}
if ( ret && ppMessage ) {
*ppMessage = pMessage;
pMessage = NULL;
}
}
if ( pMessage ) {
ldap_msgfree( pMessage );
}
} else {
printf( "FAILED: ldap_search_s failed for search term \""
STRING_FMTA "\": 0x%x",
SearchTerm,
dwErr );
SetLastError( dwErr );
}
} else {
printf( "FAILED: default naming context does not include"
" requisite attribute " STRING_FMTA ".\n",
U(NamingContextAttribute) );
SetLastError( ERROR_CLASS_DOES_NOT_EXIST );
}
ldap_msgfree( pResult );
} else {
printf( "FAILED: unable to query default naming context: 0x%x.\n",
dwErr );
SetLastError( dwErr );
}
return ret;
}
#pragma warning(disable:4100) /* unreferenced formal parameter */
BOOL
U(LdapQueryUlongAttribute)( IN OPTIONAL PLDAP pLdap,
IN OPTIONAL LPTSTR DomainName, // ignored
IN LPTSTR SamAccountName,
IN LPTSTR AttributeName,
OUT PULONG pulAttributeContents ) {
BOOL CloseLdap = FALSE;
BOOL ret = FALSE;
LPTSTR Query = NULL;
LPTSTR StringAttr = NULL;
LPTSTR ArrayOfAttributes[] = { AttributeName, NULL };
PLDAPMessage pMessage = NULL;
#if 1
ASSERT( pLdap != NULL ); /* Change from the spec. */
#else
if ( !pLdap ) {
CloseLdap = ConnectAndBindToDefaultDsa( &pLdap );
}
#endif
if ( pLdap ) {
#define EXTRA_STUFF TEXT("(objectClass=*)")
Query = (LPTSTR) malloc( ( lstrlen( SamAccountName ) +
sizeof( "( & (=) )") /* remaining
components */ )
* sizeof( TCHAR ) +
sizeof( U(SamAccountAttribute )) +
sizeof( EXTRA_STUFF ) );
if ( Query ) {
wsprintf( Query,
TEXT("(& ")
EXTRA_STUFF
TEXT("(%s=%s))"),
U(SamAccountAttribute),
SamAccountName );
if ( U(LdapSearchForUniqueDn)( pLdap,
Query,
ArrayOfAttributes,
NULL, // don't need the DN back.
&pMessage )) {
if ( U(LdapFindAttributeInMessage)( pLdap,
pMessage,
AttributeName,
NULL, // don't care about length
&StringAttr ) ) {
*pulAttributeContents = _tcstoul( StringAttr,
NULL, // no endpoint
0 /* use hex or dec as
appropriate */ );
ret = TRUE;
} // else message already printed
ldap_msgfree( pMessage );
} // else message already printed.
free( Query );
} else {
printf( "FAILED: couldn't allocate memory.\n" );
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
}
// close the ldap handle if we opened it.
if ( CloseLdap ) ldap_unbind( pLdap );
} // else printf'd already.
return ret;
}
/*++**************************************************************
NAME: U(QueryAccountControlFlags)
Opens a user and retrieves the user account control flags for it,
using the DS.
MODIFIES: pulControlFlags - returned control flags on the user.
TAKES: pLdap -- optional LDAP connection; if null, we'll
make our own and close it when finished.
DomainName -- domain in which to search for that account.
This is not currently implemented-- for
future use in order to support nonunique
accountnames that differ only by domain name.
SamAccountName -- accountname to query (with $ for computers)
RETURNS: TRUE when the function succeeds.
FALSE otherwise.
LASTERROR: set.
LOGGING: printf on failure.
CALLED BY: anyone
FREE WITH: n/a
**************************************************************--*/
BOOL
U(QueryAccountControlFlags)( IN OPTIONAL PLDAP pLdap,
IN OPTIONAL LPTSTR DomainName, // ignored
IN LPTSTR SamAccountName,
OUT PULONG pulControlFlags ) {
return U(LdapQueryUlongAttribute)( pLdap,
DomainName,
SamAccountName,
U(UserAccountAttribute),
pulControlFlags );
}
/*++**************************************************************
NAME: U(SetAccountControlFlags)
Sets the accountcontrolflags on a specified account.
Pretty much what the function name says.
MODIFIES: account's control flags
TAKES: pLdap -- if specified, DS handle to use
DomainName -- account's domain (mbz)
SamAccountName -- account for which to search
AccountControlFlags -- flags to set on the account
RETURNS: TRUE when the function succeeds.
FALSE otherwise.
LASTERROR: set
LOGGING: printf on failure
CALLED BY: anyone
FREE WITH: n/a
**************************************************************--*/
BOOL
U(SetAccountControlFlags)( IN OPTIONAL PLDAP pLdap,
IN OPTIONAL LPTSTR DomainName,
IN LPTSTR SamAccountName,
IN ULONG AccountControlFlags ) {
BOOL CloseLdap = FALSE;
BOOL ret = FALSE;
LPTSTR Query = NULL;
LPTSTR StringAttr = NULL;
LPTSTR ArrayOfAttributes[] = { U(UserAccountAttribute), NULL };
LPTSTR Dn;
DWORD dwErr;
#if 1
ASSERT( pLdap != NULL ); /* Change from the spec. */
#else
if ( !pLdap ) {
CloseLdap = ConnectAndBindToDefaultDsa( &pLdap );
}
#endif
if ( pLdap ) {
Query = (LPTSTR) malloc( ( lstrlen( SamAccountName ) +
sizeof( "( & (=) )") /* remaining
components */ )
* sizeof( TCHAR ) +
sizeof( U(SamAccountAttribute )) +
sizeof( EXTRA_STUFF ) );
if ( Query ) {
wsprintf( Query,
TEXT("(& ")
EXTRA_STUFF
TEXT("(%s=%s))"),
U(SamAccountAttribute),
SamAccountName );
if ( U(LdapSearchForUniqueDn)( pLdap,
Query,
ArrayOfAttributes,
&Dn,
NULL /* don't need the message
back */ ) ) {
#pragma warning(disable:4204) /* nonstandard extension:
non-constant aggregate initializer
(e.g. assign an array in the initialization
of a structure) */
TCHAR Buffer[ 50 ]; // arbitrary
LPTSTR Strings[] = { Buffer, NULL };
LDAPMod TheMod = {
LDAP_MOD_REPLACE,
U(UserAccountAttribute),
Strings,
};
PLDAPMod rzMods[] = {
&TheMod,
NULL
};
wsprintf( Buffer,
TEXT("%ld"),
AccountControlFlags );
dwErr = ldap_modify_s( pLdap,
Dn,
rzMods );
if ( dwErr == LDAP_SUCCESS ) {
ret = TRUE;
} else {
printf( "Failed to modify " STRING_FMTA
" attribute to %ld (0x%x)"
" on " STRING_FMTA
": 0x%x\n",
U(UserAccountAttribute),
AccountControlFlags,
AccountControlFlags,
Dn,
dwErr );
SetLastError( dwErr );
}
ldap_memfree( Dn );
} // else message already printed.
free( Query );
} else {
printf( "FAILED: couldn't allocate memory.\n" );
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
}
// close the ldap handle if we opened it.
if ( CloseLdap ) ldap_unbind( pLdap );
} // else printf'd already.
return ret;
}