nsswitch/wb_common.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003 
00004    winbind client common code
00005 
00006    Copyright (C) Tim Potter 2000
00007    Copyright (C) Andrew Tridgell 2000
00008    Copyright (C) Andrew Bartlett 2002
00009    
00010    
00011    This library is free software; you can redistribute it and/or
00012    modify it under the terms of the GNU Library General Public
00013    License as published by the Free Software Foundation; either
00014    version 2 of the License, or (at your option) any later version.
00015    
00016    This library is distributed in the hope that it will be useful,
00017    but WITHOUT ANY WARRANTY; without even the implied warranty of
00018    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00019    Library General Public License for more details.
00020    
00021    You should have received a copy of the GNU Library General Public
00022    License along with this library; if not, write to the
00023    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00024    Boston, MA  02111-1307, USA.   
00025 */
00026 
00027 #include "winbind_client.h"
00028 
00029 BOOL winbind_env_set( void );
00030 BOOL winbind_off( void );
00031 BOOL winbind_on( void );
00032 
00033 /* Global variables.  These are effectively the client state information */
00034 
00035 int winbindd_fd = -1;           /* fd for winbindd socket */
00036 static int is_privileged = 0;
00037 
00038 /* Free a response structure */
00039 
00040 void free_response(struct winbindd_response *response)
00041 {
00042         /* Free any allocated extra_data */
00043 
00044         if (response)
00045                 SAFE_FREE(response->extra_data.data);
00046 }
00047 
00048 /* Initialise a request structure */
00049 
00050 void init_request(struct winbindd_request *request, int request_type)
00051 {
00052         request->length = sizeof(struct winbindd_request);
00053 
00054         request->cmd = (enum winbindd_cmd)request_type;
00055         request->pid = getpid();
00056 
00057 }
00058 
00059 /* Initialise a response structure */
00060 
00061 static void init_response(struct winbindd_response *response)
00062 {
00063         /* Initialise return value */
00064 
00065         response->result = WINBINDD_ERROR;
00066 }
00067 
00068 /* Close established socket */
00069 
00070 void close_sock(void)
00071 {
00072         if (winbindd_fd != -1) {
00073                 close(winbindd_fd);
00074                 winbindd_fd = -1;
00075         }
00076 }
00077 
00078 #define CONNECT_TIMEOUT 30
00079 
00080 /* Make sure socket handle isn't stdin, stdout or stderr */
00081 #define RECURSION_LIMIT 3
00082 
00083 static int make_nonstd_fd_internals(int fd, int limit /* Recursion limiter */) 
00084 {
00085         int new_fd;
00086         if (fd >= 0 && fd <= 2) {
00087 #ifdef F_DUPFD 
00088                 if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
00089                         return -1;
00090                 }
00091                 /* Paranoia */
00092                 if (new_fd < 3) {
00093                         close(new_fd);
00094                         return -1;
00095                 }
00096                 close(fd);
00097                 return new_fd;
00098 #else
00099                 if (limit <= 0)
00100                         return -1;
00101                 
00102                 new_fd = dup(fd);
00103                 if (new_fd == -1) 
00104                         return -1;
00105 
00106                 /* use the program stack to hold our list of FDs to close */
00107                 new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
00108                 close(fd);
00109                 return new_fd;
00110 #endif
00111         }
00112         return fd;
00113 }
00114 
00115 /****************************************************************************
00116  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
00117  else
00118  if SYSV use O_NDELAY
00119  if BSD use FNDELAY
00120  Set close on exec also.
00121 ****************************************************************************/
00122 
00123 static int make_safe_fd(int fd) 
00124 {
00125         int result, flags;
00126         int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
00127         if (new_fd == -1) {
00128                 close(fd);
00129                 return -1;
00130         }
00131 
00132         /* Socket should be nonblocking. */
00133 #ifdef O_NONBLOCK
00134 #define FLAG_TO_SET O_NONBLOCK
00135 #else
00136 #ifdef SYSV
00137 #define FLAG_TO_SET O_NDELAY
00138 #else /* BSD */
00139 #define FLAG_TO_SET FNDELAY
00140 #endif
00141 #endif
00142 
00143         if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
00144                 close(new_fd);
00145                 return -1;
00146         }
00147 
00148         flags |= FLAG_TO_SET;
00149         if (fcntl(new_fd, F_SETFL, flags) == -1) {
00150                 close(new_fd);
00151                 return -1;
00152         }
00153 
00154 #undef FLAG_TO_SET
00155 
00156         /* Socket should be closed on exec() */
00157 #ifdef FD_CLOEXEC
00158         result = flags = fcntl(new_fd, F_GETFD, 0);
00159         if (flags >= 0) {
00160                 flags |= FD_CLOEXEC;
00161                 result = fcntl( new_fd, F_SETFD, flags );
00162         }
00163         if (result < 0) {
00164                 close(new_fd);
00165                 return -1;
00166         }
00167 #endif
00168         return new_fd;
00169 }
00170 
00171 /* Connect to winbindd socket */
00172 
00173 static int winbind_named_pipe_sock(const char *dir)
00174 {
00175         struct sockaddr_un sunaddr;
00176         struct stat st;
00177         pstring path;
00178         int fd;
00179         int wait_time;
00180         int slept;
00181         
00182         /* Check permissions on unix socket directory */
00183         
00184         if (lstat(dir, &st) == -1) {
00185                 errno = ENOENT;
00186                 return -1;
00187         }
00188         
00189         if (!S_ISDIR(st.st_mode) || 
00190             (st.st_uid != 0 && st.st_uid != geteuid())) {
00191                 errno = ENOENT;
00192                 return -1;
00193         }
00194         
00195         /* Connect to socket */
00196         
00197         strncpy(path, dir, sizeof(path) - 1);
00198         path[sizeof(path) - 1] = '\0';
00199         
00200         strncat(path, "/", sizeof(path) - 1 - strlen(path));
00201         path[sizeof(path) - 1] = '\0';
00202         
00203         strncat(path, WINBINDD_SOCKET_NAME, sizeof(path) - 1 - strlen(path));
00204         path[sizeof(path) - 1] = '\0';
00205         
00206         ZERO_STRUCT(sunaddr);
00207         sunaddr.sun_family = AF_UNIX;
00208         strncpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path) - 1);
00209         
00210         /* If socket file doesn't exist, don't bother trying to connect
00211            with retry.  This is an attempt to make the system usable when
00212            the winbindd daemon is not running. */
00213 
00214         if (lstat(path, &st) == -1) {
00215                 errno = ENOENT;
00216                 return -1;
00217         }
00218         
00219         /* Check permissions on unix socket file */
00220         
00221         if (!S_ISSOCK(st.st_mode) || 
00222             (st.st_uid != 0 && st.st_uid != geteuid())) {
00223                 errno = ENOENT;
00224                 return -1;
00225         }
00226         
00227         /* Connect to socket */
00228         
00229         if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
00230                 return -1;
00231         }
00232 
00233         /* Set socket non-blocking and close on exec. */
00234 
00235         if ((fd = make_safe_fd( fd)) == -1) {
00236                 return fd;
00237         }
00238 
00239         for (wait_time = 0; connect(fd, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1;
00240                         wait_time += slept) {
00241                 struct timeval tv;
00242                 fd_set w_fds;
00243                 int ret;
00244                 int connect_errno = 0;
00245                 socklen_t errnosize;
00246 
00247                 if (wait_time >= CONNECT_TIMEOUT)
00248                         goto error_out;
00249 
00250                 switch (errno) {
00251                         case EINPROGRESS:
00252                                 FD_ZERO(&w_fds);
00253                                 FD_SET(fd, &w_fds);
00254                                 tv.tv_sec = CONNECT_TIMEOUT - wait_time;
00255                                 tv.tv_usec = 0;
00256 
00257                                 ret = select(fd + 1, NULL, &w_fds, NULL, &tv);
00258 
00259                                 if (ret > 0) {
00260                                         errnosize = sizeof(connect_errno);
00261 
00262                                         ret = getsockopt(fd, SOL_SOCKET,
00263                                                         SO_ERROR, &connect_errno, &errnosize);
00264 
00265                                         if (ret >= 0 && connect_errno == 0) {
00266                                                 /* Connect succeed */
00267                                                 goto out;
00268                                         }
00269                                 }
00270 
00271                                 slept = CONNECT_TIMEOUT;
00272                                 break;
00273                         case EAGAIN:
00274                                 slept = rand() % 3 + 1;
00275                                 sleep(slept);
00276                                 break;
00277                         default:
00278                                 goto error_out;
00279                 }
00280 
00281         }
00282 
00283   out:
00284 
00285         return fd;
00286 
00287   error_out:
00288 
00289         close(fd);
00290         return -1;
00291 }
00292 
00293 /* Connect to winbindd socket */
00294 
00295 static int winbind_open_pipe_sock(int recursing, int need_priv)
00296 {
00297 #ifdef HAVE_UNIXSOCKET
00298         static pid_t our_pid;
00299         struct winbindd_request request;
00300         struct winbindd_response response;
00301         ZERO_STRUCT(request);
00302         ZERO_STRUCT(response);
00303 
00304         if (our_pid != getpid()) {
00305                 close_sock();
00306                 our_pid = getpid();
00307         }
00308 
00309         if ((need_priv != 0) && (is_privileged == 0)) {
00310                 close_sock();
00311         }
00312         
00313         if (winbindd_fd != -1) {
00314                 return winbindd_fd;
00315         }
00316 
00317         if (recursing) {
00318                 return -1;
00319         }
00320 
00321         if ((winbindd_fd = winbind_named_pipe_sock(WINBINDD_SOCKET_DIR)) == -1) {
00322                 return -1;
00323         }
00324 
00325         is_privileged = 0;
00326 
00327         /* version-check the socket */
00328 
00329         request.flags = WBFLAG_RECURSE;
00330         if ((winbindd_request_response(WINBINDD_INTERFACE_VERSION, &request, &response) != NSS_STATUS_SUCCESS) || (response.data.interface_version != WINBIND_INTERFACE_VERSION)) {
00331                 close_sock();
00332                 return -1;
00333         }
00334 
00335         /* try and get priv pipe */
00336 
00337         request.flags = WBFLAG_RECURSE;
00338         if (winbindd_request_response(WINBINDD_PRIV_PIPE_DIR, &request, &response) == NSS_STATUS_SUCCESS) {
00339                 int fd;
00340                 if ((fd = winbind_named_pipe_sock((char *)response.extra_data.data)) != -1) {
00341                         close(winbindd_fd);
00342                         winbindd_fd = fd;
00343                         is_privileged = 1;
00344                 }
00345         }
00346 
00347         if ((need_priv != 0) && (is_privileged == 0)) {
00348                 return -1;
00349         }
00350 
00351         SAFE_FREE(response.extra_data.data);
00352 
00353         return winbindd_fd;
00354 #else
00355         return -1;
00356 #endif /* HAVE_UNIXSOCKET */
00357 }
00358 
00359 /* Write data to winbindd socket */
00360 
00361 int write_sock(void *buffer, int count, int recursing, int need_priv)
00362 {
00363         int result, nwritten;
00364         
00365         /* Open connection to winbind daemon */
00366         
00367  restart:
00368         
00369         if (winbind_open_pipe_sock(recursing, need_priv) == -1) {
00370                 errno = ENOENT;
00371                 return -1;
00372         }
00373         
00374         /* Write data to socket */
00375         
00376         nwritten = 0;
00377         
00378         while(nwritten < count) {
00379                 struct timeval tv;
00380                 fd_set r_fds;
00381                 
00382                 /* Catch pipe close on other end by checking if a read()
00383                    call would not block by calling select(). */
00384 
00385                 FD_ZERO(&r_fds);
00386                 FD_SET(winbindd_fd, &r_fds);
00387                 ZERO_STRUCT(tv);
00388                 
00389                 if (select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv) == -1) {
00390                         close_sock();
00391                         return -1;                   /* Select error */
00392                 }
00393                 
00394                 /* Write should be OK if fd not available for reading */
00395                 
00396                 if (!FD_ISSET(winbindd_fd, &r_fds)) {
00397                         
00398                         /* Do the write */
00399                         
00400                         result = write(winbindd_fd,
00401                                        (char *)buffer + nwritten, 
00402                                        count - nwritten);
00403                         
00404                         if ((result == -1) || (result == 0)) {
00405                                 
00406                                 /* Write failed */
00407                                 
00408                                 close_sock();
00409                                 return -1;
00410                         }
00411                         
00412                         nwritten += result;
00413                         
00414                 } else {
00415                         
00416                         /* Pipe has closed on remote end */
00417                         
00418                         close_sock();
00419                         goto restart;
00420                 }
00421         }
00422         
00423         return nwritten;
00424 }
00425 
00426 /* Read data from winbindd socket */
00427 
00428 static int read_sock(void *buffer, int count)
00429 {
00430         int nread = 0;
00431         int total_time = 0, selret;
00432 
00433         if (winbindd_fd == -1) {
00434                 return -1;
00435         }
00436 
00437         /* Read data from socket */
00438         while(nread < count) {
00439                 struct timeval tv;
00440                 fd_set r_fds;
00441                 
00442                 /* Catch pipe close on other end by checking if a read()
00443                    call would not block by calling select(). */
00444 
00445                 FD_ZERO(&r_fds);
00446                 FD_SET(winbindd_fd, &r_fds);
00447                 ZERO_STRUCT(tv);
00448                 /* Wait for 5 seconds for a reply. May need to parameterise this... */
00449                 tv.tv_sec = 5;
00450 
00451                 if ((selret = select(winbindd_fd + 1, &r_fds, NULL, NULL, &tv)) == -1) {
00452                         close_sock();
00453                         return -1;                   /* Select error */
00454                 }
00455                 
00456                 if (selret == 0) {
00457                         /* Not ready for read yet... */
00458                         if (total_time >= 30) {
00459                                 /* Timeout */
00460                                 close_sock();
00461                                 return -1;
00462                         }
00463                         total_time += 5;
00464                         continue;
00465                 }
00466 
00467                 if (FD_ISSET(winbindd_fd, &r_fds)) {
00468                         
00469                         /* Do the Read */
00470                         
00471                         int result = read(winbindd_fd, (char *)buffer + nread, 
00472                               count - nread);
00473                         
00474                         if ((result == -1) || (result == 0)) {
00475                                 
00476                                 /* Read failed.  I think the only useful thing we
00477                                    can do here is just return -1 and fail since the
00478                                    transaction has failed half way through. */
00479                         
00480                                 close_sock();
00481                                 return -1;
00482                         }
00483                         
00484                         nread += result;
00485                         
00486                 }
00487         }
00488         
00489         return nread;
00490 }
00491 
00492 /* Read reply */
00493 
00494 int read_reply(struct winbindd_response *response)
00495 {
00496         int result1, result2 = 0;
00497 
00498         if (!response) {
00499                 return -1;
00500         }
00501         
00502         /* Read fixed length response */
00503         
00504         if ((result1 = read_sock(response, sizeof(struct winbindd_response)))
00505             == -1) {
00506                 
00507                 return -1;
00508         }
00509         
00510         /* We actually send the pointer value of the extra_data field from
00511            the server.  This has no meaning in the client's address space
00512            so we clear it out. */
00513 
00514         response->extra_data.data = NULL;
00515 
00516         /* Read variable length response */
00517         
00518         if (response->length > sizeof(struct winbindd_response)) {
00519                 int extra_data_len = response->length - 
00520                         sizeof(struct winbindd_response);
00521                 
00522                 /* Mallocate memory for extra data */
00523                 
00524                 if (!(response->extra_data.data = malloc(extra_data_len))) {
00525                         return -1;
00526                 }
00527                 
00528                 if ((result2 = read_sock(response->extra_data.data, extra_data_len))
00529                     == -1) {
00530                         free_response(response);
00531                         return -1;
00532                 }
00533         }
00534         
00535         /* Return total amount of data read */
00536         
00537         return result1 + result2;
00538 }
00539 
00540 BOOL winbind_env_set( void )
00541 {
00542         char *env;
00543         
00544         if ((env=getenv(WINBINDD_DONT_ENV)) != NULL) {
00545                 if(strcmp(env, "1") == 0) {
00546                         return True;
00547                 }
00548         }
00549         return False;
00550 }
00551 
00552 /* 
00553  * send simple types of requests 
00554  */
00555 
00556 NSS_STATUS winbindd_send_request(int req_type, int need_priv,
00557                                  struct winbindd_request *request)
00558 {
00559         struct winbindd_request lrequest;
00560 
00561         /* Check for our tricky environment variable */
00562 
00563         if (winbind_env_set()) {
00564                 return NSS_STATUS_NOTFOUND;
00565         }
00566 
00567         if (!request) {
00568                 ZERO_STRUCT(lrequest);
00569                 request = &lrequest;
00570         }
00571         
00572         /* Fill in request and send down pipe */
00573 
00574         init_request(request, req_type);
00575         
00576         if (write_sock(request, sizeof(*request),
00577                        request->flags & WBFLAG_RECURSE, need_priv) == -1) {
00578                 /* Set ENOENT for consistency.  Required by some apps */
00579                 errno = ENOENT;
00580                 
00581                 return NSS_STATUS_UNAVAIL;
00582         }
00583 
00584         if ((request->extra_len != 0) &&
00585             (write_sock(request->extra_data.data, request->extra_len,
00586                         request->flags & WBFLAG_RECURSE, need_priv) == -1)) {
00587                 /* Set ENOENT for consistency.  Required by some apps */
00588                 errno = ENOENT;
00589 
00590                 return NSS_STATUS_UNAVAIL;
00591         }
00592         
00593         return NSS_STATUS_SUCCESS;
00594 }
00595 
00596 /*
00597  * Get results from winbindd request
00598  */
00599 
00600 NSS_STATUS winbindd_get_response(struct winbindd_response *response)
00601 {
00602         struct winbindd_response lresponse;
00603 
00604         if (!response) {
00605                 ZERO_STRUCT(lresponse);
00606                 response = &lresponse;
00607         }
00608 
00609         init_response(response);
00610 
00611         /* Wait for reply */
00612         if (read_reply(response) == -1) {
00613                 /* Set ENOENT for consistency.  Required by some apps */
00614                 errno = ENOENT;
00615 
00616                 return NSS_STATUS_UNAVAIL;
00617         }
00618 
00619         /* Throw away extra data if client didn't request it */
00620         if (response == &lresponse) {
00621                 free_response(response);
00622         }
00623 
00624         /* Copy reply data from socket */
00625         if (response->result != WINBINDD_OK) {
00626                 return NSS_STATUS_NOTFOUND;
00627         }
00628         
00629         return NSS_STATUS_SUCCESS;
00630 }
00631 
00632 /* Handle simple types of requests */
00633 
00634 NSS_STATUS winbindd_request_response(int req_type, 
00635                             struct winbindd_request *request,
00636                             struct winbindd_response *response)
00637 {
00638         NSS_STATUS status = NSS_STATUS_UNAVAIL;
00639         int count = 0;
00640 
00641         while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
00642                 status = winbindd_send_request(req_type, 0, request);
00643                 if (status != NSS_STATUS_SUCCESS) 
00644                         return(status);
00645                 status = winbindd_get_response(response);
00646                 count += 1;
00647         }
00648 
00649         return status;
00650 }
00651 
00652 NSS_STATUS winbindd_priv_request_response(int req_type, 
00653                                           struct winbindd_request *request,
00654                                           struct winbindd_response *response)
00655 {
00656         NSS_STATUS status = NSS_STATUS_UNAVAIL;
00657         int count = 0;
00658 
00659         while ((status == NSS_STATUS_UNAVAIL) && (count < 10)) {
00660                 status = winbindd_send_request(req_type, 1, request);
00661                 if (status != NSS_STATUS_SUCCESS) 
00662                         return(status);
00663                 status = winbindd_get_response(response);
00664                 count += 1;
00665         }
00666 
00667         return status;
00668 }
00669 
00670 /*************************************************************************
00671  A couple of simple functions to disable winbindd lookups and re-
00672  enable them
00673  ************************************************************************/
00674  
00675 /* Use putenv() instead of setenv() in these functions as not all
00676    environments have the latter. */
00677 
00678 BOOL winbind_off( void )
00679 {
00680         static char *s = CONST_DISCARD(char *, WINBINDD_DONT_ENV "=1");
00681 
00682         return putenv(s) != -1;
00683 }
00684 
00685 BOOL winbind_on( void )
00686 {
00687         static char *s = CONST_DISCARD(char *, WINBINDD_DONT_ENV "=0");
00688 
00689         return putenv(s) != -1;
00690 }
00691 

Sambaに対してSat Aug 29 21:23:09 2009に生成されました。  doxygen 1.4.7