2001-11-20 11:54:15 +03:00
/*
Unix SMB / Netbios implementation .
Version 3.0
ads ( active directory ) utility library
Copyright ( C ) Andrew Tridgell 2001
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# ifdef HAVE_ADS
/* return a dn of the form "dc=AA,dc=BB,dc=CC" from a
realm of the form AA . BB . CC
caller must free
*/
/*
return a string for an error from a ads routine
*/
char * ads_errstr ( int rc )
{
return ldap_err2string ( rc ) ;
}
/*
this is a minimal interact function , just enough for SASL to talk
GSSAPI / kerberos to W2K
2001-11-24 17:16:41 +03:00
Error handling is a bit of a problem . I can ' t see how to get Cyrus - sasl
to give sensible errors
2001-11-20 11:54:15 +03:00
*/
static int sasl_interact ( LDAP * ld , unsigned flags , void * defaults , void * in )
{
sasl_interact_t * interact = in ;
while ( interact - > id ! = SASL_CB_LIST_END ) {
interact - > result = strdup ( " " ) ;
2001-11-24 17:16:41 +03:00
interact - > len = strlen ( interact - > result ) ;
2001-11-20 11:54:15 +03:00
interact + + ;
}
return LDAP_SUCCESS ;
}
/*
connect to the LDAP server
*/
int ads_connect ( ADS_STRUCT * ads )
{
int version = LDAP_VERSION3 ;
int rc ;
ads - > ld = ldap_open ( ads - > ldap_server , ads - > ldap_port ) ;
if ( ! ads - > ld ) {
return errno ;
}
ldap_set_option ( ads - > ld , LDAP_OPT_PROTOCOL_VERSION , & version ) ;
2001-11-24 17:16:41 +03:00
rc = ldap_sasl_interactive_bind_s ( ads - > ld , NULL , NULL , NULL , NULL ,
LDAP_SASL_QUIET ,
2001-11-20 11:54:15 +03:00
sasl_interact , NULL ) ;
return rc ;
}
2001-11-25 04:31:07 +03:00
/*
do a general ADS search
*/
int ads_search ( ADS_STRUCT * ads , void * * res ,
const char * exp ,
const char * * attrs )
{
* res = NULL ;
return ldap_search_s ( ads - > ld , ads - > bind_path ,
LDAP_SCOPE_SUBTREE , exp , ( char * * ) attrs , 0 , ( LDAPMessage * * ) res ) ;
}
2001-12-04 15:08:16 +03:00
/*
do a search on a specific DistinguishedName
*/
int ads_search_dn ( ADS_STRUCT * ads , void * * res ,
const char * dn ,
const char * * attrs )
{
* res = NULL ;
return ldap_search_s ( ads - > ld , dn ,
LDAP_SCOPE_BASE , " (objectclass=*) " , ( char * * ) attrs , 0 , ( LDAPMessage * * ) res ) ;
}
2001-11-25 04:31:07 +03:00
2001-11-20 11:54:15 +03:00
/*
find a machine account given a hostname
*/
int ads_find_machine_acct ( ADS_STRUCT * ads , void * * res , const char * host )
{
int ret ;
char * exp ;
/* the easiest way to find a machine account anywhere in the tree
is to look for hostname $ */
asprintf ( & exp , " (samAccountName=%s$) " , host ) ;
2001-11-25 04:31:07 +03:00
ret = ads_search ( ads , res , exp , NULL ) ;
2001-11-20 11:54:15 +03:00
free ( exp ) ;
return ret ;
}
/*
a convenient routine for adding a generic LDAP record
*/
int ads_gen_add ( ADS_STRUCT * ads , const char * new_dn , . . . )
{
int i ;
va_list ap ;
LDAPMod * * mods ;
char * name , * value ;
int ret ;
# define MAX_MOD_VALUES 10
/* count the number of attributes */
va_start ( ap , new_dn ) ;
for ( i = 0 ; va_arg ( ap , char * ) ; i + + ) {
/* skip the values */
while ( va_arg ( ap , char * ) ) ;
}
va_end ( ap ) ;
mods = malloc ( sizeof ( LDAPMod * ) * ( i + 1 ) ) ;
va_start ( ap , new_dn ) ;
for ( i = 0 ; ( name = va_arg ( ap , char * ) ) ; i + + ) {
char * * values ;
int j ;
values = ( char * * ) malloc ( sizeof ( char * ) * ( MAX_MOD_VALUES + 1 ) ) ;
for ( j = 0 ; ( value = va_arg ( ap , char * ) ) & & j < MAX_MOD_VALUES ; j + + ) {
values [ j ] = value ;
}
values [ j ] = NULL ;
mods [ i ] = malloc ( sizeof ( LDAPMod ) ) ;
mods [ i ] - > mod_type = name ;
mods [ i ] - > mod_op = LDAP_MOD_ADD ;
mods [ i ] - > mod_values = values ;
}
mods [ i ] = NULL ;
va_end ( ap ) ;
ret = ldap_add_s ( ads - > ld , new_dn , mods ) ;
for ( i = 0 ; mods [ i ] ; i + + ) {
free ( mods [ i ] - > mod_values ) ;
free ( mods [ i ] ) ;
}
free ( mods ) ;
return ret ;
}
/*
add a machine account to the ADS server
*/
static int ads_add_machine_acct ( ADS_STRUCT * ads , const char * hostname )
{
int ret ;
char * host_spn , * host_upn , * new_dn , * samAccountName , * controlstr ;
asprintf ( & host_spn , " HOST/%s " , hostname ) ;
asprintf ( & host_upn , " %s@%s " , host_spn , ads - > realm ) ;
asprintf ( & new_dn , " cn=%s,cn=Computers,%s " , hostname , ads - > bind_path ) ;
asprintf ( & samAccountName , " %s$ " , hostname ) ;
asprintf ( & controlstr , " %u " ,
2001-11-28 06:56:30 +03:00
UF_DONT_EXPIRE_PASSWD | UF_WORKSTATION_TRUST_ACCOUNT |
2001-11-20 11:54:15 +03:00
UF_TRUSTED_FOR_DELEGATION | UF_USE_DES_KEY_ONLY ) ;
ret = ads_gen_add ( ads , new_dn ,
" cn " , hostname , NULL ,
" sAMAccountName " , samAccountName , NULL ,
" objectClass " ,
" top " , " person " , " organizationalPerson " ,
" user " , " computer " , NULL ,
" userPrincipalName " , host_upn , NULL ,
" servicePrincipalName " , host_spn , NULL ,
" dNSHostName " , hostname , NULL ,
" userAccountControl " , controlstr , NULL ,
" operatingSystem " , " Samba " , NULL ,
" operatingSystemVersion " , VERSION , NULL ,
NULL ) ;
free ( host_spn ) ;
free ( host_upn ) ;
free ( new_dn ) ;
free ( samAccountName ) ;
free ( controlstr ) ;
return ret ;
}
2001-11-25 04:06:56 +03:00
/*
dump a binary result from ldap
*/
static void dump_binary ( const char * field , struct berval * * values )
{
int i , j ;
for ( i = 0 ; values [ i ] ; i + + ) {
printf ( " %s: " , field ) ;
for ( j = 0 ; j < values [ i ] - > bv_len ; j + + ) {
printf ( " %02X " , ( unsigned char ) values [ i ] - > bv_val [ j ] ) ;
}
printf ( " \n " ) ;
}
}
/*
dump a string result from ldap
*/
static void dump_string ( const char * field , struct berval * * values )
{
int i ;
for ( i = 0 ; values [ i ] ; i + + ) {
printf ( " %s: %s \n " , field , values [ i ] - > bv_val ) ;
}
}
2001-11-20 11:54:15 +03:00
/*
dump a record from LDAP on stdout
used for debugging
*/
void ads_dump ( ADS_STRUCT * ads , void * res )
{
char * field ;
2001-12-03 09:04:18 +03:00
void * msg ;
2001-11-20 11:54:15 +03:00
BerElement * b ;
2001-11-25 04:06:56 +03:00
struct {
char * name ;
void ( * handler ) ( const char * , struct berval * * ) ;
} handlers [ ] = {
{ " objectGUID " , dump_binary } ,
{ " objectSid " , dump_binary } ,
{ NULL , NULL }
} ;
2001-11-20 11:54:15 +03:00
2001-12-03 09:04:18 +03:00
for ( msg = ads_first_entry ( ads , res ) ; msg ; msg = ads_next_entry ( ads , msg ) ) {
for ( field = ldap_first_attribute ( ads - > ld , ( LDAPMessage * ) msg , & b ) ;
2001-11-20 11:54:15 +03:00
field ;
2001-12-03 09:04:18 +03:00
field = ldap_next_attribute ( ads - > ld , ( LDAPMessage * ) msg , b ) ) {
2001-11-25 04:06:56 +03:00
struct berval * * values ;
int i ;
2001-12-03 09:04:18 +03:00
values = ldap_get_values_len ( ads - > ld , ( LDAPMessage * ) msg , field ) ;
2001-11-25 04:06:56 +03:00
for ( i = 0 ; handlers [ i ] . name ; i + + ) {
if ( StrCaseCmp ( handlers [ i ] . name , field ) = = 0 ) {
handlers [ i ] . handler ( field , values ) ;
break ;
}
}
if ( ! handlers [ i ] . name ) {
dump_string ( field , values ) ;
2001-11-20 11:54:15 +03:00
}
2001-11-25 04:06:56 +03:00
ldap_value_free_len ( values ) ;
2001-11-20 11:54:15 +03:00
ldap_memfree ( field ) ;
}
ber_free ( b , 1 ) ;
printf ( " \n " ) ;
}
}
/*
count how many replies are in a LDAPMessage
*/
int ads_count_replies ( ADS_STRUCT * ads , void * res )
{
return ldap_count_entries ( ads - > ld , ( LDAPMessage * ) res ) ;
}
/*
join a machine to a realm , creating the machine account
and setting the machine password
*/
int ads_join_realm ( ADS_STRUCT * ads , const char * hostname )
{
int rc ;
LDAPMessage * res ;
2001-11-25 04:06:56 +03:00
char * host ;
/* hostname must be lowercase */
host = strdup ( hostname ) ;
strlower ( host ) ;
2001-11-20 11:54:15 +03:00
2001-11-25 04:06:56 +03:00
rc = ads_find_machine_acct ( ads , ( void * * ) & res , host ) ;
2001-11-20 11:54:15 +03:00
if ( rc = = LDAP_SUCCESS & & ads_count_replies ( ads , res ) = = 1 ) {
2001-11-25 04:06:56 +03:00
DEBUG ( 0 , ( " Host account for %s already exists \n " , host ) ) ;
2001-11-24 17:16:41 +03:00
return LDAP_SUCCESS ;
2001-11-20 11:54:15 +03:00
}
2001-11-25 04:06:56 +03:00
rc = ads_add_machine_acct ( ads , host ) ;
2001-11-20 11:54:15 +03:00
if ( rc ! = LDAP_SUCCESS ) {
DEBUG ( 0 , ( " ads_add_machine_acct: %s \n " , ads_errstr ( rc ) ) ) ;
return rc ;
}
2001-11-25 04:06:56 +03:00
rc = ads_find_machine_acct ( ads , ( void * * ) & res , host ) ;
2001-11-20 11:54:15 +03:00
if ( rc ! = LDAP_SUCCESS | | ads_count_replies ( ads , res ) ! = 1 ) {
DEBUG ( 0 , ( " Host account test failed \n " ) ) ;
/* hmmm, we need NTSTATUS */
return - 1 ;
}
2001-11-25 04:06:56 +03:00
free ( host ) ;
2001-11-24 17:16:41 +03:00
return LDAP_SUCCESS ;
}
2001-11-20 11:54:15 +03:00
2001-11-24 17:16:41 +03:00
/*
delete a machine from the realm
*/
int ads_leave_realm ( ADS_STRUCT * ads , const char * hostname )
{
int rc ;
void * res ;
2001-11-25 04:06:56 +03:00
char * hostnameDN , * host ;
/* hostname must be lowercase */
host = strdup ( hostname ) ;
strlower ( host ) ;
2001-11-24 17:16:41 +03:00
2001-11-25 04:06:56 +03:00
rc = ads_find_machine_acct ( ads , & res , host ) ;
2001-11-24 17:16:41 +03:00
if ( rc ! = LDAP_SUCCESS | | ads_count_replies ( ads , res ) ! = 1 ) {
2001-11-25 04:06:56 +03:00
DEBUG ( 0 , ( " Host account for %s does not exist. \n " , host ) ) ;
2001-11-24 17:16:41 +03:00
return - 1 ;
}
hostnameDN = ldap_get_dn ( ads - > ld , ( LDAPMessage * ) res ) ;
rc = ldap_delete_s ( ads - > ld , hostnameDN ) ;
ldap_memfree ( hostnameDN ) ;
if ( rc ! = LDAP_SUCCESS ) {
DEBUG ( 0 , ( " ldap_delete_s: %s \n " , ads_errstr ( rc ) ) ) ;
return rc ;
}
2001-11-25 04:06:56 +03:00
rc = ads_find_machine_acct ( ads , & res , host ) ;
2001-11-24 17:16:41 +03:00
if ( rc = = LDAP_SUCCESS & & ads_count_replies ( ads , res ) = = 1 ) {
DEBUG ( 0 , ( " Failed to remove host account. \n " ) ) ;
/*hmmm, we need NTSTATUS */
return - 1 ;
}
2001-11-25 04:06:56 +03:00
free ( host ) ;
2001-11-20 11:54:15 +03:00
return LDAP_SUCCESS ;
}
2001-11-24 17:16:41 +03:00
NTSTATUS ads_set_machine_password ( ADS_STRUCT * ads ,
const char * hostname ,
const char * password )
{
2001-11-25 04:06:56 +03:00
NTSTATUS ret ;
char * host = strdup ( hostname ) ;
strlower ( host ) ;
ret = krb5_set_password ( ads - > kdc_server , host , ads - > realm , password ) ;
free ( host ) ;
return ret ;
2001-11-24 17:16:41 +03:00
}
2001-12-03 09:04:18 +03:00
/*
pull the first entry from a ADS result
*/
void * ads_first_entry ( ADS_STRUCT * ads , void * res )
{
return ( void * ) ldap_first_entry ( ads - > ld , ( LDAPMessage * ) res ) ;
}
/*
pull the next entry from a ADS result
*/
void * ads_next_entry ( ADS_STRUCT * ads , void * res )
{
return ( void * ) ldap_next_entry ( ads - > ld , ( LDAPMessage * ) res ) ;
}
/*
pull a single string from a ADS result
*/
char * ads_pull_string ( ADS_STRUCT * ads ,
TALLOC_CTX * mem_ctx , void * msg , const char * field )
{
char * * values ;
char * ret ;
values = ldap_get_values ( ads - > ld , msg , field ) ;
if ( ! values | | ! values [ 0 ] ) return NULL ;
ret = talloc_strdup ( mem_ctx , values [ 0 ] ) ;
ldap_value_free ( values ) ;
return ret ;
}
/*
pull a single uint32 from a ADS result
*/
BOOL ads_pull_uint32 ( ADS_STRUCT * ads ,
void * msg , const char * field , uint32 * v )
{
char * * values ;
values = ldap_get_values ( ads - > ld , msg , field ) ;
if ( ! values | | ! values [ 0 ] ) return False ;
* v = atoi ( values [ 0 ] ) ;
ldap_value_free ( values ) ;
return True ;
}
/*
pull a single DOM_SID from a ADS result
*/
BOOL ads_pull_sid ( ADS_STRUCT * ads ,
void * msg , const char * field , DOM_SID * sid )
{
struct berval * * values ;
BOOL ret ;
values = ldap_get_values_len ( ads - > ld , msg , field ) ;
if ( ! values | | ! values [ 0 ] ) return False ;
ret = sid_parse ( values [ 0 ] - > bv_val , values [ 0 ] - > bv_len , sid ) ;
ldap_value_free_len ( values ) ;
return ret ;
}
2001-12-04 15:08:16 +03:00
/*
pull an array of DOM_SIDs from a ADS result
return the count of SIDs pulled
*/
int ads_pull_sids ( ADS_STRUCT * ads , TALLOC_CTX * mem_ctx ,
void * msg , const char * field , DOM_SID * * sids )
{
struct berval * * values ;
BOOL ret ;
int count , i ;
values = ldap_get_values_len ( ads - > ld , msg , field ) ;
if ( ! values ) return 0 ;
for ( i = 0 ; values [ i ] ; i + + ) /* nop */ ;
( * sids ) = talloc ( mem_ctx , sizeof ( DOM_SID ) * i ) ;
count = 0 ;
for ( i = 0 ; values [ i ] ; i + + ) {
ret = sid_parse ( values [ i ] - > bv_val , values [ i ] - > bv_len , & ( * sids ) [ count ] ) ;
if ( ret ) count + + ;
}
ldap_value_free_len ( values ) ;
return count ;
}
2001-12-03 09:04:18 +03:00
2001-11-28 06:56:30 +03:00
/* find the update serial number - this is the core of the ldap cache */
2001-12-03 09:04:18 +03:00
BOOL ads_USN ( ADS_STRUCT * ads , uint32 * usn )
2001-11-28 06:56:30 +03:00
{
const char * attrs [ ] = { " highestCommittedUSN " , NULL } ;
int rc ;
void * res ;
rc = ldap_search_s ( ads - > ld , ads - > bind_path ,
2001-12-04 15:08:16 +03:00
LDAP_SCOPE_BASE , " (objectclass=*) " , attrs , 0 , ( LDAPMessage * * ) & res ) ;
2001-11-28 06:56:30 +03:00
if ( rc | | ads_count_replies ( ads , res ) ! = 1 ) return False ;
2001-12-03 09:04:18 +03:00
return ads_pull_uint32 ( ads , res , " highestCommittedUSN " , usn ) ;
2001-11-28 06:56:30 +03:00
}
2001-12-03 09:04:18 +03:00
2001-11-20 11:54:15 +03:00
# endif