LCOV - code coverage report
Current view: top level - sss_client - common.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 212 495 42.8 %
Date: 2015-10-19 Functions: 25 35 71.4 %

          Line data    Source code
       1             : /*
       2             :  * System Security Services Daemon. NSS client interface
       3             :  *
       4             :  * Copyright (C) Simo Sorce 2007
       5             :  *
       6             :  * Winbind derived code:
       7             :  * Copyright (C) Tim Potter 2000
       8             :  * Copyright (C) Andrew Tridgell 2000
       9             :  * Copyright (C) Andrew Bartlett 2002
      10             :  *
      11             :  * This program is free software; you can redistribute it and/or modify
      12             :  * it under the terms of the GNU Lesser General Public License as
      13             :  * published by the Free Software Foundation; either version 2.1 of the
      14             :  * License, or (at your option) any later version.
      15             :  *
      16             :  * This program is distributed in the hope that it will be useful,
      17             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :  * GNU Lesser General Public License for more details.
      20             :  *
      21             :  * You should have received a copy of the GNU Lesser General Public License
      22             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             :  */
      24             : 
      25             : #include "config.h"
      26             : 
      27             : #include <nss.h>
      28             : #include <security/pam_modules.h>
      29             : #include <errno.h>
      30             : #include <sys/types.h>
      31             : #include <sys/socket.h>
      32             : #include <sys/un.h>
      33             : #include <sys/stat.h>
      34             : #include <unistd.h>
      35             : #include <stdlib.h>
      36             : #include <stdbool.h>
      37             : #include <stdint.h>
      38             : #include <string.h>
      39             : #include <fcntl.h>
      40             : #include <poll.h>
      41             : #include <time.h>
      42             : 
      43             : #include <libintl.h>
      44             : #define _(STRING) dgettext (PACKAGE, STRING)
      45             : #include "sss_cli.h"
      46             : 
      47             : #if HAVE_PTHREAD
      48             : #include <pthread.h>
      49             : #endif
      50             : 
      51             : /*
      52             : * Note we set MSG_NOSIGNAL to avoid
      53             : * having to fiddle with signal masks
      54             : * but also do not want to die in case
      55             : * SIGPIPE gets raised and the application
      56             : * does not handle it.
      57             : */
      58             : #ifdef MSG_NOSIGNAL
      59             : #define SSS_DEFAULT_WRITE_FLAGS MSG_NOSIGNAL
      60             : #else
      61             : #define SSS_DEFAULT_WRITE_FLAGS 0
      62             : #endif
      63             : 
      64             : /* common functions */
      65             : 
      66             : int sss_cli_sd = -1; /* the sss client socket descriptor */
      67             : struct stat sss_cli_sb; /* the sss client stat buffer */
      68             : 
      69             : #if HAVE_FUNCTION_ATTRIBUTE_DESTRUCTOR
      70             : __attribute__((destructor))
      71             : #endif
      72          28 : static void sss_cli_close_socket(void)
      73             : {
      74          28 :     if (sss_cli_sd != -1) {
      75           0 :         close(sss_cli_sd);
      76           0 :         sss_cli_sd = -1;
      77             :     }
      78          28 : }
      79             : 
      80             : /* Requests:
      81             :  *
      82             :  * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X)
      83             :  * byte 4-7: 32bit unsigned with command code
      84             :  * byte 8-11: 32bit unsigned (reserved)
      85             :  * byte 12-15: 32bit unsigned (reserved)
      86             :  * byte 16-X: (optional) request structure associated to the command code used
      87             :  */
      88           2 : static enum sss_status sss_cli_send_req(enum sss_cli_command cmd,
      89             :                                         struct sss_cli_req_data *rd,
      90             :                                         int *errnop)
      91             : {
      92             :     uint32_t header[4];
      93             :     size_t datasent;
      94             : 
      95           2 :     header[0] = SSS_NSS_HEADER_SIZE + (rd?rd->len:0);
      96           2 :     header[1] = cmd;
      97           2 :     header[2] = 0;
      98           2 :     header[3] = 0;
      99             : 
     100           2 :     datasent = 0;
     101             : 
     102           8 :     while (datasent < header[0]) {
     103             :         struct pollfd pfd;
     104             :         int rdsent;
     105             :         int res, error;
     106             : 
     107           4 :         *errnop = 0;
     108           4 :         pfd.fd = sss_cli_sd;
     109           4 :         pfd.events = POLLOUT;
     110             : 
     111             :         do {
     112           4 :             errno = 0;
     113           4 :             res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
     114           4 :             error = errno;
     115             : 
     116             :             /* If error is EINTR here, we'll try again
     117             :              * If it's any other error, we'll catch it
     118             :              * below.
     119             :              */
     120           4 :         } while (error == EINTR);
     121             : 
     122           4 :         switch (res) {
     123             :         case -1:
     124           0 :             *errnop = error;
     125           0 :             break;
     126             :         case 0:
     127           0 :             *errnop = ETIME;
     128           0 :             break;
     129             :         case 1:
     130           4 :             if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
     131           0 :                 *errnop = EPIPE;
     132             :             }
     133           4 :             if (!(pfd.revents & POLLOUT)) {
     134           0 :                 *errnop = EBUSY;
     135             :             }
     136           4 :             break;
     137             :         default: /* more than one avail ?? */
     138           0 :             *errnop = EBADF;
     139           0 :             break;
     140             :         }
     141           4 :         if (*errnop) {
     142           0 :             sss_cli_close_socket();
     143           0 :             return SSS_STATUS_UNAVAIL;
     144             :         }
     145             : 
     146           4 :         errno = 0;
     147           4 :         if (datasent < SSS_NSS_HEADER_SIZE) {
     148           2 :             res = send(sss_cli_sd,
     149             :                        (char *)header + datasent,
     150             :                        SSS_NSS_HEADER_SIZE - datasent,
     151             :                        SSS_DEFAULT_WRITE_FLAGS);
     152             :         } else {
     153           2 :             rdsent = datasent - SSS_NSS_HEADER_SIZE;
     154           4 :             res = send(sss_cli_sd,
     155           2 :                        (const char *)rd->data + rdsent,
     156           2 :                        rd->len - rdsent,
     157             :                        SSS_DEFAULT_WRITE_FLAGS);
     158             :         }
     159           4 :         error = errno;
     160             : 
     161           4 :         if ((res == -1) || (res == 0)) {
     162           0 :             if ((error == EINTR) || error == EAGAIN) {
     163             :                 /* If the write was interrupted, go back through
     164             :                  * the loop and try again
     165             :                  */
     166           0 :                 continue;
     167             :             }
     168             : 
     169             :             /* Write failed */
     170           0 :             sss_cli_close_socket();
     171           0 :             *errnop = error;
     172           0 :             return SSS_STATUS_UNAVAIL;
     173             :         }
     174             : 
     175           4 :         datasent += res;
     176             :     }
     177             : 
     178           2 :     return SSS_STATUS_SUCCESS;
     179             : }
     180             : 
     181             : /* Replies:
     182             :  *
     183             :  * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X)
     184             :  * byte 4-7: 32bit unsigned with command code
     185             :  * byte 8-11: 32bit unsigned with the request status (server errno)
     186             :  * byte 12-15: 32bit unsigned (reserved)
     187             :  * byte 16-X: (optional) reply structure associated to the command code used
     188             :  */
     189             : 
     190           2 : static enum sss_status sss_cli_recv_rep(enum sss_cli_command cmd,
     191             :                                         uint8_t **_buf, int *_len,
     192             :                                         int *errnop)
     193             : {
     194             :     uint32_t header[4];
     195             :     size_t datarecv;
     196           2 :     uint8_t *buf = NULL;
     197           2 :     bool pollhup = false;
     198             :     int len;
     199             :     int ret;
     200             : 
     201           2 :     header[0] = SSS_NSS_HEADER_SIZE; /* unitl we know the real length */
     202           2 :     header[1] = 0;
     203           2 :     header[2] = 0;
     204           2 :     header[3] = 0;
     205             : 
     206           2 :     datarecv = 0;
     207           2 :     buf = NULL;
     208           2 :     len = 0;
     209           2 :     *errnop = 0;
     210             : 
     211           8 :     while (datarecv < header[0]) {
     212             :         struct pollfd pfd;
     213             :         int bufrecv;
     214             :         int res, error;
     215             : 
     216           4 :         pfd.fd = sss_cli_sd;
     217           4 :         pfd.events = POLLIN;
     218             : 
     219             :         do {
     220           4 :             errno = 0;
     221           4 :             res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
     222           4 :             error = errno;
     223             : 
     224             :             /* If error is EINTR here, we'll try again
     225             :              * If it's any other error, we'll catch it
     226             :              * below.
     227             :              */
     228           4 :         } while (error == EINTR);
     229             : 
     230           4 :         switch (res) {
     231             :         case -1:
     232           0 :             *errnop = error;
     233           0 :             break;
     234             :         case 0:
     235           0 :             *errnop = ETIME;
     236           0 :             break;
     237             :         case 1:
     238           4 :             if (pfd.revents & (POLLHUP)) {
     239           0 :                 pollhup = true;
     240             :             }
     241           4 :             if (pfd.revents & (POLLERR | POLLNVAL)) {
     242           0 :                 *errnop = EPIPE;
     243             :             }
     244           4 :             if (!(pfd.revents & POLLIN)) {
     245           0 :                 *errnop = EBUSY;
     246             :             }
     247           4 :             break;
     248             :         default: /* more than one avail ?? */
     249           0 :             *errnop = EBADF;
     250           0 :             break;
     251             :         }
     252           4 :         if (*errnop) {
     253           0 :             sss_cli_close_socket();
     254           0 :             ret = SSS_STATUS_UNAVAIL;
     255           0 :             goto failed;
     256             :         }
     257             : 
     258           4 :         errno = 0;
     259           4 :         if (datarecv < SSS_NSS_HEADER_SIZE) {
     260           2 :             res = read(sss_cli_sd,
     261             :                        (char *)header + datarecv,
     262             :                        SSS_NSS_HEADER_SIZE - datarecv);
     263             :         } else {
     264           2 :             bufrecv = datarecv - SSS_NSS_HEADER_SIZE;
     265           2 :             res = read(sss_cli_sd,
     266             :                        (char *) buf + bufrecv,
     267           2 :                        header[0] - datarecv);
     268             :         }
     269           4 :         error = errno;
     270             : 
     271           4 :         if ((res == -1) || (res == 0)) {
     272           0 :             if ((error == EINTR) || error == EAGAIN) {
     273             :                 /* If the read was interrupted, go back through
     274             :                  * the loop and try again
     275             :                  */
     276           0 :                 continue;
     277             :             }
     278             : 
     279             :             /* Read failed.  I think the only useful thing
     280             :              * we can do here is just return -1 and fail
     281             :              * since the transaction has failed half way
     282             :              * through. */
     283             : 
     284           0 :             sss_cli_close_socket();
     285           0 :             *errnop = error;
     286           0 :             ret = SSS_STATUS_UNAVAIL;
     287           0 :             goto failed;
     288             :         }
     289             : 
     290           4 :         datarecv += res;
     291             : 
     292           4 :         if (datarecv == SSS_NSS_HEADER_SIZE && len == 0) {
     293             :             /* at this point recv buf is not yet
     294             :              * allocated and the header has just
     295             :              * been read, do checks and proceed */
     296           2 :             if (header[2] != 0) {
     297             :                 /* server side error */
     298           0 :                 sss_cli_close_socket();
     299           0 :                 *errnop = header[2];
     300           0 :                 if (*errnop == EAGAIN) {
     301           0 :                     ret = SSS_STATUS_TRYAGAIN;
     302           0 :                     goto failed;
     303             :                 } else {
     304           0 :                     ret = SSS_STATUS_UNAVAIL;
     305           0 :                     goto failed;
     306             :                 }
     307             :             }
     308           2 :             if (header[1] != cmd) {
     309             :                 /* wrong command id */
     310           0 :                 sss_cli_close_socket();
     311           0 :                 *errnop = EBADMSG;
     312           0 :                 ret = SSS_STATUS_UNAVAIL;
     313           0 :                 goto failed;
     314             :             }
     315           2 :             if (header[0] > SSS_NSS_HEADER_SIZE) {
     316           2 :                 len = header[0] - SSS_NSS_HEADER_SIZE;
     317           2 :                 buf = malloc(len);
     318           2 :                 if (!buf) {
     319           0 :                     sss_cli_close_socket();
     320           0 :                     *errnop = ENOMEM;
     321           0 :                     ret = SSS_STATUS_UNAVAIL;
     322           0 :                     goto failed;
     323             :                 }
     324             :             }
     325             :         }
     326             :     }
     327             : 
     328           2 :     if (pollhup) {
     329           0 :         sss_cli_close_socket();
     330             :     }
     331             : 
     332           2 :     *_len = len;
     333           2 :     *_buf = buf;
     334             : 
     335           2 :     return SSS_STATUS_SUCCESS;
     336             : 
     337             : failed:
     338           0 :     free(buf);
     339           0 :     return ret;
     340             : }
     341             : 
     342             : /* this function will check command codes match and returned length is ok */
     343             : /* repbuf and replen report only the data section not the header */
     344           2 : static enum sss_status sss_cli_make_request_nochecks(
     345             :                                        enum sss_cli_command cmd,
     346             :                                        struct sss_cli_req_data *rd,
     347             :                                        uint8_t **repbuf, size_t *replen,
     348             :                                        int *errnop)
     349             : {
     350             :     enum sss_status ret;
     351           2 :     uint8_t *buf = NULL;
     352           2 :     int len = 0;
     353             : 
     354             :     /* send data */
     355           2 :     ret = sss_cli_send_req(cmd, rd, errnop);
     356           2 :     if (ret != SSS_STATUS_SUCCESS) {
     357           0 :         return ret;
     358             :     }
     359             : 
     360             :     /* data sent, now get reply */
     361           2 :     ret = sss_cli_recv_rep(cmd, &buf, &len, errnop);
     362           2 :     if (ret != SSS_STATUS_SUCCESS) {
     363           0 :         return ret;
     364             :     }
     365             : 
     366             :     /* we got through, now we have the custom data in buf if any,
     367             :      * return it if requested */
     368           2 :     if (repbuf && buf) {
     369           2 :         *repbuf = buf;
     370           4 :         if (replen) {
     371           2 :             *replen = len;
     372             :         }
     373             :     } else {
     374           0 :         free(buf);
     375           0 :         if (replen) {
     376           0 :             *replen = 0;
     377             :         }
     378             :     }
     379             : 
     380           2 :     return SSS_STATUS_SUCCESS;
     381             : }
     382             : 
     383             : /* GET_VERSION Reply:
     384             :  * 0-3: 32bit unsigned version number
     385             :  */
     386             : 
     387           1 : static bool sss_cli_check_version(const char *socket_name)
     388             : {
     389           1 :     uint8_t *repbuf = NULL;
     390             :     size_t replen;
     391             :     enum sss_status nret;
     392             :     int errnop;
     393             :     uint32_t expected_version;
     394             :     uint32_t obtained_version;
     395             :     struct sss_cli_req_data req;
     396             : 
     397           1 :     if (strcmp(socket_name, SSS_NSS_SOCKET_NAME) == 0) {
     398           1 :         expected_version = SSS_NSS_PROTOCOL_VERSION;
     399           0 :     } else if (strcmp(socket_name, SSS_PAM_SOCKET_NAME) == 0 ||
     400           0 :                strcmp(socket_name, SSS_PAM_PRIV_SOCKET_NAME) == 0) {
     401           0 :         expected_version = SSS_PAM_PROTOCOL_VERSION;
     402           0 :     } else if (strcmp(socket_name, SSS_SUDO_SOCKET_NAME) == 0) {
     403           0 :         expected_version = SSS_SUDO_PROTOCOL_VERSION;
     404           0 :     } else if (strcmp(socket_name, SSS_AUTOFS_SOCKET_NAME) == 0) {
     405           0 :         expected_version = SSS_AUTOFS_PROTOCOL_VERSION;
     406           0 :     } else if (strcmp(socket_name, SSS_SSH_SOCKET_NAME) == 0) {
     407           0 :         expected_version = SSS_SSH_PROTOCOL_VERSION;
     408           0 :     } else if (strcmp(socket_name, SSS_PAC_SOCKET_NAME) == 0) {
     409           0 :         expected_version = SSS_PAC_PROTOCOL_VERSION;
     410             :     } else {
     411           0 :         return false;
     412             :     }
     413             : 
     414           1 :     req.len = sizeof(expected_version);
     415           1 :     req.data = &expected_version;
     416             : 
     417           1 :     nret = sss_cli_make_request_nochecks(SSS_GET_VERSION, &req,
     418             :                                          &repbuf, &replen, &errnop);
     419           1 :     if (nret != SSS_STATUS_SUCCESS) {
     420           0 :         return false;
     421             :     }
     422             : 
     423           1 :     if (!repbuf) {
     424           0 :         return false;
     425             :     }
     426             : 
     427           1 :     SAFEALIGN_COPY_UINT32(&obtained_version, repbuf, NULL);
     428           1 :     free(repbuf);
     429             : 
     430           1 :     return (obtained_version == expected_version);
     431             : }
     432             : 
     433             : /* this 2 functions are adapted from samba3 winbinbd's wb_common.c */
     434             : 
     435             : /* Make sure socket handle isn't stdin (0), stdout(1) or stderr(2) by setting
     436             :  * the limit to 3 */
     437             : #define RECURSION_LIMIT 3
     438             : 
     439           1 : static int make_nonstd_fd_internals(int fd, int limit)
     440             : {
     441             :     int new_fd;
     442           1 :     if (fd >= 0 && fd <= 2) {
     443             : #ifdef F_DUPFD
     444           0 :         if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
     445           0 :             return -1;
     446             :         }
     447             :         /* Paranoia */
     448           0 :         if (new_fd < 3) {
     449           0 :             close(new_fd);
     450           0 :             return -1;
     451             :         }
     452           0 :         close(fd);
     453           0 :         return new_fd;
     454             : #else
     455             :         if (limit <= 0)
     456             :             return -1;
     457             : 
     458             :         new_fd = dup(fd);
     459             :         if (new_fd == -1)
     460             :             return -1;
     461             : 
     462             :         /* use the program stack to hold our list of FDs to close */
     463             :         new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
     464             :         close(fd);
     465             :         return new_fd;
     466             : #endif
     467             :     }
     468           1 :     return fd;
     469             : }
     470             : 
     471             : /****************************************************************************
     472             :  Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
     473             :  else
     474             :  if SYSV use O_NDELAY
     475             :  if BSD use FNDELAY
     476             :  Set close on exec also.
     477             : ****************************************************************************/
     478             : 
     479           1 : static int make_safe_fd(int fd)
     480             : {
     481             :     int result, flags;
     482           1 :     int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
     483           1 :     if (new_fd == -1) {
     484           0 :         close(fd);
     485           0 :         return -1;
     486             :     }
     487             : 
     488             :     /* Socket should be nonblocking. */
     489             : #ifdef O_NONBLOCK
     490             : #define FLAG_TO_SET O_NONBLOCK
     491             : #else
     492             : #ifdef SYSV
     493             : #define FLAG_TO_SET O_NDELAY
     494             : #else /* BSD */
     495             : #define FLAG_TO_SET FNDELAY
     496             : #endif
     497             : #endif
     498             : 
     499           1 :     if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
     500           0 :         close(new_fd);
     501           0 :         return -1;
     502             :     }
     503             : 
     504           1 :     flags |= FLAG_TO_SET;
     505           1 :     if (fcntl(new_fd, F_SETFL, flags) == -1) {
     506           0 :         close(new_fd);
     507           0 :         return -1;
     508             :     }
     509             : 
     510             : #undef FLAG_TO_SET
     511             : 
     512             :     /* Socket should be closed on exec() */
     513             : #ifdef FD_CLOEXEC
     514           1 :     result = flags = fcntl(new_fd, F_GETFD, 0);
     515           1 :     if (flags >= 0) {
     516           1 :         flags |= FD_CLOEXEC;
     517           1 :         result = fcntl( new_fd, F_SETFD, flags );
     518             :     }
     519           1 :     if (result < 0) {
     520           0 :         close(new_fd);
     521           0 :         return -1;
     522             :     }
     523             : #endif
     524           1 :     return new_fd;
     525             : }
     526             : 
     527           1 : static int sss_cli_open_socket(int *errnop, const char *socket_name)
     528             : {
     529             :     struct sockaddr_un nssaddr;
     530           1 :     bool inprogress = true;
     531           1 :     bool connected = false;
     532             :     unsigned int wait_time;
     533             :     unsigned int sleep_time;
     534           1 :     time_t start_time = time(NULL);
     535             :     int ret;
     536             :     int sd;
     537             : 
     538           1 :     memset(&nssaddr, 0, sizeof(struct sockaddr_un));
     539           1 :     nssaddr.sun_family = AF_UNIX;
     540           1 :     strncpy(nssaddr.sun_path, socket_name,
     541           1 :             strlen(socket_name) + 1);
     542             : 
     543           1 :     sd = socket(AF_UNIX, SOCK_STREAM, 0);
     544           1 :     if (sd == -1) {
     545           0 :         *errnop = errno;
     546           0 :         return -1;
     547             :     }
     548             : 
     549             :     /* set as non-blocking, close on exec, and make sure standard
     550             :      * descriptors are not used */
     551           1 :     sd = make_safe_fd(sd);
     552           1 :     if (sd == -1) {
     553           0 :         *errnop = errno;
     554           0 :         return -1;
     555             :     }
     556             : 
     557             :     /* this piece is adapted from winbind client code */
     558           1 :     wait_time = 0;
     559           1 :     sleep_time = 0;
     560           2 :     while (inprogress) {
     561           1 :         int connect_errno = 0;
     562             :         socklen_t errnosize;
     563             :         struct pollfd pfd;
     564             : 
     565           1 :         wait_time += sleep_time;
     566             : 
     567           1 :         ret = connect(sd, (struct sockaddr *)&nssaddr,
     568             :                       sizeof(nssaddr));
     569           1 :         if (ret == 0) {
     570           1 :             connected = true;
     571           1 :             break;
     572             :         }
     573             : 
     574           0 :         switch(errno) {
     575             :         case EINPROGRESS:
     576           0 :             pfd.fd = sd;
     577           0 :             pfd.events = POLLOUT;
     578             : 
     579           0 :             ret = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT - wait_time);
     580             : 
     581           0 :             if (ret > 0) {
     582           0 :                 errnosize = sizeof(connect_errno);
     583           0 :                 ret = getsockopt(sd, SOL_SOCKET, SO_ERROR,
     584             :                                  &connect_errno, &errnosize);
     585           0 :                 if (ret >= 0 && connect_errno == 0) {
     586           0 :                     connected = true;
     587           0 :                     break;
     588             :                 }
     589             :             }
     590           0 :             wait_time = time(NULL) - start_time;
     591           0 :             break;
     592             :         case EAGAIN:
     593           0 :             if (wait_time < SSS_CLI_SOCKET_TIMEOUT) {
     594           0 :                 sleep_time = rand() % 2 + 1;
     595           0 :                 sleep(sleep_time);
     596             :             }
     597           0 :             break;
     598             :         default:
     599           0 :             *errnop = errno;
     600           0 :             inprogress = false;
     601           0 :             break;
     602             :         }
     603             : 
     604           0 :         if (wait_time >= SSS_CLI_SOCKET_TIMEOUT) {
     605           0 :             inprogress = false;
     606             :         }
     607             : 
     608           0 :         if (connected) {
     609           0 :             inprogress = false;
     610             :         }
     611             :     }
     612             : 
     613           1 :     if (!connected) {
     614           0 :         close(sd);
     615           0 :         return -1;
     616             :     }
     617             : 
     618           1 :     ret = fstat(sd, &sss_cli_sb);
     619           1 :     if (ret != 0) {
     620           0 :         close(sd);
     621           0 :         return -1;
     622             :     }
     623             : 
     624           1 :     return sd;
     625             : }
     626             : 
     627           1 : static enum sss_status sss_cli_check_socket(int *errnop, const char *socket_name)
     628             : {
     629             :     static pid_t mypid;
     630             :     struct stat mysb;
     631             :     int mysd;
     632             :     int ret;
     633             : 
     634           1 :     if (getpid() != mypid) {
     635           1 :         ret = fstat(sss_cli_sd, &mysb);
     636           1 :         if (ret == 0) {
     637           0 :             if (S_ISSOCK(mysb.st_mode) &&
     638           0 :                 mysb.st_dev == sss_cli_sb.st_dev &&
     639           0 :                 mysb.st_ino == sss_cli_sb.st_ino) {
     640           0 :                 sss_cli_close_socket();
     641             :             }
     642             :         }
     643           1 :         sss_cli_sd = -1;
     644           1 :         mypid = getpid();
     645             :     }
     646             : 
     647             :     /* check if the socket has been closed on the other side */
     648           1 :     if (sss_cli_sd != -1) {
     649             :         struct pollfd pfd;
     650             :         int res, error;
     651             : 
     652           0 :         *errnop = 0;
     653           0 :         pfd.fd = sss_cli_sd;
     654           0 :         pfd.events = POLLIN | POLLOUT;
     655             : 
     656             :         do {
     657           0 :             errno = 0;
     658           0 :             res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
     659           0 :             error = errno;
     660             : 
     661             :             /* If error is EINTR here, we'll try again
     662             :              * If it's any other error, we'll catch it
     663             :              * below.
     664             :              */
     665           0 :         } while (error == EINTR);
     666             : 
     667           0 :         switch (res) {
     668             :         case -1:
     669           0 :             *errnop = error;
     670           0 :             break;
     671             :         case 0:
     672           0 :             *errnop = ETIME;
     673           0 :             break;
     674             :         case 1:
     675           0 :             if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
     676           0 :                 *errnop = EPIPE;
     677             :             }
     678           0 :             if (!(pfd.revents & (POLLIN | POLLOUT))) {
     679           0 :                 *errnop = EBUSY;
     680             :             }
     681           0 :             break;
     682             :         default: /* more than one avail ?? */
     683           0 :             *errnop = EBADF;
     684           0 :             break;
     685             :         }
     686           0 :         if (*errnop == 0) {
     687           0 :             return SSS_STATUS_SUCCESS;
     688             :         }
     689             : 
     690           0 :         sss_cli_close_socket();
     691             :     }
     692             : 
     693           1 :     mysd = sss_cli_open_socket(errnop, socket_name);
     694           1 :     if (mysd == -1) {
     695           0 :         return SSS_STATUS_UNAVAIL;
     696             :     }
     697             : 
     698           1 :     sss_cli_sd = mysd;
     699             : 
     700           1 :     if (sss_cli_check_version(socket_name)) {
     701           1 :         return SSS_STATUS_SUCCESS;
     702             :     }
     703             : 
     704           0 :     sss_cli_close_socket();
     705           0 :     *errnop = EFAULT;
     706           0 :     return SSS_STATUS_UNAVAIL;
     707             : }
     708             : 
     709             : /* this function will check command codes match and returned length is ok */
     710             : /* repbuf and replen report only the data section not the header */
     711           1 : enum nss_status sss_nss_make_request(enum sss_cli_command cmd,
     712             :                       struct sss_cli_req_data *rd,
     713             :                       uint8_t **repbuf, size_t *replen,
     714             :                       int *errnop)
     715             : {
     716             :     enum sss_status ret;
     717             :     char *envval;
     718             : 
     719             :     /* avoid looping in the nss daemon */
     720           1 :     envval = getenv("_SSS_LOOPS");
     721           1 :     if (envval && strcmp(envval, "NO") == 0) {
     722           0 :         return NSS_STATUS_NOTFOUND;
     723             :     }
     724             : 
     725           1 :     ret = sss_cli_check_socket(errnop, SSS_NSS_SOCKET_NAME);
     726           1 :     if (ret != SSS_STATUS_SUCCESS) {
     727             : #ifdef NONSTANDARD_SSS_NSS_BEHAVIOUR
     728             :         *errnop = 0;
     729             :         errno = 0;
     730             :         return NSS_STATUS_NOTFOUND;
     731             : #else
     732           0 :         return NSS_STATUS_UNAVAIL;
     733             : #endif
     734             :     }
     735             : 
     736           1 :     ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     737           1 :     switch (ret) {
     738             :     case SSS_STATUS_TRYAGAIN:
     739           0 :         return NSS_STATUS_TRYAGAIN;
     740             :     case SSS_STATUS_SUCCESS:
     741           1 :         return NSS_STATUS_SUCCESS;
     742             :     case SSS_STATUS_UNAVAIL:
     743             :     default:
     744             : #ifdef NONSTANDARD_SSS_NSS_BEHAVIOUR
     745             :         *errnop = 0;
     746             :         errno = 0;
     747             :         return NSS_STATUS_NOTFOUND;
     748             : #else
     749           0 :         return NSS_STATUS_UNAVAIL;
     750             : #endif
     751             :     }
     752             : }
     753             : 
     754           0 : int sss_pac_check_and_open(void)
     755             : {
     756             :     enum sss_status ret;
     757             :     int errnop;
     758             : 
     759           0 :     ret = sss_cli_check_socket(&errnop, SSS_PAC_SOCKET_NAME);
     760           0 :     if (ret != SSS_STATUS_SUCCESS) {
     761           0 :         return EIO;
     762             :     }
     763             : 
     764           0 :     return EOK;
     765             : }
     766             : 
     767           0 : int sss_pac_make_request(enum sss_cli_command cmd,
     768             :                          struct sss_cli_req_data *rd,
     769             :                          uint8_t **repbuf, size_t *replen,
     770             :                          int *errnop)
     771             : {
     772             :     enum sss_status ret;
     773             :     char *envval;
     774             : 
     775             :     /* avoid looping in the nss daemon */
     776           0 :     envval = getenv("_SSS_LOOPS");
     777           0 :     if (envval && strcmp(envval, "NO") == 0) {
     778           0 :         return NSS_STATUS_NOTFOUND;
     779             :     }
     780             : 
     781           0 :     ret = sss_cli_check_socket(errnop, SSS_PAC_SOCKET_NAME);
     782           0 :     if (ret != SSS_STATUS_SUCCESS) {
     783           0 :         return NSS_STATUS_UNAVAIL;
     784             :     }
     785             : 
     786           0 :     ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     787           0 :     switch (ret) {
     788             :     case SSS_STATUS_TRYAGAIN:
     789           0 :         return NSS_STATUS_TRYAGAIN;
     790             :     case SSS_STATUS_SUCCESS:
     791           0 :         return NSS_STATUS_SUCCESS;
     792             :     case SSS_STATUS_UNAVAIL:
     793             :     default:
     794           0 :         return NSS_STATUS_UNAVAIL;
     795             :     }
     796             : }
     797             : 
     798           0 : errno_t check_server_cred(int sockfd)
     799             : {
     800             : #ifdef HAVE_UCRED
     801             :     int ret;
     802             :     struct ucred server_cred;
     803           0 :     socklen_t server_cred_len = sizeof(server_cred);
     804             : 
     805           0 :     ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &server_cred,
     806             :                      &server_cred_len);
     807           0 :     if (ret != 0) {
     808           0 :         return errno;
     809             :     }
     810             : 
     811           0 :     if (server_cred_len != sizeof(struct ucred)) {
     812           0 :         return ESSS_BAD_CRED_MSG;
     813             :     }
     814             : 
     815           0 :     if (server_cred.uid != 0 || server_cred.gid != 0) {
     816           0 :         return ESSS_SERVER_NOT_TRUSTED;
     817             :     }
     818             : #endif
     819           0 :     return 0;
     820             : }
     821             : 
     822           0 : int sss_pam_make_request(enum sss_cli_command cmd,
     823             :                       struct sss_cli_req_data *rd,
     824             :                       uint8_t **repbuf, size_t *replen,
     825             :                       int *errnop)
     826             : {
     827             :     int ret, statret;
     828             :     errno_t error;
     829             :     enum sss_status status;
     830             :     char *envval;
     831             :     struct stat stat_buf;
     832             : 
     833           0 :     sss_pam_lock();
     834             : 
     835             :     /* avoid looping in the pam daemon */
     836           0 :     envval = getenv("_SSS_LOOPS");
     837           0 :     if (envval && strcmp(envval, "NO") == 0) {
     838           0 :         ret = PAM_SERVICE_ERR;
     839           0 :         goto out;
     840             :     }
     841             : 
     842             :     /* only root shall use the privileged pipe */
     843           0 :     if (getuid() == 0 && getgid() == 0) {
     844           0 :         statret = stat(SSS_PAM_PRIV_SOCKET_NAME, &stat_buf);
     845           0 :         if (statret != 0) {
     846           0 :             ret = PAM_SERVICE_ERR;
     847           0 :             goto out;
     848             :         }
     849           0 :         if ( ! (stat_buf.st_uid == 0 &&
     850           0 :                 stat_buf.st_gid == 0 &&
     851           0 :                 S_ISSOCK(stat_buf.st_mode) &&
     852           0 :                 (stat_buf.st_mode & ~S_IFMT) == 0600 )) {
     853           0 :             *errnop = ESSS_BAD_PRIV_SOCKET;
     854           0 :             ret = PAM_SERVICE_ERR;
     855           0 :             goto out;
     856             :         }
     857             : 
     858           0 :         status = sss_cli_check_socket(errnop, SSS_PAM_PRIV_SOCKET_NAME);
     859             :     } else {
     860           0 :         statret = stat(SSS_PAM_SOCKET_NAME, &stat_buf);
     861           0 :         if (statret != 0) {
     862           0 :             ret = PAM_SERVICE_ERR;
     863           0 :             goto out;
     864             :         }
     865           0 :         if ( ! (stat_buf.st_uid == 0 &&
     866           0 :                 stat_buf.st_gid == 0 &&
     867           0 :                 S_ISSOCK(stat_buf.st_mode) &&
     868           0 :                 (stat_buf.st_mode & ~S_IFMT) == 0666 )) {
     869           0 :             *errnop = ESSS_BAD_PUB_SOCKET;
     870           0 :             ret = PAM_SERVICE_ERR;
     871           0 :             goto out;
     872             :         }
     873             : 
     874           0 :         status = sss_cli_check_socket(errnop, SSS_PAM_SOCKET_NAME);
     875             :     }
     876           0 :     if (status != SSS_STATUS_SUCCESS) {
     877           0 :         ret = PAM_SERVICE_ERR;
     878           0 :         goto out;
     879             :     }
     880             : 
     881           0 :     error = check_server_cred(sss_cli_sd);
     882           0 :     if (error != 0) {
     883           0 :         sss_cli_close_socket();
     884           0 :         *errnop = error;
     885           0 :         ret = PAM_SERVICE_ERR;
     886           0 :         goto out;
     887             :     }
     888             : 
     889           0 :     status = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     890           0 :     if (status == SSS_STATUS_SUCCESS) {
     891           0 :         ret = PAM_SUCCESS;
     892             :     } else {
     893           0 :         ret = PAM_SERVICE_ERR;
     894             :     }
     895             : 
     896             : out:
     897           0 :     sss_pam_unlock();
     898           0 :     return ret;
     899             : }
     900             : 
     901          11 : void sss_pam_close_fd(void)
     902             : {
     903          11 :     sss_pam_lock();
     904             : 
     905          11 :     if (sss_cli_sd != -1) {
     906           0 :         close(sss_cli_sd);
     907           0 :         sss_cli_sd = -1;
     908             :     }
     909             : 
     910          11 :     sss_pam_unlock();
     911          11 : }
     912             : 
     913           0 : int sss_sudo_make_request(enum sss_cli_command cmd,
     914             :                           struct sss_cli_req_data *rd,
     915             :                           uint8_t **repbuf, size_t *replen,
     916             :                           int *errnop)
     917             : {
     918           0 :     enum sss_status ret = SSS_STATUS_UNAVAIL;
     919             : 
     920           0 :     ret = sss_cli_check_socket(errnop, SSS_SUDO_SOCKET_NAME);
     921           0 :     if (ret != SSS_STATUS_SUCCESS) {
     922           0 :         return SSS_STATUS_UNAVAIL;
     923             :     }
     924             : 
     925           0 :     ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     926             : 
     927           0 :     return ret;
     928             : }
     929             : 
     930           0 : int sss_autofs_make_request(enum sss_cli_command cmd,
     931             :                             struct sss_cli_req_data *rd,
     932             :                             uint8_t **repbuf, size_t *replen,
     933             :                             int *errnop)
     934             : {
     935           0 :     enum sss_status ret = SSS_STATUS_UNAVAIL;
     936             : 
     937           0 :     ret = sss_cli_check_socket(errnop, SSS_AUTOFS_SOCKET_NAME);
     938           0 :     if (ret != SSS_STATUS_SUCCESS) {
     939           0 :         return SSS_STATUS_UNAVAIL;
     940             :     }
     941             : 
     942           0 :     ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     943             : 
     944           0 :     return ret;
     945             : }
     946             : 
     947           0 : int sss_ssh_make_request(enum sss_cli_command cmd,
     948             :                          struct sss_cli_req_data *rd,
     949             :                          uint8_t **repbuf, size_t *replen,
     950             :                          int *errnop)
     951             : {
     952           0 :     enum sss_status ret = SSS_STATUS_UNAVAIL;
     953             : 
     954           0 :     ret = sss_cli_check_socket(errnop, SSS_SSH_SOCKET_NAME);
     955           0 :     if (ret != SSS_STATUS_SUCCESS) {
     956           0 :         return SSS_STATUS_UNAVAIL;
     957             :     }
     958             : 
     959           0 :     ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
     960             : 
     961           0 :     return ret;
     962             : }
     963             : 
     964             : 
     965           0 : const char *ssscli_err2string(int err)
     966             : {
     967             :     const char *m;
     968             : 
     969           0 :     switch(err) {
     970             :         case ESSS_BAD_PRIV_SOCKET:
     971           0 :             return _("Privileged socket has wrong ownership or permissions.");
     972             :             break;
     973             :         case ESSS_BAD_PUB_SOCKET:
     974           0 :             return _("Public socket has wrong ownership or permissions.");
     975             :             break;
     976             :         case ESSS_BAD_CRED_MSG:
     977           0 :             return _("Unexpected format of the server credential message.");
     978             :             break;
     979             :         case ESSS_SERVER_NOT_TRUSTED:
     980           0 :             return _("SSSD is not run by root.");
     981             :             break;
     982             :         default:
     983           0 :             m = strerror(err);
     984           0 :             if (m == NULL) {
     985           0 :                 return _("An error occurred, but no description can be found.");
     986             :             }
     987           0 :             return m;
     988             :             break;
     989             :     }
     990             : 
     991             :     return _("Unexpected error while looking for an error description");
     992             : }
     993             : 
     994             : /* Return strlen(str) or maxlen, whichever is shorter
     995             :  * Returns EINVAL if str is NULL, EFBIG if str is longer than maxlen
     996             :  * _len will return the result
     997             :  *
     998             :  * This function is useful for preventing buffer overflow attacks.
     999             :  */
    1000           6 : errno_t sss_strnlen(const char *str, size_t maxlen, size_t *len)
    1001             : {
    1002           6 :     if (!str) {
    1003           0 :         return EINVAL;
    1004             :     }
    1005             : 
    1006             : #if defined __USE_GNU
    1007           6 :     *len = strnlen(str, maxlen);
    1008             : #else
    1009             :     *len = 0;
    1010             :     while (*len < maxlen) {
    1011             :         if (str[*len] == '\0') break;
    1012             :         (*len)++;
    1013             :     }
    1014             : #endif
    1015             : 
    1016           6 :     if (*len == maxlen && str[*len] != '\0') {
    1017           0 :         return EFBIG;
    1018             :     }
    1019             : 
    1020           6 :     return 0;
    1021             : }
    1022             : 
    1023             : #if HAVE_PTHREAD
    1024             : typedef void (*sss_mutex_init)(void);
    1025             : 
    1026             : struct sss_mutex {
    1027             :     pthread_mutex_t mtx;
    1028             : 
    1029             :     pthread_once_t once;
    1030             :     sss_mutex_init init;
    1031             : };
    1032             : 
    1033             : static void sss_nss_mt_init(void);
    1034             : static void sss_pam_mt_init(void);
    1035             : static void sss_nss_mc_mt_init(void);
    1036             : 
    1037             : static struct sss_mutex sss_nss_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER,
    1038             :                                         .once = PTHREAD_ONCE_INIT,
    1039             :                                         .init = sss_nss_mt_init };
    1040             : 
    1041             : static struct sss_mutex sss_pam_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER,
    1042             :                                         .once = PTHREAD_ONCE_INIT,
    1043             :                                         .init = sss_pam_mt_init };
    1044             : 
    1045             : static struct sss_mutex sss_nss_mc_mtx = { .mtx  = PTHREAD_MUTEX_INITIALIZER,
    1046             :                                            .once = PTHREAD_ONCE_INIT,
    1047             :                                            .init = sss_nss_mc_mt_init };
    1048             : 
    1049             : /* Wrappers for robust mutex support */
    1050          14 : static int sss_mutexattr_setrobust (pthread_mutexattr_t *attr)
    1051             : {
    1052             : #ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST
    1053          14 :     return pthread_mutexattr_setrobust(attr, PTHREAD_MUTEX_ROBUST);
    1054             : #elif defined(HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP)
    1055             :     return pthread_mutexattr_setrobust_np(attr, PTHREAD_MUTEX_ROBUST_NP);
    1056             : #else
    1057             : #warning Robust mutexes are not supported on this platform.
    1058             :     return 0;
    1059             : #endif
    1060             : }
    1061             : 
    1062           0 : static int sss_mutex_consistent(pthread_mutex_t *mtx)
    1063             : {
    1064             : #ifdef HAVE_PTHREAD_MUTEX_CONSISTENT
    1065           0 :     return pthread_mutex_consistent(mtx);
    1066             : #elif defined(HAVE_PTHREAD_MUTEX_CONSISTENT_NP)
    1067             :     return pthread_mutex_consistent_np(mtx);
    1068             : #else
    1069             : #warning Robust mutexes are not supported on this platform.
    1070             :     return 0;
    1071             : #endif
    1072             : }
    1073             : 
    1074             : /* Generic mutex init, lock, unlock functions */
    1075          14 : static void sss_mt_init(struct sss_mutex *m)
    1076             : {
    1077             :     pthread_mutexattr_t attr;
    1078             : 
    1079          14 :     if (pthread_mutexattr_init(&attr) != 0) {
    1080           0 :         return;
    1081             :     }
    1082          14 :     if (sss_mutexattr_setrobust(&attr) != 0) {
    1083           0 :         return;
    1084             :     }
    1085             : 
    1086          14 :     pthread_mutex_init(&m->mtx, &attr);
    1087          14 :     pthread_mutexattr_destroy(&attr);
    1088             : }
    1089             : 
    1090          18 : static void sss_mt_lock(struct sss_mutex *m)
    1091             : {
    1092          18 :     pthread_once(&m->once, m->init);
    1093          18 :     if (pthread_mutex_lock(&m->mtx) == EOWNERDEAD) {
    1094           0 :         sss_cli_close_socket();
    1095           0 :         sss_mutex_consistent(&m->mtx);
    1096             :     }
    1097          18 : }
    1098             : 
    1099          18 : static void sss_mt_unlock(struct sss_mutex *m)
    1100             : {
    1101          18 :     pthread_mutex_unlock(&m->mtx);
    1102          18 : }
    1103             : 
    1104             : /* NSS mutex wrappers */
    1105           2 : static void sss_nss_mt_init(void)
    1106             : {
    1107           2 :     sss_mt_init(&sss_nss_mtx);
    1108           2 : }
    1109           6 : void sss_nss_lock(void)
    1110             : {
    1111           6 :     sss_mt_lock(&sss_nss_mtx);
    1112           6 : }
    1113           6 : void sss_nss_unlock(void)
    1114             : {
    1115           6 :     sss_mt_unlock(&sss_nss_mtx);
    1116           6 : }
    1117             : 
    1118             : /* NSS mutex wrappers */
    1119          11 : static void sss_pam_mt_init(void)
    1120             : {
    1121          11 :     sss_mt_init(&sss_pam_mtx);
    1122          11 : }
    1123          11 : void sss_pam_lock(void)
    1124             : {
    1125          11 :     sss_mt_lock(&sss_pam_mtx);
    1126          11 : }
    1127          11 : void sss_pam_unlock(void)
    1128             : {
    1129          11 :     sss_mt_unlock(&sss_pam_mtx);
    1130          11 : }
    1131             : 
    1132             : /* NSS mutex wrappers */
    1133           1 : static void sss_nss_mc_mt_init(void)
    1134             : {
    1135           1 :     sss_mt_init(&sss_nss_mc_mtx);
    1136           1 : }
    1137           1 : void sss_nss_mc_lock(void)
    1138             : {
    1139           1 :     sss_mt_lock(&sss_nss_mc_mtx);
    1140           1 : }
    1141           1 : void sss_nss_mc_unlock(void)
    1142             : {
    1143           1 :     sss_mt_unlock(&sss_nss_mc_mtx);
    1144           1 : }
    1145             : 
    1146             : #else
    1147             : 
    1148             : /* sorry no mutexes available */
    1149             : void sss_nss_lock(void) { return; }
    1150             : void sss_nss_unlock(void) { return; }
    1151             : void sss_pam_lock(void) { return; }
    1152             : void sss_pam_unlock(void) { return; }
    1153             : void sss_nss_mc_lock(void) { return; }
    1154             : void sss_nss_mc_unlock(void) { return; }
    1155             : #endif
    1156             : 
    1157             : 
    1158           0 : errno_t sss_readrep_copy_string(const char *in,
    1159             :                                 size_t *offset,
    1160             :                                 size_t *slen,
    1161             :                                 size_t *dlen,
    1162             :                                 char **out,
    1163             :                                 size_t *size)
    1164             : {
    1165           0 :     size_t i = 0;
    1166           0 :     while (*slen > *offset && *dlen > 0) {
    1167           0 :         (*out)[i] = in[*offset];
    1168           0 :         if ((*out)[i] == '\0') break;
    1169           0 :         i++;
    1170           0 :         (*offset)++;
    1171           0 :         (*dlen)--;
    1172             :     }
    1173           0 :     if (*slen <= *offset) { /* premature end of buf */
    1174           0 :         return EBADMSG;
    1175             :     }
    1176           0 :     if (*dlen == 0) { /* not enough memory */
    1177           0 :         return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
    1178             :     }
    1179           0 :     (*offset)++;
    1180           0 :     (*dlen)--;
    1181           0 :     if (size) {
    1182           0 :         *size = i;
    1183             :     }
    1184             : 
    1185           0 :     return EOK;
    1186             : }

Generated by: LCOV version 1.10