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 14 : static void sss_cli_close_socket(void)
73 : {
74 14 : if (sss_cli_sd != -1) {
75 0 : close(sss_cli_sd);
76 0 : sss_cli_sd = -1;
77 : }
78 14 : }
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 0 : 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 0 : header[0] = SSS_NSS_HEADER_SIZE + (rd?rd->len:0);
96 0 : header[1] = cmd;
97 0 : header[2] = 0;
98 0 : header[3] = 0;
99 :
100 0 : datasent = 0;
101 :
102 0 : while (datasent < header[0]) {
103 : struct pollfd pfd;
104 : int rdsent;
105 : int res, error;
106 :
107 0 : *errnop = 0;
108 0 : pfd.fd = sss_cli_sd;
109 0 : pfd.events = POLLOUT;
110 :
111 : do {
112 0 : errno = 0;
113 0 : res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
114 0 : 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 0 : } while (error == EINTR);
121 :
122 0 : 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 0 : if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
131 0 : *errnop = EPIPE;
132 : }
133 0 : if (!(pfd.revents & POLLOUT)) {
134 0 : *errnop = EBUSY;
135 : }
136 0 : break;
137 : default: /* more than one avail ?? */
138 0 : *errnop = EBADF;
139 0 : break;
140 : }
141 0 : if (*errnop) {
142 0 : sss_cli_close_socket();
143 0 : return SSS_STATUS_UNAVAIL;
144 : }
145 :
146 0 : errno = 0;
147 0 : if (datasent < SSS_NSS_HEADER_SIZE) {
148 0 : res = send(sss_cli_sd,
149 : (char *)header + datasent,
150 : SSS_NSS_HEADER_SIZE - datasent,
151 : SSS_DEFAULT_WRITE_FLAGS);
152 : } else {
153 0 : rdsent = datasent - SSS_NSS_HEADER_SIZE;
154 0 : res = send(sss_cli_sd,
155 0 : (const char *)rd->data + rdsent,
156 0 : rd->len - rdsent,
157 : SSS_DEFAULT_WRITE_FLAGS);
158 : }
159 0 : error = errno;
160 :
161 0 : 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 0 : datasent += res;
176 : }
177 :
178 0 : 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 0 : 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 0 : uint8_t *buf = NULL;
197 0 : bool pollhup = false;
198 : int len;
199 : int ret;
200 :
201 0 : header[0] = SSS_NSS_HEADER_SIZE; /* unitl we know the real length */
202 0 : header[1] = 0;
203 0 : header[2] = 0;
204 0 : header[3] = 0;
205 :
206 0 : datarecv = 0;
207 0 : buf = NULL;
208 0 : len = 0;
209 0 : *errnop = 0;
210 :
211 0 : while (datarecv < header[0]) {
212 : struct pollfd pfd;
213 : int bufrecv;
214 : int res, error;
215 :
216 0 : pfd.fd = sss_cli_sd;
217 0 : pfd.events = POLLIN;
218 :
219 : do {
220 0 : errno = 0;
221 0 : res = poll(&pfd, 1, SSS_CLI_SOCKET_TIMEOUT);
222 0 : 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 0 : } while (error == EINTR);
229 :
230 0 : 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 0 : if (pfd.revents & (POLLHUP)) {
239 0 : pollhup = true;
240 : }
241 0 : if (pfd.revents & (POLLERR | POLLNVAL)) {
242 0 : *errnop = EPIPE;
243 : }
244 0 : if (!(pfd.revents & POLLIN)) {
245 0 : *errnop = EBUSY;
246 : }
247 0 : break;
248 : default: /* more than one avail ?? */
249 0 : *errnop = EBADF;
250 0 : break;
251 : }
252 0 : if (*errnop) {
253 0 : sss_cli_close_socket();
254 0 : ret = SSS_STATUS_UNAVAIL;
255 0 : goto failed;
256 : }
257 :
258 0 : errno = 0;
259 0 : if (datarecv < SSS_NSS_HEADER_SIZE) {
260 0 : res = read(sss_cli_sd,
261 : (char *)header + datarecv,
262 : SSS_NSS_HEADER_SIZE - datarecv);
263 : } else {
264 0 : bufrecv = datarecv - SSS_NSS_HEADER_SIZE;
265 0 : res = read(sss_cli_sd,
266 : (char *) buf + bufrecv,
267 0 : header[0] - datarecv);
268 : }
269 0 : error = errno;
270 :
271 0 : 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 0 : datarecv += res;
291 :
292 0 : 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 0 : 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 0 : 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 0 : if (header[0] > SSS_NSS_HEADER_SIZE) {
316 0 : len = header[0] - SSS_NSS_HEADER_SIZE;
317 0 : buf = malloc(len);
318 0 : 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 0 : if (pollhup) {
329 0 : sss_cli_close_socket();
330 : }
331 :
332 0 : *_len = len;
333 0 : *_buf = buf;
334 :
335 0 : 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 0 : 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 0 : uint8_t *buf = NULL;
352 0 : int len = 0;
353 :
354 : /* send data */
355 0 : ret = sss_cli_send_req(cmd, rd, errnop);
356 0 : if (ret != SSS_STATUS_SUCCESS) {
357 0 : return ret;
358 : }
359 :
360 : /* data sent, now get reply */
361 0 : ret = sss_cli_recv_rep(cmd, &buf, &len, errnop);
362 0 : 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 0 : if (repbuf && buf) {
369 0 : *repbuf = buf;
370 0 : if (replen) {
371 0 : *replen = len;
372 : }
373 : } else {
374 0 : free(buf);
375 0 : if (replen) {
376 0 : *replen = 0;
377 : }
378 : }
379 :
380 0 : return SSS_STATUS_SUCCESS;
381 : }
382 :
383 : /* GET_VERSION Reply:
384 : * 0-3: 32bit unsigned version number
385 : */
386 :
387 0 : static bool sss_cli_check_version(const char *socket_name)
388 : {
389 0 : 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 0 : if (strcmp(socket_name, SSS_NSS_SOCKET_NAME) == 0) {
398 0 : 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 0 : req.len = sizeof(expected_version);
415 0 : req.data = &expected_version;
416 :
417 0 : nret = sss_cli_make_request_nochecks(SSS_GET_VERSION, &req,
418 : &repbuf, &replen, &errnop);
419 0 : if (nret != SSS_STATUS_SUCCESS) {
420 0 : return false;
421 : }
422 :
423 0 : if (!repbuf) {
424 0 : return false;
425 : }
426 :
427 0 : SAFEALIGN_COPY_UINT32(&obtained_version, repbuf, NULL);
428 0 : free(repbuf);
429 :
430 0 : 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 65 : static int make_nonstd_fd_internals(int fd, int limit)
440 : {
441 : int new_fd;
442 65 : 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 65 : 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 65 : static int make_safe_fd(int fd)
480 : {
481 : int result, flags;
482 65 : int new_fd = make_nonstd_fd_internals(fd, RECURSION_LIMIT);
483 65 : 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 65 : if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
500 0 : close(new_fd);
501 0 : return -1;
502 : }
503 :
504 65 : flags |= FLAG_TO_SET;
505 65 : 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 65 : result = flags = fcntl(new_fd, F_GETFD, 0);
515 65 : if (flags >= 0) {
516 65 : flags |= FD_CLOEXEC;
517 65 : result = fcntl( new_fd, F_SETFD, flags );
518 : }
519 65 : if (result < 0) {
520 0 : close(new_fd);
521 0 : return -1;
522 : }
523 : #endif
524 65 : return new_fd;
525 : }
526 :
527 65 : static int sss_cli_open_socket(int *errnop, const char *socket_name)
528 : {
529 : struct sockaddr_un nssaddr;
530 65 : bool inprogress = true;
531 65 : bool connected = false;
532 : unsigned int wait_time;
533 : unsigned int sleep_time;
534 65 : time_t start_time = time(NULL);
535 : int ret;
536 : int sd;
537 :
538 65 : memset(&nssaddr, 0, sizeof(struct sockaddr_un));
539 65 : nssaddr.sun_family = AF_UNIX;
540 65 : strncpy(nssaddr.sun_path, socket_name,
541 65 : strlen(socket_name) + 1);
542 :
543 65 : sd = socket(AF_UNIX, SOCK_STREAM, 0);
544 65 : 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 65 : sd = make_safe_fd(sd);
552 65 : if (sd == -1) {
553 0 : *errnop = errno;
554 0 : return -1;
555 : }
556 :
557 : /* this piece is adapted from winbind client code */
558 65 : wait_time = 0;
559 65 : sleep_time = 0;
560 195 : while (inprogress) {
561 65 : int connect_errno = 0;
562 : socklen_t errnosize;
563 : struct pollfd pfd;
564 :
565 65 : wait_time += sleep_time;
566 :
567 65 : ret = connect(sd, (struct sockaddr *)&nssaddr,
568 : sizeof(nssaddr));
569 65 : if (ret == 0) {
570 0 : connected = true;
571 0 : break;
572 : }
573 :
574 65 : 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 65 : *errnop = errno;
600 65 : inprogress = false;
601 65 : break;
602 : }
603 :
604 65 : if (wait_time >= SSS_CLI_SOCKET_TIMEOUT) {
605 0 : inprogress = false;
606 : }
607 :
608 65 : if (connected) {
609 0 : inprogress = false;
610 : }
611 : }
612 :
613 65 : if (!connected) {
614 65 : close(sd);
615 65 : return -1;
616 : }
617 :
618 0 : ret = fstat(sd, &sss_cli_sb);
619 0 : if (ret != 0) {
620 0 : close(sd);
621 0 : return -1;
622 : }
623 :
624 0 : return sd;
625 : }
626 :
627 65 : 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 65 : if (getpid() != mypid) {
635 4 : ret = fstat(sss_cli_sd, &mysb);
636 4 : 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 4 : sss_cli_sd = -1;
644 4 : mypid = getpid();
645 : }
646 :
647 : /* check if the socket has been closed on the other side */
648 65 : 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 65 : mysd = sss_cli_open_socket(errnop, socket_name);
694 65 : if (mysd == -1) {
695 65 : return SSS_STATUS_UNAVAIL;
696 : }
697 :
698 0 : sss_cli_sd = mysd;
699 :
700 0 : if (sss_cli_check_version(socket_name)) {
701 0 : 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 65 : 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 65 : envval = getenv("_SSS_LOOPS");
721 65 : if (envval && strcmp(envval, "NO") == 0) {
722 0 : return NSS_STATUS_NOTFOUND;
723 : }
724 :
725 65 : ret = sss_cli_check_socket(errnop, SSS_NSS_SOCKET_NAME);
726 65 : 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 65 : return NSS_STATUS_UNAVAIL;
733 : #endif
734 : }
735 :
736 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
737 0 : if (ret == SSS_STATUS_UNAVAIL && *errnop == EPIPE) {
738 : /* try reopen socket */
739 0 : ret = sss_cli_check_socket(errnop, SSS_NSS_SOCKET_NAME);
740 0 : if (ret != SSS_STATUS_SUCCESS) {
741 : #ifdef NONSTANDARD_SSS_NSS_BEHAVIOUR
742 : *errnop = 0;
743 : errno = 0;
744 : return NSS_STATUS_NOTFOUND;
745 : #else
746 0 : return NSS_STATUS_UNAVAIL;
747 : #endif
748 : }
749 :
750 : /* and make request one more time */
751 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
752 : }
753 0 : switch (ret) {
754 : case SSS_STATUS_TRYAGAIN:
755 0 : return NSS_STATUS_TRYAGAIN;
756 : case SSS_STATUS_SUCCESS:
757 0 : return NSS_STATUS_SUCCESS;
758 : case SSS_STATUS_UNAVAIL:
759 : default:
760 : #ifdef NONSTANDARD_SSS_NSS_BEHAVIOUR
761 : *errnop = 0;
762 : errno = 0;
763 : return NSS_STATUS_NOTFOUND;
764 : #else
765 0 : return NSS_STATUS_UNAVAIL;
766 : #endif
767 : }
768 : }
769 :
770 0 : int sss_pac_check_and_open(void)
771 : {
772 : enum sss_status ret;
773 : int errnop;
774 :
775 0 : ret = sss_cli_check_socket(&errnop, SSS_PAC_SOCKET_NAME);
776 0 : if (ret != SSS_STATUS_SUCCESS) {
777 0 : return EIO;
778 : }
779 :
780 0 : return EOK;
781 : }
782 :
783 0 : int sss_pac_make_request(enum sss_cli_command cmd,
784 : struct sss_cli_req_data *rd,
785 : uint8_t **repbuf, size_t *replen,
786 : int *errnop)
787 : {
788 : enum sss_status ret;
789 : char *envval;
790 :
791 : /* avoid looping in the nss daemon */
792 0 : envval = getenv("_SSS_LOOPS");
793 0 : if (envval && strcmp(envval, "NO") == 0) {
794 0 : return NSS_STATUS_NOTFOUND;
795 : }
796 :
797 0 : ret = sss_cli_check_socket(errnop, SSS_PAC_SOCKET_NAME);
798 0 : if (ret != SSS_STATUS_SUCCESS) {
799 0 : return NSS_STATUS_UNAVAIL;
800 : }
801 :
802 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
803 0 : if (ret == SSS_STATUS_UNAVAIL && *errnop == EPIPE) {
804 : /* try reopen socket */
805 0 : ret = sss_cli_check_socket(errnop, SSS_PAC_SOCKET_NAME);
806 0 : if (ret != SSS_STATUS_SUCCESS) {
807 0 : return NSS_STATUS_UNAVAIL;
808 : }
809 :
810 : /* and make request one more time */
811 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
812 : }
813 0 : switch (ret) {
814 : case SSS_STATUS_TRYAGAIN:
815 0 : return NSS_STATUS_TRYAGAIN;
816 : case SSS_STATUS_SUCCESS:
817 0 : return NSS_STATUS_SUCCESS;
818 : case SSS_STATUS_UNAVAIL:
819 : default:
820 0 : return NSS_STATUS_UNAVAIL;
821 : }
822 : }
823 :
824 0 : errno_t check_server_cred(int sockfd)
825 : {
826 : #ifdef HAVE_UCRED
827 : int ret;
828 : struct ucred server_cred;
829 0 : socklen_t server_cred_len = sizeof(server_cred);
830 :
831 0 : ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &server_cred,
832 : &server_cred_len);
833 0 : if (ret != 0) {
834 0 : return errno;
835 : }
836 :
837 0 : if (server_cred_len != sizeof(struct ucred)) {
838 0 : return ESSS_BAD_CRED_MSG;
839 : }
840 :
841 0 : if (server_cred.uid != 0 || server_cred.gid != 0) {
842 0 : return ESSS_SERVER_NOT_TRUSTED;
843 : }
844 : #endif
845 0 : return 0;
846 : }
847 :
848 0 : int sss_pam_make_request(enum sss_cli_command cmd,
849 : struct sss_cli_req_data *rd,
850 : uint8_t **repbuf, size_t *replen,
851 : int *errnop)
852 : {
853 : int ret, statret;
854 : errno_t error;
855 : enum sss_status status;
856 : char *envval;
857 : struct stat stat_buf;
858 : const char *socket_name;
859 :
860 0 : sss_pam_lock();
861 :
862 : /* avoid looping in the pam daemon */
863 0 : envval = getenv("_SSS_LOOPS");
864 0 : if (envval && strcmp(envval, "NO") == 0) {
865 0 : ret = PAM_SERVICE_ERR;
866 0 : goto out;
867 : }
868 :
869 : /* only root shall use the privileged pipe */
870 0 : if (getuid() == 0 && getgid() == 0) {
871 0 : socket_name = SSS_PAM_PRIV_SOCKET_NAME;
872 0 : statret = stat(socket_name, &stat_buf);
873 0 : if (statret != 0) {
874 0 : ret = PAM_SERVICE_ERR;
875 0 : goto out;
876 : }
877 0 : if ( ! (stat_buf.st_uid == 0 &&
878 0 : stat_buf.st_gid == 0 &&
879 0 : S_ISSOCK(stat_buf.st_mode) &&
880 0 : (stat_buf.st_mode & ~S_IFMT) == 0600 )) {
881 0 : *errnop = ESSS_BAD_PRIV_SOCKET;
882 0 : ret = PAM_SERVICE_ERR;
883 0 : goto out;
884 : }
885 : } else {
886 0 : socket_name = SSS_PAM_SOCKET_NAME;
887 0 : statret = stat(socket_name, &stat_buf);
888 0 : if (statret != 0) {
889 0 : ret = PAM_SERVICE_ERR;
890 0 : goto out;
891 : }
892 0 : if ( ! (stat_buf.st_uid == 0 &&
893 0 : stat_buf.st_gid == 0 &&
894 0 : S_ISSOCK(stat_buf.st_mode) &&
895 0 : (stat_buf.st_mode & ~S_IFMT) == 0666 )) {
896 0 : *errnop = ESSS_BAD_PUB_SOCKET;
897 0 : ret = PAM_SERVICE_ERR;
898 0 : goto out;
899 : }
900 : }
901 :
902 0 : status = sss_cli_check_socket(errnop, socket_name);
903 0 : if (status != SSS_STATUS_SUCCESS) {
904 0 : ret = PAM_SERVICE_ERR;
905 0 : goto out;
906 : }
907 :
908 0 : error = check_server_cred(sss_cli_sd);
909 0 : if (error != 0) {
910 0 : sss_cli_close_socket();
911 0 : *errnop = error;
912 0 : ret = PAM_SERVICE_ERR;
913 0 : goto out;
914 : }
915 :
916 0 : status = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
917 0 : if (status == SSS_STATUS_UNAVAIL && *errnop == EPIPE) {
918 : /* try reopen socket */
919 0 : status = sss_cli_check_socket(errnop, socket_name);
920 0 : if (status != SSS_STATUS_SUCCESS) {
921 0 : ret = PAM_SERVICE_ERR;
922 0 : goto out;
923 : }
924 :
925 : /* and make request one more time */
926 0 : status = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
927 : }
928 :
929 0 : if (status == SSS_STATUS_SUCCESS) {
930 0 : ret = PAM_SUCCESS;
931 : } else {
932 0 : ret = PAM_SERVICE_ERR;
933 : }
934 :
935 : out:
936 0 : sss_pam_unlock();
937 0 : return ret;
938 : }
939 :
940 0 : void sss_pam_close_fd(void)
941 : {
942 0 : sss_pam_lock();
943 :
944 0 : if (sss_cli_sd != -1) {
945 0 : close(sss_cli_sd);
946 0 : sss_cli_sd = -1;
947 : }
948 :
949 0 : sss_pam_unlock();
950 0 : }
951 :
952 : static enum sss_status
953 0 : sss_cli_make_request_with_checks(enum sss_cli_command cmd,
954 : struct sss_cli_req_data *rd,
955 : uint8_t **repbuf, size_t *replen,
956 : int *errnop,
957 : const char *socket_name)
958 : {
959 0 : enum sss_status ret = SSS_STATUS_UNAVAIL;
960 :
961 0 : ret = sss_cli_check_socket(errnop, socket_name);
962 0 : if (ret != SSS_STATUS_SUCCESS) {
963 0 : return SSS_STATUS_UNAVAIL;
964 : }
965 :
966 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
967 0 : if (ret == SSS_STATUS_UNAVAIL && *errnop == EPIPE) {
968 : /* try reopen socket */
969 0 : ret = sss_cli_check_socket(errnop, socket_name);
970 0 : if (ret != SSS_STATUS_SUCCESS) {
971 0 : return SSS_STATUS_UNAVAIL;
972 : }
973 :
974 : /* and make request one more time */
975 0 : ret = sss_cli_make_request_nochecks(cmd, rd, repbuf, replen, errnop);
976 : }
977 :
978 0 : return ret;
979 : }
980 :
981 0 : int sss_sudo_make_request(enum sss_cli_command cmd,
982 : struct sss_cli_req_data *rd,
983 : uint8_t **repbuf, size_t *replen,
984 : int *errnop)
985 : {
986 0 : return sss_cli_make_request_with_checks(cmd, rd, repbuf, replen, errnop,
987 : SSS_SUDO_SOCKET_NAME);
988 : }
989 :
990 0 : int sss_autofs_make_request(enum sss_cli_command cmd,
991 : struct sss_cli_req_data *rd,
992 : uint8_t **repbuf, size_t *replen,
993 : int *errnop)
994 : {
995 0 : return sss_cli_make_request_with_checks(cmd, rd, repbuf, replen, errnop,
996 : SSS_AUTOFS_SOCKET_NAME);
997 : }
998 :
999 0 : int sss_ssh_make_request(enum sss_cli_command cmd,
1000 : struct sss_cli_req_data *rd,
1001 : uint8_t **repbuf, size_t *replen,
1002 : int *errnop)
1003 : {
1004 0 : return sss_cli_make_request_with_checks(cmd, rd, repbuf, replen, errnop,
1005 : SSS_SSH_SOCKET_NAME);
1006 : }
1007 :
1008 :
1009 0 : const char *ssscli_err2string(int err)
1010 : {
1011 : const char *m;
1012 :
1013 0 : switch(err) {
1014 : case ESSS_BAD_PRIV_SOCKET:
1015 0 : return _("Privileged socket has wrong ownership or permissions.");
1016 : break;
1017 : case ESSS_BAD_PUB_SOCKET:
1018 0 : return _("Public socket has wrong ownership or permissions.");
1019 : break;
1020 : case ESSS_BAD_CRED_MSG:
1021 0 : return _("Unexpected format of the server credential message.");
1022 : break;
1023 : case ESSS_SERVER_NOT_TRUSTED:
1024 0 : return _("SSSD is not run by root.");
1025 : break;
1026 : default:
1027 0 : m = strerror(err);
1028 0 : if (m == NULL) {
1029 0 : return _("An error occurred, but no description can be found.");
1030 : }
1031 0 : return m;
1032 : break;
1033 : }
1034 :
1035 : return _("Unexpected error while looking for an error description");
1036 : }
1037 :
1038 : /* Return strlen(str) or maxlen, whichever is shorter
1039 : * Returns EINVAL if str is NULL, EFBIG if str is longer than maxlen
1040 : * _len will return the result
1041 : *
1042 : * This function is useful for preventing buffer overflow attacks.
1043 : */
1044 67 : errno_t sss_strnlen(const char *str, size_t maxlen, size_t *len)
1045 : {
1046 67 : if (!str) {
1047 0 : return EINVAL;
1048 : }
1049 :
1050 : #if defined __USE_GNU
1051 67 : *len = strnlen(str, maxlen);
1052 : #else
1053 : *len = 0;
1054 : while (*len < maxlen) {
1055 : if (str[*len] == '\0') break;
1056 : (*len)++;
1057 : }
1058 : #endif
1059 :
1060 67 : if (*len == maxlen && str[*len] != '\0') {
1061 0 : return EFBIG;
1062 : }
1063 :
1064 67 : return 0;
1065 : }
1066 :
1067 : #if HAVE_PTHREAD
1068 : typedef void (*sss_mutex_init)(void);
1069 :
1070 : struct sss_mutex {
1071 : pthread_mutex_t mtx;
1072 :
1073 : pthread_once_t once;
1074 : sss_mutex_init init;
1075 : };
1076 :
1077 : static void sss_nss_mt_init(void);
1078 : static void sss_pam_mt_init(void);
1079 : static void sss_nss_mc_mt_init(void);
1080 :
1081 : static struct sss_mutex sss_nss_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER,
1082 : .once = PTHREAD_ONCE_INIT,
1083 : .init = sss_nss_mt_init };
1084 :
1085 : static struct sss_mutex sss_pam_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER,
1086 : .once = PTHREAD_ONCE_INIT,
1087 : .init = sss_pam_mt_init };
1088 :
1089 : static struct sss_mutex sss_nss_mc_mtx = { .mtx = PTHREAD_MUTEX_INITIALIZER,
1090 : .once = PTHREAD_ONCE_INIT,
1091 : .init = sss_nss_mc_mt_init };
1092 :
1093 : /* Wrappers for robust mutex support */
1094 9 : static int sss_mutexattr_setrobust (pthread_mutexattr_t *attr)
1095 : {
1096 : #ifdef HAVE_PTHREAD_MUTEXATTR_SETROBUST
1097 9 : return pthread_mutexattr_setrobust(attr, PTHREAD_MUTEX_ROBUST);
1098 : #elif defined(HAVE_PTHREAD_MUTEXATTR_SETROBUST_NP)
1099 : return pthread_mutexattr_setrobust_np(attr, PTHREAD_MUTEX_ROBUST_NP);
1100 : #else
1101 : #warning Robust mutexes are not supported on this platform.
1102 : return 0;
1103 : #endif
1104 : }
1105 :
1106 0 : static int sss_mutex_consistent(pthread_mutex_t *mtx)
1107 : {
1108 : #ifdef HAVE_PTHREAD_MUTEX_CONSISTENT
1109 0 : return pthread_mutex_consistent(mtx);
1110 : #elif defined(HAVE_PTHREAD_MUTEX_CONSISTENT_NP)
1111 : return pthread_mutex_consistent_np(mtx);
1112 : #else
1113 : #warning Robust mutexes are not supported on this platform.
1114 : return 0;
1115 : #endif
1116 : }
1117 :
1118 : /* Generic mutex init, lock, unlock functions */
1119 9 : static void sss_mt_init(struct sss_mutex *m)
1120 : {
1121 : pthread_mutexattr_t attr;
1122 :
1123 9 : if (pthread_mutexattr_init(&attr) != 0) {
1124 0 : return;
1125 : }
1126 9 : if (sss_mutexattr_setrobust(&attr) != 0) {
1127 0 : return;
1128 : }
1129 :
1130 9 : pthread_mutex_init(&m->mtx, &attr);
1131 9 : pthread_mutexattr_destroy(&attr);
1132 : }
1133 :
1134 200 : static void sss_mt_lock(struct sss_mutex *m)
1135 : {
1136 200 : pthread_once(&m->once, m->init);
1137 200 : if (pthread_mutex_lock(&m->mtx) == EOWNERDEAD) {
1138 0 : sss_cli_close_socket();
1139 0 : sss_mutex_consistent(&m->mtx);
1140 : }
1141 200 : }
1142 :
1143 200 : static void sss_mt_unlock(struct sss_mutex *m)
1144 : {
1145 200 : pthread_mutex_unlock(&m->mtx);
1146 200 : }
1147 :
1148 : /* NSS mutex wrappers */
1149 5 : static void sss_nss_mt_init(void)
1150 : {
1151 5 : sss_mt_init(&sss_nss_mtx);
1152 5 : }
1153 70 : void sss_nss_lock(void)
1154 : {
1155 70 : sss_mt_lock(&sss_nss_mtx);
1156 70 : }
1157 70 : void sss_nss_unlock(void)
1158 : {
1159 70 : sss_mt_unlock(&sss_nss_mtx);
1160 70 : }
1161 :
1162 : /* NSS mutex wrappers */
1163 0 : static void sss_pam_mt_init(void)
1164 : {
1165 0 : sss_mt_init(&sss_pam_mtx);
1166 0 : }
1167 0 : void sss_pam_lock(void)
1168 : {
1169 0 : sss_mt_lock(&sss_pam_mtx);
1170 0 : }
1171 0 : void sss_pam_unlock(void)
1172 : {
1173 0 : sss_mt_unlock(&sss_pam_mtx);
1174 0 : }
1175 :
1176 : /* NSS mutex wrappers */
1177 4 : static void sss_nss_mc_mt_init(void)
1178 : {
1179 4 : sss_mt_init(&sss_nss_mc_mtx);
1180 4 : }
1181 130 : void sss_nss_mc_lock(void)
1182 : {
1183 130 : sss_mt_lock(&sss_nss_mc_mtx);
1184 130 : }
1185 130 : void sss_nss_mc_unlock(void)
1186 : {
1187 130 : sss_mt_unlock(&sss_nss_mc_mtx);
1188 130 : }
1189 :
1190 : #else
1191 :
1192 : /* sorry no mutexes available */
1193 : void sss_nss_lock(void) { return; }
1194 : void sss_nss_unlock(void) { return; }
1195 : void sss_pam_lock(void) { return; }
1196 : void sss_pam_unlock(void) { return; }
1197 : void sss_nss_mc_lock(void) { return; }
1198 : void sss_nss_mc_unlock(void) { return; }
1199 : #endif
1200 :
1201 :
1202 0 : errno_t sss_readrep_copy_string(const char *in,
1203 : size_t *offset,
1204 : size_t *slen,
1205 : size_t *dlen,
1206 : char **out,
1207 : size_t *size)
1208 : {
1209 0 : size_t i = 0;
1210 0 : while (*slen > *offset && *dlen > 0) {
1211 0 : (*out)[i] = in[*offset];
1212 0 : if ((*out)[i] == '\0') break;
1213 0 : i++;
1214 0 : (*offset)++;
1215 0 : (*dlen)--;
1216 : }
1217 0 : if (*slen <= *offset) { /* premature end of buf */
1218 0 : return EBADMSG;
1219 : }
1220 0 : if (*dlen == 0) { /* not enough memory */
1221 0 : return ERANGE; /* not ENOMEM, ERANGE is what glibc looks for */
1222 : }
1223 0 : (*offset)++;
1224 0 : (*dlen)--;
1225 0 : if (size) {
1226 0 : *size = i;
1227 : }
1228 :
1229 0 : return EOK;
1230 : }
|