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