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
22   	 * License along with this program; if not, write to the Free Software
23   	 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24   	 */
25   	
26   	/* for struct ucred */
27   	#define _GNU_SOURCE
28   	
29   	#include <nss.h>
30   	#include <security/pam_modules.h>
31   	#include <errno.h>
32   	#include <sys/types.h>
33   	#include <sys/socket.h>
34   	#include <sys/un.h>
35   	#include <sys/stat.h>
36   	#include <unistd.h>
37   	#include <stdlib.h>
38   	#include <stdint.h>
39   	#include <string.h>
40   	#include <fcntl.h>
41   	#include <poll.h>
42   	
43   	#include <libintl.h>
44   	#define _(STRING) dgettext (PACKAGE, STRING)
45   	#include "config.h"
46   	#include "sss_cli.h"
47   	
48   	/* common functions */
49   	
50   	int sss_cli_sd = -1; /* the sss client socket descriptor */
51   	
52   	static void sss_cli_close_socket(void)
53   	{
54   	    if (sss_cli_sd != -1) {
55   	        close(sss_cli_sd);
56   	        sss_cli_sd = -1;
57   	    }
58   	}
59   	
60   	/* Requests:
61   	 *
62   	 * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X)
63   	 * byte 4-7: 32bit unsigned with command code
64   	 * byte 8-11: 32bit unsigned (reserved)
65   	 * byte 12-15: 32bit unsigned (reserved)
66   	 * byte 16-X: (optional) request structure associated to the command code used
67   	 */
68   	static enum nss_status sss_nss_send_req(enum sss_cli_command cmd,
69   	                                        struct sss_cli_req_data *rd,
70   	                                        int *errnop)
71   	{
72   	    uint32_t header[4];
73   	    size_t datasent;
74   	
75   	    header[0] = SSS_NSS_HEADER_SIZE + (rd?rd->len:0);
76   	    header[1] = cmd;
77   	    header[2] = 0;
78   	    header[3] = 0;
79   	
80   	    datasent = 0;
81   	
82   	    while (datasent < header[0]) {
83   	        struct pollfd pfd;
84   	        int rdsent;
85   	        int res, error;
86   	
87   	        *errnop = 0;
88   	        pfd.fd = sss_cli_sd;
89   	        pfd.events = POLLOUT;
90   	
91   	        do {
92   	            errno = 0;
93   	            res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
94   	            error = errno;
95   	
96   	            /* If error is EINTR here, we'll try again
97   	             * If it's any other error, we'll catch it
98   	             * below.
99   	             */
100  	        } while (error == EINTR);
101  	
102  	        switch (res) {
103  	        case -1:
104  	            *errnop = error;
105  	            break;
106  	        case 0:
107  	            *errnop = ETIME;
108  	            break;
109  	        case 1:
110  	            if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
111  	                *errnop = EPIPE;
112  	            }
113  	            if (!(pfd.revents & POLLOUT)) {
114  	                *errnop = EBUSY;
115  	            }
116  	            break;
117  	        default: /* more than one avail ?? */
118  	            *errnop = EBADF;
119  	            break;
120  	        }
121  	        if (*errnop) {
122  	            sss_cli_close_socket();
123  	            return NSS_STATUS_UNAVAIL;
124  	        }
125  	
126  	        if (datasent < SSS_NSS_HEADER_SIZE) {
127  	            res = write(sss_cli_sd,
128  	                        (char *)header + datasent,
129  	                        SSS_NSS_HEADER_SIZE - datasent);
130  	        } else {
131  	            rdsent = datasent - SSS_NSS_HEADER_SIZE;
132  	            res = write(sss_cli_sd,
133  	                        (const char *)rd->data + rdsent,
134  	                        rd->len - rdsent);
135  	        }
136  	
137  	        if ((res == -1) || (res == 0)) {
138  	
139  	            /* Write failed */
140  	            sss_cli_close_socket();
141  	            *errnop = errno;
142  	            return NSS_STATUS_UNAVAIL;
143  	        }
144  	
145  	        datasent += res;
146  	    }
147  	
148  	    return NSS_STATUS_SUCCESS;
149  	}
150  	
151  	/* Replies:
152  	 *
153  	 * byte 0-3: 32bit unsigned with length (the complete packet length: 0 to X)
154  	 * byte 4-7: 32bit unsigned with command code
155  	 * byte 8-11: 32bit unsigned with the request status (server errno)
156  	 * byte 12-15: 32bit unsigned (reserved)
157  	 * byte 16-X: (optional) reply structure associated to the command code used
158  	 */
159  	
160  	static enum nss_status sss_nss_recv_rep(enum sss_cli_command cmd,
161  	                                        uint8_t **buf, int *len,
162  	                                        int *errnop)
163  	{
164  	    uint32_t header[4];
165  	    size_t datarecv;
166  	
167  	    header[0] = SSS_NSS_HEADER_SIZE; /* unitl we know the real lenght */
168  	    header[1] = 0;
169  	    header[2] = 0;
170  	    header[3] = 0;
171  	
172  	    datarecv = 0;
173  	    *buf = NULL;
174  	    *len = 0;
175  	    *errnop = 0;
176  	
At conditional (1): "datarecv < header[0]": Taking true branch.
At conditional (17): "datarecv < header[0]": Taking true branch.
177  	    while (datarecv < header[0]) {
178  	        struct pollfd pfd;
179  	        int bufrecv;
180  	        int res, error;
181  	
182  	        pfd.fd = sss_cli_sd;
183  	        pfd.events = POLLIN;
184  	
185  	        do {
186  	            errno = 0;
187  	            res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
188  	            error = errno;
189  	
190  	            /* If error is EINTR here, we'll try again
191  	             * If it's any other error, we'll catch it
192  	             * below.
193  	             */
At conditional (2): "error == 4": Taking true branch.
At conditional (3): "error == 4": Taking false branch.
At conditional (18): "error == 4": Taking true branch.
At conditional (19): "error == 4": Taking false branch.
194  	        } while (error == EINTR);
195  	
196  	        switch (res) {
197  	        case -1:
198  	            *errnop = error;
199  	            break;
At conditional (20): switch case value "0": Taking true branch.
200  	        case 0:
201  	            *errnop = ETIME;
202  	            break;
At conditional (4): switch case value "1": Taking true branch.
203  	        case 1:
At conditional (5): "pfd.revents & 0x38": Taking false branch.
204  	            if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
205  	                *errnop = EPIPE;
206  	            }
At conditional (6): "!(pfd.revents & 1)": Taking false branch.
207  	            if (!(pfd.revents & POLLIN)) {
208  	                *errnop = EBUSY;
209  	            }
210  	            break;
211  	        default: /* more than one avail ?? */
212  	            *errnop = EBADF;
213  	            break;
214  	        }
At conditional (7): "*errnop": Taking false branch.
At conditional (21): "*errnop": Taking true branch.
215  	        if (*errnop) {
216  	            sss_cli_close_socket();
217  	            return NSS_STATUS_UNAVAIL;
218  	        }
219  	
At conditional (8): "datarecv < 16UL": Taking true branch.
220  	        if (datarecv < SSS_NSS_HEADER_SIZE) {
221  	            res = read(sss_cli_sd,
222  	                       (char *)header + datarecv,
223  	                       SSS_NSS_HEADER_SIZE - datarecv);
224  	        } else {
225  	            bufrecv = datarecv - SSS_NSS_HEADER_SIZE;
226  	            res = read(sss_cli_sd,
227  	                       (char *)(*buf) + bufrecv,
228  	                       header[0] - datarecv);
229  	        }
230  	
At conditional (9): "res == -1": Taking false branch.
At conditional (10): "res == 0": Taking false branch.
231  	        if ((res == -1) || (res == 0)) {
232  	
233  	            /* Read failed.  I think the only useful thing
234  	             * we can do here is just return -1 and fail
235  	             * since the transaction has failed half way
236  	             * through. */
237  	
238  	            sss_cli_close_socket();
239  	            *errnop = errno;
240  	            return NSS_STATUS_UNAVAIL;
241  	        }
242  	
243  	        datarecv += res;
244  	
At conditional (11): "datarecv == 16UL": Taking true branch.
At conditional (12): "*len == 0": Taking true branch.
245  	        if (datarecv == SSS_NSS_HEADER_SIZE && *len == 0) {
246  	            /* at this point recv buf is not yet
247  	             * allocated and the header has just
248  	             * been read, do checks and proceed */
At conditional (13): "header[2] != 0U": Taking false branch.
249  	            if (header[2] != 0) {
250  	                /* server side error */
251  	                sss_cli_close_socket();
252  	                *errnop = header[2];
253  	                if (*errnop == EAGAIN) {
254  	                    return NSS_STATUS_TRYAGAIN;
255  	                } else {
256  	                    return NSS_STATUS_UNAVAIL;
257  	                }
258  	            }
At conditional (14): "header[1] != cmd": Taking false branch.
259  	            if (header[1] != cmd) {
260  	                /* wrong command id */
261  	                sss_cli_close_socket();
262  	                *errnop = EBADMSG;
263  	                return NSS_STATUS_UNAVAIL;
264  	            }
At conditional (15): "header[0] > 16UL": Taking true branch.
265  	            if (header[0] > SSS_NSS_HEADER_SIZE) {
266  	                *len = header[0] - SSS_NSS_HEADER_SIZE;
Event alloc_fn: Storage is returned from allocation function "malloc".
Event var_assign: Assigning: "*buf" = "malloc(*len)".
267  	                *buf = malloc(*len);
At conditional (16): "!*buf": Taking false branch.
268  	                if (!*buf) {
269  	                    sss_cli_close_socket();
270  	                    *errnop =  ENOMEM;
271  	                    return NSS_STATUS_UNAVAIL;
272  	                }
273  	            }
274  	        }
275  	    }
276  	
277  	    return NSS_STATUS_SUCCESS;
278  	}
279  	
280  	/* this function will check command codes match and returned length is ok */
281  	/* repbuf and replen report only the data section not the header */
282  	static enum nss_status sss_nss_make_request_nochecks(
283  	                                       enum sss_cli_command cmd,
284  	                                       struct sss_cli_req_data *rd,
285  	                                       uint8_t **repbuf, size_t *replen,
286  	                                       int *errnop)
287  	{
288  	    enum nss_status ret;
289  	    uint8_t *buf = NULL;
290  	    int len = 0;
291  	
292  	    /* send data */
293  	    ret = sss_nss_send_req(cmd, rd, errnop);
294  	    if (ret != NSS_STATUS_SUCCESS) {
295  	        return ret;
296  	    }
297  	
298  	    /* data sent, now get reply */
Event alloc_arg: Calling allocation function "sss_nss_recv_rep" on "buf". [model]
Also see events: [leaked_storage]
299  	    ret = sss_nss_recv_rep(cmd, &buf, &len, errnop);
At conditional (1): "ret != 1": Taking true branch.
300  	    if (ret != NSS_STATUS_SUCCESS) {
Event leaked_storage: Variable "buf" going out of scope leaks the storage it points to.
Also see events: [alloc_arg]
301  	        return ret;
302  	    }
303  	
304  	    /* we got through, now we have the custom data in buf if any,
305  	     * return it if requested */
306  	    if (repbuf && buf) {
307  	        *repbuf = buf;
308  	        if (replen) {
309  	            *replen = len;
310  	        }
311  	    } else {
312  	        free(buf);
313  	        if (replen) {
314  	            *replen = 0;
315  	        }
316  	    }
317  	
318  	    return NSS_STATUS_SUCCESS;
319  	}
320  	
321  	/* GET_VERSION Reply:
322  	 * 0-3: 32bit unsigned version number
323  	 */
324  	
325  	static int sss_nss_check_version(const char *socket_name)
326  	{
327  	    uint8_t *repbuf;
328  	    size_t replen;
329  	    enum nss_status nret;
330  	    int errnop;
331  	    int res = NSS_STATUS_UNAVAIL;
332  	    uint32_t expected_version;
333  	    struct sss_cli_req_data req;
334  	
335  	    if (strcmp(socket_name, SSS_NSS_SOCKET_NAME) == 0) {
336  	        expected_version = SSS_NSS_PROTOCOL_VERSION;
337  	    } else if (strcmp(socket_name, SSS_PAM_SOCKET_NAME) == 0 ||
338  	               strcmp(socket_name, SSS_PAM_PRIV_SOCKET_NAME) == 0) {
339  	        expected_version = SSS_PAM_PROTOCOL_VERSION;
340  	    } else {
341  	        return NSS_STATUS_UNAVAIL;
342  	    }
343  	
344  	    req.len = sizeof(expected_version);
345  	    req.data = &expected_version;
346  	
347  	    nret = sss_nss_make_request_nochecks(SSS_GET_VERSION, &req,
348  	                                         &repbuf, &replen, &errnop);
349  	    if (nret != NSS_STATUS_SUCCESS) {
350  	        return nret;
351  	    }
352  	
353  	    if (!repbuf) {
354  	        return res;
355  	    }
356  	
357  	    if (((uint32_t *)repbuf)[0] == expected_version) {
358  	        res = NSS_STATUS_SUCCESS;
359  	    }
360  	
361  	    free(repbuf);
362  	    return res;
363  	}
364  	
365  	/* this 2 functions are adapted from samba3 winbinbd's wb_common.c */
366  	
367  	/* Make sure socket handle isn't stdin (0), stdout(1) or stderr(2) by setting
368  	 * the limit to 3 */
369  	#define RECURSION_LIMIT 3
370  	
371  	static int make_nonstd_fd_internals(int fd, int limit)
372  	{
373  	    int new_fd;
374  	    if (fd >= 0 && fd <= 2) {
375  	#ifdef F_DUPFD
376  	        if ((new_fd = fcntl(fd, F_DUPFD, 3)) == -1) {
377  	            return -1;
378  	        }
379  	        /* Paranoia */
380  	        if (new_fd < 3) {
381  	            close(new_fd);
382  	            return -1;
383  	        }
384  	        close(fd);
385  	        return new_fd;
386  	#else
387  	        if (limit <= 0)
388  	            return -1;
389  	
390  	        new_fd = dup(fd);
391  	        if (new_fd == -1)
392  	            return -1;
393  	
394  	        /* use the program stack to hold our list of FDs to close */
395  	        new_fd = make_nonstd_fd_internals(new_fd, limit - 1);
396  	        close(fd);
397  	        return new_fd;
398  	#endif
399  	    }
400  	    return fd;
401  	}
402  	
403  	/****************************************************************************
404  	 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
405  	 else
406  	 if SYSV use O_NDELAY
407  	 if BSD use FNDELAY
408  	 Set close on exec also.
409  	****************************************************************************/
410  	
411  	static int make_safe_fd(int fd)
412  	{
413  	    int result, flags;
414  	    int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
415  	    if (new_fd == -1) {
416  	        close(fd);
417  	        return -1;
418  	    }
419  	
420  	    /* Socket should be nonblocking. */
421  	#ifdef O_NONBLOCK
422  	#define FLAG_TO_SET O_NONBLOCK
423  	#else
424  	#ifdef SYSV
425  	#define FLAG_TO_SET O_NDELAY
426  	#else /* BSD */
427  	#define FLAG_TO_SET FNDELAY
428  	#endif
429  	#endif
430  	
431  	    if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
432  	        close(new_fd);
433  	        return -1;
434  	    }
435  	
436  	    flags |= FLAG_TO_SET;
437  	    if (fcntl(new_fd, F_SETFL, flags) == -1) {
438  	        close(new_fd);
439  	        return -1;
440  	    }
441  	
442  	#undef FLAG_TO_SET
443  	
444  	    /* Socket should be closed on exec() */
445  	#ifdef FD_CLOEXEC
446  	    result = flags = fcntl(new_fd, F_GETFD, 0);
447  	    if (flags >= 0) {
448  	        flags |= FD_CLOEXEC;
449  	        result = fcntl( new_fd, F_SETFD, flags );
450  	    }
451  	    if (result < 0) {
452  	        close(new_fd);
453  	        return -1;
454  	    }
455  	#endif
456  	    return new_fd;
457  	}
458  	
459  	static int sss_nss_open_socket(int *errnop, const char *socket_name)
460  	{
461  	    struct sockaddr_un nssaddr;
462  	    int inprogress = 1;
463  	    int wait_time, sleep_time;
464  	    int sd;
465  	
466  	    memset(&nssaddr, 0, sizeof(struct sockaddr_un));
467  	    nssaddr.sun_family = AF_UNIX;
468  	    strncpy(nssaddr.sun_path, socket_name,
469  	            strlen(socket_name) + 1);
470  	
471  	    sd = socket(AF_UNIX, SOCK_STREAM, 0);
472  	    if (sd == -1) {
473  	        *errnop = errno;
474  	        return -1;
475  	    }
476  	
477  	    /* set as non-blocking, close on exec, and make sure standard
478  	     * descriptors are not used */
479  	    sd = make_safe_fd(sd);
480  	    if (sd == -1) {
481  	        *errnop = errno;
482  	        return -1;
483  	    }
484  	
485  	    /* this piece is adapted from winbind client code */
486  	    wait_time = 0;
487  	    sleep_time = 0;
488  	    while(inprogress) {
489  	        int connect_errno = 0;
490  	        socklen_t errnosize;
491  	        struct timeval tv;
492  	        fd_set w_fds;
493  	        int ret;
494  	
495  	        wait_time += sleep_time;
496  	
497  	        ret = connect(sd, (struct sockaddr *)&nssaddr,
498  	                      sizeof(nssaddr));
499  	        if (ret == 0) {
500  	            return sd;
501  	        }
502  	
503  	        switch(errno) {
504  	        case EINPROGRESS:
505  	            FD_ZERO(&w_fds);
506  	            FD_SET(sd, &w_fds);
507  	            tv.tv_sec = SSS_CLI_SOCKET_TIMEOUT - wait_time;
508  	            tv.tv_usec = 0;
509  	
510  	            ret = select(sd + 1, NULL, &w_fds, NULL, &tv);
511  	
512  	            if (ret > 0) {
513  	                errnosize = sizeof(connect_errno);
514  	                ret = getsockopt(sd, SOL_SOCKET, SO_ERROR,
515  	                                 &connect_errno, &errnosize);
516  	                if (ret >= 0 && connect_errno == 0) {
517  	                    return sd;
518  	                }
519  	            }
520  	            wait_time += SSS_CLI_SOCKET_TIMEOUT;
521  	            break;
522  	        case EAGAIN:
523  	            if (wait_time < SSS_CLI_SOCKET_TIMEOUT) {
524  	                sleep_time = rand() % 2 + 1;
525  	                sleep(sleep_time);
526  	            }
527  	            break;
528  	        default:
529  	            *errnop = errno;
530  	            inprogress = 0;
531  	            break;
532  	        }
533  	
534  	        if (wait_time >= SSS_CLI_SOCKET_TIMEOUT) {
535  	            inprogress = 0;
536  	        }
537  	    }
538  	
539  	    /* if we get here connect() failed or we timed out */
540  	
541  	    close(sd);
542  	    return -1;
543  	}
544  	
545  	static enum sss_status sss_cli_check_socket(int *errnop, const char *socket_name)
546  	{
547  	    static pid_t mypid;
548  	    int mysd;
549  	
550  	    if (getpid() != mypid) {
551  	        sss_cli_close_socket();
552  	        mypid = getpid();
553  	    }
554  	
555  	    /* check if the socket has been closed on the other side */
556  	    if (sss_cli_sd != -1) {
557  	        struct pollfd pfd;
558  	        int res, error;
559  	
560  	        *errnop = 0;
561  	        pfd.fd = sss_cli_sd;
562  	        pfd.events = POLLIN | POLLOUT;
563  	
564  	        do {
565  	            errno = 0;
566  	            res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
567  	            error = errno;
568  	
569  	            /* If error is EINTR here, we'll try again
570  	             * If it's any other error, we'll catch it
571  	             * below.
572  	             */
573  	        } while (error == EINTR);
574  	
575  	        switch (res) {
576  	        case -1:
577  	            *errnop = error;
578  	            break;
579  	        case 0:
580  	            *errnop = ETIME;
581  	            break;
582  	        case 1:
583  	            if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
584  	                *errnop = EPIPE;
585  	            }
586  	            if (!(pfd.revents & (POLLIN | POLLOUT))) {
587  	                *errnop = EBUSY;
588  	            }
589  	            break;
590  	        default: /* more than one avail ?? */
591  	            *errnop = EBADF;
592  	            break;
593  	        }
594  	        if (*errnop) {
595  	            sss_cli_close_socket();
596  	            return SSS_STATUS_UNAVAIL;
597  	        }
598  	
599  	        return SSS_STATUS_SUCCESS;
600  	    }
601  	
602  	    mysd = sss_nss_open_socket(errnop, socket_name);
603  	    if (mysd == -1) {
604  	        return SSS_STATUS_UNAVAIL;
605  	    }
606  	
607  	    sss_cli_sd = mysd;
608  	
609  	    if (sss_nss_check_version(socket_name) == NSS_STATUS_SUCCESS) {
610  	        return SSS_STATUS_SUCCESS;
611  	    }
612  	
613  	    sss_cli_close_socket();
614  	    *errnop = EFAULT;
615  	    return SSS_STATUS_UNAVAIL;
616  	}
617  	
618  	/* this function will check command codes match and returned length is ok */
619  	/* repbuf and replen report only the data section not the header */
620  	enum nss_status sss_nss_make_request(enum sss_cli_command cmd,
621  	                      struct sss_cli_req_data *rd,
622  	                      uint8_t **repbuf, size_t *replen,
623  	                      int *errnop)
624  	{
625  	    enum nss_status ret;
626  	    char *envval;
627  	
628  	    /* avoid looping in the nss daemon */
629  	    envval = getenv("_SSS_LOOPS");
630  	    if (envval && strcmp(envval, "NO") == 0) {
631  	        return NSS_STATUS_NOTFOUND;
632  	    }
633  	
634  	    ret = sss_cli_check_socket(errnop, SSS_NSS_SOCKET_NAME);
635  	    if (ret != SSS_STATUS_SUCCESS) {
636  	        return NSS_STATUS_UNAVAIL;
637  	    }
638  	
639  	    return sss_nss_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
640  	}
641  	
642  	errno_t check_server_cred(int sockfd)
643  	{
644  	#ifdef HAVE_UCRED
645  	    int ret;
646  	    struct ucred server_cred;
647  	    socklen_t server_cred_len = sizeof(server_cred);
648  	
649  	    ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &server_cred,
650  	                     &server_cred_len);
651  	    if (ret != 0) {
652  	        return errno;
653  	    }
654  	
655  	    if (server_cred_len != sizeof(struct ucred)) {
656  	        return ESSS_BAD_CRED_MSG;
657  	    }
658  	
659  	    if (server_cred.uid != 0 || server_cred.gid != 0) {
660  	        return ESSS_SERVER_NOT_TRUSTED;
661  	    }
662  	#endif
663  	    return 0;
664  	}
665  	int sss_pam_make_request(enum sss_cli_command cmd,
666  	                      struct sss_cli_req_data *rd,
667  	                      uint8_t **repbuf, size_t *replen,
668  	                      int *errnop)
669  	{
670  	    int ret;
671  	    char *envval;
672  	    struct stat stat_buf;
673  	
674  	    /* avoid looping in the pam daemon */
675  	    envval = getenv("_SSS_LOOPS");
676  	    if (envval && strcmp(envval, "NO") == 0) {
677  	        return PAM_SERVICE_ERR;
678  	    }
679  	
680  	    /* only root shall use the privileged pipe */
681  	    if (getuid() == 0 && getgid() == 0) {
682  	        ret = stat(SSS_PAM_PRIV_SOCKET_NAME, &stat_buf);
683  	        if (ret != 0) return PAM_SERVICE_ERR;
684  	        if ( ! (stat_buf.st_uid == 0 &&
685  	                stat_buf.st_gid == 0 &&
686  	                S_ISSOCK(stat_buf.st_mode) &&
687  	                (stat_buf.st_mode & ~S_IFMT) == 0600 )) {
688  	            *errnop = ESSS_BAD_PRIV_SOCKET;
689  	            return PAM_SERVICE_ERR;
690  	        }
691  	
692  	        ret = sss_cli_check_socket(errnop, SSS_PAM_PRIV_SOCKET_NAME);
693  	    } else {
694  	        ret = stat(SSS_PAM_SOCKET_NAME, &stat_buf);
695  	        if (ret != 0) return PAM_SERVICE_ERR;
696  	        if ( ! (stat_buf.st_uid == 0 &&
697  	                stat_buf.st_gid == 0 &&
698  	                S_ISSOCK(stat_buf.st_mode) &&
699  	                (stat_buf.st_mode & ~S_IFMT) == 0666 )) {
700  	            *errnop = ESSS_BAD_PUB_SOCKET;
701  	            return PAM_SERVICE_ERR;
702  	        }
703  	
704  	        ret = sss_cli_check_socket(errnop, SSS_PAM_SOCKET_NAME);
705  	    }
706  	    if (ret != NSS_STATUS_SUCCESS) {
707  	        return PAM_SERVICE_ERR;
708  	    }
709  	
710  	    ret = check_server_cred(sss_cli_sd);
711  	    if (ret != 0) {
712  	        sss_cli_close_socket();
713  	        *errnop = ret;
714  	        return PAM_SERVICE_ERR;
715  	    }
716  	
717  	    return sss_nss_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
718  	}
719  	
720  	
721  	const char *ssscli_err2string(int err)
722  	{
723  	    const char *m;
724  	
725  	    switch(err) {
726  	        case ESSS_BAD_PRIV_SOCKET:
727  	            return _("Privileged socket has wrong ownership or permissions.");
728  	            break;
729  	        case ESSS_BAD_PUB_SOCKET:
730  	            return _("Public socket has wrong ownership or permissions.");
731  	            break;
732  	        case ESSS_BAD_CRED_MSG:
733  	            return _("Unexpected format of the server credential message.");
734  	            break;
735  	        case ESSS_SERVER_NOT_TRUSTED:
736  	            return _("SSSD is not run by root.");
737  	            break;
738  	        default:
739  	            m = strerror(err);
740  	            if (m == NULL) {
741  	                return _("An error occurred, but no description can be found.");
742  	            }
743  	            return m;
744  	            break;
745  	    }
746  	
747  	    return _("Unexpected error while looking for an error description");
748  	}