2002-08-19 07:17:03 +04:00
/*
Samba Unix / Linux SMB client library
net ads cldap functions
Copyright ( C ) 2001 Andrew Tridgell ( tridge @ samba . org )
2003-02-28 06:35:45 +03:00
Copyright ( C ) 2003 Jim McDonough ( jmcd @ us . ibm . com )
2002-08-19 07:17:03 +04:00
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"
# include "../utils/net.h"
# ifdef HAVE_ADS
2003-05-20 17:51:43 +04:00
# define MAX_DNS_LABEL 255 + 1
2003-02-28 06:35:45 +03:00
2002-08-19 19:30:26 +04:00
struct cldap_netlogon_reply {
2003-02-28 06:35:45 +03:00
uint32 type ;
2002-08-19 19:59:14 +04:00
uint32 flags ;
2004-04-13 18:38:52 +04:00
UUID_FLAT guid ;
2002-08-19 19:30:26 +04:00
2003-05-20 17:51:43 +04:00
char forest [ MAX_DNS_LABEL ] ;
char domain [ MAX_DNS_LABEL ] ;
char hostname [ MAX_DNS_LABEL ] ;
2003-02-28 06:35:45 +03:00
2003-05-20 17:51:43 +04:00
char netbios_domain [ MAX_DNS_LABEL ] ;
char netbios_hostname [ MAX_DNS_LABEL ] ;
2003-02-28 06:35:45 +03:00
2003-09-26 10:35:11 +04:00
char unk [ MAX_DNS_LABEL ] ;
2003-05-20 17:51:43 +04:00
char user_name [ MAX_DNS_LABEL ] ;
char site_name [ MAX_DNS_LABEL ] ;
char site_name_2 [ MAX_DNS_LABEL ] ;
2003-02-28 06:35:45 +03:00
uint32 version ;
uint16 lmnt_token ;
uint16 lm20_token ;
} ;
2002-08-19 19:59:14 +04:00
/*
2003-05-20 17:51:43 +04:00
These seem to be strings as described in RFC1035 4.1 .4 and can be :
- a sequence of labels ending in a zero octet
- a pointer
- a sequence of labels ending with a pointer
A label is a byte where the first two bits must be zero and the remaining
bits represent the length of the label followed by the label itself .
Therefore , the length of a label is at max 64 bytes . Under RFC1035 , a
sequence of labels cannot exceed 255 bytes .
A pointer consists of a 14 bit offset from the beginning of the data .
struct ptr {
unsigned ident : 2 ; // must be 11
unsigned offset : 14 ; // from the beginning of data
} ;
This is used as a method to compress the packet by eliminated duplicate
domain components . Since a UDP packet should probably be < 512 bytes and a
DNS name can be up to 255 bytes , this actually makes a lot of sense .
2002-08-19 19:59:14 +04:00
*/
2003-05-20 17:51:43 +04:00
static unsigned pull_netlogon_string ( char * ret , const char * ptr ,
const char * data )
2002-08-19 19:59:14 +04:00
{
2003-05-20 17:51:43 +04:00
char * pret = ret ;
int followed_ptr = 0 ;
unsigned ret_len = 0 ;
2002-08-19 19:59:14 +04:00
2003-05-20 17:51:43 +04:00
memset ( pret , 0 , MAX_DNS_LABEL ) ;
2003-02-28 06:35:45 +03:00
do {
2003-05-20 17:51:43 +04:00
if ( ( * ptr & 0xc0 ) = = 0xc0 ) {
uint16 len ;
if ( ! followed_ptr ) {
ret_len + = 2 ;
followed_ptr = 1 ;
}
len = ( ( ptr [ 0 ] & 0x3f ) < < 8 ) | ptr [ 1 ] ;
ptr = data + len ;
} else if ( * ptr ) {
uint8 len = ( uint8 ) * ( ptr + + ) ;
if ( ( pret - ret + len + 1 ) > = MAX_DNS_LABEL ) {
d_printf ( " DC returning too long DNS name \n " ) ;
return 0 ;
}
if ( pret ! = ret ) {
* pret = ' . ' ;
pret + + ;
}
memcpy ( pret , ptr , len ) ;
pret + = len ;
ptr + = len ;
if ( ! followed_ptr ) {
ret_len + = ( len + 1 ) ;
}
2002-08-19 19:59:14 +04:00
}
2003-05-20 17:51:43 +04:00
} while ( * ptr ) ;
2002-08-19 19:59:14 +04:00
2003-09-26 10:35:11 +04:00
return followed_ptr ? ret_len : ret_len + 1 ;
2002-08-19 19:59:14 +04:00
}
2002-08-19 07:17:03 +04:00
/*
do a cldap netlogon query
*/
2002-08-19 19:30:26 +04:00
static int send_cldap_netlogon ( int sock , const char * domain ,
const char * hostname , unsigned ntversion )
2002-08-19 07:17:03 +04:00
{
ASN1_DATA data ;
char ntver [ 4 ] ;
2003-05-20 17:51:43 +04:00
# ifdef CLDAP_USER_QUERY
char aac [ 4 ] ;
2002-08-19 07:17:03 +04:00
2003-05-20 17:51:43 +04:00
SIVAL ( aac , 0 , 0x00000180 ) ;
# endif
2002-08-19 19:30:26 +04:00
SIVAL ( ntver , 0 , ntversion ) ;
2002-08-19 07:17:03 +04:00
memset ( & data , 0 , sizeof ( data ) ) ;
asn1_push_tag ( & data , ASN1_SEQUENCE ( 0 ) ) ;
asn1_write_Integer ( & data , 4 ) ;
asn1_push_tag ( & data , ASN1_APPLICATION ( 3 ) ) ;
asn1_write_OctetString ( & data , NULL , 0 ) ;
asn1_write_enumerated ( & data , 0 ) ;
asn1_write_enumerated ( & data , 0 ) ;
asn1_write_Integer ( & data , 0 ) ;
asn1_write_Integer ( & data , 0 ) ;
asn1_write_BOOLEAN2 ( & data , False ) ;
asn1_push_tag ( & data , ASN1_CONTEXT ( 0 ) ) ;
asn1_push_tag ( & data , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( & data , " DnsDomain " , 9 ) ;
2002-08-19 19:30:26 +04:00
asn1_write_OctetString ( & data , domain , strlen ( domain ) ) ;
2002-08-19 07:17:03 +04:00
asn1_pop_tag ( & data ) ;
asn1_push_tag ( & data , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( & data , " Host " , 4 ) ;
2002-08-19 19:30:26 +04:00
asn1_write_OctetString ( & data , hostname , strlen ( hostname ) ) ;
2002-08-19 07:17:03 +04:00
asn1_pop_tag ( & data ) ;
2003-05-20 17:51:43 +04:00
# ifdef CLDAP_USER_QUERY
asn1_push_tag ( & data , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( & data , " User " , 4 ) ;
asn1_write_OctetString ( & data , " SAMBA$ " , 6 ) ;
asn1_pop_tag ( & data ) ;
asn1_push_tag ( & data , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( & data , " AAC " , 4 ) ;
asn1_write_OctetString ( & data , aac , 4 ) ;
asn1_pop_tag ( & data ) ;
# endif
2002-08-19 07:17:03 +04:00
asn1_push_tag ( & data , ASN1_CONTEXT ( 3 ) ) ;
asn1_write_OctetString ( & data , " NtVer " , 5 ) ;
asn1_write_OctetString ( & data , ntver , 4 ) ;
asn1_pop_tag ( & data ) ;
asn1_pop_tag ( & data ) ;
asn1_push_tag ( & data , ASN1_SEQUENCE ( 0 ) ) ;
asn1_write_OctetString ( & data , " NetLogon " , 8 ) ;
asn1_pop_tag ( & data ) ;
asn1_pop_tag ( & data ) ;
asn1_pop_tag ( & data ) ;
if ( data . has_error ) {
d_printf ( " Failed to build cldap netlogon at offset %d \n " , ( int ) data . ofs ) ;
asn1_free ( & data ) ;
return - 1 ;
}
2003-03-18 02:04:03 +03:00
if ( write ( sock , data . data , data . length ) ! = ( ssize_t ) data . length ) {
2002-08-19 19:30:26 +04:00
d_printf ( " failed to send cldap query (%s) \n " , strerror ( errno ) ) ;
}
asn1_free ( & data ) ;
return 0 ;
}
/*
receive a cldap netlogon reply
*/
static int recv_cldap_netlogon ( int sock , struct cldap_netlogon_reply * reply )
{
int ret ;
ASN1_DATA data ;
DATA_BLOB blob ;
DATA_BLOB os1 , os2 , os3 ;
2002-08-19 19:59:14 +04:00
uint32 i1 ;
char * p ;
2002-08-19 19:30:26 +04:00
blob = data_blob ( NULL , 8192 ) ;
ret = read ( sock , blob . data , blob . length ) ;
2002-08-23 02:50:57 +04:00
2002-08-19 19:30:26 +04:00
if ( ret < = 0 ) {
2002-08-23 02:50:57 +04:00
d_printf ( " no reply received to cldap netlogon \n " ) ;
2002-08-19 19:30:26 +04:00
return - 1 ;
}
blob . length = ret ;
asn1_load ( & data , blob ) ;
asn1_start_tag ( & data , ASN1_SEQUENCE ( 0 ) ) ;
2002-08-19 19:59:14 +04:00
asn1_read_Integer ( & data , & i1 ) ;
2002-08-19 19:30:26 +04:00
asn1_start_tag ( & data , ASN1_APPLICATION ( 4 ) ) ;
asn1_read_OctetString ( & data , & os1 ) ;
asn1_start_tag ( & data , ASN1_SEQUENCE ( 0 ) ) ;
asn1_start_tag ( & data , ASN1_SEQUENCE ( 0 ) ) ;
asn1_read_OctetString ( & data , & os2 ) ;
asn1_start_tag ( & data , ASN1_SET ) ;
asn1_read_OctetString ( & data , & os3 ) ;
asn1_end_tag ( & data ) ;
asn1_end_tag ( & data ) ;
asn1_end_tag ( & data ) ;
asn1_end_tag ( & data ) ;
asn1_end_tag ( & data ) ;
2002-08-23 02:50:57 +04:00
if ( data . has_error ) {
d_printf ( " Failed to parse cldap reply \n " ) ;
return - 1 ;
}
2003-08-15 08:42:05 +04:00
p = ( char * ) os3 . data ;
2002-08-19 19:59:14 +04:00
2003-02-28 06:35:45 +03:00
reply - > type = IVAL ( p , 0 ) ; p + = 4 ;
2002-08-19 19:59:14 +04:00
reply - > flags = IVAL ( p , 0 ) ; p + = 4 ;
2003-02-28 06:35:45 +03:00
2004-04-13 18:38:52 +04:00
memcpy ( & reply - > guid . info , p , UUID_FLAT_SIZE ) ;
p + = UUID_FLAT_SIZE ;
2003-02-28 06:35:45 +03:00
2003-08-15 08:42:05 +04:00
p + = pull_netlogon_string ( reply - > forest , p , ( const char * ) os3 . data ) ;
p + = pull_netlogon_string ( reply - > domain , p , ( const char * ) os3 . data ) ;
p + = pull_netlogon_string ( reply - > hostname , p , ( const char * ) os3 . data ) ;
p + = pull_netlogon_string ( reply - > netbios_domain , p , ( const char * ) os3 . data ) ;
p + = pull_netlogon_string ( reply - > netbios_hostname , p , ( const char * ) os3 . data ) ;
2003-09-26 10:35:11 +04:00
p + = pull_netlogon_string ( reply - > unk , p , ( const char * ) os3 . data ) ;
2003-05-20 17:51:43 +04:00
if ( reply - > type = = SAMLOGON_AD_R ) {
2003-08-15 08:42:05 +04:00
p + = pull_netlogon_string ( reply - > user_name , p , ( const char * ) os3 . data ) ;
2003-05-20 17:51:43 +04:00
} else {
* reply - > user_name = 0 ;
}
2003-02-28 06:35:45 +03:00
2003-08-15 08:42:05 +04:00
p + = pull_netlogon_string ( reply - > site_name , p , ( const char * ) os3 . data ) ;
p + = pull_netlogon_string ( reply - > site_name_2 , p , ( const char * ) os3 . data ) ;
2003-02-28 06:35:45 +03:00
reply - > version = IVAL ( p , 0 ) ;
reply - > lmnt_token = SVAL ( p , 4 ) ;
reply - > lm20_token = SVAL ( p , 6 ) ;
2002-08-19 19:59:14 +04:00
2002-08-19 19:30:26 +04:00
data_blob_free ( & os1 ) ;
data_blob_free ( & os2 ) ;
data_blob_free ( & os3 ) ;
data_blob_free ( & blob ) ;
return 0 ;
}
/*
do a cldap netlogon query
*/
int ads_cldap_netlogon ( ADS_STRUCT * ads )
{
int sock ;
int ret ;
struct cldap_netlogon_reply reply ;
2004-08-12 07:28:57 +04:00
const char * target = opt_host ? opt_host : inet_ntoa ( ads - > ldap_ip ) ;
2002-08-19 19:30:26 +04:00
2004-08-12 07:28:57 +04:00
sock = open_udp_socket ( target , ads - > ldap_port ) ;
2002-08-19 07:17:03 +04:00
if ( sock = = - 1 ) {
d_printf ( " Failed to open udp socket to %s:%u \n " ,
inet_ntoa ( ads - > ldap_ip ) ,
ads - > ldap_port ) ;
return - 1 ;
2003-05-20 17:51:43 +04:00
2002-08-19 07:17:03 +04:00
}
2002-11-13 02:20:50 +03:00
ret = send_cldap_netlogon ( sock , ads - > config . realm , global_myname ( ) , 6 ) ;
2002-08-19 19:30:26 +04:00
if ( ret ! = 0 ) {
return ret ;
}
ret = recv_cldap_netlogon ( sock , & reply ) ;
close ( sock ) ;
2002-08-19 19:59:14 +04:00
2002-08-23 02:50:57 +04:00
if ( ret = = - 1 ) {
return - 1 ;
}
2003-02-28 06:35:45 +03:00
d_printf ( " Information for Domain Controller: %s \n \n " ,
ads - > config . ldap_server_name ) ;
2003-05-20 17:51:43 +04:00
d_printf ( " Response Type: " ) ;
switch ( reply . type ) {
case SAMLOGON_AD_UNK_R :
d_printf ( " SAMLOGON \n " ) ;
break ;
case SAMLOGON_AD_R :
d_printf ( " SAMLOGON_USER \n " ) ;
break ;
default :
d_printf ( " 0x%x \n " , reply . type ) ;
break ;
}
2004-04-13 18:38:52 +04:00
d_printf ( " GUID: %s \n " ,
smb_uuid_string_static ( smb_uuid_unpack_static ( reply . guid ) ) ) ;
2003-02-28 06:35:45 +03:00
d_printf ( " Flags: \n "
" \t Is a PDC: %s \n "
" \t Is a GC of the forest: %s \n "
" \t Is an LDAP server: %s \n "
" \t Supports DS: %s \n "
" \t Is running a KDC: %s \n "
" \t Is running time services: %s \n "
" \t Is the closest DC: %s \n "
" \t Is writable: %s \n "
" \t Has a hardware clock: %s \n "
" \t Is a non-domain NC serviced by LDAP server: %s \n " ,
( reply . flags & ADS_PDC ) ? " yes " : " no " ,
( reply . flags & ADS_GC ) ? " yes " : " no " ,
( reply . flags & ADS_LDAP ) ? " yes " : " no " ,
( reply . flags & ADS_DS ) ? " yes " : " no " ,
( reply . flags & ADS_KDC ) ? " yes " : " no " ,
( reply . flags & ADS_TIMESERV ) ? " yes " : " no " ,
( reply . flags & ADS_CLOSEST ) ? " yes " : " no " ,
( reply . flags & ADS_WRITABLE ) ? " yes " : " no " ,
( reply . flags & ADS_GOOD_TIMESERV ) ? " yes " : " no " ,
( reply . flags & ADS_NDNC ) ? " yes " : " no " ) ;
2003-05-20 17:51:43 +04:00
printf ( " Forest: \t \t \t %s \n " , reply . forest ) ;
printf ( " Domain: \t \t \t %s \n " , reply . domain ) ;
printf ( " Domain Controller: \t %s \n " , reply . hostname ) ;
2003-02-28 06:35:45 +03:00
2003-05-20 17:51:43 +04:00
printf ( " Pre-Win2k Domain: \t %s \n " , reply . netbios_domain ) ;
printf ( " Pre-Win2k Hostname: \t %s \n " , reply . netbios_hostname ) ;
2003-02-28 06:35:45 +03:00
2003-09-26 10:35:11 +04:00
if ( * reply . unk ) printf ( " Unk: \t \t \t %s \n " , reply . unk ) ;
2003-05-20 17:51:43 +04:00
if ( * reply . user_name ) printf ( " User name: \t %s \n " , reply . user_name ) ;
printf ( " Site Name: \t \t %s \n " , reply . site_name ) ;
printf ( " Site Name (2): \t \t %s \n " , reply . site_name_2 ) ;
2003-02-28 06:35:45 +03:00
d_printf ( " NT Version: %d \n " , reply . version ) ;
d_printf ( " LMNT Token: %.2x \n " , reply . lmnt_token ) ;
d_printf ( " LM20 Token: %.2x \n " , reply . lm20_token ) ;
2002-08-23 02:50:57 +04:00
2002-08-19 19:30:26 +04:00
return ret ;
2002-08-19 07:17:03 +04:00
}
# endif