git ssb

0+

cel / sslh



Tree: 6cc33820d166bc790c79f8e42aec8f53bcefd2ba

Files: 6cc33820d166bc790c79f8e42aec8f53bcefd2ba / common.c

21291 bytesRaw
1/* Code and variables that is common to both fork and select-based
2 * servers.
3 *
4 * No code here should assume whether sockets are blocking or not.
5 **/
6
7#define _GNU_SOURCE
8#include <stdarg.h>
9#include <grp.h>
10
11#include <sys/types.h>
12#include <ifaddrs.h>
13#include <netinet/in.h>
14
15#include "common.h"
16#include "probe.h"
17
18/* Added to make the code compilable under CYGWIN
19 * */
20#ifndef SA_NOCLDWAIT
21#define SA_NOCLDWAIT 0
22#endif
23
24/* Make use of systemd socket activation
25 * */
26#ifdef SYSTEMD
27#include <systemd/sd-daemon.h>
28#endif
29
30/*
31 * Settings that depend on the command line. They're set in main(), but also
32 * used in other places in common.c, and it'd be heavy-handed to pass it all as
33 * parameters
34 */
35int verbose = 0;
36int probing_timeout = 2;
37int inetd = 0;
38int foreground = 0;
39int background = 0;
40int transparent = 0;
41int numeric = 0;
42const char *user_name, *pid_file;
43
44struct addrinfo *addr_listen = NULL; /* what addresses do we listen to? */
45
46#ifdef LIBWRAP
47#include <tcpd.h>
48int allow_severity =0, deny_severity = 0;
49#endif
50
51typedef enum {
52 CR_DIE,
53 CR_WARN
54} CR_ACTION;
55
56/* check result and die, printing the offending address and error */
57void check_res_dump(CR_ACTION act, int res, struct addrinfo *addr, char* syscall)
58{
59 char buf[NI_MAXHOST];
60
61 if (res == -1) {
62 fprintf(stderr, "%s:%s: %s\n",
63 sprintaddr(buf, sizeof(buf), addr),
64 syscall,
65 strerror(errno));
66
67 if (act == CR_DIE)
68 exit(1);
69 }
70}
71
72int get_fd_sockets(int *sockfd[])
73{
74 int sd = 0;
75
76#ifdef SYSTEMD
77 sd = sd_listen_fds(0);
78 if (sd < 0) {
79 fprintf(stderr, "sd_listen_fds(): %s\n", strerror(-sd));
80 exit(1);
81 }
82 if (sd > 0) {
83 *sockfd = malloc(sd * sizeof(*sockfd[0]));
84 for (int i = 0; i < sd; i++) {
85 (*sockfd)[i] = SD_LISTEN_FDS_START + i;
86 }
87 }
88#endif
89
90 return sd;
91}
92
93/* Starts listening sockets on specified addresses.
94 * IN: addr[], num_addr
95 * OUT: *sockfd[] pointer to newly-allocated array of file descriptors
96 * Returns number of addresses bound
97 * Bound file descriptors are returned in newly-allocated *sockfd pointer
98 */
99int start_listen_sockets(int *sockfd[], struct addrinfo *addr_list)
100{
101 struct sockaddr_storage *saddr;
102 struct addrinfo *addr;
103 int i, res, one;
104 int num_addr = 0;
105 int sd_socks = 0;
106
107 sd_socks = get_fd_sockets(sockfd);
108
109 if (sd_socks > 0) {
110 return sd_socks;
111 }
112
113 for (addr = addr_list; addr; addr = addr->ai_next)
114 num_addr++;
115
116 if (verbose)
117 fprintf(stderr, "listening to %d addresses\n", num_addr);
118
119 *sockfd = malloc(num_addr * sizeof(*sockfd[0]));
120
121 for (i = 0, addr = addr_list; i < num_addr && addr; i++, addr = addr->ai_next) {
122 if (!addr) {
123 fprintf(stderr, "FATAL: Inconsistent listen number. This should not happen.\n");
124 exit(1);
125 }
126 saddr = (struct sockaddr_storage*)addr->ai_addr;
127
128 (*sockfd)[i] = socket(saddr->ss_family, SOCK_STREAM, 0);
129 check_res_dump(CR_DIE, (*sockfd)[i], addr, "socket");
130
131 one = 1;
132 res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one));
133 check_res_dump(CR_DIE, res, addr, "setsockopt(SO_REUSEADDR)");
134
135 if (addr->ai_flags & SO_KEEPALIVE) {
136 res = setsockopt((*sockfd)[i], SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
137 check_res_dump(CR_DIE, res, addr, "setsockopt(SO_KEEPALIVE)");
138 printf("set up keepalive\n");
139 }
140
141 if (IP_FREEBIND) {
142 res = setsockopt((*sockfd)[i], IPPROTO_IP, IP_FREEBIND, (char*)&one, sizeof(one));
143 check_res_dump(CR_WARN, res, addr, "setsockopt(IP_FREEBIND)");
144 }
145
146 res = bind((*sockfd)[i], addr->ai_addr, addr->ai_addrlen);
147 check_res_dump(CR_DIE, res, addr, "bind");
148
149 res = listen ((*sockfd)[i], 50);
150 check_res_dump(CR_DIE, res, addr, "listen");
151
152 }
153
154 return num_addr;
155}
156
157/* Transparent proxying: bind the peer address of fd to the peer address of
158 * fd_from */
159#define IP_TRANSPARENT 19
160int bind_peer(int fd, int fd_from)
161{
162 struct addrinfo from;
163 struct sockaddr_storage ss;
164 int res, trans = 1;
165
166 memset(&from, 0, sizeof(from));
167 from.ai_addr = (struct sockaddr*)&ss;
168 from.ai_addrlen = sizeof(ss);
169
170 /* getpeername can fail with ENOTCONN if connection was dropped before we
171 * got here */
172 res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
173 CHECK_RES_RETURN(res, "getpeername");
174
175 // if the destination is the same machine, there's no need to do bind
176 struct ifaddrs *ifaddrs_p = NULL, *ifa;
177
178 getifaddrs(&ifaddrs_p);
179
180 for (ifa = ifaddrs_p; ifa != NULL; ifa = ifa->ifa_next)
181 {
182 if (!ifa->ifa_addr)
183 continue;
184 int match = 0;
185 if (from.ai_addr->sa_family == ifa->ifa_addr->sa_family)
186 {
187 int family = ifa->ifa_addr->sa_family;
188 if (family == AF_INET)
189 {
190 struct sockaddr_in *from_addr = (struct sockaddr_in*)from.ai_addr;
191 struct sockaddr_in *ifa_addr = (struct sockaddr_in*)ifa->ifa_addr;
192 if (from_addr->sin_addr.s_addr == ifa_addr->sin_addr.s_addr)
193 match = 1;
194 }
195 else if (family == AF_INET6)
196 {
197 struct sockaddr_in6 *from_addr = (struct sockaddr_in6*)from.ai_addr;
198 struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6*)ifa->ifa_addr;
199 if (!memcmp(from_addr->sin6_addr.s6_addr, ifa_addr->sin6_addr.s6_addr, 16))
200 match = 1;
201 }
202 }
203 if (match) // the destination is the same as the source, should not create a transparent bind
204 return 0;
205 }
206
207#ifndef IP_BINDANY /* use IP_TRANSPARENT */
208 res = setsockopt(fd, IPPROTO_IP, IP_TRANSPARENT, &trans, sizeof(trans));
209 CHECK_RES_DIE(res, "setsockopt");
210#else
211 if (from.ai_addr->sa_family==AF_INET) { /* IPv4 */
212 res = setsockopt(fd, IPPROTO_IP, IP_BINDANY, &trans, sizeof(trans));
213 CHECK_RES_RETURN(res, "setsockopt IP_BINDANY");
214#ifdef IPV6_BINDANY
215 } else { /* IPv6 */
216 res = setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, &trans, sizeof(trans));
217 CHECK_RES_RETURN(res, "setsockopt IPV6_BINDANY");
218#endif /* IPV6_BINDANY */
219 }
220#endif /* IP_TRANSPARENT / IP_BINDANY */
221 res = bind(fd, from.ai_addr, from.ai_addrlen);
222 CHECK_RES_RETURN(res, "bind");
223
224 return 0;
225}
226
227/* Connect to first address that works and returns a file descriptor, or -1 if
228 * none work.
229 * If transparent proxying is on, use fd_from peer address on external address
230 * of new file descriptor. */
231int connect_addr(struct connection *cnx, int fd_from)
232{
233 struct addrinfo *a, from;
234 struct sockaddr_storage ss;
235 char buf[NI_MAXHOST];
236 int fd, res, one;
237
238 memset(&from, 0, sizeof(from));
239 from.ai_addr = (struct sockaddr*)&ss;
240 from.ai_addrlen = sizeof(ss);
241
242 res = getpeername(fd_from, from.ai_addr, &from.ai_addrlen);
243 CHECK_RES_RETURN(res, "getpeername");
244
245 for (a = cnx->proto->saddr; a; a = a->ai_next) {
246 /* When transparent, make sure both connections use the same address family */
247 if (transparent && a->ai_family != from.ai_addr->sa_family)
248 continue;
249 if (verbose)
250 fprintf(stderr, "connecting to %s family %d len %d\n",
251 sprintaddr(buf, sizeof(buf), a),
252 a->ai_addr->sa_family, a->ai_addrlen);
253
254 /* XXX Needs to match ai_family from fd_from when being transparent! */
255 fd = socket(a->ai_family, SOCK_STREAM, 0);
256 if (fd == -1) {
257 log_message(LOG_ERR, "forward to %s failed:socket: %s\n",
258 cnx->proto->description, strerror(errno));
259 } else {
260 if (transparent) {
261 res = bind_peer(fd, fd_from);
262 CHECK_RES_RETURN(res, "bind_peer");
263 }
264 res = connect(fd, a->ai_addr, a->ai_addrlen);
265 if (res == -1) {
266 log_message(LOG_ERR, "forward to %s failed:connect: %s\n",
267 cnx->proto->description, strerror(errno));
268 close(fd);
269 } else {
270 if (cnx->proto->keepalive) {
271 one = 1;
272 res = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&one, sizeof(one));
273 CHECK_RES_RETURN(res, "setsockopt(SO_KEEPALIVE)");
274 printf("set up keepalive\n");
275 }
276 return fd;
277 }
278 }
279 }
280 return -1;
281}
282
283/* Store some data to write to the queue later */
284int defer_write(struct queue *q, void* data, int data_size)
285{
286 char *p;
287 if (verbose)
288 fprintf(stderr, "**** writing deferred on fd %d\n", q->fd);
289
290 p = realloc(q->begin_deferred_data, q->deferred_data_size + data_size);
291 if (!p) {
292 perror("realloc");
293 exit(1);
294 }
295
296 q->deferred_data = q->begin_deferred_data = p;
297 p += q->deferred_data_size;
298 q->deferred_data_size += data_size;
299 memcpy(p, data, data_size);
300
301 return 0;
302}
303
304/* tries to flush some of the data for specified queue
305 * Upon success, the number of bytes written is returned.
306 * Upon failure, -1 returned (e.g. connexion closed)
307 * */
308int flush_deferred(struct queue *q)
309{
310 int n;
311
312 if (verbose)
313 fprintf(stderr, "flushing deferred data to fd %d\n", q->fd);
314
315 n = write(q->fd, q->deferred_data, q->deferred_data_size);
316 if (n == -1)
317 return n;
318
319 if (n == q->deferred_data_size) {
320 /* All has been written -- release the memory */
321 free(q->begin_deferred_data);
322 q->begin_deferred_data = NULL;
323 q->deferred_data = NULL;
324 q->deferred_data_size = 0;
325 } else {
326 /* There is data left */
327 q->deferred_data += n;
328 q->deferred_data_size -= n;
329 }
330
331 return n;
332}
333
334
335void init_cnx(struct connection *cnx)
336{
337 memset(cnx, 0, sizeof(*cnx));
338 cnx->q[0].fd = -1;
339 cnx->q[1].fd = -1;
340 cnx->proto = get_first_protocol();
341}
342
343void dump_connection(struct connection *cnx)
344{
345 printf("state: %d\n", cnx->state);
346 printf("fd %d, %d deferred\n", cnx->q[0].fd, cnx->q[0].deferred_data_size);
347 printf("fd %d, %d deferred\n", cnx->q[1].fd, cnx->q[1].deferred_data_size);
348}
349
350
351/*
352 * moves data from one fd to other
353 *
354 * returns number of bytes copied if success
355 * returns 0 (FD_CNXCLOSED) if incoming socket closed
356 * returns FD_NODATA if no data was available
357 * returns FD_STALLED if data was read, could not be written, and has been
358 * stored in temporary buffer.
359 */
360int fd2fd(struct queue *target_q, struct queue *from_q)
361{
362 char buffer[BUFSIZ];
363 int target, from, size_r, size_w;
364
365 target = target_q->fd;
366 from = from_q->fd;
367
368 size_r = read(from, buffer, sizeof(buffer));
369 if (size_r == -1) {
370 switch (errno) {
371 case EAGAIN:
372 if (verbose)
373 fprintf(stderr, "reading 0 from %d\n", from);
374 return FD_NODATA;
375
376 case ECONNRESET:
377 case EPIPE:
378 return FD_CNXCLOSED;
379 }
380 }
381
382 CHECK_RES_RETURN(size_r, "read");
383
384 if (size_r == 0)
385 return FD_CNXCLOSED;
386
387 size_w = write(target, buffer, size_r);
388 /* process -1 when we know how to deal with it */
389 if (size_w == -1) {
390 switch (errno) {
391 case EAGAIN:
392 /* write blocked: Defer data */
393 defer_write(target_q, buffer, size_r);
394 return FD_STALLED;
395
396 case ECONNRESET:
397 case EPIPE:
398 /* remove end closed -- drop the connection */
399 return FD_CNXCLOSED;
400 }
401 } else if (size_w < size_r) {
402 /* incomplete write -- defer the rest of the data */
403 defer_write(target_q, buffer + size_w, size_r - size_w);
404 return FD_STALLED;
405 }
406
407 CHECK_RES_RETURN(size_w, "write");
408
409 return size_w;
410}
411
412/* returns a string that prints the IP and port of the sockaddr */
413char* sprintaddr(char* buf, size_t size, struct addrinfo *a)
414{
415 char host[NI_MAXHOST], serv[NI_MAXSERV];
416 int res;
417
418 res = getnameinfo(a->ai_addr, a->ai_addrlen,
419 host, sizeof(host),
420 serv, sizeof(serv),
421 numeric ? NI_NUMERICHOST | NI_NUMERICSERV : 0 );
422
423 if (res) {
424 log_message(LOG_ERR, "sprintaddr:getnameinfo: %s\n", gai_strerror(res));
425 /* Name resolution failed: do it numerically instead */
426 res = getnameinfo(a->ai_addr, a->ai_addrlen,
427 host, sizeof(host),
428 serv, sizeof(serv),
429 NI_NUMERICHOST | NI_NUMERICSERV);
430 /* should not fail but... */
431 if (res) {
432 log_message(LOG_ERR, "sprintaddr:getnameinfo(NUM): %s\n", gai_strerror(res));
433 strcpy(host, "?");
434 strcpy(serv, "?");
435 }
436 }
437
438 snprintf(buf, size, "%s:%s", host, serv);
439
440 return buf;
441}
442
443/* Turns a hostname and port (or service) into a list of struct addrinfo
444 * returns 0 on success, -1 otherwise and logs error
445 *
446 * *host gets modified
447 **/
448int resolve_split_name(struct addrinfo **out, char* host, const char* serv)
449{
450 struct addrinfo hint;
451 char *end;
452 int res;
453
454 memset(&hint, 0, sizeof(hint));
455 hint.ai_family = PF_UNSPEC;
456 hint.ai_socktype = SOCK_STREAM;
457
458 /* If it is a RFC-Compliant IPv6 address ("[1234::12]:443"), remove brackets
459 * around IP address */
460 if (host[0] == '[') {
461 end = strrchr(host, ']');
462 if (!end) {
463 fprintf(stderr, "%s: no closing bracket in IPv6 address?\n", host);
464 }
465 host++; /* skip first bracket */
466 *end = 0; /* remove last bracket */
467 }
468
469
470 res = getaddrinfo(host, serv, &hint, out);
471 if (res)
472 log_message(LOG_ERR, "%s `%s:%s'\n", gai_strerror(res), host, serv);
473 return res;
474}
475
476/* turns a "hostname:port" string into a list of struct addrinfo;
477out: list of newly allocated addrinfo (see getaddrinfo(3)); freeaddrinfo(3) when done
478fullname: input string -- it gets clobbered
479*/
480void resolve_name(struct addrinfo **out, char* fullname)
481{
482 char *serv, *host;
483 int res;
484
485 /* Find port */
486 char *sep = strrchr(fullname, ':');
487 if (!sep) { /* No separator: parameter is just a port */
488 fprintf(stderr, "%s: names must be fully specified as hostname:port\n", fullname);
489 exit(1);
490 }
491 serv = sep+1;
492 *sep = 0;
493
494 host = fullname;
495
496 res = resolve_split_name(out, host, serv);
497 if (res) {
498 fprintf(stderr, "%s `%s'\n", gai_strerror(res), fullname);
499 if (res == EAI_SERVICE)
500 fprintf(stderr, "(Check you have specified all ports)\n");
501 exit(4);
502 }
503}
504
505/* Log to syslog or stderr if foreground */
506void log_message(int type, char* msg, ...)
507{
508 va_list ap;
509
510 va_start(ap, msg);
511 if (foreground)
512 vfprintf(stderr, msg, ap);
513 else
514 vsyslog(type, msg, ap);
515 va_end(ap);
516}
517
518/* syslogs who connected to where */
519void log_connection(struct connection *cnx)
520{
521 struct addrinfo addr;
522 struct sockaddr_storage ss;
523#define MAX_NAMELENGTH (NI_MAXHOST + NI_MAXSERV + 1)
524 char peer[MAX_NAMELENGTH], service[MAX_NAMELENGTH],
525 local[MAX_NAMELENGTH], target[MAX_NAMELENGTH];
526 int res;
527
528 if (cnx->proto->log_level < 1)
529 return;
530
531 addr.ai_addr = (struct sockaddr*)&ss;
532 addr.ai_addrlen = sizeof(ss);
533
534 res = getpeername(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
535 if (res == -1) return; /* Can happen if connection drops before we get here.
536 In that case, don't log anything (there is no connection) */
537 sprintaddr(peer, sizeof(peer), &addr);
538
539 addr.ai_addrlen = sizeof(ss);
540 res = getsockname(cnx->q[0].fd, addr.ai_addr, &addr.ai_addrlen);
541 if (res == -1) return;
542 sprintaddr(service, sizeof(service), &addr);
543
544 addr.ai_addrlen = sizeof(ss);
545 res = getpeername(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
546 if (res == -1) return;
547 sprintaddr(target, sizeof(target), &addr);
548
549 addr.ai_addrlen = sizeof(ss);
550 res = getsockname(cnx->q[1].fd, addr.ai_addr, &addr.ai_addrlen);
551 if (res == -1) return;
552 sprintaddr(local, sizeof(local), &addr);
553
554 log_message(LOG_INFO, "%s:connection from %s to %s forwarded from %s to %s\n",
555 cnx->proto->description,
556 peer,
557 service,
558 local,
559 target);
560}
561
562
563/* libwrap (tcpd): check the connection is legal. This is necessary because
564 * the actual server will only see a connection coming from localhost and can't
565 * apply the rules itself.
566 *
567 * Returns -1 if access is denied, 0 otherwise
568 */
569int check_access_rights(int in_socket, const char* service)
570{
571#ifdef LIBWRAP
572 union {
573 struct sockaddr saddr;
574 struct sockaddr_storage ss;
575 } peer;
576 socklen_t size = sizeof(peer);
577 char addr_str[NI_MAXHOST], host[NI_MAXHOST];
578 int res;
579
580 res = getpeername(in_socket, &peer.saddr, &size);
581 CHECK_RES_RETURN(res, "getpeername");
582
583 /* extract peer address */
584 res = getnameinfo(&peer.saddr, size, addr_str, sizeof(addr_str), NULL, 0, NI_NUMERICHOST);
585 if (res) {
586 if (verbose)
587 fprintf(stderr, "getnameinfo(NI_NUMERICHOST):%s\n", gai_strerror(res));
588 strcpy(addr_str, STRING_UNKNOWN);
589 }
590 /* extract peer name */
591 strcpy(host, STRING_UNKNOWN);
592 if (!numeric) {
593 res = getnameinfo(&peer.saddr, size, host, sizeof(host), NULL, 0, NI_NAMEREQD);
594 if (res) {
595 if (verbose)
596 fprintf(stderr, "getnameinfo(NI_NAMEREQD):%s\n", gai_strerror(res));
597 }
598 }
599
600 if (!hosts_ctl(service, host, addr_str, STRING_UNKNOWN)) {
601 if (verbose)
602 fprintf(stderr, "access denied\n");
603 log_message(LOG_INFO, "connection from %s(%s): access denied", host, addr_str);
604 close(in_socket);
605 return -1;
606 }
607#endif
608 return 0;
609}
610
611void setup_signals(void)
612{
613 int res;
614 struct sigaction action;
615
616 /* Request no SIGCHLD is sent upon termination of
617 * the children */
618 memset(&action, 0, sizeof(action));
619 action.sa_handler = NULL;
620 action.sa_flags = SA_NOCLDWAIT;
621 res = sigaction(SIGCHLD, &action, NULL);
622 CHECK_RES_DIE(res, "sigaction");
623
624 /* Set SIGTERM to exit. For some reason if it's not set explicitly,
625 * coverage information is lost when killing the process */
626 memset(&action, 0, sizeof(action));
627 action.sa_handler = exit;
628 res = sigaction(SIGTERM, &action, NULL);
629 CHECK_RES_DIE(res, "sigaction");
630
631 /* Ignore SIGPIPE . */
632 action.sa_handler = SIG_IGN;
633 res = sigaction(SIGPIPE, &action, NULL);
634 CHECK_RES_DIE(res, "sigaction");
635
636}
637
638/* Open syslog connection with appropriate banner;
639 * banner is made up of basename(bin_name)+"[pid]" */
640void setup_syslog(const char* bin_name) {
641 char *name1, *name2;
642 int res;
643
644 name1 = strdup(bin_name);
645 res = asprintf(&name2, "%s[%d]", basename(name1), getpid());
646 CHECK_RES_DIE(res, "asprintf");
647 openlog(name2, LOG_CONS, LOG_AUTH);
648 free(name1);
649 /* Don't free name2, as openlog(3) uses it (at least in glibc) */
650
651 log_message(LOG_INFO, "%s %s started\n", server_type, VERSION);
652}
653
654/* Ask OS to keep capabilities over a setuid(nonzero) */
655void set_keepcaps(int val) {
656#ifdef LIBCAP
657 int res;
658 res = prctl(PR_SET_KEEPCAPS, val, 0, 0, 0);
659 if (res) {
660 perror("prctl");
661 exit(1);
662 }
663#endif
664}
665
666/* set needed capabilities for effective and permitted, clear rest */
667void set_capabilities(void) {
668#ifdef LIBCAP
669 int res;
670 cap_t caps;
671 cap_value_t cap_list[10];
672 int ncap = 0;
673
674 if (transparent)
675 cap_list[ncap++] = CAP_NET_ADMIN;
676
677 caps = cap_init();
678
679#define _cap_set_flag(flag) do { \
680 res = cap_clear_flag(caps, flag); \
681 CHECK_RES_DIE(res, "cap_clear_flag(" #flag ")"); \
682 if (ncap > 0) { \
683 res = cap_set_flag(caps, flag, ncap, cap_list, CAP_SET); \
684 CHECK_RES_DIE(res, "cap_set_flag(" #flag ")"); \
685 } \
686 } while(0)
687
688 _cap_set_flag(CAP_EFFECTIVE);
689 _cap_set_flag(CAP_PERMITTED);
690
691#undef _cap_set_flag
692
693 res = cap_set_proc(caps);
694 CHECK_RES_DIE(res, "cap_set_proc");
695
696 res = cap_free(caps);
697 if (res) {
698 perror("cap_free");
699 exit(1);
700 }
701#endif
702}
703
704/* We don't want to run as root -- drop privileges if required */
705void drop_privileges(const char* user_name)
706{
707 int res;
708 struct passwd *pw = getpwnam(user_name);
709 if (!pw) {
710 fprintf(stderr, "%s: not found\n", user_name);
711 exit(2);
712 }
713 if (verbose)
714 fprintf(stderr, "turning into %s\n", user_name);
715
716 set_keepcaps(1);
717
718 /* remove extraneous groups in case we belong to several extra groups that
719 * may have unwanted rights. If non-root when calling setgroups(), it
720 * fails, which is fine because... we have no unwanted rights
721 * (see POS36-C for security context)
722 * */
723 setgroups(0, NULL);
724
725 res = setgid(pw->pw_gid);
726 CHECK_RES_DIE(res, "setgid");
727 res = setuid(pw->pw_uid);
728 CHECK_RES_DIE(res, "setuid");
729
730 set_capabilities();
731 set_keepcaps(0);
732}
733
734/* Writes my PID */
735void write_pid_file(const char* pidfile)
736{
737 FILE *f;
738
739 f = fopen(pidfile, "w");
740 if (!f) {
741 perror(pidfile);
742 exit(3);
743 }
744
745 fprintf(f, "%d\n", getpid());
746 fclose(f);
747}
748
749

Built with git-ssb-web