2000-05-09 15:43:00 +04:00
/*
Unix SMB / Netbios implementation .
Version 2.0
winbind client common code
Copyright ( C ) Tim Potter 2000
Copyright ( C ) Andrew Tridgell 2000
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation ; either
version 2 of the License , or ( at your option ) any later version .
This library 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
Library General Public License for more details .
You should have received a copy of the GNU Library General Public
License along with this library ; if not , write to the
Free Software Foundation , Inc . , 59 Temple Place - Suite 330 ,
Boston , MA 02111 - 1307 , USA .
*/
2000-05-10 18:17:21 +04:00
# include "winbind_nss_config.h"
# include "winbindd_nss.h"
2000-05-09 15:43:00 +04:00
/* Global variables. These are effectively the client state information */
2001-12-22 03:51:32 +03:00
int winbindd_fd = - 1 ; /* fd for winbindd socket */
2001-07-10 06:28:17 +04:00
static char * excluded_domain ;
2001-12-22 03:51:32 +03:00
/* Free a response structure */
void free_response ( struct winbindd_response * response )
{
/* Free any allocated extra_data */
if ( response )
SAFE_FREE ( response - > extra_data ) ;
}
2001-07-10 06:28:17 +04:00
/*
smbd needs to be able to exclude lookups for its own domain
*/
void winbind_exclude_domain ( const char * domain )
{
2001-09-17 08:52:45 +04:00
SAFE_FREE ( excluded_domain ) ;
2001-07-10 06:28:17 +04:00
excluded_domain = strdup ( domain ) ;
}
2000-05-09 15:43:00 +04:00
2000-07-17 06:37:11 +04:00
/* Initialise a request structure */
2000-05-09 15:43:00 +04:00
2000-07-17 06:37:11 +04:00
void init_request ( struct winbindd_request * request , int request_type )
2000-05-09 15:43:00 +04:00
{
static char * domain_env ;
static BOOL initialised ;
2001-08-25 00:32:01 +04:00
request - > cmd = ( enum winbindd_cmd ) request_type ;
2000-07-17 06:37:11 +04:00
request - > pid = getpid ( ) ;
request - > domain [ 0 ] = ' \0 ' ;
2000-05-09 15:43:00 +04:00
if ( ! initialised ) {
initialised = True ;
domain_env = getenv ( WINBINDD_DOMAIN_ENV ) ;
}
if ( domain_env ) {
2000-07-17 06:37:11 +04:00
strncpy ( request - > domain , domain_env ,
sizeof ( request - > domain ) - 1 ) ;
request - > domain [ sizeof ( request - > domain ) - 1 ] = ' \0 ' ;
2000-05-09 15:43:00 +04:00
}
}
2000-07-17 06:37:11 +04:00
/* Initialise a response structure */
void init_response ( struct winbindd_response * response )
{
/* Initialise return value */
2001-04-25 09:47:50 +04:00
response - > result = WINBINDD_ERROR ;
2000-07-17 06:37:11 +04:00
}
2000-05-09 15:43:00 +04:00
/* Close established socket */
void close_sock ( void )
{
2001-12-22 03:51:32 +03:00
if ( winbindd_fd ! = - 1 ) {
close ( winbindd_fd ) ;
winbindd_fd = - 1 ;
2000-06-30 10:48:47 +04:00
}
2000-05-09 15:43:00 +04:00
}
/* Connect to winbindd socket */
2001-12-22 03:51:32 +03:00
int winbind_open_pipe_sock ( void )
2000-05-09 15:43:00 +04:00
{
2000-06-30 10:48:47 +04:00
struct sockaddr_un sunaddr ;
static pid_t our_pid ;
struct stat st ;
pstring path ;
if ( our_pid ! = getpid ( ) ) {
2001-12-22 03:51:32 +03:00
close_sock ( ) ;
2000-06-30 10:48:47 +04:00
our_pid = getpid ( ) ;
}
2001-12-22 03:51:32 +03:00
if ( winbindd_fd ! = - 1 ) {
return winbindd_fd ;
2000-06-30 10:48:47 +04:00
}
/* Check permissions on unix socket directory */
if ( lstat ( WINBINDD_SOCKET_DIR , & st ) = = - 1 ) {
return - 1 ;
}
2001-07-08 22:25:19 +04:00
if ( ! S_ISDIR ( st . st_mode ) | |
( st . st_uid ! = 0 & & st . st_uid ! = geteuid ( ) ) ) {
2000-06-30 10:48:47 +04:00
return - 1 ;
}
/* Connect to socket */
strncpy ( path , WINBINDD_SOCKET_DIR , sizeof ( path ) - 1 ) ;
path [ sizeof ( path ) - 1 ] = ' \0 ' ;
strncat ( path , " / " , sizeof ( path ) - 1 ) ;
path [ sizeof ( path ) - 1 ] = ' \0 ' ;
strncat ( path , WINBINDD_SOCKET_NAME , sizeof ( path ) - 1 ) ;
path [ sizeof ( path ) - 1 ] = ' \0 ' ;
ZERO_STRUCT ( sunaddr ) ;
sunaddr . sun_family = AF_UNIX ;
strncpy ( sunaddr . sun_path , path , sizeof ( sunaddr . sun_path ) - 1 ) ;
/* If socket file doesn't exist, don't bother trying to connect
with retry . This is an attempt to make the system usable when
the winbindd daemon is not running . */
2000-05-09 15:43:00 +04:00
2000-06-30 10:48:47 +04:00
if ( lstat ( path , & st ) = = - 1 ) {
return - 1 ;
}
/* Check permissions on unix socket file */
2001-07-08 22:25:19 +04:00
if ( ! S_ISSOCK ( st . st_mode ) | |
( st . st_uid ! = 0 & & st . st_uid ! = geteuid ( ) ) ) {
2000-06-30 10:48:47 +04:00
return - 1 ;
}
/* Connect to socket */
2001-12-22 03:51:32 +03:00
if ( ( winbindd_fd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 ) {
2000-06-30 10:48:47 +04:00
return - 1 ;
}
2001-12-22 03:51:32 +03:00
if ( connect ( winbindd_fd , ( struct sockaddr * ) & sunaddr ,
2000-06-30 10:48:47 +04:00
sizeof ( sunaddr ) ) = = - 1 ) {
close_sock ( ) ;
return - 1 ;
}
2000-05-09 15:43:00 +04:00
2001-12-22 03:51:32 +03:00
return winbindd_fd ;
2000-05-09 15:43:00 +04:00
}
/* Write data to winbindd socket with timeout */
int write_sock ( void * buffer , int count )
{
2000-06-30 10:48:47 +04:00
int result , nwritten ;
/* Open connection to winbind daemon */
2000-05-09 15:43:00 +04:00
restart :
2000-06-30 10:48:47 +04:00
2001-12-22 03:51:32 +03:00
if ( winbind_open_pipe_sock ( ) = = - 1 ) {
2000-06-30 10:48:47 +04:00
return - 1 ;
}
/* Write data to socket */
nwritten = 0 ;
while ( nwritten < count ) {
struct timeval tv ;
fd_set r_fds ;
/* Catch pipe close on other end by checking if a read()
call would not block by calling select ( ) . */
FD_ZERO ( & r_fds ) ;
2001-12-22 03:51:32 +03:00
FD_SET ( winbindd_fd , & r_fds ) ;
2000-06-30 10:48:47 +04:00
ZERO_STRUCT ( tv ) ;
2001-12-22 03:51:32 +03:00
if ( select ( winbindd_fd + 1 , & r_fds , NULL , NULL , & tv ) = = - 1 ) {
2000-06-30 10:48:47 +04:00
close_sock ( ) ;
return - 1 ; /* Select error */
}
/* Write should be OK if fd not available for reading */
2001-12-22 03:51:32 +03:00
if ( ! FD_ISSET ( winbindd_fd , & r_fds ) ) {
2000-06-30 10:48:47 +04:00
/* Do the write */
2001-12-22 03:51:32 +03:00
result = write ( winbindd_fd ,
2000-06-30 10:48:47 +04:00
( char * ) buffer + nwritten ,
count - nwritten ) ;
if ( ( result = = - 1 ) | | ( result = = 0 ) ) {
/* Write failed */
close_sock ( ) ;
return - 1 ;
}
nwritten + = result ;
} else {
/* Pipe has closed on remote end */
close_sock ( ) ;
goto restart ;
}
}
return nwritten ;
2000-05-09 15:43:00 +04:00
}
/* Read data from winbindd socket with timeout */
static int read_sock ( void * buffer , int count )
{
2000-06-30 10:48:47 +04:00
int result = 0 , nread = 0 ;
2000-05-09 15:43:00 +04:00
2000-06-30 10:48:47 +04:00
/* Read data from socket */
while ( nread < count ) {
2001-12-22 03:51:32 +03:00
result = read ( winbindd_fd , ( char * ) buffer + nread ,
2000-06-30 10:48:47 +04:00
count - nread ) ;
if ( ( result = = - 1 ) | | ( result = = 0 ) ) {
/* Read failed. I think the only useful thing we
can do here is just return - 1 and fail since the
transaction has failed half way through . */
close_sock ( ) ;
return - 1 ;
}
nread + = result ;
}
return result ;
2000-05-09 15:43:00 +04:00
}
/* Read reply */
int read_reply ( struct winbindd_response * response )
{
2000-06-30 10:48:47 +04:00
int result1 , result2 = 0 ;
2000-05-09 15:43:00 +04:00
2000-06-30 10:48:47 +04:00
if ( ! response ) {
return - 1 ;
}
/* Read fixed length response */
if ( ( result1 = read_sock ( response , sizeof ( struct winbindd_response ) ) )
= = - 1 ) {
return - 1 ;
}
/* We actually send the pointer value of the extra_data field from
the server . This has no meaning in the client ' s address space
so we clear it out . */
2000-05-09 15:43:00 +04:00
2000-06-30 10:48:47 +04:00
response - > extra_data = NULL ;
2000-05-09 15:43:00 +04:00
2000-06-30 10:48:47 +04:00
/* Read variable length response */
if ( response - > length > sizeof ( struct winbindd_response ) ) {
int extra_data_len = response - > length -
sizeof ( struct winbindd_response ) ;
/* Mallocate memory for extra data */
if ( ! ( response - > extra_data = malloc ( extra_data_len ) ) ) {
return - 1 ;
}
if ( ( result2 = read_sock ( response - > extra_data , extra_data_len ) )
= = - 1 ) {
2001-12-22 03:51:32 +03:00
free_response ( response ) ;
2000-06-30 10:48:47 +04:00
return - 1 ;
}
}
/* Return total amount of data read */
return result1 + result2 ;
2000-05-09 15:43:00 +04:00
}
2001-12-22 03:51:32 +03:00
/*
* send simple types of requests
*/
2000-06-14 13:58:12 +04:00
2001-12-22 03:51:32 +03:00
NSS_STATUS winbindd_send_request ( int req_type , struct winbindd_request * request )
2000-06-14 13:58:12 +04:00
{
struct winbindd_request lrequest ;
/* Check for our tricky environment variable */
if ( getenv ( WINBINDD_DONT_ENV ) ) {
return NSS_STATUS_NOTFOUND ;
}
2001-07-10 06:28:17 +04:00
/* smbd may have excluded this domain */
if ( excluded_domain & &
strcasecmp ( excluded_domain , request - > domain ) = = 0 ) {
return NSS_STATUS_NOTFOUND ;
}
2000-06-30 10:48:47 +04:00
if ( ! request ) {
ZERO_STRUCT ( lrequest ) ;
request = & lrequest ;
}
2000-06-14 13:58:12 +04:00
/* Fill in request and send down pipe */
2000-07-17 06:37:11 +04:00
2000-06-14 13:58:12 +04:00
init_request ( request , req_type ) ;
if ( write_sock ( request , sizeof ( * request ) ) = = - 1 ) {
return NSS_STATUS_UNAVAIL ;
}
2001-12-22 03:51:32 +03:00
return NSS_STATUS_SUCCESS ;
}
/*
* Get results from winbindd request
*/
NSS_STATUS winbindd_get_response ( struct winbindd_response * response )
{
struct winbindd_response lresponse ;
if ( ! response ) {
ZERO_STRUCT ( lresponse ) ;
response = & lresponse ;
}
init_response ( response ) ;
2000-06-14 13:58:12 +04:00
/* Wait for reply */
if ( read_reply ( response ) = = - 1 ) {
return NSS_STATUS_UNAVAIL ;
}
/* Throw away extra data if client didn't request it */
if ( response = = & lresponse ) {
free_response ( response ) ;
}
/* Copy reply data from socket */
if ( response - > result ! = WINBINDD_OK ) {
return NSS_STATUS_NOTFOUND ;
}
return NSS_STATUS_SUCCESS ;
}
2001-12-22 03:51:32 +03:00
/* Handle simple types of requests */
NSS_STATUS winbindd_request ( int req_type ,
struct winbindd_request * request ,
struct winbindd_response * response )
{
NSS_STATUS status ;
status = winbindd_send_request ( req_type , request ) ;
if ( status ! = NSS_STATUS_SUCCESS )
return ( status ) ;
return winbindd_get_response ( response ) ;
}