Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/blocklist/lib/old_bl.c
48068 views
1
/* $NetBSD: bl.c,v 1.9 2025/03/30 01:53:59 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
#ifdef HAVE_SYS_CDEFS_H
36
#include <sys/cdefs.h>
37
#endif
38
__RCSID("$NetBSD: bl.c,v 1.9 2025/03/30 01:53:59 christos Exp $");
39
40
#include <sys/param.h>
41
#include <sys/types.h>
42
#include <sys/socket.h>
43
#include <sys/stat.h>
44
#include <sys/un.h>
45
46
#include <stdio.h>
47
#include <string.h>
48
#include <syslog.h>
49
#include <signal.h>
50
#include <fcntl.h>
51
#include <stdlib.h>
52
#include <unistd.h>
53
#include <stdint.h>
54
#include <stdbool.h>
55
#include <errno.h>
56
#include <stdarg.h>
57
#include <netinet/in.h>
58
#ifdef _REENTRANT
59
#include <pthread.h>
60
#endif
61
62
#if defined(SO_RECVUCRED)
63
#include <ucred.h>
64
#endif
65
66
#include "old_bl.h"
67
68
typedef struct {
69
uint32_t bl_len;
70
uint32_t bl_version;
71
uint32_t bl_type;
72
uint32_t bl_salen;
73
struct sockaddr_storage bl_ss;
74
char bl_data[];
75
} bl_message_t;
76
77
struct blacklist {
78
#ifdef _REENTRANT
79
pthread_mutex_t b_mutex;
80
# define BL_INIT(b) pthread_mutex_init(&b->b_mutex, NULL)
81
# define BL_LOCK(b) pthread_mutex_lock(&b->b_mutex)
82
# define BL_UNLOCK(b) pthread_mutex_unlock(&b->b_mutex)
83
#else
84
# define BL_INIT(b) do {} while(/*CONSTCOND*/0)
85
# define BL_LOCK(b) BL_INIT(b)
86
# define BL_UNLOCK(b) BL_INIT(b)
87
#endif
88
int b_fd;
89
int b_connected;
90
struct sockaddr_un b_sun;
91
struct syslog_data b_syslog_data;
92
void (*b_fun)(int, struct syslog_data *, const char *, va_list);
93
bl_info_t b_info;
94
};
95
96
#define BL_VERSION 1
97
98
bool
99
bl_isconnected(bl_t b)
100
{
101
return b->b_connected == 0;
102
}
103
104
int
105
bl_getfd(bl_t b)
106
{
107
return b->b_fd;
108
}
109
110
static void
111
bl_reset(bl_t b, bool locked)
112
{
113
int serrno = errno;
114
if (!locked)
115
BL_LOCK(b);
116
close(b->b_fd);
117
errno = serrno;
118
b->b_fd = -1;
119
b->b_connected = -1;
120
if (!locked)
121
BL_UNLOCK(b);
122
}
123
124
static void
125
bl_log(bl_t b, int level, const char *fmt, ...)
126
{
127
va_list ap;
128
int serrno = errno;
129
130
if (b->b_fun == NULL)
131
return;
132
133
va_start(ap, fmt);
134
(*b->b_fun)(level, &b->b_syslog_data, fmt, ap);
135
va_end(ap);
136
errno = serrno;
137
}
138
139
static int
140
bl_init(bl_t b, bool srv)
141
{
142
static int one = 1;
143
/* AF_UNIX address of local logger */
144
mode_t om;
145
int rv, serrno;
146
struct sockaddr_un *sun = &b->b_sun;
147
148
#ifndef SOCK_NONBLOCK
149
#define SOCK_NONBLOCK 0
150
#endif
151
#ifndef SOCK_CLOEXEC
152
#define SOCK_CLOEXEC 0
153
#endif
154
#ifndef SOCK_NOSIGPIPE
155
#define SOCK_NOSIGPIPE 0
156
#endif
157
158
BL_LOCK(b);
159
160
if (b->b_fd == -1) {
161
b->b_fd = socket(PF_LOCAL,
162
SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK|SOCK_NOSIGPIPE, 0);
163
if (b->b_fd == -1) {
164
bl_log(b, LOG_ERR, "%s: socket failed (%s)",
165
__func__, strerror(errno));
166
BL_UNLOCK(b);
167
return -1;
168
}
169
#if SOCK_CLOEXEC == 0
170
fcntl(b->b_fd, F_SETFD, FD_CLOEXEC);
171
#endif
172
#if SOCK_NONBLOCK == 0
173
fcntl(b->b_fd, F_SETFL, fcntl(b->b_fd, F_GETFL) | O_NONBLOCK);
174
#endif
175
#if SOCK_NOSIGPIPE == 0
176
#ifdef SO_NOSIGPIPE
177
int o = 1;
178
setsockopt(b->b_fd, SOL_SOCKET, SO_NOSIGPIPE, &o, sizeof(o));
179
#else
180
signal(SIGPIPE, SIG_IGN);
181
#endif
182
#endif
183
}
184
185
if (bl_isconnected(b)) {
186
BL_UNLOCK(b);
187
return 0;
188
}
189
190
/*
191
* We try to connect anyway even when we are a server to verify
192
* that no other server is listening to the socket. If we succeed
193
* to connect and we are a server, someone else owns it.
194
*/
195
rv = connect(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
196
if (rv == 0) {
197
if (srv) {
198
bl_log(b, LOG_ERR,
199
"%s: another daemon is handling `%s'",
200
__func__, sun->sun_path);
201
goto out;
202
}
203
} else {
204
if (!srv) {
205
/*
206
* If the daemon is not running, we just try a
207
* connect, so leave the socket alone until it does
208
* and only log once.
209
*/
210
if (b->b_connected != 1) {
211
bl_log(b, LOG_DEBUG,
212
"%s: connect failed for `%s' (%s)",
213
__func__, sun->sun_path, strerror(errno));
214
b->b_connected = 1;
215
}
216
BL_UNLOCK(b);
217
return -1;
218
}
219
bl_log(b, LOG_DEBUG, "Connected to blacklist server", __func__);
220
}
221
222
if (srv) {
223
(void)unlink(sun->sun_path);
224
om = umask(0);
225
rv = bind(b->b_fd, (const void *)sun, (socklen_t)sizeof(*sun));
226
serrno = errno;
227
(void)umask(om);
228
errno = serrno;
229
if (rv == -1) {
230
bl_log(b, LOG_ERR, "%s: bind failed for `%s' (%s)",
231
__func__, sun->sun_path, strerror(errno));
232
goto out;
233
}
234
}
235
236
b->b_connected = 0;
237
#define GOT_FD 1
238
#if defined(LOCAL_CREDS)
239
#define CRED_LEVEL 0
240
#define CRED_NAME LOCAL_CREDS
241
#define CRED_SC_UID(x) (x)->sc_euid
242
#define CRED_SC_GID(x) (x)->sc_egid
243
#define CRED_MESSAGE SCM_CREDS
244
#define CRED_SIZE SOCKCREDSIZE(NGROUPS_MAX)
245
#define CRED_TYPE struct sockcred
246
#define GOT_CRED 2
247
#elif defined(SO_PASSCRED)
248
#define CRED_LEVEL SOL_SOCKET
249
#define CRED_NAME SO_PASSCRED
250
#define CRED_SC_UID(x) (x)->uid
251
#define CRED_SC_GID(x) (x)->gid
252
#define CRED_MESSAGE SCM_CREDENTIALS
253
#define CRED_SIZE sizeof(struct ucred)
254
#define CRED_TYPE struct ucred
255
#define GOT_CRED 2
256
#elif defined(SO_RECVUCRED)
257
#define CRED_LEVEL SOL_SOCKET
258
#define CRED_NAME SO_RECVUCRED
259
#define CRED_SC_UID(x) ucred_geteuid(x)
260
#define CRED_SC_GID(x) ucred_getegid(x)
261
#define CRED_MESSAGE SCM_UCRED
262
#define CRED_SIZE ucred_size()
263
#define CRED_TYPE ucred_t
264
#define GOT_CRED 2
265
#else
266
#define GOT_CRED 0
267
/*
268
* getpeereid() and LOCAL_PEERCRED don't help here
269
* because we are not a stream socket!
270
*/
271
#define CRED_SIZE 0
272
#define CRED_TYPE void * __unused
273
#endif
274
275
#ifdef CRED_LEVEL
276
if (setsockopt(b->b_fd, CRED_LEVEL, CRED_NAME,
277
&one, (socklen_t)sizeof(one)) == -1) {
278
bl_log(b, LOG_ERR, "%s: setsockopt %s "
279
"failed (%s)", __func__, __STRING(CRED_NAME),
280
strerror(errno));
281
goto out;
282
}
283
#endif
284
285
BL_UNLOCK(b);
286
return 0;
287
out:
288
bl_reset(b, true);
289
BL_UNLOCK(b);
290
return -1;
291
}
292
293
bl_t
294
bl_create(bool srv, const char *path,
295
void (*fun)(int, struct syslog_data *, const char *, va_list))
296
{
297
static struct syslog_data sd = SYSLOG_DATA_INIT;
298
bl_t b = calloc(1, sizeof(*b));
299
if (b == NULL)
300
return NULL;
301
b->b_fun = fun;
302
b->b_syslog_data = sd;
303
b->b_fd = -1;
304
b->b_connected = -1;
305
BL_INIT(b);
306
307
memset(&b->b_sun, 0, sizeof(b->b_sun));
308
b->b_sun.sun_family = AF_LOCAL;
309
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
310
b->b_sun.sun_len = sizeof(b->b_sun);
311
#endif
312
strlcpy(b->b_sun.sun_path,
313
path ? path : _PATH_BLSOCK, sizeof(b->b_sun.sun_path));
314
315
bl_init(b, srv);
316
return b;
317
}
318
319
void
320
bl_destroy(bl_t b)
321
{
322
bl_reset(b, false);
323
free(b);
324
}
325
326
static int
327
bl_getsock(bl_t b, struct sockaddr_storage *ss, const struct sockaddr *sa,
328
socklen_t slen, const char *ctx)
329
{
330
uint8_t family;
331
332
memset(ss, 0, sizeof(*ss));
333
334
switch (slen) {
335
case 0:
336
return 0;
337
case sizeof(struct sockaddr_in):
338
family = AF_INET;
339
break;
340
case sizeof(struct sockaddr_in6):
341
family = AF_INET6;
342
break;
343
default:
344
bl_log(b, LOG_ERR, "%s: invalid socket len %u (%s)",
345
__func__, (unsigned)slen, ctx);
346
errno = EINVAL;
347
return -1;
348
}
349
350
memcpy(ss, sa, slen);
351
352
if (ss->ss_family != family) {
353
bl_log(b, LOG_INFO,
354
"%s: correcting socket family %d to %d (%s)",
355
__func__, ss->ss_family, family, ctx);
356
ss->ss_family = family;
357
}
358
359
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
360
if (ss->ss_len != slen) {
361
bl_log(b, LOG_INFO,
362
"%s: correcting socket len %u to %u (%s)",
363
__func__, ss->ss_len, (unsigned)slen, ctx);
364
ss->ss_len = (uint8_t)slen;
365
}
366
#endif
367
return 0;
368
}
369
370
int
371
bl_send(bl_t b, bl_type_t e, int pfd, const struct sockaddr *sa,
372
socklen_t slen, const char *ctx)
373
{
374
struct msghdr msg;
375
struct iovec iov;
376
union {
377
char ctrl[CMSG_SPACE(sizeof(int))];
378
uint32_t fd;
379
} ua;
380
struct cmsghdr *cmsg;
381
union {
382
bl_message_t bl;
383
char buf[512];
384
} ub;
385
size_t ctxlen, tried;
386
#define NTRIES 5
387
388
ctxlen = strlen(ctx);
389
if (ctxlen > 128)
390
ctxlen = 128;
391
392
iov.iov_base = ub.buf;
393
iov.iov_len = sizeof(bl_message_t) + ctxlen;
394
ub.bl.bl_len = (uint32_t)iov.iov_len;
395
ub.bl.bl_version = BL_VERSION;
396
ub.bl.bl_type = (uint32_t)e;
397
398
if (bl_getsock(b, &ub.bl.bl_ss, sa, slen, ctx) == -1)
399
return -1;
400
401
402
ub.bl.bl_salen = slen;
403
memcpy(ub.bl.bl_data, ctx, ctxlen);
404
405
msg.msg_name = NULL;
406
msg.msg_namelen = 0;
407
msg.msg_iov = &iov;
408
msg.msg_iovlen = 1;
409
msg.msg_flags = 0;
410
411
msg.msg_control = ua.ctrl;
412
msg.msg_controllen = sizeof(ua.ctrl);
413
414
cmsg = CMSG_FIRSTHDR(&msg);
415
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
416
cmsg->cmsg_level = SOL_SOCKET;
417
cmsg->cmsg_type = SCM_RIGHTS;
418
419
memcpy(CMSG_DATA(cmsg), &pfd, sizeof(pfd));
420
421
tried = 0;
422
again:
423
if (bl_init(b, false) == -1)
424
return -1;
425
426
if ((sendmsg(b->b_fd, &msg, 0) == -1) && tried++ < NTRIES) {
427
bl_reset(b, false);
428
goto again;
429
}
430
return tried >= NTRIES ? -1 : 0;
431
}
432
433
bl_info_t *
434
bl_recv(bl_t b)
435
{
436
struct msghdr msg;
437
struct iovec iov;
438
union {
439
char ctrl[CMSG_SPACE(sizeof(int)) + CMSG_SPACE(CRED_SIZE)];
440
uint32_t fd;
441
} ua;
442
struct cmsghdr *cmsg;
443
#if GOT_CRED != 0
444
CRED_TYPE *sc;
445
#endif
446
union {
447
bl_message_t bl;
448
char buf[512];
449
} ub;
450
int got;
451
ssize_t rlen;
452
size_t rem;
453
bl_info_t *bi = &b->b_info;
454
455
got = 0;
456
memset(bi, 0, sizeof(*bi));
457
458
iov.iov_base = ub.buf;
459
iov.iov_len = sizeof(ub);
460
461
msg.msg_name = NULL;
462
msg.msg_namelen = 0;
463
msg.msg_iov = &iov;
464
msg.msg_iovlen = 1;
465
msg.msg_flags = 0;
466
467
msg.msg_control = ua.ctrl;
468
msg.msg_controllen = sizeof(ua.ctrl);
469
470
rlen = recvmsg(b->b_fd, &msg, 0);
471
if (rlen == -1) {
472
bl_log(b, LOG_ERR, "%s: recvmsg failed (%s)", __func__,
473
strerror(errno));
474
return NULL;
475
}
476
477
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
478
if (cmsg->cmsg_level != SOL_SOCKET) {
479
bl_log(b, LOG_ERR,
480
"%s: unexpected cmsg_level %d",
481
__func__, cmsg->cmsg_level);
482
continue;
483
}
484
switch (cmsg->cmsg_type) {
485
case SCM_RIGHTS:
486
if (cmsg->cmsg_len != CMSG_LEN(sizeof(int))) {
487
int *fd = (void *)CMSG_DATA(cmsg);
488
size_t len = cmsg->cmsg_len / sizeof(int);
489
bl_log(b, LOG_ERR,
490
"%s: unexpected cmsg_len %d != %zu",
491
__func__, cmsg->cmsg_len,
492
CMSG_LEN(sizeof(int)));
493
494
for (size_t i = 0; i < len; i++)
495
(void)close(fd[i]);
496
continue;
497
}
498
memcpy(&bi->bi_fd, CMSG_DATA(cmsg), sizeof(bi->bi_fd));
499
got |= GOT_FD;
500
break;
501
#ifdef CRED_MESSAGE
502
case CRED_MESSAGE:
503
sc = (void *)CMSG_DATA(cmsg);
504
bi->bi_uid = CRED_SC_UID(sc);
505
bi->bi_gid = CRED_SC_GID(sc);
506
got |= GOT_CRED;
507
break;
508
#endif
509
default:
510
bl_log(b, LOG_ERR,
511
"%s: unexpected cmsg_type %d",
512
__func__, cmsg->cmsg_type);
513
continue;
514
}
515
516
}
517
518
if (got != (GOT_CRED|GOT_FD)) {
519
bl_log(b, LOG_ERR, "message missing %s %s",
520
#if GOT_CRED != 0
521
(got & GOT_CRED) == 0 ? "cred" :
522
#endif
523
"", (got & GOT_FD) == 0 ? "fd" : "");
524
return NULL;
525
}
526
527
rem = (size_t)rlen;
528
if (rem < sizeof(ub.bl)) {
529
bl_log(b, LOG_ERR, "message too short %zd", rlen);
530
return NULL;
531
}
532
rem -= sizeof(ub.bl);
533
534
if (ub.bl.bl_version != BL_VERSION) {
535
bl_log(b, LOG_ERR, "bad version %d", ub.bl.bl_version);
536
return NULL;
537
}
538
539
bi->bi_type = ub.bl.bl_type;
540
bi->bi_slen = ub.bl.bl_salen;
541
bi->bi_ss = ub.bl.bl_ss;
542
#ifndef CRED_MESSAGE
543
bi->bi_uid = -1;
544
bi->bi_gid = -1;
545
#endif
546
if (rem == 0)
547
bi->bi_msg[0] = '\0';
548
else {
549
rem = MIN(sizeof(bi->bi_msg) - 1, rem);
550
memcpy(bi->bi_msg, ub.bl.bl_data, rem);
551
bi->bi_msg[rem] = '\0';
552
}
553
return bi;
554
}
555
556