2001-12-03 09:04:18 +03:00
/*
Unix SMB / Netbios implementation .
Winbind ADS backend functions
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 "winbindd.h"
# ifdef HAVE_ADS
/* Query display info for a realm. This is the basic user list fn */
2001-12-03 14:32:55 +03:00
static NTSTATUS query_user_list ( struct winbindd_domain * domain ,
2001-12-03 09:04:18 +03:00
TALLOC_CTX * mem_ctx ,
uint32 * start_ndx , uint32 * num_entries ,
2001-12-03 14:32:55 +03:00
WINBIND_USERINFO * * info )
2001-12-03 09:04:18 +03:00
{
ADS_STRUCT * ads ;
const char * attrs [ ] = { " sAMAccountName " , " name " , " objectSid " , " primaryGroupID " ,
" userAccountControl " , NULL } ;
int rc , i , count ;
void * res ;
void * msg ;
2001-12-03 14:32:55 +03:00
DEBUG ( 3 , ( " ads: query_user_list \n " ) ) ;
2001-12-03 09:04:18 +03:00
if ( ( * start_ndx ) ! = 0 ) {
DEBUG ( 1 , ( " ads backend start_ndx not implemented \n " ) ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
ads = ads_init ( NULL , NULL , NULL ) ;
if ( ! ads ) {
DEBUG ( 1 , ( " ads_init failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_connect ( ads ) ;
if ( rc ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list ads_connect: %s \n " , ads_errstr ( rc ) ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_search ( ads , & res , " (objectclass=user) " , attrs ) ;
if ( rc ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list ads_search: %s \n " , ads_errstr ( rc ) ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
count = ads_count_replies ( ads , res ) ;
if ( count = = 0 ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list: No users found \n " ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
( * info ) = talloc ( mem_ctx , count * sizeof ( * * info ) ) ;
if ( ! * info ) return NT_STATUS_NO_MEMORY ;
i = 0 ;
for ( msg = ads_first_entry ( ads , res ) ; msg ; msg = ads_next_entry ( ads , msg ) ) {
char * name , * gecos ;
DOM_SID sid ;
uint32 rid , group ;
uint32 account_control ;
if ( ! ads_pull_uint32 ( ads , msg , " userAccountControl " ,
& account_control ) | |
! ( account_control & UF_NORMAL_ACCOUNT ) ) continue ;
name = ads_pull_string ( ads , mem_ctx , msg , " sAMAccountName " ) ;
gecos = ads_pull_string ( ads , mem_ctx , msg , " name " ) ;
if ( ! ads_pull_sid ( ads , msg , " objectSid " , & sid ) ) {
DEBUG ( 1 , ( " No sid for %s !? \n " , name ) ) ;
continue ;
}
if ( ! ads_pull_uint32 ( ads , msg , " primaryGroupID " , & group ) ) {
DEBUG ( 1 , ( " No primary group for %s !? \n " , name ) ) ;
continue ;
}
if ( ! sid_peek_rid ( & sid , & rid ) ) {
DEBUG ( 1 , ( " No rid for %s !? \n " , name ) ) ;
continue ;
}
( * info ) [ i ] . acct_name = name ;
( * info ) [ i ] . full_name = gecos ;
( * info ) [ i ] . user_rid = rid ;
( * info ) [ i ] . group_rid = group ;
i + + ;
}
( * num_entries ) = i ;
ads_destroy ( & ads ) ;
return NT_STATUS_OK ;
}
/* list all domain groups */
static NTSTATUS enum_dom_groups ( struct winbindd_domain * domain ,
TALLOC_CTX * mem_ctx ,
uint32 * start_ndx , uint32 * num_entries ,
struct acct_info * * info )
{
ADS_STRUCT * ads ;
const char * attrs [ ] = { " sAMAccountName " , " name " , " objectSid " ,
" sAMAccountType " , NULL } ;
int rc , i , count ;
void * res ;
void * msg ;
DEBUG ( 3 , ( " ads: enum_dom_groups \n " ) ) ;
if ( ( * start_ndx ) ! = 0 ) {
DEBUG ( 1 , ( " ads backend start_ndx not implemented \n " ) ) ;
return NT_STATUS_NOT_IMPLEMENTED ;
}
ads = ads_init ( NULL , NULL , NULL ) ;
if ( ! ads ) {
DEBUG ( 1 , ( " ads_init failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_connect ( ads ) ;
if ( rc ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list ads_connect: %s \n " , ads_errstr ( rc ) ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_search ( ads , & res , " (objectclass=group) " , attrs ) ;
if ( rc ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list ads_search: %s \n " , ads_errstr ( rc ) ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
count = ads_count_replies ( ads , res ) ;
if ( count = = 0 ) {
2001-12-03 14:32:55 +03:00
DEBUG ( 1 , ( " query_user_list: No users found \n " ) ) ;
2001-12-03 09:04:18 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
( * info ) = talloc ( mem_ctx , count * sizeof ( * * info ) ) ;
if ( ! * info ) return NT_STATUS_NO_MEMORY ;
i = 0 ;
for ( msg = ads_first_entry ( ads , res ) ; msg ; msg = ads_next_entry ( ads , msg ) ) {
char * name , * gecos ;
DOM_SID sid ;
uint32 rid ;
uint32 account_type ;
if ( ! ads_pull_uint32 ( ads , msg , " sAMAccountType " ,
& account_type ) | |
2001-12-03 11:17:46 +03:00
! ( account_type & ATYPE_GROUP ) ) continue ;
2001-12-03 09:04:18 +03:00
name = ads_pull_string ( ads , mem_ctx , msg , " sAMAccountName " ) ;
gecos = ads_pull_string ( ads , mem_ctx , msg , " name " ) ;
if ( ! ads_pull_sid ( ads , msg , " objectSid " , & sid ) ) {
DEBUG ( 1 , ( " No sid for %s !? \n " , name ) ) ;
continue ;
}
if ( ! sid_peek_rid ( & sid , & rid ) ) {
DEBUG ( 1 , ( " No rid for %s !? \n " , name ) ) ;
continue ;
}
fstrcpy ( ( * info ) [ i ] . acct_name , name ) ;
fstrcpy ( ( * info ) [ i ] . acct_desc , gecos ) ;
( * info ) [ i ] . rid = rid ;
i + + ;
}
( * num_entries ) = i ;
ads_destroy ( & ads ) ;
return NT_STATUS_OK ;
}
2001-12-03 11:17:46 +03:00
/* convert a single name to a sid in a domain */
static NTSTATUS name_to_sid ( struct winbindd_domain * domain ,
const char * name ,
DOM_SID * sid ,
enum SID_NAME_USE * type )
{
ADS_STRUCT * ads ;
const char * attrs [ ] = { " objectSid " , " sAMAccountType " , NULL } ;
int rc , count ;
void * res ;
char * exp ;
uint32 t ;
fstring name2 , dom2 ;
/* sigh. Need to fix interface to give us a raw name */
parse_domain_user ( name , dom2 , name2 ) ;
DEBUG ( 3 , ( " ads: name_to_sid \n " ) ) ;
ads = ads_init ( NULL , NULL , NULL ) ;
if ( ! ads ) {
DEBUG ( 1 , ( " ads_init failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_connect ( ads ) ;
if ( rc ) {
DEBUG ( 1 , ( " name_to_sid ads_connect: %s \n " , ads_errstr ( rc ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
asprintf ( & exp , " (sAMAccountName=%s) " , name2 ) ;
rc = ads_search ( ads , & res , exp , attrs ) ;
free ( exp ) ;
if ( rc ) {
DEBUG ( 1 , ( " name_to_sid ads_search: %s \n " , ads_errstr ( rc ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
count = ads_count_replies ( ads , res ) ;
if ( count ! = 1 ) {
DEBUG ( 1 , ( " name_to_sid: %s not found \n " , name ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
if ( ! ads_pull_sid ( ads , res , " objectSid " , sid ) ) {
DEBUG ( 1 , ( " No sid for %s !? \n " , name ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
if ( ! ads_pull_uint32 ( ads , res , " sAMAccountType " , & t ) ) {
DEBUG ( 1 , ( " No sAMAccountType for %s !? \n " , name ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
switch ( t & 0xF0000000 ) {
case ATYPE_GROUP :
* type = SID_NAME_DOM_GRP ;
break ;
case ATYPE_USER :
* type = SID_NAME_USER ;
break ;
default :
DEBUG ( 1 , ( " hmm, need to map account type 0x%x \n " , t ) ) ;
* type = SID_NAME_UNKNOWN ;
break ;
}
ads_destroy ( & ads ) ;
return NT_STATUS_OK ;
}
2001-12-04 09:17:39 +03:00
/* Lookup user information from a rid or username. */
static NTSTATUS query_user ( struct winbindd_domain * domain ,
TALLOC_CTX * mem_ctx ,
const char * user_name , uint32 user_rid ,
WINBIND_USERINFO * info )
{
ADS_STRUCT * ads ;
const char * attrs [ ] = { " sAMAccountName " , " name " , " objectSid " , " primaryGroupID " ,
" userAccountControl " , NULL } ;
int rc , count ;
void * msg ;
char * exp ;
DOM_SID sid ;
fstring dom2 , name2 ;
/* sigh. Need to fix interface to give us a raw name */
parse_domain_user ( user_name , dom2 , name2 ) ;
DEBUG ( 3 , ( " ads: query_user \n " ) ) ;
ads = ads_init ( NULL , NULL , NULL ) ;
if ( ! ads ) {
DEBUG ( 1 , ( " ads_init failed \n " ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
rc = ads_connect ( ads ) ;
if ( rc ) {
DEBUG ( 1 , ( " query_user ads_connect: %s \n " , ads_errstr ( rc ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
asprintf ( & exp , " (sAMAccountName=%s) " , name2 ) ;
rc = ads_search ( ads , & msg , exp , attrs ) ;
free ( exp ) ;
if ( rc ) {
DEBUG ( 1 , ( " query_user(%s) ads_search: %s \n " , user_name , ads_errstr ( rc ) ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
count = ads_count_replies ( ads , msg ) ;
if ( count ! = 1 ) {
DEBUG ( 1 , ( " query_user(%s): Not found \n " , user_name ) ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
info - > acct_name = ads_pull_string ( ads , mem_ctx , msg , " sAMAccountName " ) ;
info - > full_name = ads_pull_string ( ads , mem_ctx , msg , " name " ) ;
if ( ! ads_pull_sid ( ads , msg , " objectSid " , & sid ) ) {
DEBUG ( 1 , ( " No sid for %s !? \n " , user_name ) ) ;
goto error ;
}
if ( ! ads_pull_uint32 ( ads , msg , " primaryGroupID " , & info - > group_rid ) ) {
DEBUG ( 1 , ( " No primary group for %s !? \n " , user_name ) ) ;
goto error ;
}
if ( ! sid_peek_rid ( & sid , & info - > user_rid ) ) {
DEBUG ( 1 , ( " No rid for %s !? \n " , user_name ) ) ;
goto error ;
}
ads_destroy ( & ads ) ;
return NT_STATUS_OK ;
error :
ads_destroy ( & ads ) ;
return NT_STATUS_UNSUCCESSFUL ;
}
2001-12-03 14:11:14 +03:00
/* the ADS backend methods are exposed via this structure */
2001-12-03 09:04:18 +03:00
struct winbindd_methods ads_methods = {
2001-12-03 14:32:55 +03:00
query_user_list ,
2001-12-03 11:17:46 +03:00
enum_dom_groups ,
2001-12-03 14:11:14 +03:00
name_to_sid ,
/* I can't see a good way to do a sid to name mapping with ldap,
and MS servers always allow RPC for this ( even in native mode ) so
2001-12-04 09:17:39 +03:00
just use RPC for sid_to_name . Maybe that ' s why they allow it ? */
winbindd_rpc_sid_to_name ,
query_user
2001-12-03 09:04:18 +03:00
} ;
# endif