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