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
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;
|
|
}
|