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 ) ;
}
/*
connect to the LDAP server
*/
int ads_connect ( ADS_STRUCT * ads )
{
int version = LDAP_VERSION3 ;
int rc ;
2001-12-05 12:19:25 +03:00
ads - > last_attempt = time ( NULL ) ;
2001-11-20 11:54:15 +03:00
ads - > ld = ldap_open ( ads - > ldap_server , ads - > ldap_port ) ;
if ( ! ads - > ld ) {
2001-12-05 13:14:22 +03:00
return LDAP_SERVER_DOWN ;
2001-11-20 11:54:15 +03:00
}
2001-12-08 14:18:56 +03:00
if ( ! ads_server_info ( ads ) ) {
DEBUG ( 1 , ( " Failed to get ldap server info \n " ) ) ;
return LDAP_SERVER_DOWN ;
}
2001-11-20 11:54:15 +03:00
ldap_set_option ( ads - > ld , LDAP_OPT_PROTOCOL_VERSION , & version ) ;
2001-12-05 12:46:53 +03:00
if ( ads - > password ) {
2001-12-06 08:41:53 +03:00
ads_kinit_password ( ads ) ;
2001-12-05 12:46:53 +03:00
}
2001-12-08 14:18:56 +03:00
rc = ads_sasl_bind ( ads ) ;
2001-11-20 11:54:15 +03:00
return rc ;
}
2001-12-05 12:19:25 +03:00
/*
2001-12-08 14:18:56 +03:00
do a search with a timeout
2001-12-05 12:19:25 +03:00
*/
2001-12-08 14:18:56 +03:00
int ads_do_search ( ADS_STRUCT * ads , const char * bind_path , int scope , const char * exp ,
const char * * attrs , void * * res )
2001-12-05 12:19:25 +03:00
{
struct timeval timeout ;
2001-12-08 14:18:56 +03:00
timeout . tv_sec = ADS_SEARCH_TIMEOUT ;
timeout . tv_usec = 0 ;
* res = NULL ;
2001-12-05 12:19:25 +03:00
2001-12-08 14:18:56 +03:00
return ldap_search_ext_s ( ads - > ld ,
bind_path , scope ,
exp , attrs , 0 , NULL , NULL ,
& timeout , LDAP_NO_LIMIT , ( LDAPMessage * * ) res ) ;
2001-12-05 12:19:25 +03:00
}
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 )
{
2001-12-08 14:18:56 +03:00
return ads_do_search ( ads , ads - > bind_path , LDAP_SCOPE_SUBTREE ,
exp , attrs , res ) ;
2001-11-25 04:31:07 +03:00
}
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 )
{
2001-12-08 14:18:56 +03:00
return ads_do_search ( ads , dn , LDAP_SCOPE_BASE , " (objectclass=*) " , attrs, res) ;
2001-12-04 15:08:16 +03:00
}
2001-12-05 09:26:56 +03:00
/*
free up memory from a ads_search
*/
void ads_msgfree ( ADS_STRUCT * ads , void * msg )
{
if ( ! msg ) return ;
ldap_msgfree ( msg ) ;
}
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-05 07:44:34 +03:00
/*
return a RFC2254 binary string representation of a buffer
used in filters
caller must free
*/
char * ads_binary_string ( char * buf , int len )
{
char * s ;
int i , j ;
const char * hex = " 0123456789ABCDEF " ;
s = malloc ( len * 3 + 1 ) ;
if ( ! s ) return NULL ;
for ( j = i = 0 ; i < len ; i + + ) {
s [ j ] = ' \\ ' ;
s [ j + 1 ] = hex [ ( ( unsigned char ) buf [ i ] ) > > 4 ] ;
s [ j + 2 ] = hex [ ( ( unsigned char ) buf [ i ] ) & 0xF ] ;
j + = 3 ;
}
s [ j ] = 0 ;
return s ;
}
/*
return the binary string representation of a DOM_SID
caller must free
*/
char * ads_sid_binstring ( DOM_SID * sid )
{
char * buf , * s ;
int len = sid_size ( sid ) ;
buf = malloc ( len ) ;
if ( ! buf ) return NULL ;
sid_linearize ( buf , len , sid ) ;
s = ads_binary_string ( buf , len ) ;
free ( buf ) ;
return s ;
}
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 ;
2001-12-05 10:35:57 +03:00
char * ret = NULL ;
2001-12-03 09:04:18 +03:00
values = ldap_get_values ( ads - > ld , msg , field ) ;
2001-12-05 10:35:57 +03:00
if ( ! values ) return NULL ;
if ( values [ 0 ] ) {
ret = talloc_strdup ( mem_ctx , values [ 0 ] ) ;
}
2001-12-03 09:04:18 +03:00
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 ) ;
2001-12-05 10:35:57 +03:00
if ( ! values ) return False ;
if ( ! values [ 0 ] ) {
ldap_value_free ( values ) ;
return False ;
}
2001-12-03 09:04:18 +03:00
* 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 ;
2001-12-05 10:35:57 +03:00
BOOL ret = False ;
2001-12-03 09:04:18 +03:00
values = ldap_get_values_len ( ads - > ld , msg , field ) ;
2001-12-05 10:35:57 +03:00
if ( ! values ) return False ;
2001-12-03 09:04:18 +03:00
2001-12-05 10:35:57 +03:00
if ( values [ 0 ] ) {
ret = sid_parse ( values [ 0 ] - > bv_val , values [ 0 ] - > bv_len , sid ) ;
}
2001-12-03 09:04:18 +03:00
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 ;
2001-12-05 12:19:25 +03:00
BOOL ret ;
2001-11-28 06:56:30 +03:00
2001-12-08 14:18:56 +03:00
rc = ads_do_search ( ads , " " , LDAP_SCOPE_BASE , " (objectclass=*) " , attrs , & res ) ;
2001-11-28 06:56:30 +03:00
if ( rc | | ads_count_replies ( ads , res ) ! = 1 ) return False ;
2001-12-05 12:19:25 +03:00
ret = ads_pull_uint32 ( ads , res , " highestCommittedUSN " , usn ) ;
ads_msgfree ( ads , res ) ;
return ret ;
2001-11-28 06:56:30 +03:00
}
2001-12-03 09:04:18 +03:00
2001-12-08 14:18:56 +03:00
/* find the servers name and realm - this can be done before authentication
The ldapServiceName field on w2k looks like this :
vnet3 . home . samba . org : win2000 - vnet3 $ @ VNET3 . HOME . SAMBA . ORG
*/
BOOL ads_server_info ( ADS_STRUCT * ads )
{
const char * attrs [ ] = { " ldapServiceName " , NULL } ;
int rc ;
void * res ;
char * * values ;
char * ret , * p ;
rc = ads_do_search ( ads , " " , LDAP_SCOPE_BASE , " (objectclass=*) " , attrs , & res ) ;
if ( rc | | ads_count_replies ( ads , res ) ! = 1 ) return False ;
values = ldap_get_values ( ads - > ld , res , " ldapServiceName " ) ;
if ( ! values | | ! values [ 0 ] ) return False ;
p = strchr ( values [ 0 ] , ' : ' ) ;
if ( ! p ) {
ldap_value_free ( values ) ;
ldap_msgfree ( res ) ;
return False ;
}
SAFE_FREE ( ads - > ldap_server_name ) ;
ads - > ldap_server_name = strdup ( p + 1 ) ;
p = strchr ( ads - > ldap_server_name , ' $ ' ) ;
if ( ! p | | p [ 1 ] ! = ' @ ' ) {
ldap_value_free ( values ) ;
ldap_msgfree ( res ) ;
SAFE_FREE ( ads - > ldap_server_name ) ;
return False ;
}
* p = 0 ;
/* in case the realm isn't configured in smb.conf */
if ( ! ads - > realm | | ! ads - > realm [ 0 ] ) {
SAFE_FREE ( ads - > realm ) ;
SAFE_FREE ( ads - > bind_path ) ;
ads - > realm = strdup ( p + 2 ) ;
ads - > bind_path = ads_build_dn ( ads - > realm ) ;
}
DEBUG ( 3 , ( " got ldap server name %s@%s \n " , ret , ads - > realm ) ) ;
return True ;
}
2001-12-03 09:04:18 +03:00
2001-11-20 11:54:15 +03:00
# endif