1*00bc1baaSchristos /* $NetBSD: bl.c,v 1.3 2024/08/02 17:11:55 christos Exp $ */ 2df83713dSchristos 3df83713dSchristos /*- 4df83713dSchristos * Copyright (c) 2014 The NetBSD Foundation, Inc. 5df83713dSchristos * All rights reserved. 6df83713dSchristos * 7df83713dSchristos * This code is derived from software contributed to The NetBSD Foundation 8df83713dSchristos * by Christos Zoulas. 9df83713dSchristos * 10df83713dSchristos * Redistribution and use in source and binary forms, with or without 11df83713dSchristos * modification, are permitted provided that the following conditions 12df83713dSchristos * are met: 13df83713dSchristos * 1. Redistributions of source code must retain the above copyright 14df83713dSchristos * notice, this list of conditions and the following disclaimer. 15df83713dSchristos * 2. Redistributions in binary form must reproduce the above copyright 16df83713dSchristos * notice, this list of conditions and the following disclaimer in the 17df83713dSchristos * documentation and/or other materials provided with the distribution. 18df83713dSchristos * 19df83713dSchristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20df83713dSchristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21df83713dSchristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22df83713dSchristos * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23df83713dSchristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24df83713dSchristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25df83713dSchristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26df83713dSchristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27df83713dSchristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28df83713dSchristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29df83713dSchristos * POSSIBILITY OF SUCH DAMAGE. 30df83713dSchristos */ 31df83713dSchristos #ifdef HAVE_CONFIG_H 32df83713dSchristos #include "config.h" 33df83713dSchristos #endif 34df83713dSchristos 35df83713dSchristos #include <sys/cdefs.h> 36*00bc1baaSchristos __RCSID("$NetBSD: bl.c,v 1.3 2024/08/02 17:11:55 christos Exp $"); 37df83713dSchristos 38df83713dSchristos #include <sys/param.h> 39df83713dSchristos #include <sys/types.h> 40df83713dSchristos #include <sys/socket.h> 41df83713dSchristos #include <sys/stat.h> 42df83713dSchristos #include <sys/un.h> 43df83713dSchristos 44df83713dSchristos #include <stdio.h> 45df83713dSchristos #include <string.h> 46df83713dSchristos #include <syslog.h> 47df83713dSchristos #include <signal.h> 48df83713dSchristos #include <fcntl.h> 49df83713dSchristos #include <stdlib.h> 50df83713dSchristos #include <unistd.h> 51df83713dSchristos #include <stdint.h> 52df83713dSchristos #include <stdbool.h> 53df83713dSchristos #include <errno.h> 54df83713dSchristos #include <stdarg.h> 55df83713dSchristos #include <netinet/in.h> 56df83713dSchristos #ifdef _REENTRANT 57df83713dSchristos #include <pthread.h> 58df83713dSchristos #endif 59df83713dSchristos 60df83713dSchristos #include "bl.h" 61df83713dSchristos 62*00bc1baaSchristos #ifndef SYSLOG_DATA_INIT 63*00bc1baaSchristos struct syslog_data { 64*00bc1baaSchristos int dummy; 65*00bc1baaSchristos }; 66*00bc1baaSchristos #define SYSLOG_DATA_INIT { 0 } 67*00bc1baaSchristos 68*00bc1baaSchristos static void 69*00bc1baaSchristos vsyslog_r(int priority, struct syslog_data *sd, const char *fmt, va_list ap) 70*00bc1baaSchristos { 71*00bc1baaSchristos vsyslog(priority, fmt, ap); 72*00bc1baaSchristos } 73*00bc1baaSchristos #endif 74*00bc1baaSchristos 75df83713dSchristos typedef struct { 76df83713dSchristos uint32_t bl_len; 77df83713dSchristos uint32_t bl_version; 78df83713dSchristos uint32_t bl_type; 79df83713dSchristos uint32_t bl_salen; 80df83713dSchristos struct sockaddr_storage bl_ss; 81df83713dSchristos char bl_data[]; 82df83713dSchristos } bl_message_t; 83df83713dSchristos 84df83713dSchristos struct blocklist { 85df83713dSchristos #ifdef _REENTRANT 86df83713dSchristos pthread_mutex_t b_mutex; 87df83713dSchristos # define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL) 88df83713dSchristos # define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex) 89df83713dSchristos # define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex) 90df83713dSchristos #else 91df83713dSchristos # define BL_INIT(b) do {} while(/*CONSTCOND*/0) 92df83713dSchristos # define BL_LOCK(b) BL_INIT(b) 93df83713dSchristos # define BL_UNLOCK(b) BL_INIT(b) 94df83713dSchristos #endif 95df83713dSchristos int b_fd; 96df83713dSchristos int b_connected; 97df83713dSchristos struct sockaddr_un b_sun; 98*00bc1baaSchristos struct syslog_data b_syslog_data; 99*00bc1baaSchristos void (*b_fun)(int, struct syslog_data *, const char *, va_list); 100df83713dSchristos bl_info_t b_info; 101df83713dSchristos }; 102df83713dSchristos 103df83713dSchristos #define BL_VERSION 1 104df83713dSchristos 105df83713dSchristos bool 106df83713dSchristos bl_isconnected(bl_t b) 107df83713dSchristos { 108df83713dSchristos return b->b_connected == 0; 109df83713dSchristos } 110df83713dSchristos 111df83713dSchristos int 112df83713dSchristos bl_getfd(bl_t b) 113df83713dSchristos { 114df83713dSchristos return b->b_fd; 115df83713dSchristos } 116df83713dSchristos 117df83713dSchristos static void 118df83713dSchristos bl_reset(bl_t b, bool locked) 119df83713dSchristos { 120df83713dSchristos int serrno = errno; 121df83713dSchristos if (!locked) 122df83713dSchristos BL_LOCK(b); 123df83713dSchristos close(b->b_fd); 124df83713dSchristos errno = serrno; 125df83713dSchristos b->b_fd = -1; 126df83713dSchristos b->b_connected = -1; 127df83713dSchristos if (!locked) 128df83713dSchristos BL_UNLOCK(b); 129df83713dSchristos } 130df83713dSchristos 131df83713dSchristos static void 132*00bc1baaSchristos bl_log(bl_t b, int level, const char *fmt, ...) 133df83713dSchristos { 134df83713dSchristos va_list ap; 135df83713dSchristos int serrno = errno; 136df83713dSchristos 137*00bc1baaSchristos if (b->b_fun == NULL) 138*00bc1baaSchristos return; 139*00bc1baaSchristos 140df83713dSchristos va_start(ap, fmt); 141*00bc1baaSchristos (*b->b_fun)(level, &b->b_syslog_data, fmt, ap); 142df83713dSchristos va_end(ap); 143df83713dSchristos errno = serrno; 144df83713dSchristos } 145df83713dSchristos 146df83713dSchristos static int 147df83713dSchristos bl_init(bl_t b, bool srv) 148df83713dSchristos { 149df83713dSchristos static int one = 1; 150df83713dSchristos /* AF_UNIX address of local logger */ 151df83713dSchristos mode_t om; 152df83713dSchristos int rv, serrno; 153df83713dSchristos struct sockaddr_un *sun = &b->b_sun; 154df83713dSchristos 155df83713dSchristos #ifndef SOCK_NONBLOCK 156df83713dSchristos #define SOCK_NONBLOCK 0 157df83713dSchristos #endif 158df83713dSchristos #ifndef SOCK_CLOEXEC 159df83713dSchristos #define SOCK_CLOEXEC 0 160df83713dSchristos #endif 161df83713dSchristos #ifndef SOCK_NOSIGPIPE 162df83713dSchristos #define SOCK_NOSIGPIPE 0 163df83713dSchristos #endif 164df83713dSchristos 165df83713dSchristos BL_LOCK(b); 166df83713dSchristos 167df83713dSchristos if (b->b_fd == -1) { 168df83713dSchristos b->b_fd = socket(PF_LOCAL, 169df83713dSchristos SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0); 170df83713dSchristos if (b->b_fd == -1) { 171*00bc1baaSchristos bl_log(b, LOG_ERR, "%s: socket failed (%s)", 172df83713dSchristos __func__, strerror(errno)); 173df83713dSchristos BL_UNLOCK(b); 174df83713dSchristos return -1; 175df83713dSchristos } 176df83713dSchristos #if SOCK_CLOEXEC == 0 177df83713dSchristos fcntl(b->b_fd, F_SETFD, FD_CLOEXEC); 178df83713dSchristos #endif 179df83713dSchristos #if SOCK_NONBLOCK == 0 180df83713dSchristos fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK); 181df83713dSchristos #endif 182df83713dSchristos #if SOCK_NOSIGPIPE == 0 183df83713dSchristos #ifdef SO_NOSIGPIPE 184df83713dSchristos int o = 1; 185df83713dSchristos setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o)); 186df83713dSchristos #else 187df83713dSchristos signal(SIGPIPE, SIG_IGN); 188df83713dSchristos #endif 189df83713dSchristos #endif 190df83713dSchristos } 191df83713dSchristos 192df83713dSchristos if (bl_isconnected(b)) { 193df83713dSchristos BL_UNLOCK(b); 194df83713dSchristos return 0; 195df83713dSchristos } 196df83713dSchristos 197df83713dSchristos /* 198df83713dSchristos * We try to connect anyway even when we are a server to verify 199df83713dSchristos * that no other server is listening to the socket. If we succeed 200df83713dSchristos * to connect and we are a server, someone else owns it. 201df83713dSchristos */ 202df83713dSchristos rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); 203df83713dSchristos if (rv == 0) { 204df83713dSchristos if (srv) { 205*00bc1baaSchristos bl_log(b, LOG_ERR, 206df83713dSchristos "%s: another daemon is handling `%s'", 207df83713dSchristos __func__, sun->sun_path); 208df83713dSchristos goto out; 209df83713dSchristos } 210df83713dSchristos } else { 211df83713dSchristos if (!srv) { 212df83713dSchristos /* 213df83713dSchristos * If the daemon is not running, we just try a 214df83713dSchristos * connect, so leave the socket alone until it does 215df83713dSchristos * and only log once. 216df83713dSchristos */ 217df83713dSchristos if (b->b_connected != 1) { 218*00bc1baaSchristos bl_log(b, LOG_DEBUG, 219df83713dSchristos "%s: connect failed for `%s' (%s)", 220df83713dSchristos __func__, sun->sun_path, strerror(errno)); 221df83713dSchristos b->b_connected = 1; 222df83713dSchristos } 223df83713dSchristos BL_UNLOCK(b); 224df83713dSchristos return -1; 225df83713dSchristos } 226*00bc1baaSchristos bl_log(b, LOG_DEBUG, "Connected to blocklist server", __func__); 227df83713dSchristos } 228df83713dSchristos 229df83713dSchristos if (srv) { 230df83713dSchristos (void)unlink(sun->sun_path); 231df83713dSchristos om = umask(0); 232df83713dSchristos rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun)); 233df83713dSchristos serrno = errno; 234df83713dSchristos (void)umask(om); 235df83713dSchristos errno = serrno; 236df83713dSchristos if (rv == -1) { 237*00bc1baaSchristos bl_log(b, LOG_ERR, "%s: bind failed for `%s' (%s)", 238df83713dSchristos __func__, sun->sun_path, strerror(errno)); 239df83713dSchristos goto out; 240df83713dSchristos } 241df83713dSchristos } 242df83713dSchristos 243df83713dSchristos b->b_connected = 0; 244df83713dSchristos #define GOT_FD 1 245df83713dSchristos #if defined(LOCAL_CREDS) 246df83713dSchristos #define CRED_LEVEL 0 247df83713dSchristos #define CRED_NAME LOCAL_CREDS 248df83713dSchristos #define CRED_SC_UID sc_euid 249df83713dSchristos #define CRED_SC_GID sc_egid 250df83713dSchristos #define CRED_MESSAGE SCM_CREDS 251df83713dSchristos #define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX) 252df83713dSchristos #define CRED_TYPE struct sockcred 253df83713dSchristos #define GOT_CRED 2 254df83713dSchristos #elif defined(SO_PASSCRED) 255df83713dSchristos #define CRED_LEVEL SOL_SOCKET 256df83713dSchristos #define CRED_NAME SO_PASSCRED 257df83713dSchristos #define CRED_SC_UID uid 258df83713dSchristos #define CRED_SC_GID gid 259df83713dSchristos #define CRED_MESSAGE SCM_CREDENTIALS 260df83713dSchristos #define CRED_SIZE sizeof(struct ucred) 261df83713dSchristos #define CRED_TYPE struct ucred 262df83713dSchristos #define GOT_CRED 2 263df83713dSchristos #else 264df83713dSchristos #define GOT_CRED 0 265df83713dSchristos /* 266df83713dSchristos * getpeereid() and LOCAL_PEERCRED don't help here 267df83713dSchristos * because we are not a stream socket! 268df83713dSchristos */ 269df83713dSchristos #define CRED_SIZE 0 270df83713dSchristos #define CRED_TYPE void * __unused 271df83713dSchristos #endif 272df83713dSchristos 273df83713dSchristos #ifdef CRED_LEVEL 274df83713dSchristos if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME, 275df83713dSchristos &one, (socklen_t)sizeof(one)) == -1) { 276*00bc1baaSchristos bl_log(b, LOG_ERR, "%s: setsockopt %s " 277df83713dSchristos "failed (%s)", __func__, __STRING(CRED_NAME), 278df83713dSchristos strerror(errno)); 279df83713dSchristos goto out; 280df83713dSchristos } 281df83713dSchristos #endif 282df83713dSchristos 283df83713dSchristos BL_UNLOCK(b); 284df83713dSchristos return 0; 285df83713dSchristos out: 286df83713dSchristos bl_reset(b, true); 287df83713dSchristos BL_UNLOCK(b); 288df83713dSchristos return -1; 289df83713dSchristos } 290df83713dSchristos 291df83713dSchristos bl_t 292*00bc1baaSchristos bl_create(bool srv, const char *path, 293*00bc1baaSchristos void (*fun)(int, struct syslog_data *, const char *, va_list)) 294df83713dSchristos { 295*00bc1baaSchristos static struct syslog_data sd = SYSLOG_DATA_INIT; 296df83713dSchristos bl_t b = calloc(1, sizeof(*b)); 297df83713dSchristos if (b == NULL) 298*00bc1baaSchristos return NULL; 299*00bc1baaSchristos b->b_fun = fun; 300*00bc1baaSchristos b->b_syslog_data = sd; 301df83713dSchristos b->b_fd = -1; 302df83713dSchristos b->b_connected = -1; 303df83713dSchristos BL_INIT(b); 304df83713dSchristos 305df83713dSchristos memset(&b->b_sun, 0, sizeof(b->b_sun)); 306df83713dSchristos b->b_sun.sun_family = AF_LOCAL; 307df83713dSchristos #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 308df83713dSchristos b->b_sun.sun_len = sizeof(b->b_sun); 309df83713dSchristos #endif 310df83713dSchristos strlcpy(b->b_sun.sun_path, 311df83713dSchristos path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path)); 312df83713dSchristos 313df83713dSchristos bl_init(b, srv); 314df83713dSchristos return b; 315df83713dSchristos } 316df83713dSchristos 317df83713dSchristos void 318df83713dSchristos bl_destroy(bl_t b) 319df83713dSchristos { 320df83713dSchristos bl_reset(b, false); 321df83713dSchristos free(b); 322df83713dSchristos } 323df83713dSchristos 324df83713dSchristos static int 325df83713dSchristos bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa, 326df83713dSchristos socklen_t slen, const char *ctx) 327df83713dSchristos { 328df83713dSchristos uint8_t family; 329df83713dSchristos 330df83713dSchristos memset(ss, 0, sizeof(*ss)); 331df83713dSchristos 332df83713dSchristos switch (slen) { 333df83713dSchristos case 0: 334df83713dSchristos return 0; 335df83713dSchristos case sizeof(struct sockaddr_in): 336df83713dSchristos family = AF_INET; 337df83713dSchristos break; 338df83713dSchristos case sizeof(struct sockaddr_in6): 339df83713dSchristos family = AF_INET6; 340df83713dSchristos break; 341df83713dSchristos default: 342*00bc1baaSchristos bl_log(b, LOG_ERR, "%s: invalid socket len %u (%s)", 343df83713dSchristos __func__, (unsigned)slen, ctx); 344df83713dSchristos errno = EINVAL; 345df83713dSchristos return -1; 346df83713dSchristos } 347df83713dSchristos 348df83713dSchristos memcpy(ss, sa, slen); 349df83713dSchristos 350df83713dSchristos if (ss->ss_family != family) { 351*00bc1baaSchristos bl_log(b, LOG_INFO, 352df83713dSchristos "%s: correcting socket family %d to %d (%s)", 353df83713dSchristos __func__, ss->ss_family, family, ctx); 354df83713dSchristos ss->ss_family = family; 355df83713dSchristos } 356df83713dSchristos 357df83713dSchristos #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN 358df83713dSchristos if (ss->ss_len != slen) { 359*00bc1baaSchristos bl_log(b, LOG_INFO, 360df83713dSchristos "%s: correcting socket len %u to %u (%s)", 361df83713dSchristos __func__, ss->ss_len, (unsigned)slen, ctx); 362df83713dSchristos ss->ss_len = (uint8_t)slen; 363df83713dSchristos } 364df83713dSchristos #endif 365df83713dSchristos return 0; 366df83713dSchristos } 367df83713dSchristos 368df83713dSchristos int 369df83713dSchristos bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa, 370df83713dSchristos socklen_t slen, const char *ctx) 371df83713dSchristos { 372df83713dSchristos struct msghdr msg; 373df83713dSchristos struct iovec iov; 374df83713dSchristos union { 375df83713dSchristos char ctrl[CMSG_SPACE(sizeof(int))]; 376df83713dSchristos uint32_t fd; 377df83713dSchristos } ua; 378df83713dSchristos struct cmsghdr *cmsg; 379df83713dSchristos union { 380df83713dSchristos bl_message_t bl; 381df83713dSchristos char buf[512]; 382df83713dSchristos } ub; 383df83713dSchristos size_t ctxlen, tried; 384df83713dSchristos #define NTRIES 5 385df83713dSchristos 386df83713dSchristos ctxlen = strlen(ctx); 387df83713dSchristos if (ctxlen > 128) 388df83713dSchristos ctxlen = 128; 389df83713dSchristos 390df83713dSchristos iov.iov_base = ub.buf; 391df83713dSchristos iov.iov_len = sizeof(bl_message_t) + ctxlen; 392df83713dSchristos ub.bl.bl_len = (uint32_t)iov.iov_len; 393df83713dSchristos ub.bl.bl_version = BL_VERSION; 394df83713dSchristos ub.bl.bl_type = (uint32_t)e; 395df83713dSchristos 396df83713dSchristos if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1) 397df83713dSchristos return -1; 398df83713dSchristos 399df83713dSchristos 400df83713dSchristos ub.bl.bl_salen = slen; 401df83713dSchristos memcpy(ub.bl.bl_data, ctx, ctxlen); 402df83713dSchristos 403df83713dSchristos msg.msg_name = NULL; 404df83713dSchristos msg.msg_namelen = 0; 405df83713dSchristos msg.msg_iov = &iov; 406df83713dSchristos msg.msg_iovlen = 1; 407df83713dSchristos msg.msg_flags = 0; 408df83713dSchristos 409df83713dSchristos msg.msg_control = ua.ctrl; 410df83713dSchristos msg.msg_controllen = sizeof(ua.ctrl); 411df83713dSchristos 412df83713dSchristos cmsg = CMSG_FIRSTHDR(&msg); 413df83713dSchristos cmsg->cmsg_len = CMSG_LEN(sizeof(int)); 414df83713dSchristos cmsg->cmsg_level = SOL_SOCKET; 415df83713dSchristos cmsg->cmsg_type = SCM_RIGHTS; 416df83713dSchristos 417df83713dSchristos memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd)); 418df83713dSchristos 419df83713dSchristos tried = 0; 420df83713dSchristos again: 421df83713dSchristos if (bl_init(b, false) == -1) 422df83713dSchristos return -1; 423df83713dSchristos 424df83713dSchristos if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) { 425df83713dSchristos bl_reset(b, false); 426df83713dSchristos goto again; 427df83713dSchristos } 428df83713dSchristos return tried >= NTRIES ? -1 : 0; 429df83713dSchristos } 430df83713dSchristos 431df83713dSchristos bl_info_t * 432df83713dSchristos bl_recv(bl_t b) 433df83713dSchristos { 434df83713dSchristos struct msghdr msg; 435df83713dSchristos struct iovec iov; 436df83713dSchristos union { 437df83713dSchristos char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)]; 438df83713dSchristos uint32_t fd; 439df83713dSchristos CRED_TYPE sc; 440df83713dSchristos } ua; 441df83713dSchristos struct cmsghdr *cmsg; 442df83713dSchristos CRED_TYPE *sc; 443df83713dSchristos union { 444df83713dSchristos bl_message_t bl; 445df83713dSchristos char buf[512]; 446df83713dSchristos } ub; 447df83713dSchristos int got; 448df83713dSchristos ssize_t rlen; 449123a18feSchristos size_t rem; 450df83713dSchristos bl_info_t *bi = &b->b_info; 451df83713dSchristos 452df83713dSchristos got = 0; 453df83713dSchristos memset(bi, 0, sizeof(*bi)); 454df83713dSchristos 455df83713dSchristos iov.iov_base = ub.buf; 456df83713dSchristos iov.iov_len = sizeof(ub); 457df83713dSchristos 458df83713dSchristos msg.msg_name = NULL; 459df83713dSchristos msg.msg_namelen = 0; 460df83713dSchristos msg.msg_iov = &iov; 461df83713dSchristos msg.msg_iovlen = 1; 462df83713dSchristos msg.msg_flags = 0; 463df83713dSchristos 464df83713dSchristos msg.msg_control = ua.ctrl; 465df83713dSchristos msg.msg_controllen = sizeof(ua.ctrl) + 100; 466df83713dSchristos 467df83713dSchristos rlen = recvmsg(b->b_fd, &msg, 0); 468df83713dSchristos if (rlen == -1) { 469*00bc1baaSchristos bl_log(b, LOG_ERR, "%s: recvmsg failed (%s)", __func__, 470df83713dSchristos strerror(errno)); 471df83713dSchristos return NULL; 472df83713dSchristos } 473df83713dSchristos 474df83713dSchristos for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { 475df83713dSchristos if (cmsg->cmsg_level != SOL_SOCKET) { 476*00bc1baaSchristos bl_log(b, LOG_ERR, 477df83713dSchristos "%s: unexpected cmsg_level %d", 478df83713dSchristos __func__, cmsg->cmsg_level); 479df83713dSchristos continue; 480df83713dSchristos } 481df83713dSchristos switch (cmsg->cmsg_type) { 482df83713dSchristos case SCM_RIGHTS: 483df83713dSchristos if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) { 484*00bc1baaSchristos bl_log(b, LOG_ERR, 485df83713dSchristos "%s: unexpected cmsg_len %d != %zu", 486df83713dSchristos __func__, cmsg->cmsg_len, 487df83713dSchristos CMSG_LEN(2 * sizeof(int))); 488df83713dSchristos continue; 489df83713dSchristos } 490df83713dSchristos memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd)); 491df83713dSchristos got |= GOT_FD; 492df83713dSchristos break; 493df83713dSchristos #ifdef CRED_MESSAGE 494df83713dSchristos case CRED_MESSAGE: 495df83713dSchristos sc = (void *)CMSG_DATA(cmsg); 496df83713dSchristos bi->bi_uid = sc->CRED_SC_UID; 497df83713dSchristos bi->bi_gid = sc->CRED_SC_GID; 498df83713dSchristos got |= GOT_CRED; 499df83713dSchristos break; 500df83713dSchristos #endif 501df83713dSchristos default: 502*00bc1baaSchristos bl_log(b, LOG_ERR, 503df83713dSchristos "%s: unexpected cmsg_type %d", 504df83713dSchristos __func__, cmsg->cmsg_type); 505df83713dSchristos continue; 506df83713dSchristos } 507df83713dSchristos 508df83713dSchristos } 509df83713dSchristos 510df83713dSchristos if (got != (GOT_CRED|GOT_FD)) { 511*00bc1baaSchristos bl_log(b, LOG_ERR, "message missing %s %s", 512df83713dSchristos #if GOT_CRED != 0 513df83713dSchristos (got & GOT_CRED) == 0 ? "cred" : 514df83713dSchristos #endif 515df83713dSchristos "", (got & GOT_FD) == 0 ? "fd" : ""); 516df83713dSchristos return NULL; 517df83713dSchristos } 518df83713dSchristos 519123a18feSchristos rem = (size_t)rlen; 520123a18feSchristos if (rem < sizeof(ub.bl)) { 521*00bc1baaSchristos bl_log(b, LOG_ERR, "message too short %zd", rlen); 522df83713dSchristos return NULL; 523df83713dSchristos } 524123a18feSchristos rem -= sizeof(ub.bl); 525df83713dSchristos 526df83713dSchristos if (ub.bl.bl_version != BL_VERSION) { 527*00bc1baaSchristos bl_log(b, LOG_ERR, "bad version %d", ub.bl.bl_version); 528df83713dSchristos return NULL; 529df83713dSchristos } 530df83713dSchristos 531df83713dSchristos bi->bi_type = ub.bl.bl_type; 532df83713dSchristos bi->bi_slen = ub.bl.bl_salen; 533df83713dSchristos bi->bi_ss = ub.bl.bl_ss; 534df83713dSchristos #ifndef CRED_MESSAGE 535df83713dSchristos bi->bi_uid = -1; 536df83713dSchristos bi->bi_gid = -1; 537df83713dSchristos #endif 538123a18feSchristos rem = MIN(sizeof(bi->bi_msg), rem); 539123a18feSchristos if (rem == 0) 540123a18feSchristos bi->bi_msg[0] = '\0'; 541123a18feSchristos else 542123a18feSchristos strlcpy(bi->bi_msg, ub.bl.bl_data, rem); 543df83713dSchristos return bi; 544df83713dSchristos } 545