1 /*
2 * Copyright 2010-2024 Red Hat, Inc.
3 *
4 * Author: Angus Salkeld <asalkeld@redhat.com>
5 *
6 * This file is part of libqb.
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "os_base.h"
22 #include <poll.h>
23
24 #if defined(HAVE_GETPEERUCRED)
25 #include <ucred.h>
26 #endif
27
28 #ifdef HAVE_SYS_UN_H
29 #include <sys/un.h>
30 #endif /* HAVE_SYS_UN_H */
31 #ifdef HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34 #ifdef HAVE_SYS_MMAN_H
35 #include <sys/mman.h>
36 #endif
37
38 #include <qb/qbatomic.h>
39 #include <qb/qbipcs.h>
40 #include <qb/qbloop.h>
41 #include <qb/qbdefs.h>
42
43 #include "util_int.h"
44 #include "ipc_int.h"
45
46 struct ipc_auth_ugp {
47 uid_t uid;
48 gid_t gid;
49 pid_t pid;
50 };
51
52 struct ipc_auth_data {
53 int32_t sock;
54 struct qb_ipcs_service *s;
55 union {
56 struct qb_ipc_connection_request req;
57 struct qb_ipc_connection_response res;
58 } msg;
59 struct msghdr msg_recv;
60 struct iovec iov_recv;
61 struct ipc_auth_ugp ugp;
62
63 size_t processed;
64 size_t len;
65
66 #ifdef SO_PASSCRED
67 char *cmsg_cred;
68 #endif
69
70 };
71
72 static int32_t qb_ipcs_us_connection_acceptor(int fd, int revent, void *data);
73
74 ssize_t
75 qb_ipc_us_send(struct qb_ipc_one_way *one_way, const void *msg, size_t len)
76 {
77 int32_t result;
78 int32_t processed = 0;
79 char *rbuf = (char *)msg;
80
81 qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
82
83 retry_send:
|
CID (unavailable; MK=cbed7ac5a68cc2984d193270fe5bb53b) (#1 of 2): Overflowed integer argument (INTEGER_OVERFLOW): |
|
(1) Event tainted_data_return: |
Called function "send(one_way->u.us.sock, &rbuf[processed], len - processed, MSG_NOSIGNAL)", and a possible return value may be less than zero. |
|
(2) Event cast_overflow: |
An assign that casts to a different type, which might trigger an overflow. |
|
(7) Event overflow: |
The expression "len - processed" is deemed overflowed because at least one of its arguments has overflowed. |
|
(8) Event overflow_sink: |
"len - processed", which might have underflowed, is passed to "send(one_way->u.us.sock, &rbuf[processed], len - processed, MSG_NOSIGNAL)". |
| Also see events: |
[overflow] |
84 result = send(one_way->u.us.sock,
85 &rbuf[processed], len - processed, MSG_NOSIGNAL);
86
|
(3) Event path: |
Condition "result == -1", taking false branch. |
87 if (result == -1) {
88 if (errno == EAGAIN && processed > 0) {
89 goto retry_send;
90 } else {
91 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
92 return -errno;
93 }
94 }
95
96 processed += result;
|
(5) Event path: |
Condition "processed != len", taking true branch. |
97 if (processed != len) {
|
(6) Event path: |
Jumping to label "retry_send". |
98 goto retry_send;
99 }
100
101 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
102
103 return processed;
104 }
105
106 static ssize_t
107 qb_ipc_us_recv_msghdr(struct ipc_auth_data *data)
108 {
109 char *msg = (char *) &data->msg;
110 int32_t result;
111
112 qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
113
114 retry_recv:
115 data->msg_recv.msg_iov->iov_base = &msg[data->processed];
116 data->msg_recv.msg_iov->iov_len = data->len - data->processed;
117
118 result = recvmsg(data->sock, &data->msg_recv, MSG_NOSIGNAL | MSG_WAITALL);
119 if (result == -1 && errno == EAGAIN) {
120 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
121 return -EAGAIN;
122 }
123 if (result == -1) {
124 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
125 return -errno;
126 }
127 if (result == 0) {
128 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
129 qb_util_log(LOG_DEBUG,
130 "recv(fd %d) got 0 bytes assuming ENOTCONN", data->sock);
131 return -ENOTCONN;
132 }
133
134 data->processed += result;
135 if (data->processed != data->len) {
136 goto retry_recv;
137 }
138 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
139 assert(data->processed == data->len);
140
141 return data->processed;
142 }
143
144 int32_t
145 qb_ipc_us_sock_error_is_disconnected(int err)
146 {
147 if (err >= 0) {
148 return QB_FALSE;
149 } else if (err == -EAGAIN ||
150 err == -ETIMEDOUT ||
151 err == -EINTR ||
152 #ifdef EWOULDBLOCK
153 err == -EWOULDBLOCK ||
154 #endif
155 err == -EMSGSIZE ||
156 err == -ENOMSG ||
157 err == -EINVAL) {
158 return QB_FALSE;
159 }
160 return QB_TRUE;
161 }
162
163 int32_t
164 qb_ipc_us_ready(struct qb_ipc_one_way * ow_data,
165 struct qb_ipc_one_way * ow_conn,
166 int32_t ms_timeout, int32_t events)
167 {
168 struct pollfd ufds[2];
169 int32_t poll_events;
170 int numfds = 1;
171 int i;
172
173 ufds[0].fd = ow_data->u.us.sock;
174 ufds[0].events = events;
175 ufds[0].revents = 0;
176
177 if (ow_conn && ow_data != ow_conn) {
178 numfds++;
179 ufds[1].fd = ow_conn->u.us.sock;
180 ufds[1].events = POLLIN;
181 ufds[1].revents = 0;
182 }
183 poll_events = poll(ufds, numfds, ms_timeout);
184 if ((poll_events == -1 && errno == EINTR) || poll_events == 0) {
185 return -EAGAIN;
186 } else if (poll_events == -1) {
187 return -errno;
188 }
189 for (i = 0; i < poll_events; i++) {
190 if (ufds[i].revents & POLLERR) {
191 qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLERR",
192 ufds[i].fd);
193 return -ENOTCONN;
194 } else if (ufds[i].revents & POLLHUP) {
195 qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLHUP",
196 ufds[i].fd);
197 return -ENOTCONN;
198 } else if (ufds[i].revents & POLLNVAL) {
199 qb_util_log(LOG_DEBUG, "poll(fd %d) got POLLNVAL",
200 ufds[i].fd);
201 return -ENOTCONN;
202 } else if (ufds[i].revents == 0) {
203 qb_util_log(LOG_DEBUG, "poll(fd %d) zero revents",
204 ufds[i].fd);
205 return -ENOTCONN;
206 }
207 }
208 return 0;
209 }
210
211 /*
212 * recv an entire message - and try hard to get all of it.
213 */
214 ssize_t
215 qb_ipc_us_recv(struct qb_ipc_one_way * one_way,
216 void *msg, size_t len, int32_t timeout)
217 {
218 int32_t result;
219 int32_t final_rc = 0;
220 int32_t processed = 0;
221 int32_t to_recv = len;
222 char *data = msg;
223
224 qb_sigpipe_ctl(QB_SIGPIPE_IGNORE);
225
226 retry_recv:
227 result = recv(one_way->u.us.sock, &data[processed], to_recv,
228 MSG_NOSIGNAL | MSG_WAITALL);
229
230 if (result == -1) {
231 if (errno == EAGAIN && (processed > 0 || timeout == -1)) {
232 result = qb_ipc_us_ready(one_way, NULL, timeout, POLLIN);
233 if (result == 0 || result == -EAGAIN) {
234 goto retry_recv;
235 }
236 final_rc = result;
237 goto cleanup_sigpipe;
238 } else if (errno == ECONNRESET || errno == EPIPE) {
239 final_rc = -ENOTCONN;
240 goto cleanup_sigpipe;
241 } else {
242 final_rc = -errno;
243 goto cleanup_sigpipe;
244 }
245 }
246
247 if (result == 0) {
248 final_rc = -ENOTCONN;
249 goto cleanup_sigpipe;
250 }
251 processed += result;
252 to_recv -= result;
253 if (processed != len) {
254 goto retry_recv;
255 }
256 final_rc = processed;
257
258 cleanup_sigpipe:
259 qb_sigpipe_ctl(QB_SIGPIPE_DEFAULT);
260 return final_rc;
261 }
262
263 static int32_t
264 qb_ipcc_stream_sock_connect(const char *socket_name, int32_t * sock_pt)
265 {
266 int32_t request_fd;
267 struct sockaddr_un address;
268 int32_t res = 0;
269
270 request_fd = socket(PF_UNIX, SOCK_STREAM, 0);
271 if (request_fd == -1) {
272 return -errno;
273 }
274
275 qb_socket_nosigpipe(request_fd);
276
277 res = qb_sys_fd_nonblock_cloexec_set(request_fd);
278 if (res < 0) {
279 goto error_connect;
280 }
281
282 memset(&address, 0, sizeof(struct sockaddr_un));
283 address.sun_family = AF_UNIX;
284 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
285 address.sun_len = QB_SUN_LEN(&address);
286 #endif
287
288 if (!use_filesystem_sockets()) {
289 snprintf(address.sun_path + 1, UNIX_PATH_MAX - 1, "%s", socket_name);
290 } else {
291 snprintf(address.sun_path, sizeof(address.sun_path), "%s/%s", SOCKETDIR,
292 socket_name);
293 }
294
295 if (connect(request_fd, (struct sockaddr *)&address,
296 QB_SUN_LEN(&address)) == -1) {
297 res = -errno;
298 goto error_connect;
299 }
300
301 *sock_pt = request_fd;
302 return 0;
303
304 error_connect:
305 close(request_fd);
306 *sock_pt = -1;
307
308 return res;
309 }
310
311 void
312 qb_ipcc_us_sock_close(int32_t sock)
313 {
314 shutdown(sock, SHUT_RDWR);
315 close(sock);
316 }
317
318 static int32_t
319 qb_ipc_auth_creds(struct ipc_auth_data *data)
320 {
321 int32_t res = 0;
322
323 /*
324 * currently support getpeerucred, getpeereid, and SO_PASSCRED credential
325 * retrieval mechanisms for various Platforms
326 */
327 #ifdef HAVE_GETPEERUCRED
328 /*
329 * Solaris and some BSD systems
330 */
331 {
332 ucred_t *uc = NULL;
333
334 if (getpeerucred(data->sock, &uc) == 0) {
335 res = 0;
336 data->ugp.uid = ucred_geteuid(uc);
337 data->ugp.gid = ucred_getegid(uc);
338 data->ugp.pid = ucred_getpid(uc);
339 ucred_free(uc);
340 } else {
341 res = -errno;
342 }
343 }
344 #elif defined(HAVE_GETPEEREID)
345 /*
346 * Usually MacOSX systems
347 */
348 {
349 /*
350 * TODO get the peer's pid.
351 * c->pid = ?;
352 */
353 if (getpeereid(data->sock, &data->ugp.uid, &data->ugp.gid) == 0) {
354 res = 0;
355 } else {
356 res = -errno;
357 }
358 }
359
360 #elif defined(SO_PASSCRED)
361 /*
362 * Usually Linux systems
363 */
364 {
365 struct ucred cred;
366 struct cmsghdr *cmsg;
367
368 res = -EINVAL;
369 for (cmsg = CMSG_FIRSTHDR(&data->msg_recv); cmsg != NULL;
370 cmsg = CMSG_NXTHDR(&data->msg_recv, cmsg)) {
371 if (cmsg->cmsg_type != SCM_CREDENTIALS)
372 continue;
373
374 /* Validate that the control message contains a full ucred structure */
375 if (cmsg->cmsg_len < CMSG_LEN(sizeof(struct ucred))) {
376 qb_util_log(LOG_WARNING,
377 "received malformed credential message (len=%zu, expected=%zu)",
378 (size_t)cmsg->cmsg_len, CMSG_LEN(sizeof(struct ucred)));
379 continue;
380 }
381
382 memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
383 res = 0;
384 data->ugp.pid = cred.pid;
385 data->ugp.uid = cred.uid;
386 data->ugp.gid = cred.gid;
387 break;
388 }
389 }
390 #else /* no credentials */
391 data->ugp.pid = 0;
392 data->ugp.uid = 0;
393 data->ugp.gid = 0;
394 res = -ENOTSUP;
395 #endif /* no credentials */
396
397 return res;
398 }
399
400 static void
401 destroy_ipc_auth_data(struct ipc_auth_data *data)
402 {
403 if (data->s) {
404 qb_ipcs_unref(data->s);
405 }
406
407 #ifdef SO_PASSCRED
408 free(data->cmsg_cred);
409 #endif
410 free(data);
411 }
412
413 static struct ipc_auth_data *
414 init_ipc_auth_data(int sock, size_t len)
415 {
416 struct ipc_auth_data *data = calloc(1, sizeof(struct ipc_auth_data));
417
418 if (data == NULL) {
419 return NULL;
420 }
421
422 data->msg_recv.msg_iov = &data->iov_recv;
423 data->msg_recv.msg_iovlen = 1;
424 data->msg_recv.msg_name = 0;
425 data->msg_recv.msg_namelen = 0;
426
427 #ifdef SO_PASSCRED
428 data->cmsg_cred = calloc(1, CMSG_SPACE(sizeof(struct ucred)));
429 if (data->cmsg_cred == NULL) {
430 destroy_ipc_auth_data(data);
431 return NULL;
432 }
433 data->msg_recv.msg_control = (void *)data->cmsg_cred;
434 data->msg_recv.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
435 #endif
436 #if defined(QB_SOLARIS) && !defined(_XPG4_2)
437 data->msg_recv.msg_accrights = 0;
438 data->msg_recv.msg_accrightslen = 0;
439 #else
440 data->msg_recv.msg_flags = 0;
441 #endif /* QB_SOLARIS */
442
443 data->len = len;
444 data->iov_recv.iov_base = (void *)&data->msg;
445 data->iov_recv.iov_len = data->len;
446 data->sock = sock;
447
448 return data;
449 }
450
451 int32_t
452 qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,
453 struct qb_ipc_connection_response *r)
454 {
455 int32_t res;
456 struct qb_ipc_connection_request request;
457 #ifdef QB_LINUX
458 int on = 1;
459 #endif
460
461 res = qb_ipcc_stream_sock_connect(c->name, &c->setup.u.us.sock);
462 if (res != 0) {
463 return res;
464 }
465 #ifdef QB_LINUX
466 res = setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &on,
467 sizeof(on));
468 if (res != 0) {
469 int err = errno;
470 qb_ipcc_us_sock_close(c->setup.u.us.sock);
471 errno = err;
472 return res;
473 }
474 #endif
475
476 memset(&request, 0, sizeof(request));
477 request.hdr.id = QB_IPC_MSG_AUTHENTICATE;
478 request.hdr.size = sizeof(request);
479 request.max_msg_size = c->setup.max_msg_size;
480 res = qb_ipc_us_send(&c->setup, &request, request.hdr.size);
481 if (res < 0) {
482 qb_ipcc_us_sock_close(c->setup.u.us.sock);
483 return res;
484 }
485
486 /* ... To be continued ... (when the FD is active) */
487 return 0;
488 }
489
490 #define AUTH_RECV_MAX_RETRIES 10
491 #define AUTH_RECV_SLEEP_TIME_US 100
492
493 /* Called from ipcc_connect_continue() when async connect socket is active */
494 int qb_ipcc_setup_connect_continue(struct qb_ipcc_connection *c, struct qb_ipc_connection_response *r)
495 {
496 struct ipc_auth_data *data;
497 int32_t res;
498 int res1;
499 int retry_count = 0;
500 #ifdef QB_LINUX
501 int off = 0;
502 #endif
503 data = init_ipc_auth_data(c->setup.u.us.sock, sizeof(struct qb_ipc_connection_response));
504 if (data == NULL) {
505 qb_ipcc_us_sock_close(c->setup.u.us.sock);
506 return -ENOMEM;
507 }
508 retry:
509 res = qb_ipc_us_recv_msghdr(data);
510 if (res == -EAGAIN && ++retry_count < AUTH_RECV_MAX_RETRIES) {
511 struct timespec ts = {0, AUTH_RECV_SLEEP_TIME_US*QB_TIME_NS_IN_USEC};
512 struct timespec ts_left = {0, 0};
513 nanosleep(&ts, &ts_left);
514 goto retry;
515 }
516
517 #ifdef QB_LINUX
518 res1 = setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &off,
519 sizeof(off));
520 if (res1 != 0) {
521 int err = errno;
522 destroy_ipc_auth_data(data);
523 errno = err;
524 return res;
525 }
526 #endif
527
528 if (res != data->len) {
529 destroy_ipc_auth_data(data);
530 return res;
531 }
532
533 memcpy(r, &data->msg.res, sizeof(struct qb_ipc_connection_response));
534
535 qb_ipc_auth_creds(data);
536 c->egid = data->ugp.gid;
537 c->euid = data->ugp.uid;
538 c->server_pid = data->ugp.pid;
539
540 destroy_ipc_auth_data(data);
541
542 return r->hdr.error;
543 }
544
545 /*
546 **************************************************************************
547 * SERVER
548 */
549
550 int32_t
551 qb_ipcs_us_publish(struct qb_ipcs_service * s)
552 {
553 struct sockaddr_un un_addr;
554 int32_t res;
555 #ifdef SO_PASSCRED
556 int on = 1;
557 #endif
558
559 /*
560 * Create socket for IPC clients, name socket, listen for connections
561 */
562 s->server_sock = socket(PF_UNIX, SOCK_STREAM, 0);
563 if (s->server_sock == -1) {
564 res = -errno;
565 qb_util_perror(LOG_ERR, "Cannot create server socket");
566 return res;
567 }
568
569 res = qb_sys_fd_nonblock_cloexec_set(s->server_sock);
570 if (res < 0) {
571 goto error_close;
572 }
573
574 memset(&un_addr, 0, sizeof(struct sockaddr_un));
575 un_addr.sun_family = AF_UNIX;
576 #if defined(QB_BSD) || defined(QB_DARWIN)
577 un_addr.sun_len = SUN_LEN(&un_addr);
578 #endif
579
580 qb_util_log(LOG_INFO, "server name: %s", s->name);
581
582 if (!use_filesystem_sockets()) {
583 snprintf(un_addr.sun_path + 1, UNIX_PATH_MAX - 1, "%s", s->name);
584 }
585 else {
586 struct stat stat_out;
587 res = stat(SOCKETDIR, &stat_out);
588 if (res == -1 || (res == 0 && !S_ISDIR(stat_out.st_mode))) {
589 res = -errno;
590 qb_util_log(LOG_CRIT,
591 "Required directory not present %s",
592 SOCKETDIR);
593 goto error_close;
594 }
595 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/%s", SOCKETDIR,
596 s->name);
597 unlink(un_addr.sun_path);
598 }
599
600 res = bind(s->server_sock, (struct sockaddr *)&un_addr,
601 QB_SUN_LEN(&un_addr));
602 if (res) {
603 res = -errno;
604 qb_util_perror(LOG_ERR, "Could not bind AF_UNIX (%s)",
605 un_addr.sun_path);
606 goto error_close;
607 }
608
609 /*
610 * Allow everyone to write to the socket since the IPC layer handles
611 * security automatically
612 */
613 if (use_filesystem_sockets()) {
614 (void)chmod(un_addr.sun_path, S_IRWXU | S_IRWXG | S_IRWXO);
615 }
616 #ifdef SO_PASSCRED
617 (void)setsockopt(s->server_sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
618 #endif
619 if (listen(s->server_sock, SERVER_BACKLOG) == -1) {
620 qb_util_perror(LOG_ERR, "socket listen failed");
621 }
622
623 res = s->poll_fns.dispatch_add(s->poll_priority, s->server_sock,
624 POLLIN | POLLPRI | POLLNVAL,
625 s, qb_ipcs_us_connection_acceptor);
626 return res;
627
628 error_close:
629 close(s->server_sock);
630 return res;
631 }
632
633 int32_t
634 qb_ipcs_us_withdraw(struct qb_ipcs_service * s)
635 {
636 qb_util_log(LOG_INFO, "withdrawing server sockets");
637 (void)s->poll_fns.dispatch_del(s->server_sock);
638 shutdown(s->server_sock, SHUT_RDWR);
639
640 if (use_filesystem_sockets()) {
641 struct sockaddr_un sockname;
642 socklen_t socklen = sizeof(sockname);
643 if ((getsockname(s->server_sock, (struct sockaddr *)&sockname, &socklen) == 0) &&
644 sockname.sun_family == AF_UNIX) {
645 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
646 /*
647 * Terminating NUL on FreeBSD is not part of the sun_path.
648 * Add it to use sun_path as a parameter of unlink
649 */
650 sockname.sun_path[sockname.sun_len - offsetof(struct sockaddr_un, sun_path)] = '\0';
651 #endif
652 unlink(sockname.sun_path);
653 }
654 }
655
656 close(s->server_sock);
657 s->server_sock = -1;
658 return 0;
659 }
660
661 static int32_t
662 handle_new_connection(struct qb_ipcs_service *s,
663 int32_t auth_result,
664 int32_t sock,
665 void *msg, size_t len, struct ipc_auth_ugp *ugp)
666 {
667 struct qb_ipcs_connection *c = NULL;
668 struct qb_ipc_connection_request *req = msg;
669 int32_t res = auth_result;
670 int32_t res2 = 0;
671 uint32_t max_buffer_size = QB_MAX(req->max_msg_size, s->max_buffer_size);
672 struct qb_ipc_connection_response response;
673 const char suffix[] = "/qb";
674 int desc_len;
675
676 c = qb_ipcs_connection_alloc(s);
677 if (c == NULL) {
678 qb_ipcc_us_sock_close(sock);
679 return -ENOMEM;
680 }
681
682 c->receive_buf = calloc(1, max_buffer_size);
683 if (c->receive_buf == NULL) {
684 free(c);
685 qb_ipcc_us_sock_close(sock);
686 return -ENOMEM;
687 }
688 c->setup.u.us.sock = sock;
689 c->request.max_msg_size = max_buffer_size;
690 c->response.max_msg_size = max_buffer_size;
691 c->event.max_msg_size = max_buffer_size;
692 c->pid = ugp->pid;
693 c->auth.uid = c->euid = ugp->uid;
694 c->auth.gid = c->egid = ugp->gid;
695 c->auth.mode = 0600;
696 c->stats.client_pid = ugp->pid;
697
698 memset(&response, 0, sizeof(response));
699
700 #if defined(QB_LINUX) || defined(QB_CYGWIN)
701 desc_len = snprintf(c->description, CONNECTION_DESCRIPTION - sizeof suffix,
702 "/dev/shm/qb-%d-%d-%d-XXXXXX", s->pid, ugp->pid, c->setup.u.us.sock);
703 if (desc_len < 0) {
704 res = -errno;
705 goto send_response;
706 }
707 if (desc_len >= CONNECTION_DESCRIPTION - sizeof suffix) {
708 res = -ENAMETOOLONG;
709 goto send_response;
710 }
711 if (mkdtemp(c->description) == NULL) {
712 res = -errno;
713 goto send_response;
714 }
715 if (chmod(c->description, 0770)) {
716 res = -errno;
717 goto send_response;
718 }
719 /* chown may fail if not root, but log it */
720 if (chown(c->description, c->auth.uid, c->auth.gid) != 0) {
721 qb_util_perror(LOG_WARNING, "failed to chown directory (%s)",
722 c->description);
723 }
724
725 /* We can't pass just a directory spec to the clients */
726 memcpy(c->description + desc_len, suffix, sizeof suffix);
727 #else
728 desc_len = snprintf(c->description, CONNECTION_DESCRIPTION,
729 "%d-%d-%d", s->pid, ugp->pid, c->setup.u.us.sock);
730 if (desc_len < 0) {
731 res = -errno;
732 goto send_response;
733 }
734 if (desc_len >= CONNECTION_DESCRIPTION) {
735 res = -ENAMETOOLONG;
736 goto send_response;
737 }
738 #endif
739
740
741
742 if (auth_result == 0 && c->service->serv_fns.connection_accept) {
743 res = c->service->serv_fns.connection_accept(c,
744 c->euid, c->egid);
745 }
746 if (res != 0) {
747 goto send_response;
748 }
749
750 qb_util_log(LOG_DEBUG, "IPC credentials authenticated (%s)",
751 c->description);
752
753 if (s->funcs.connect) {
754 res = s->funcs.connect(s, c, &response);
755 if (res != 0) {
756 goto send_response;
757 }
758 }
759 /*
760 * The connection is good, add it to the active connection list
761 */
762 c->state = QB_IPCS_CONNECTION_ACTIVE;
763 qb_list_add(&c->list, &s->connections);
764
765 send_response:
766 response.hdr.id = QB_IPC_MSG_AUTHENTICATE;
767 response.hdr.size = sizeof(response);
768 response.hdr.error = res;
769 if (res == 0) {
770 response.connection = (intptr_t) c;
771 response.connection_type = s->type;
772 response.max_msg_size = c->request.max_msg_size;
773 s->stats.active_connections++;
774 }
775
776 res2 = qb_ipc_us_send(&c->setup, &response, response.hdr.size);
777 if (res == 0 && res2 != response.hdr.size) {
778 res = res2;
779 }
780
781 if (res == 0) {
782 qb_ipcs_connection_ref(c);
783 if (s->serv_fns.connection_created) {
784 s->serv_fns.connection_created(c);
785 }
786 if (c->state == QB_IPCS_CONNECTION_ACTIVE) {
787 c->state = QB_IPCS_CONNECTION_ESTABLISHED;
788 }
789 qb_ipcs_connection_unref(c);
790 } else {
791 if (res == -EACCES) {
792 qb_util_log(LOG_INFO, "IPC connection credentials rejected (%s)",
793 c->description);
794 } else if (res == -EAGAIN) {
795 qb_util_log(LOG_INFO, "IPC connection not ready (%s)",
796 c->description);
797 } else {
798 qb_util_perror(LOG_INFO, "IPC connection setup failed (%s)",
799 c->description);
800 errno = -res;
801 }
802
803 if (c->state == QB_IPCS_CONNECTION_INACTIVE) {
804 /* This removes the initial alloc ref */
805 qb_ipcs_connection_unref(c);
806 qb_ipcc_us_sock_close(sock);
807 } else {
808 qb_ipcs_disconnect(c);
809 }
810 }
811 return res;
812 }
813
814 static int32_t
815 process_auth(int32_t fd, int32_t revents, void *d)
816 {
817 struct ipc_auth_data *data = (struct ipc_auth_data *) d;
818
819 int32_t res = 0;
820 int res1;
821 #ifdef SO_PASSCRED
822 int off = 0;
823 #endif
824
825 if (data->s->server_sock == -1) {
826 qb_util_log(LOG_DEBUG, "Closing fd (%d) for server shutdown", fd);
827 res = -ESHUTDOWN;
828 goto cleanup_and_return;
829 }
830
831 if (revents & POLLNVAL) {
832 qb_util_log(LOG_DEBUG, "NVAL conn fd (%d)", fd);
833 res = -EINVAL;
834 goto cleanup_and_return;
835 }
836 if (revents & POLLHUP) {
837 qb_util_log(LOG_DEBUG, "HUP conn fd (%d)", fd);
838 res = -ESHUTDOWN;
839 goto cleanup_and_return;
840 }
841 if ((revents & POLLIN) == 0) {
842 return 0;
843 }
844
845 res = qb_ipc_us_recv_msghdr(data);
846 if (res == -EAGAIN) {
847 /* yield to mainloop, Let mainloop call us again */
848 return 0;
849 }
850
851 if (res != data->len) {
852 res = -EIO;
853 goto cleanup_and_return;
854 }
855
856 res = qb_ipc_auth_creds(data);
857
858 cleanup_and_return:
859 #ifdef SO_PASSCRED
860 res1 = setsockopt(data->sock, SOL_SOCKET, SO_PASSCRED, &off, sizeof(off));
861 if (res1 != 0) {
862 int err = errno;
863 close(data->sock);
864 errno = err;
865 return res;
866 }
867 #endif
868
869 (void)data->s->poll_fns.dispatch_del(data->sock);
870
871 if (res < 0) {
872 close(data->sock);
873 } else if (data->msg.req.hdr.id == QB_IPC_MSG_AUTHENTICATE) {
874 (void)handle_new_connection(data->s, res, data->sock, &data->msg, data->len, &data->ugp);
875 } else {
876 close(data->sock);
877 }
878 destroy_ipc_auth_data(data);
879
880 return 1;
881 }
882
883 static void
884 qb_ipcs_uc_recv_and_auth(int32_t sock, struct qb_ipcs_service *s)
885 {
886 int res = 0;
887 int res1;
888 struct ipc_auth_data *data = NULL;
889 #ifdef SO_PASSCRED
890 int on = 1;
891 #endif
892
893 data = init_ipc_auth_data(sock, sizeof(struct qb_ipc_connection_request));
894 if (data == NULL) {
895 close(sock);
896 /* -ENOMEM */
897 return;
898 }
899
900 data->s = s;
901 qb_ipcs_ref(data->s);
902
903 #ifdef SO_PASSCRED
904 res1 = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
905 if (res1 != 0) {
906 close(sock);
907 return;
908 }
909 #endif
910
911 res = s->poll_fns.dispatch_add(s->poll_priority,
912 data->sock,
913 POLLIN | POLLPRI | POLLNVAL,
914 data, process_auth);
915 if (res < 0) {
916 qb_util_log(LOG_DEBUG, "Failed to arrange for AUTH for fd (%d)",
917 data->sock);
918 close(sock);
919 destroy_ipc_auth_data(data);
920 }
921 }
922
923 static int32_t
924 qb_ipcs_us_connection_acceptor(int fd, int revent, void *data)
925 {
926 struct sockaddr_un un_addr;
927 int32_t new_fd;
928 struct qb_ipcs_service *s = (struct qb_ipcs_service *)data;
929 int32_t res;
930 socklen_t addrlen = sizeof(struct sockaddr_un);
931
932 if (revent & (POLLNVAL | POLLHUP | POLLERR)) {
933 /*
934 * handle shutdown more cleanly.
935 */
936 return -1;
937 }
938
939 retry_accept:
940 errno = 0;
941 new_fd = accept(fd, (struct sockaddr *)&un_addr, &addrlen);
942 if (new_fd == -1 && errno == EINTR) {
943 goto retry_accept;
944 }
945
946 if (new_fd == -1 && errno == EBADF) {
947 qb_util_perror(LOG_ERR,
948 "Could not accept client connection from fd:%d",
949 fd);
950 return -1;
951 }
952 if (new_fd == -1) {
953 qb_util_perror(LOG_ERR, "Could not accept client connection");
954 /* This is an error, but -1 would indicate disconnect
955 * from the poll loop
956 */
957 return 0;
958 }
959
960 res = qb_sys_fd_nonblock_cloexec_set(new_fd);
961 if (res < 0) {
962 close(new_fd);
963 /* This is an error, but -1 would indicate disconnect
964 * from the poll loop
965 */
966 return 0;
967 }
968
969 qb_ipcs_uc_recv_and_auth(new_fd, s);
970 return 0;
971 }
972
973 void remove_tempdir(const char *name)
974 {
975 #if defined(QB_LINUX) || defined(QB_CYGWIN)
976 char dirname[PATH_MAX];
977 const char *slash = strrchr(name, '/');
978
979 if (slash && slash - name < sizeof dirname) {
980 memcpy(dirname, name, slash - name);
981 dirname[slash - name] = '\0';
982 /* This gets called more than it needs to be really, so we don't check
983 * the return code. It's more of a desperate attempt to clean up after ourself
984 * in either the server or client.
985 */
986 (void)rmdir(dirname);
987 }
988 #endif
989 }
990