2000-05-09 15:43:00 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2000-05-09 15:43:00 +04:00
winbind client common code
Copyright ( C ) Tim Potter 2000
Copyright ( C ) Andrew Tridgell 2000
2002-09-25 19:19:00 +04:00
Copyright ( C ) Andrew Bartlett 2002
2000-05-09 15:43:00 +04:00
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 .
*/
2002-12-20 04:37:39 +03:00
# include "winbind_client.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
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 ) ;
}
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
{
2002-01-11 02:45:29 +03:00
request - > length = sizeof ( struct winbindd_request ) ;
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 ( ) ;
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 */
2002-09-25 19:19:00 +04:00
void close_sock ( void )
2000-05-09 15:43:00 +04:00
{
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
}
2002-09-25 19:19:00 +04:00
/* Make sure socket handle isn't stdin, stdout or stderr */
# define RECURSION_LIMIT 3
static int make_nonstd_fd_internals ( int fd , int limit /* Recursion limiter */ )
{
int new_fd ;
if ( fd > = 0 & & fd < = 2 ) {
# ifdef F_DUPFD
if ( ( new_fd = fcntl ( fd , F_DUPFD , 3 ) ) = = - 1 ) {
return - 1 ;
}
/* Parinoia */
if ( new_fd < 3 ) {
close ( new_fd ) ;
return - 1 ;
}
close ( fd ) ;
return new_fd ;
# else
if ( limit < = 0 )
return - 1 ;
new_fd = dup ( fd ) ;
if ( new_fd = = - 1 )
return - 1 ;
/* use the program stack to hold our list of FDs to close */
new_fd = make_nonstd_fd_internals ( new_fd , limit - 1 ) ;
close ( fd ) ;
return new_fd ;
# endif
}
return fd ;
}
static int make_safe_fd ( int fd )
{
int result , flags ;
int new_fd = make_nonstd_fd_internals ( fd , RECURSION_LIMIT ) ;
if ( new_fd = = - 1 ) {
close ( fd ) ;
return - 1 ;
}
/* Socket should be closed on exec() */
# ifdef FD_CLOEXEC
result = flags = fcntl ( new_fd , F_GETFD , 0 ) ;
if ( flags > = 0 ) {
flags | = FD_CLOEXEC ;
result = fcntl ( new_fd , F_SETFD , flags ) ;
}
if ( result < 0 ) {
close ( new_fd ) ;
return - 1 ;
}
# endif
return new_fd ;
}
2000-05-09 15:43:00 +04:00
/* Connect to winbindd socket */
2003-03-24 12:54:13 +03:00
static int winbind_named_pipe_sock ( const char * dir )
2000-05-09 15:43:00 +04:00
{
2000-06-30 10:48:47 +04:00
struct sockaddr_un sunaddr ;
struct stat st ;
pstring path ;
2002-09-25 19:19:00 +04:00
int fd ;
2000-06-30 10:48:47 +04:00
/* Check permissions on unix socket directory */
2003-03-24 12:54:13 +03:00
if ( lstat ( dir , & st ) = = - 1 ) {
2000-06-30 10:48:47 +04:00
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 */
2003-03-24 12:54:13 +03:00
strncpy ( path , dir , sizeof ( path ) - 1 ) ;
2000-06-30 10:48:47 +04:00
path [ sizeof ( path ) - 1 ] = ' \0 ' ;
2003-03-24 12:54:13 +03:00
strncat ( path , " / " , sizeof ( path ) - 1 - strlen ( path ) ) ;
2000-06-30 10:48:47 +04:00
path [ sizeof ( path ) - 1 ] = ' \0 ' ;
2003-03-24 12:54:13 +03:00
strncat ( path , WINBINDD_SOCKET_NAME , sizeof ( path ) - 1 - strlen ( path ) ) ;
2000-06-30 10:48:47 +04:00
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 */
2002-09-25 19:19:00 +04:00
if ( ( fd = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 ) {
2000-06-30 10:48:47 +04:00
return - 1 ;
}
2002-09-25 19:19:00 +04:00
2003-03-24 12:54:13 +03:00
if ( ( fd = make_safe_fd ( fd ) ) = = - 1 ) {
return fd ;
2002-09-25 19:19:00 +04:00
}
2000-06-30 10:48:47 +04:00
2003-03-24 12:54:13 +03:00
if ( connect ( fd , ( struct sockaddr * ) & sunaddr ,
2000-06-30 10:48:47 +04:00
sizeof ( sunaddr ) ) = = - 1 ) {
2003-03-24 12:54:13 +03:00
close ( fd ) ;
2000-06-30 10:48:47 +04:00
return - 1 ;
}
2000-05-09 15:43:00 +04:00
2003-03-24 12:54:13 +03:00
return fd ;
}
/* Connect to winbindd socket */
int winbind_open_pipe_sock ( void )
{
# ifdef HAVE_UNIXSOCKET
static pid_t our_pid ;
struct winbindd_request request ;
struct winbindd_response response ;
ZERO_STRUCT ( request ) ;
ZERO_STRUCT ( response ) ;
if ( our_pid ! = getpid ( ) ) {
close_sock ( ) ;
our_pid = getpid ( ) ;
}
if ( winbindd_fd ! = - 1 ) {
return winbindd_fd ;
}
if ( ( winbindd_fd = winbind_named_pipe_sock ( WINBINDD_SOCKET_DIR ) ) = = - 1 ) {
return - 1 ;
}
/* version-check the socket */
if ( ( winbindd_request ( WINBINDD_INTERFACE_VERSION , & request , & response ) ! = NSS_STATUS_SUCCESS ) | | ( response . data . interface_version ! = WINBIND_INTERFACE_VERSION ) ) {
close_sock ( ) ;
return - 1 ;
}
/* try and get priv pipe */
if ( winbindd_request ( WINBINDD_PRIV_PIPE_DIR , & request , & response ) = = NSS_STATUS_SUCCESS ) {
int fd ;
if ( ( fd = winbind_named_pipe_sock ( response . extra_data ) ) ! = - 1 ) {
close ( winbindd_fd ) ;
winbindd_fd = fd ;
}
}
2001-12-22 03:51:32 +03:00
return winbindd_fd ;
2002-09-25 19:19:00 +04:00
# else
return - 1 ;
# endif /* HAVE_UNIXSOCKET */
2000-05-09 15:43:00 +04:00
}
2002-07-15 14:35:28 +04:00
/* Write data to winbindd socket */
2000-05-09 15:43:00 +04:00
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
}
2002-07-15 14:35:28 +04:00
/* Read data from winbindd socket */
2000-05-09 15:43:00 +04:00
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 ;
2003-06-30 20:18:29 +04:00
char * env ;
int value ;
2000-06-14 13:58:12 +04:00
/* Check for our tricky environment variable */
2003-06-30 20:18:29 +04:00
if ( ( env = getenv ( WINBINDD_DONT_ENV ) ) ! = NULL ) {
value = atoi ( env ) ;
if ( value = = 1 )
return NSS_STATUS_NOTFOUND ;
2000-06-14 13:58:12 +04:00
}
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 ,
2002-09-25 19:19:00 +04:00
struct winbindd_request * request ,
struct winbindd_response * response )
2001-12-22 03:51:32 +03:00
{
NSS_STATUS status ;
status = winbindd_send_request ( req_type , request ) ;
if ( status ! = NSS_STATUS_SUCCESS )
return ( status ) ;
return winbindd_get_response ( response ) ;
}