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
|
(8) Event label: |
Reached label "retry_send". |
83 retry_send:
|
(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. |
|
(9) Event overflow: |
The expression "len - processed" is deemed overflowed because at least one of its arguments has overflowed. |
|
(10) 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 cond_false: |
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 }
|
(4) Event if_end: |
End of if statement. |
94 }
95
96 processed += result;
|
(6) Event cond_true: |
Condition "processed != len", taking true branch. |
97 if (processed != len) {
|
(7) Event goto: |
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 memcpy(&cred, CMSG_DATA(cmsg), sizeof(struct ucred));
375 res = 0;
376 data->ugp.pid = cred.pid;
377 data->ugp.uid = cred.uid;
378 data->ugp.gid = cred.gid;
379 break;
380 }
381 }
382 #else /* no credentials */
383 data->ugp.pid = 0;
384 data->ugp.uid = 0;
385 data->ugp.gid = 0;
386 res = -ENOTSUP;
387 #endif /* no credentials */
388
389 return res;
390 }
391
392 static void
393 destroy_ipc_auth_data(struct ipc_auth_data *data)
394 {
395 if (data->s) {
396 qb_ipcs_unref(data->s);
397 }
398
399 #ifdef SO_PASSCRED
400 free(data->cmsg_cred);
401 #endif
402 free(data);
403 }
404
405 static struct ipc_auth_data *
406 init_ipc_auth_data(int sock, size_t len)
407 {
408 struct ipc_auth_data *data = calloc(1, sizeof(struct ipc_auth_data));
409
410 if (data == NULL) {
411 return NULL;
412 }
413
414 data->msg_recv.msg_iov = &data->iov_recv;
415 data->msg_recv.msg_iovlen = 1;
416 data->msg_recv.msg_name = 0;
417 data->msg_recv.msg_namelen = 0;
418
419 #ifdef SO_PASSCRED
420 data->cmsg_cred = calloc(1, CMSG_SPACE(sizeof(struct ucred)));
421 if (data->cmsg_cred == NULL) {
422 destroy_ipc_auth_data(data);
423 return NULL;
424 }
425 data->msg_recv.msg_control = (void *)data->cmsg_cred;
426 data->msg_recv.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
427 #endif
428 #if defined(QB_SOLARIS) && !defined(_XPG4_2)
429 data->msg_recv.msg_accrights = 0;
430 data->msg_recv.msg_accrightslen = 0;
431 #else
432 data->msg_recv.msg_flags = 0;
433 #endif /* QB_SOLARIS */
434
435 data->len = len;
436 data->iov_recv.iov_base = (void *)&data->msg;
437 data->iov_recv.iov_len = data->len;
438 data->sock = sock;
439
440 return data;
441 }
442
443 int32_t
444 qb_ipcc_us_setup_connect(struct qb_ipcc_connection *c,
445 struct qb_ipc_connection_response *r)
446 {
447 int32_t res;
448 struct qb_ipc_connection_request request;
449 #ifdef QB_LINUX
450 int on = 1;
451 #endif
452
453 res = qb_ipcc_stream_sock_connect(c->name, &c->setup.u.us.sock);
454 if (res != 0) {
455 return res;
456 }
457 #ifdef QB_LINUX
458 res = setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &on,
459 sizeof(on));
460 if (res != 0) {
461 int err = errno;
462 qb_ipcc_us_sock_close(c->setup.u.us.sock);
463 errno = err;
464 return res;
465 }
466 #endif
467
468 memset(&request, 0, sizeof(request));
469 request.hdr.id = QB_IPC_MSG_AUTHENTICATE;
470 request.hdr.size = sizeof(request);
471 request.max_msg_size = c->setup.max_msg_size;
472 res = qb_ipc_us_send(&c->setup, &request, request.hdr.size);
473 if (res < 0) {
474 qb_ipcc_us_sock_close(c->setup.u.us.sock);
475 return res;
476 }
477
478 /* ... To be continued ... (when the FD is active) */
479 return 0;
480 }
481
482 #define AUTH_RECV_MAX_RETRIES 10
483 #define AUTH_RECV_SLEEP_TIME_US 100
484
485 /* Called from ipcc_connect_continue() when async connect socket is active */
486 int qb_ipcc_setup_connect_continue(struct qb_ipcc_connection *c, struct qb_ipc_connection_response *r)
487 {
488 struct ipc_auth_data *data;
489 int32_t res;
490 int res1;
491 int retry_count = 0;
492 #ifdef QB_LINUX
493 int off = 0;
494 #endif
495 data = init_ipc_auth_data(c->setup.u.us.sock, sizeof(struct qb_ipc_connection_response));
496 if (data == NULL) {
497 qb_ipcc_us_sock_close(c->setup.u.us.sock);
498 return -ENOMEM;
499 }
500 retry:
501 res = qb_ipc_us_recv_msghdr(data);
502 if (res == -EAGAIN && ++retry_count < AUTH_RECV_MAX_RETRIES) {
503 struct timespec ts = {0, AUTH_RECV_SLEEP_TIME_US*QB_TIME_NS_IN_USEC};
504 struct timespec ts_left = {0, 0};
505 nanosleep(&ts, &ts_left);
506 goto retry;
507 }
508
509 #ifdef QB_LINUX
510 res1 = setsockopt(c->setup.u.us.sock, SOL_SOCKET, SO_PASSCRED, &off,
511 sizeof(off));
512 if (res1 != 0) {
513 int err = errno;
514 destroy_ipc_auth_data(data);
515 errno = err;
516 return res;
517 }
518 #endif
519
520 if (res != data->len) {
521 destroy_ipc_auth_data(data);
522 return res;
523 }
524
525 memcpy(r, &data->msg.res, sizeof(struct qb_ipc_connection_response));
526
527 qb_ipc_auth_creds(data);
528 c->egid = data->ugp.gid;
529 c->euid = data->ugp.uid;
530 c->server_pid = data->ugp.pid;
531
532 destroy_ipc_auth_data(data);
533
534 return r->hdr.error;
535 }
536
537 /*
538 **************************************************************************
539 * SERVER
540 */
541
542 int32_t
543 qb_ipcs_us_publish(struct qb_ipcs_service * s)
544 {
545 struct sockaddr_un un_addr;
546 int32_t res;
547 #ifdef SO_PASSCRED
548 int on = 1;
549 #endif
550
551 /*
552 * Create socket for IPC clients, name socket, listen for connections
553 */
554 s->server_sock = socket(PF_UNIX, SOCK_STREAM, 0);
555 if (s->server_sock == -1) {
556 res = -errno;
557 qb_util_perror(LOG_ERR, "Cannot create server socket");
558 return res;
559 }
560
561 res = qb_sys_fd_nonblock_cloexec_set(s->server_sock);
562 if (res < 0) {
563 goto error_close;
564 }
565
566 memset(&un_addr, 0, sizeof(struct sockaddr_un));
567 un_addr.sun_family = AF_UNIX;
568 #if defined(QB_BSD) || defined(QB_DARWIN)
569 un_addr.sun_len = SUN_LEN(&un_addr);
570 #endif
571
572 qb_util_log(LOG_INFO, "server name: %s", s->name);
573
574 if (!use_filesystem_sockets()) {
575 snprintf(un_addr.sun_path + 1, UNIX_PATH_MAX - 1, "%s", s->name);
576 }
577 else {
578 struct stat stat_out;
579 res = stat(SOCKETDIR, &stat_out);
580 if (res == -1 || (res == 0 && !S_ISDIR(stat_out.st_mode))) {
581 res = -errno;
582 qb_util_log(LOG_CRIT,
583 "Required directory not present %s",
584 SOCKETDIR);
585 goto error_close;
586 }
587 snprintf(un_addr.sun_path, sizeof(un_addr.sun_path), "%s/%s", SOCKETDIR,
588 s->name);
589 unlink(un_addr.sun_path);
590 }
591
592 res = bind(s->server_sock, (struct sockaddr *)&un_addr,
593 QB_SUN_LEN(&un_addr));
594 if (res) {
595 res = -errno;
596 qb_util_perror(LOG_ERR, "Could not bind AF_UNIX (%s)",
597 un_addr.sun_path);
598 goto error_close;
599 }
600
601 /*
602 * Allow everyone to write to the socket since the IPC layer handles
603 * security automatically
604 */
605 if (use_filesystem_sockets()) {
606 (void)chmod(un_addr.sun_path, S_IRWXU | S_IRWXG | S_IRWXO);
607 }
608 #ifdef SO_PASSCRED
609 (void)setsockopt(s->server_sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
610 #endif
611 if (listen(s->server_sock, SERVER_BACKLOG) == -1) {
612 qb_util_perror(LOG_ERR, "socket listen failed");
613 }
614
615 res = s->poll_fns.dispatch_add(s->poll_priority, s->server_sock,
616 POLLIN | POLLPRI | POLLNVAL,
617 s, qb_ipcs_us_connection_acceptor);
618 return res;
619
620 error_close:
621 close(s->server_sock);
622 return res;
623 }
624
625 int32_t
626 qb_ipcs_us_withdraw(struct qb_ipcs_service * s)
627 {
628 qb_util_log(LOG_INFO, "withdrawing server sockets");
629 (void)s->poll_fns.dispatch_del(s->server_sock);
630 shutdown(s->server_sock, SHUT_RDWR);
631
632 if (use_filesystem_sockets()) {
633 struct sockaddr_un sockname;
634 socklen_t socklen = sizeof(sockname);
635 if ((getsockname(s->server_sock, (struct sockaddr *)&sockname, &socklen) == 0) &&
636 sockname.sun_family == AF_UNIX) {
637 #ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
638 /*
639 * Terminating NUL on FreeBSD is not part of the sun_path.
640 * Add it to use sun_path as a parameter of unlink
641 */
642 sockname.sun_path[sockname.sun_len - offsetof(struct sockaddr_un, sun_path)] = '\0';
643 #endif
644 unlink(sockname.sun_path);
645 }
646 }
647
648 close(s->server_sock);
649 s->server_sock = -1;
650 return 0;
651 }
652
653 static int32_t
654 handle_new_connection(struct qb_ipcs_service *s,
655 int32_t auth_result,
656 int32_t sock,
657 void *msg, size_t len, struct ipc_auth_ugp *ugp)
658 {
659 struct qb_ipcs_connection *c = NULL;
660 struct qb_ipc_connection_request *req = msg;
661 int32_t res = auth_result;
662 int32_t res2 = 0;
663 uint32_t max_buffer_size = QB_MAX(req->max_msg_size, s->max_buffer_size);
664 struct qb_ipc_connection_response response;
665 const char suffix[] = "/qb";
666 int desc_len;
667
668 c = qb_ipcs_connection_alloc(s);
669 if (c == NULL) {
670 qb_ipcc_us_sock_close(sock);
671 return -ENOMEM;
672 }
673
674 c->receive_buf = calloc(1, max_buffer_size);
675 if (c->receive_buf == NULL) {
676 free(c);
677 qb_ipcc_us_sock_close(sock);
678 return -ENOMEM;
679 }
680 c->setup.u.us.sock = sock;
681 c->request.max_msg_size = max_buffer_size;
682 c->response.max_msg_size = max_buffer_size;
683 c->event.max_msg_size = max_buffer_size;
684 c->pid = ugp->pid;
685 c->auth.uid = c->euid = ugp->uid;
686 c->auth.gid = c->egid = ugp->gid;
687 c->auth.mode = 0600;
688 c->stats.client_pid = ugp->pid;
689
690 memset(&response, 0, sizeof(response));
691
692 #if defined(QB_LINUX) || defined(QB_CYGWIN)
693 desc_len = snprintf(c->description, CONNECTION_DESCRIPTION - sizeof suffix,
694 "/dev/shm/qb-%d-%d-%d-XXXXXX", s->pid, ugp->pid, c->setup.u.us.sock);
695 if (desc_len < 0) {
696 res = -errno;
697 goto send_response;
698 }
699 if (desc_len >= CONNECTION_DESCRIPTION - sizeof suffix) {
700 res = -ENAMETOOLONG;
701 goto send_response;
702 }
703 if (mkdtemp(c->description) == NULL) {
704 res = -errno;
705 goto send_response;
706 }
707 if (chmod(c->description, 0770)) {
708 res = -errno;
709 goto send_response;
710 }
711 /* chown can fail because we might not be root */
712 (void)chown(c->description, c->auth.uid, c->auth.gid);
713
714 /* We can't pass just a directory spec to the clients */
715 memcpy(c->description + desc_len, suffix, sizeof suffix);
716 #else
717 desc_len = snprintf(c->description, CONNECTION_DESCRIPTION,
718 "%d-%d-%d", s->pid, ugp->pid, c->setup.u.us.sock);
719 if (desc_len < 0) {
720 res = -errno;
721 goto send_response;
722 }
723 if (desc_len >= CONNECTION_DESCRIPTION) {
724 res = -ENAMETOOLONG;
725 goto send_response;
726 }
727 #endif
728
729
730
731 if (auth_result == 0 && c->service->serv_fns.connection_accept) {
732 res = c->service->serv_fns.connection_accept(c,
733 c->euid, c->egid);
734 }
735 if (res != 0) {
736 goto send_response;
737 }
738
739 qb_util_log(LOG_DEBUG, "IPC credentials authenticated (%s)",
740 c->description);
741
742 if (s->funcs.connect) {
743 res = s->funcs.connect(s, c, &response);
744 if (res != 0) {
745 goto send_response;
746 }
747 }
748 /*
749 * The connection is good, add it to the active connection list
750 */
751 c->state = QB_IPCS_CONNECTION_ACTIVE;
752 qb_list_add(&c->list, &s->connections);
753
754 send_response:
755 response.hdr.id = QB_IPC_MSG_AUTHENTICATE;
756 response.hdr.size = sizeof(response);
757 response.hdr.error = res;
758 if (res == 0) {
759 response.connection = (intptr_t) c;
760 response.connection_type = s->type;
761 response.max_msg_size = c->request.max_msg_size;
762 s->stats.active_connections++;
763 }
764
765 res2 = qb_ipc_us_send(&c->setup, &response, response.hdr.size);
766 if (res == 0 && res2 != response.hdr.size) {
767 res = res2;
768 }
769
770 if (res == 0) {
771 qb_ipcs_connection_ref(c);
772 if (s->serv_fns.connection_created) {
773 s->serv_fns.connection_created(c);
774 }
775 if (c->state == QB_IPCS_CONNECTION_ACTIVE) {
776 c->state = QB_IPCS_CONNECTION_ESTABLISHED;
777 }
778 qb_ipcs_connection_unref(c);
779 } else {
780 if (res == -EACCES) {
781 qb_util_log(LOG_INFO, "IPC connection credentials rejected (%s)",
782 c->description);
783 } else if (res == -EAGAIN) {
784 qb_util_log(LOG_INFO, "IPC connection not ready (%s)",
785 c->description);
786 } else {
787 qb_util_perror(LOG_INFO, "IPC connection setup failed (%s)",
788 c->description);
789 errno = -res;
790 }
791
792 if (c->state == QB_IPCS_CONNECTION_INACTIVE) {
793 /* This removes the initial alloc ref */
794 qb_ipcs_connection_unref(c);
795 qb_ipcc_us_sock_close(sock);
796 } else {
797 qb_ipcs_disconnect(c);
798 }
799 }
800 return res;
801 }
802
803 static int32_t
804 process_auth(int32_t fd, int32_t revents, void *d)
805 {
806 struct ipc_auth_data *data = (struct ipc_auth_data *) d;
807
808 int32_t res = 0;
809 int res1;
810 #ifdef SO_PASSCRED
811 int off = 0;
812 #endif
813
814 if (data->s->server_sock == -1) {
815 qb_util_log(LOG_DEBUG, "Closing fd (%d) for server shutdown", fd);
816 res = -ESHUTDOWN;
817 goto cleanup_and_return;
818 }
819
820 if (revents & POLLNVAL) {
821 qb_util_log(LOG_DEBUG, "NVAL conn fd (%d)", fd);
822 res = -EINVAL;
823 goto cleanup_and_return;
824 }
825 if (revents & POLLHUP) {
826 qb_util_log(LOG_DEBUG, "HUP conn fd (%d)", fd);
827 res = -ESHUTDOWN;
828 goto cleanup_and_return;
829 }
830 if ((revents & POLLIN) == 0) {
831 return 0;
832 }
833
834 res = qb_ipc_us_recv_msghdr(data);
835 if (res == -EAGAIN) {
836 /* yield to mainloop, Let mainloop call us again */
837 return 0;
838 }
839
840 if (res != data->len) {
841 res = -EIO;
842 goto cleanup_and_return;
843 }
844
845 res = qb_ipc_auth_creds(data);
846
847 cleanup_and_return:
848 #ifdef SO_PASSCRED
849 res1 = setsockopt(data->sock, SOL_SOCKET, SO_PASSCRED, &off, sizeof(off));
850 if (res1 != 0) {
851 int err = errno;
852 close(data->sock);
853 errno = err;
854 return res;
855 }
856 #endif
857
858 (void)data->s->poll_fns.dispatch_del(data->sock);
859
860 if (res < 0) {
861 close(data->sock);
862 } else if (data->msg.req.hdr.id == QB_IPC_MSG_AUTHENTICATE) {
863 (void)handle_new_connection(data->s, res, data->sock, &data->msg, data->len, &data->ugp);
864 } else {
865 close(data->sock);
866 }
867 destroy_ipc_auth_data(data);
868
869 return 1;
870 }
871
872 static void
873 qb_ipcs_uc_recv_and_auth(int32_t sock, struct qb_ipcs_service *s)
874 {
875 int res = 0;
876 int res1;
877 struct ipc_auth_data *data = NULL;
878 #ifdef SO_PASSCRED
879 int on = 1;
880 #endif
881
882 data = init_ipc_auth_data(sock, sizeof(struct qb_ipc_connection_request));
883 if (data == NULL) {
884 close(sock);
885 /* -ENOMEM */
886 return;
887 }
888
889 data->s = s;
890 qb_ipcs_ref(data->s);
891
892 #ifdef SO_PASSCRED
893 res1 = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
894 if (res1 != 0) {
895 close(sock);
896 return;
897 }
898 #endif
899
900 res = s->poll_fns.dispatch_add(s->poll_priority,
901 data->sock,
902 POLLIN | POLLPRI | POLLNVAL,
903 data, process_auth);
904 if (res < 0) {
905 qb_util_log(LOG_DEBUG, "Failed to arrange for AUTH for fd (%d)",
906 data->sock);
907 close(sock);
908 destroy_ipc_auth_data(data);
909 }
910 }
911
912 static int32_t
913 qb_ipcs_us_connection_acceptor(int fd, int revent, void *data)
914 {
915 struct sockaddr_un un_addr;
916 int32_t new_fd;
917 struct qb_ipcs_service *s = (struct qb_ipcs_service *)data;
918 int32_t res;
919 socklen_t addrlen = sizeof(struct sockaddr_un);
920
921 if (revent & (POLLNVAL | POLLHUP | POLLERR)) {
922 /*
923 * handle shutdown more cleanly.
924 */
925 return -1;
926 }
927
928 retry_accept:
929 errno = 0;
930 new_fd = accept(fd, (struct sockaddr *)&un_addr, &addrlen);
931 if (new_fd == -1 && errno == EINTR) {
932 goto retry_accept;
933 }
934
935 if (new_fd == -1 && errno == EBADF) {
936 qb_util_perror(LOG_ERR,
937 "Could not accept client connection from fd:%d",
938 fd);
939 return -1;
940 }
941 if (new_fd == -1) {
942 qb_util_perror(LOG_ERR, "Could not accept client connection");
943 /* This is an error, but -1 would indicate disconnect
944 * from the poll loop
945 */
946 return 0;
947 }
948
949 res = qb_sys_fd_nonblock_cloexec_set(new_fd);
950 if (res < 0) {
951 close(new_fd);
952 /* This is an error, but -1 would indicate disconnect
953 * from the poll loop
954 */
955 return 0;
956 }
957
958 qb_ipcs_uc_recv_and_auth(new_fd, s);
959 return 0;
960 }
961
962 void remove_tempdir(const char *name)
963 {
964 #if defined(QB_LINUX) || defined(QB_CYGWIN)
965 char dirname[PATH_MAX];
966 char *slash = strrchr(name, '/');
967
968 if (slash && slash - name < sizeof dirname) {
969 memcpy(dirname, name, slash - name);
970 dirname[slash - name] = '\0';
971 /* This gets called more than it needs to be really, so we don't check
972 * the return code. It's more of a desperate attempt to clean up after ourself
973 * in either the server or client.
974 */
975 (void)rmdir(dirname);
976 }
977 #endif
978 }
979