1 /*
2 * scamper_privsep.c: code that does root-required tasks
3 *
4 * $Id: scamper_privsep.c,v 1.93 2020/04/30 07:17:14 mjl Exp $
5 *
6 * Copyright (C) 2004-2006 Matthew Luckie
7 * Copyright (C) 2006-2011 The University of Waikato
8 * Copyright (C) 2013-2014 The Regents of the University of California
9 * Copyright (C) 2016-2020 Matthew Luckie
10 * Author: Matthew Luckie
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation, version 2.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #ifndef WITHOUT_PRIVSEP
32
33 #include "internal.h"
34
35 #include "scamper_privsep.h"
36 #include "scamper_debug.h"
37
38 #include "scamper_dl.h"
39 #include "scamper_rtsock.h"
40 #include "scamper_firewall.h"
41 #include "scamper_icmp4.h"
42 #include "scamper_icmp6.h"
43 #include "scamper_udp4.h"
44 #include "scamper_ip4.h"
45
46 #include "utils.h"
47
48 typedef struct privsep_msg
49 {
50 uint16_t plen;
51 uint16_t type;
52 } privsep_msg_t;
53
54 typedef struct privsep_func
55 {
56 int (*dofunc)(uint16_t len, const uint8_t *param);
57 int (*txfunc)(int, int, uint8_t);
58 } privsep_func_t;
59
60 static pid_t root_pid = -1; /* the process id of the root code */
61 static int root_fd = -1; /* the fd the root code send/recv on */
62 static int lame_fd = -1; /* the fd that the lame code uses */
63 static void *cmsgbuf = NULL; /* cmsgbuf sized for one fd */
64
65 /*
66 * the privilege separation code works by allowing the lame process to send
67 * request messages to the root process. these define the messages that
68 * the root process understands.
69 */
70 #define SCAMPER_PRIVSEP_EXIT 0x00U
71 #define SCAMPER_PRIVSEP_OPEN_DATALINK 0x01U
72 #define SCAMPER_PRIVSEP_OPEN_FILE 0x02U
73 #define SCAMPER_PRIVSEP_OPEN_RTSOCK 0x03U
74 #define SCAMPER_PRIVSEP_OPEN_ICMP 0x04U
75 #define SCAMPER_PRIVSEP_OPEN_SOCK 0x05U
76 #define SCAMPER_PRIVSEP_OPEN_RAWUDP 0x06U
77 #define SCAMPER_PRIVSEP_OPEN_UNIX 0x07U
78 #define SCAMPER_PRIVSEP_OPEN_RAWIP 0x08U
79 #define SCAMPER_PRIVSEP_UNLINK 0x09U
80 #define SCAMPER_PRIVSEP_IPFW_INIT 0x0aU
81 #define SCAMPER_PRIVSEP_IPFW_CLEANUP 0x0bU
82 #define SCAMPER_PRIVSEP_IPFW_ADD 0x0cU
83 #define SCAMPER_PRIVSEP_IPFW_DEL 0x0dU
84 #define SCAMPER_PRIVSEP_PF_INIT 0x0eU
85 #define SCAMPER_PRIVSEP_PF_CLEANUP 0x0fU
86 #define SCAMPER_PRIVSEP_PF_ADD 0x10U
87 #define SCAMPER_PRIVSEP_PF_DEL 0x11U
88
89 #define SCAMPER_PRIVSEP_MAXTYPE (SCAMPER_PRIVSEP_PF_DEL)
90
91 /*
92 * privsep_open_rawsock
93 *
94 * open a raw icmp socket. one integer parameter corresponding to the 'type'
95 * is supplied in param.
96 *
97 */
privsep_open_icmp(uint16_t plen,const uint8_t * param)98 static int privsep_open_icmp(uint16_t plen, const uint8_t *param)
99 {
100 int type;
101
102 if(plen != sizeof(type))
103 {
104 scamper_debug(__func__, "plen %d != %d", plen, sizeof(type));
105 return -1;
106 }
107
108 memcpy(&type, param, sizeof(type));
109
110 if(type == AF_INET)
111 return scamper_icmp4_open_fd();
112 if(type == AF_INET6)
113 return scamper_icmp6_open_fd();
114
115 scamper_debug(__func__, "type %d != AF_INET || AF_INET6", type);
116 errno = EINVAL;
117 return -1;
118 }
119
120 /*
121 * privsep_open_rtsock
122 *
123 * open a routing socket. there are no parameters permitted to this
124 * method call.
125 */
privsep_open_rtsock(uint16_t plen,const uint8_t * param)126 static int privsep_open_rtsock(uint16_t plen, const uint8_t *param)
127 {
128 if(plen != 0)
129 {
130 scamper_debug(__func__, "plen %d != 0", plen, 0);
131 errno = EINVAL;
132 return -1;
133 }
134
135 return scamper_rtsock_open_fd();
136 }
137
138 /*
139 * privsep_open_datalink
140 *
141 * open a BPF or PF_PACKET socket to the datalink. the param has a single
142 * field: the ifindex of the device to monitor.
143 */
privsep_open_datalink(uint16_t plen,const uint8_t * param)144 static int privsep_open_datalink(uint16_t plen, const uint8_t *param)
145 {
146 int ifindex;
147
148 /* the payload should have an integer field - no more, no less. */
149 if(plen != sizeof(ifindex))
150 {
151 scamper_debug(__func__, "plen %d != %d", plen, sizeof(ifindex));
152 errno = EINVAL;
153 return -1;
154 }
155
156 memcpy(&ifindex, param, sizeof(ifindex));
157
158 return scamper_dl_open_fd(ifindex);
159 }
160
privsep_open_sock(uint16_t plen,const uint8_t * param)161 static int privsep_open_sock(uint16_t plen, const uint8_t *param)
162 {
163 struct sockaddr_in sin4;
164 struct sockaddr_in6 sin6;
165 int domain, type, protocol, port;
166 const size_t size = sizeof(domain) + sizeof(protocol) + sizeof(port);
167 size_t off = 0;
168 int fd = -1;
169
170 if(plen != size)
171 {
172 scamper_debug(__func__, "plen %d != %d", plen, size);
173 errno = EINVAL;
174 goto err;
175 }
176 off = 0;
177 memcpy(&domain, param+off, sizeof(domain)); off += sizeof(domain);
178 memcpy(&protocol, param+off, sizeof(protocol)); off += sizeof(protocol);
179 memcpy(&port, param+off, sizeof(port)); off += sizeof(port);
180
181 if(off != plen)
182 goto inval;
183
184 if(port < 1 || port > 65535)
185 {
186 scamper_debug(__func__, "refusing to bind to port %d", port);
187 goto inval;
188 }
189
190 if(protocol == IPPROTO_TCP) type = SOCK_STREAM;
191 else if(protocol == IPPROTO_UDP) type = SOCK_DGRAM;
192 else
193 {
194 scamper_debug(__func__, "unhandled IPv4 protocol %d", protocol);
195 goto inval;
196 }
197
198 if(domain == AF_INET)
199 {
200 if((fd = socket(AF_INET, type, protocol)) == -1)
201 {
202 printerror(__func__, "could not open IPv4 socket");
203 goto err;
204 }
205 sockaddr_compose((struct sockaddr *)&sin4, AF_INET, NULL, port);
206 if(bind(fd, (struct sockaddr *)&sin4, sizeof(sin4)) == -1)
207 {
208 printerror(__func__, "could not bind to IPv4 protocol %d port %d",
209 protocol, port);
210 goto err;
211 }
212 }
213 else if(domain == AF_INET6)
214 {
215 if((fd = socket(AF_INET6, type, protocol)) == -1)
216 {
217 printerror(__func__, "could not open IPv6 socket");
218 goto err;
219 }
220 sockaddr_compose((struct sockaddr *)&sin6, AF_INET6, NULL, port);
221 if(bind(fd, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
222 {
223 printerror(__func__, "could not bind to IPv6 protocol %d port %d",
224 protocol, port);
225 goto err;
226 }
227 }
228 else return -1;
229
230 return fd;
231
232 inval:
233 errno = EINVAL;
234 err:
235 if(fd != -1) close(fd);
236 return -1;
237 }
238
privsep_open_rawudp(uint16_t plen,const uint8_t * param)239 static int privsep_open_rawudp(uint16_t plen, const uint8_t *param)
240 {
241 struct in_addr in;
242
243 if(plen != 4)
244 {
245 scamper_debug(__func__, "plen %d != 4", plen);
246 errno = EINVAL;
247 return -1;
248 }
249
250 memcpy(&in, param+0, sizeof(in));
251 return scamper_udp4_openraw_fd(&in);
252 }
253
privsep_open_rawip(uint16_t plen,const uint8_t * param)254 static int privsep_open_rawip(uint16_t plen, const uint8_t *param)
255 {
256 if(plen != 0)
257 {
258 scamper_debug(__func__, "plen %d != 4", plen);
259 errno = EINVAL;
260 return -1;
261 }
262 return scamper_ip4_openraw_fd();
263 }
264
privsep_ipfw_init(uint16_t plen,const uint8_t * param)265 static int privsep_ipfw_init(uint16_t plen, const uint8_t *param)
266 {
267 #ifdef HAVE_IPFW
268 if(plen != 0)
269 {
270 scamper_debug(__func__, "plen %d != 0", plen);
271 errno = EINVAL;
272 return -1;
273 }
274 return scamper_firewall_ipfw_init();
275 #else
276 scamper_debug(__func__, "not on ipfw system");
277 errno = EINVAL;
278 return -1;
279 #endif
280 }
281
privsep_ipfw_cleanup(uint16_t plen,const uint8_t * param)282 static int privsep_ipfw_cleanup(uint16_t plen, const uint8_t *param)
283 {
284 #ifdef HAVE_IPFW
285 if(plen != 0)
286 {
287 scamper_debug(__func__, "plen %d != 0", plen);
288 errno = EINVAL;
289 return -1;
290 }
291 scamper_firewall_ipfw_cleanup();
292 return 0;
293 #else
294 scamper_debug(__func__, "not on ipfw system");
295 errno = EINVAL;
296 return -1;
297 #endif
298 }
299
privsep_ipfw_add(uint16_t plen,const uint8_t * param)300 static int privsep_ipfw_add(uint16_t plen, const uint8_t *param)
301 {
302 #ifdef HAVE_IPFW
303 int n, af, p, sp, dp;
304 struct in_addr s4, d4;
305 struct in6_addr s6, d6;
306 void *s, *d = NULL;
307 uint8_t df;
308 uint16_t off = 0, al;
309
310 if(plen < 1 + sizeof(int))
311 {
312 scamper_debug(__func__, "plen %d < %d", plen, 1 + sizeof(int));
313 goto inval;
314 }
315
316 df = param[0]; off++;
317 if(df != 0 && df != 1)
318 {
319 scamper_debug(__func__, "df %d", df);
320 goto inval;
321 }
322
323 memcpy(&af, param+off, sizeof(af)); off += sizeof(af);
324 if(af == AF_INET)
325 {
326 s = &s4;
327 al = 4;
328 if(df == 0)
329 {
330 if(plen != 1 + (sizeof(int) * 5) + sizeof(struct in_addr))
331 goto inval;
332 }
333 else
334 {
335 if(plen != 1 + (sizeof(int) * 5) + (sizeof(struct in_addr) * 2))
336 goto inval;
337 d = &d4;
338 }
339 }
340 else if(af == AF_INET6)
341 {
342 s = &s6;
343 al = 16;
344 if(df == 0)
345 {
346 if(plen != 1 + (sizeof(int) * 5) + sizeof(struct in6_addr))
347 goto inval;
348 }
349 else
350 {
351 if(plen != 1 + (sizeof(int) * 5) + (sizeof(struct in6_addr) * 2))
352 goto inval;
353 d = &d6;
354 }
355 }
356 else goto inval;
357
358 memcpy(&n, param+off, sizeof(n)); off += sizeof(n);
359 memcpy(&p, param+off, sizeof(p)); off += sizeof(p);
360 memcpy(s, param+off, al); off += al;
361
362 if(df != 0)
363 {
364 memcpy(d, param+off, al);
365 off += al;
366 }
367
368 memcpy(&sp, param+off, sizeof(sp)); off += sizeof(sp);
369 memcpy(&dp, param+off, sizeof(dp)); off += sizeof(dp);
370
371 if(off != plen)
372 goto inval;
373 if(sp < 0 || sp > 65535 || dp < 0 || dp > 65535)
374 goto inval;
375 if(p != IPPROTO_TCP && p != IPPROTO_UDP)
376 goto inval;
377
378 return scamper_firewall_ipfw_add(n, af, p, s, d, sp, dp);
379
380 inval:
381 errno = EINVAL;
382 return -1;
383 #else
384 scamper_debug(__func__, "not on ipfw system");
385 errno = EINVAL;
386 return -1;
387 #endif
388 }
389
privsep_ipfw_del(uint16_t plen,const uint8_t * param)390 static int privsep_ipfw_del(uint16_t plen, const uint8_t *param)
391 {
392 #ifdef HAVE_IPFW
393 int n, af;
394 uint16_t off = 0;
395
396 if(plen != sizeof(int) * 2)
397 {
398 scamper_debug(__func__, "plen %d != %d", plen, sizeof(int) * 2);
399 goto inval;
400 }
401
402 memcpy(&n, param+off, sizeof(n)); off += sizeof(n);
403 memcpy(&af, param+off, sizeof(af)); off += sizeof(af);
404 if(off != plen)
405 goto inval;
406
407 return scamper_firewall_ipfw_del(n, af);
408
409 inval:
410 errno = EINVAL;
411 return -1;
412 #else
413 scamper_debug(__func__, "not on ipfw system");
414 errno = EINVAL;
415 return -1;
416 #endif
417 }
418
privsep_pf_init(uint16_t plen,const uint8_t * param)419 static int privsep_pf_init(uint16_t plen, const uint8_t *param)
420 {
421 #ifdef HAVE_PF
422 const char *name = (const char *)param;
423 if(plen == 0)
424 {
425 scamper_debug(__func__, "plen == 0", plen);
426 errno = EINVAL;
427 return -1;
428 }
429 if(string_isprint(name, plen) == 0)
430 {
431 scamper_debug(__func__, "name is not printable");
432 errno = EINVAL;
433 return -1;
434 }
435 if(name[plen] != '\0' || strlen(name) + 1 != plen)
436 {
437 scamper_debug(__func__, "malformed initialisation");
438 errno = EINVAL;
439 return -1;
440 }
441 return scamper_firewall_pf_init(name);
442 #else
443 scamper_debug(__func__, "not on pf system");
444 errno = EINVAL;
445 return -1;
446 #endif
447 }
448
privsep_pf_cleanup(uint16_t plen,const uint8_t * param)449 static int privsep_pf_cleanup(uint16_t plen, const uint8_t *param)
450 {
451 #ifdef HAVE_PF
452 if(plen != 0)
453 {
454 scamper_debug(__func__, "plen %d != 0", plen);
455 errno = EINVAL;
456 return -1;
457 }
458 scamper_firewall_pf_cleanup();
459 return 0;
460 #else
461 scamper_debug(__func__, "not on pf system");
462 errno = EINVAL;
463 return -1;
464 #endif
465 }
466
privsep_pf_add(uint16_t plen,const uint8_t * param)467 static int privsep_pf_add(uint16_t plen, const uint8_t *param)
468 {
469 #ifdef HAVE_PF
470 int n, af, p, sp, dp;
471 struct in_addr s4, d4;
472 struct in6_addr s6, d6;
473 void *s, *d;
474 uint16_t off = 0, al;
475
476 if(plen < sizeof(int))
477 {
478 scamper_debug(__func__, "plen %d < %d", plen, sizeof(int));
479 goto inval;
480 }
481
482 memcpy(&af, param+off, sizeof(af)); off += sizeof(af);
483 if(af == AF_INET)
484 {
485 if(plen != (sizeof(int) * 5) + (sizeof(struct in_addr) * 2))
486 goto inval;
487 s = &s4;
488 d = &d4;
489 al = 4;
490 }
491 else if(af == AF_INET6)
492 {
493 if(plen != (sizeof(int) * 5) + (sizeof(struct in6_addr) * 2))
494 goto inval;
495 s = &s6;
496 d = &d6;
497 al = 16;
498 }
499 else goto inval;
500
501 memcpy(&n, param+off, sizeof(n)); off += sizeof(n);
502 memcpy(&p, param+off, sizeof(p)); off += sizeof(p);
503 memcpy(s, param+off, al); off += al;
504 memcpy(d, param+off, al); off += al;
505 memcpy(&sp, param+off, sizeof(sp)); off += sizeof(sp);
506 memcpy(&dp, param+off, sizeof(dp)); off += sizeof(dp);
507
508 if(off != plen)
509 goto inval;
510 if(sp < 0 || sp > 65535 || dp < 0 || dp > 65535)
511 goto inval;
512 if(p != IPPROTO_TCP && p != IPPROTO_UDP)
513 goto inval;
514
515 return scamper_firewall_pf_add(n, af, p, s, d, sp, dp);
516
517 inval:
518 errno = EINVAL;
519 return -1;
520 #else
521 scamper_debug(__func__, "not on pf system");
522 errno = EINVAL;
523 return -1;
524 #endif
525 }
526
privsep_pf_del(uint16_t plen,const uint8_t * param)527 static int privsep_pf_del(uint16_t plen, const uint8_t *param)
528 {
529 #ifdef HAVE_PF
530 int n;
531 if(plen != sizeof(int))
532 {
533 scamper_debug(__func__, "plen %d != %d", plen, sizeof(int));
534 errno = EINVAL;
535 return -1;
536 }
537 memcpy(&n, param, sizeof(n));
538 return scamper_firewall_pf_del(n);
539 #else
540 scamper_debug(__func__, "not on pf system");
541 errno = EINVAL;
542 return -1;
543 #endif
544 }
545
privsep_unlink(uint16_t plen,const uint8_t * param)546 static int privsep_unlink(uint16_t plen, const uint8_t *param)
547 {
548 const char *name = (const char *)param;
549 uid_t uid, euid;
550
551 if(plen < 2 || name[plen-1] != '\0')
552 return -1;
553
554 uid = getuid();
555 euid = geteuid();
556 if(seteuid(uid) != 0)
557 return -1;
558 unlink(name);
559 if(seteuid(euid) != 0)
560 exit(-errno);
561
562 return 0;
563 }
564
privsep_open_unix(uint16_t plen,const uint8_t * param)565 static int privsep_open_unix(uint16_t plen, const uint8_t *param)
566 {
567 const char *name = (const char *)param;
568 struct sockaddr_un sn;
569 uid_t uid, euid;
570 int fd, brc;
571
572 if(plen < 2 || name[plen-1] != '\0')
573 return -1;
574
575 if(sockaddr_compose_un((struct sockaddr *)&sn, name) != 0)
576 return -1;
577
578 /* set our effective uid to be the user who started scamper */
579 uid = getuid();
580 euid = geteuid();
581 if(seteuid(uid) != 0)
582 return -1;
583
584 /* open the socket, bind it, and make it listen */
585 if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) != -1)
586 {
587 if((brc = bind(fd, (struct sockaddr *)&sn, sizeof(sn))) != 0 ||
588 listen(fd, -1) != 0)
589 {
590 close(fd);
591 fd = -1;
592 if(brc == 0)
593 unlink(name);
594 }
595 }
596
597 if(seteuid(euid) != 0)
598 {
599 if(fd != -1) close(fd);
600 exit(-errno);
601 }
602
603 return fd;
604 }
605
606 /*
607 * privsep_open_file
608 *
609 * switch to the user running the process and open the file specified.
610 * the param has two fields in it: the mode of open, and the file to open.
611 */
privsep_open_file(uint16_t plen,const uint8_t * param)612 static int privsep_open_file(uint16_t plen, const uint8_t *param)
613 {
614 const char *file;
615 uid_t uid, euid;
616 int flags;
617 mode_t mode = 0;
618 uint16_t off;
619 int fd;
620
621 /*
622 * if the payload of param is not large enough to hold the flags and a
623 * filename, then don't go any further
624 */
625 if(plen < sizeof(int) + 2)
626 return -1;
627
628 memcpy(&flags, param, sizeof(int));
629 off = sizeof(int);
630
631 /* if the O_CREAT flag is set, we need to fetch the mode parameter too */
632 if(flags & O_CREAT)
633 {
634 /*
635 * the payload length of the parameter must be large enough to hold
636 * the flags, mode, and a filename
637 */
638 if(plen < off + sizeof(mode_t) + 2)
639 return -1;
640
641 memcpy(&mode, param+off, sizeof(mode));
642 off += sizeof(mode);
643 }
644
645 file = (const char *)(param + off);
646
647 /*
648 * make sure the length of the file to open checks out.
649 * the last byte of the string must be a null character.
650 */
651 if(file[plen-off-1] != '\0')
652 {
653 scamper_debug(__func__, "filename not terminated with a null");
654 return -1;
655 }
656
657 uid = getuid();
658 euid = geteuid();
659
660 /* set our effective uid to be the user who started scamper */
661 if(seteuid(uid) == -1)
662 {
663 return -1;
664 }
665
666 if(flags & O_CREAT)
667 fd = open(file, flags, mode);
668 else
669 fd = open(file, flags);
670
671 /*
672 * ask for our root permissions back. if we can't get them back, then
673 * this process is crippled and it might as well exit now.
674 */
675 if(seteuid(euid) == -1)
676 {
677 if(fd != -1) close(fd);
678 exit(-errno);
679 }
680
681 return fd;
682 }
683
privsep_send_rc(int rc,int error,uint8_t msg_type)684 static int privsep_send_rc(int rc, int error, uint8_t msg_type)
685 {
686 uint8_t buf[sizeof(int) * 2];
687 struct msghdr msg;
688 struct iovec vec;
689
690 scamper_debug(__func__, "rc: %d, error: %d, msg_type: 0x%02x",
691 rc, error, msg_type);
692
693 memset(&vec, 0, sizeof(vec));
694 memset(&msg, 0, sizeof(msg));
695
696 memcpy(buf, &rc, sizeof(int));
697 memcpy(buf+sizeof(int), &error, sizeof(int));
698 vec.iov_base = (void *)buf;
699 vec.iov_len = sizeof(buf);
700
701 msg.msg_iov = &vec;
702 msg.msg_iovlen = 1;
703
704 if(sendmsg(root_fd, &msg, 0) == -1)
705 return -1;
706
707 return 0;
708
709 }
710
711 /*
712 * privsep_send_fd
713 *
714 * send the fd created using the priviledged code. if the fd was not
715 * successfully created, we send the errno back in the payload of the
716 * message.
717 */
privsep_send_fd(int fd,int error,uint8_t msg_type)718 static int privsep_send_fd(int fd, int error, uint8_t msg_type)
719 {
720 struct msghdr msg;
721 struct iovec vec;
722 struct cmsghdr *cmsg;
723
724 scamper_debug(__func__, "fd: %d error: %d msg_type: 0x%02x",
725 fd, error, msg_type);
726
727 memset(&vec, 0, sizeof(vec));
728 memset(&msg, 0, sizeof(msg));
729
730 vec.iov_base = (void *)&error;
731 vec.iov_len = sizeof(error);
732
733 msg.msg_iov = &vec;
734 msg.msg_iovlen = 1;
735
736 if(fd != -1)
737 {
738 msg.msg_control = (caddr_t)cmsgbuf;
739 msg.msg_controllen = CMSG_SPACE(sizeof(fd));
740
741 cmsg = CMSG_FIRSTHDR(&msg);
742 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
743 cmsg->cmsg_level = SOL_SOCKET;
744 cmsg->cmsg_type = SCM_RIGHTS;
745 memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
746 }
747
748 if(sendmsg(root_fd, &msg, 0) == -1)
749 {
750 printerror(__func__, "couldnt send fd");
751 return -1;
752 }
753
754 return 0;
755 }
756
privsep_recv_fd(void)757 static int privsep_recv_fd(void)
758 {
759 struct msghdr msg;
760 struct iovec vec;
761 ssize_t rc;
762 int fd = -1, error = 0, *ptr;
763 struct cmsghdr *cmsg;
764
765 memset(&vec, 0, sizeof(vec));
766 memset(&msg, 0, sizeof(msg));
767
768 vec.iov_base = (char *)&error;
769 vec.iov_len = sizeof(error);
770
771 msg.msg_iov = &vec;
772 msg.msg_iovlen = 1;
773
774 msg.msg_control = cmsgbuf;
775 msg.msg_controllen = CMSG_SPACE(sizeof(fd));
776
777 if((rc = recvmsg(lame_fd, &msg, 0)) == -1)
778 {
779 printerror(__func__, "recvmsg failed");
780 return -1;
781 }
782 else if(rc != sizeof(error))
783 {
784 return -1;
785 }
786
787 if(error == 0)
788 {
789 cmsg = CMSG_FIRSTHDR(&msg);
790 if(cmsg != NULL && cmsg->cmsg_type == SCM_RIGHTS)
791 {
792 ptr = (int *)CMSG_DATA(cmsg);
793 fd = *ptr;
794 }
795 }
796 else
797 {
798 errno = error;
799 }
800
801 return fd;
802 }
803
804 /*
805 * privsep_do
806 *
807 * this is the only piece of code with root priviledges. we use it to
808 * create raw sockets, routing/netlink sockets, BPF/PF_PACKET sockets, and
809 * ordinary files that scamper itself cannot do by itself.
810 */
privsep_do(void)811 static int privsep_do(void)
812 {
813 static const privsep_func_t funcs[] = {
814 {NULL, NULL},
815 {privsep_open_datalink, privsep_send_fd},
816 {privsep_open_file, privsep_send_fd},
817 {privsep_open_rtsock, privsep_send_fd},
818 {privsep_open_icmp, privsep_send_fd},
819 {privsep_open_sock, privsep_send_fd},
820 {privsep_open_rawudp, privsep_send_fd},
821 {privsep_open_unix, privsep_send_fd},
822 {privsep_open_rawip, privsep_send_fd},
823 {privsep_unlink, privsep_send_rc},
824 {privsep_ipfw_init, privsep_send_rc},
825 {privsep_ipfw_cleanup, privsep_send_rc},
826 {privsep_ipfw_add, privsep_send_rc},
827 {privsep_ipfw_del, privsep_send_rc},
828 {privsep_pf_init, privsep_send_rc},
829 {privsep_pf_cleanup, privsep_send_rc},
830 {privsep_pf_add, privsep_send_rc},
831 {privsep_pf_del, privsep_send_rc},
832 };
833
834 privsep_msg_t msg;
835 void *data = NULL;
836 int ret = 0, error;
837 int fd;
838
839 #if defined(HAVE_SETPROCTITLE)
840 setproctitle("%s", "[priv]");
841 #endif
842
843 /* might as well set our copy of the root_pid to something useful */
844 root_pid = getpid();
845
846 /*
847 * the priviledged process does not need the lame file descriptor for
848 * anything, so get rid of it
849 */
850 close(lame_fd);
851 lame_fd = -1;
852
853 for(;;)
854 {
855 /* read the msg header */
856 if((ret = read_wrap(root_fd, (uint8_t *)&msg, NULL, sizeof(msg))) != 0)
857 {
858 if(ret == -1)
859 printerror(__func__, "could not read msg hdr");
860 break;
861 }
862
863 /* if we've been told to exit, then do so now */
864 if(msg.type == SCAMPER_PRIVSEP_EXIT)
865 break;
866
867 if(msg.type > SCAMPER_PRIVSEP_MAXTYPE)
868 {
869 scamper_debug(__func__, "msg %d > maxtype", msg.type);
870 ret = -EINVAL;
871 break;
872 }
873
874 /* if there is more data to read, read it now */
875 if(msg.plen != 0)
876 {
877 if((data = malloc_zero(msg.plen)) == NULL)
878 {
879 printerror(__func__, "couldnt malloc data");
880 ret = (-errno);
881 break;
882 }
883
884 if((ret = read_wrap(root_fd, data, NULL, msg.plen)) != 0)
885 {
886 printerror(__func__, "couldnt read data");
887 free(data);
888 break;
889 }
890 }
891 else data = NULL;
892
893 if((fd = funcs[msg.type].dofunc(msg.plen, data)) == -1)
894 error = errno;
895 else
896 error = 0;
897
898 /* we don't need the data we read anymore */
899 if(data != NULL)
900 free(data);
901
902 if(funcs[msg.type].txfunc(fd, error, msg.type) != 0)
903 break;
904
905 if(funcs[msg.type].txfunc == privsep_send_fd && fd != -1)
906 close(fd);
907 }
908
909 close(root_fd);
910 return ret;
911 }
912
913 /*
914 * privsep_lame_send
915 *
916 * compose and send the messages necessary to communicate with the root
917 * process.
918 */
privsep_lame_send(const uint16_t type,const uint16_t len,const uint8_t * param)919 static int privsep_lame_send(const uint16_t type, const uint16_t len,
920 const uint8_t *param)
921 {
922 privsep_msg_t msg;
923
924 assert(type != SCAMPER_PRIVSEP_EXIT);
925 assert(type <= SCAMPER_PRIVSEP_MAXTYPE);
926
927 /* send the header first */
928 msg.type = type;
929 msg.plen = len;
930 if(write_wrap(lame_fd, &msg, NULL, sizeof(msg)) == -1)
931 {
932 printerror(__func__, "could not send msg header");
933 return -1;
934 }
935
936 /* if there is a parameter data to send, send it now */
937 if(len != 0 && write_wrap(lame_fd, param, NULL, len) == -1)
938 {
939 printerror(__func__, "could not send msg param");
940 return -1;
941 }
942
943 return 0;
944 }
945
946 /*
947 * privsep_getfd
948 *
949 * send a request to the piece of code running as root to do open a file
950 * descriptor that requires priviledge to do. return the file descriptor.
951 */
privsep_getfd(uint16_t type,uint16_t len,const uint8_t * param)952 static int privsep_getfd(uint16_t type, uint16_t len, const uint8_t *param)
953 {
954 if(privsep_lame_send(type, len, param) == -1)
955 {
956 return -1;
957 }
958
959 return privsep_recv_fd();
960 }
961
privsep_dotask(uint16_t type,uint16_t len,const uint8_t * param)962 static int privsep_dotask(uint16_t type, uint16_t len, const uint8_t *param)
963 {
964 uint8_t buf[sizeof(int)*2];
965 int error;
966 int rc;
967
968 if(privsep_lame_send(type, len, param) == -1)
969 return -1;
970
971 if(read_wrap(lame_fd, buf, NULL, sizeof(buf)) == -1)
972 return -1;
973
974 memcpy(&rc, buf, sizeof(rc));
975 memcpy(&error, buf+sizeof(int), sizeof(error));
976
977 if(rc != 0)
978 errno = error;
979
980 return rc;
981 }
982
privsep_getfd_1int(const uint16_t type,const int p1)983 static int privsep_getfd_1int(const uint16_t type, const int p1)
984 {
985 uint8_t param[sizeof(p1)];
986 memcpy(param, &p1, sizeof(p1));
987 return privsep_getfd(type, sizeof(param), param);
988 }
989
privsep_getfd_3int(const uint16_t type,const int p1,const int p2,const int p3)990 static int privsep_getfd_3int(const uint16_t type,
991 const int p1, const int p2, const int p3)
992 {
993 uint8_t param[sizeof(p1)+sizeof(p2)+sizeof(p3)];
994 size_t off = 0;
995 memcpy(param+off, &p1, sizeof(p1)); off += sizeof(p1);
996 memcpy(param+off, &p2, sizeof(p2)); off += sizeof(p2);
997 memcpy(param+off, &p3, sizeof(p3));
998 return privsep_getfd(type, sizeof(param), param);
999 }
1000
scamper_privsep_open_datalink(const int ifindex)1001 int scamper_privsep_open_datalink(const int ifindex)
1002 {
1003 return privsep_getfd_1int(SCAMPER_PRIVSEP_OPEN_DATALINK, ifindex);
1004 }
1005
scamper_privsep_open_unix(const char * file)1006 int scamper_privsep_open_unix(const char *file)
1007 {
1008 int len = strlen(file) + 1;
1009 return privsep_getfd(SCAMPER_PRIVSEP_OPEN_UNIX, len, (const uint8_t *)file);
1010 }
1011
scamper_privsep_open_file(const char * file,const int flags,const mode_t mode)1012 int scamper_privsep_open_file(const char *file,
1013 const int flags, const mode_t mode)
1014 {
1015 uint8_t *param;
1016 int off, len, fd;
1017
1018 /*
1019 * decide how big the message is going to be. don't pass it if the message
1020 * length parameter constrains us
1021 */
1022 len = sizeof(flags) + strlen(file) + 1;
1023 if(flags & O_CREAT)
1024 len += sizeof(mode);
1025
1026 /*
1027 * the len is fixed because the length parameter used in the privsep
1028 * header is a 16-bit unsigned integer.
1029 */
1030 if(len > 65535)
1031 return -1;
1032
1033 /* allocate the parameter */
1034 if((param = malloc_zero(len)) == NULL)
1035 return -1;
1036
1037 /* copy in the flags parameter, and the mode parameter if necessary */
1038 memcpy(param, &flags, sizeof(flags)); off = sizeof(flags);
1039 if(flags & O_CREAT)
1040 {
1041 memcpy(param+off, &mode, sizeof(mode));
1042 off += sizeof(mode);
1043 }
1044
1045 /* finally copy in the name of the file to open */
1046 memcpy(param+off, file, len-off);
1047
1048 /* get the file descriptor and return it */
1049 fd = privsep_getfd(SCAMPER_PRIVSEP_OPEN_FILE, len, param);
1050 free(param);
1051 return fd;
1052 }
1053
scamper_privsep_open_rtsock(void)1054 int scamper_privsep_open_rtsock(void)
1055 {
1056 return privsep_getfd(SCAMPER_PRIVSEP_OPEN_RTSOCK, 0, NULL);
1057 }
1058
scamper_privsep_open_icmp(const int domain)1059 int scamper_privsep_open_icmp(const int domain)
1060 {
1061 return privsep_getfd_1int(SCAMPER_PRIVSEP_OPEN_ICMP, domain);
1062 }
1063
scamper_privsep_open_tcp(const int domain,const int port)1064 int scamper_privsep_open_tcp(const int domain, const int port)
1065 {
1066 return privsep_getfd_3int(SCAMPER_PRIVSEP_OPEN_SOCK,domain,IPPROTO_TCP,port);
1067 }
1068
scamper_privsep_open_udp(const int domain,const int port)1069 int scamper_privsep_open_udp(const int domain, const int port)
1070 {
1071 return privsep_getfd_3int(SCAMPER_PRIVSEP_OPEN_SOCK,domain,IPPROTO_UDP,port);
1072 }
1073
scamper_privsep_open_rawudp(const void * addr)1074 int scamper_privsep_open_rawudp(const void *addr)
1075 {
1076 uint8_t param[4];
1077
1078 if(addr == NULL)
1079 memset(param, 0, 4);
1080 else
1081 memcpy(param, addr, 4);
1082
1083 return privsep_getfd(SCAMPER_PRIVSEP_OPEN_RAWUDP, sizeof(param), param);
1084 }
1085
scamper_privsep_open_rawip(void)1086 int scamper_privsep_open_rawip(void)
1087 {
1088 return privsep_getfd(SCAMPER_PRIVSEP_OPEN_RAWIP, 0, NULL);
1089 }
1090
scamper_privsep_unlink(const char * file)1091 int scamper_privsep_unlink(const char *file)
1092 {
1093 int len = strlen(file) + 1;
1094 return privsep_dotask(SCAMPER_PRIVSEP_UNLINK, len, (const uint8_t *)file);
1095 }
1096
scamper_privsep_ipfw_init(void)1097 int scamper_privsep_ipfw_init(void)
1098 {
1099 return privsep_dotask(SCAMPER_PRIVSEP_IPFW_INIT, 0, NULL);
1100 }
1101
scamper_privsep_ipfw_cleanup(void)1102 int scamper_privsep_ipfw_cleanup(void)
1103 {
1104 return privsep_dotask(SCAMPER_PRIVSEP_IPFW_CLEANUP, 0, NULL);
1105 }
1106
scamper_privsep_ipfw_add(int n,int af,int p,void * s,void * d,int sp,int dp)1107 int scamper_privsep_ipfw_add(int n,int af,int p,void *s,void *d,int sp,int dp)
1108 {
1109 uint8_t param[1 + (sizeof(int) * 5) + (16 * 2)];
1110 uint16_t len = 0;
1111 uint16_t al;
1112
1113 if(d == NULL)
1114 param[0] = 0;
1115 else
1116 param[0] = 1;
1117 len++;
1118
1119 if(af == AF_INET)
1120 al = 4;
1121 else if(af == AF_INET6)
1122 al = 16;
1123 else
1124 return -1;
1125
1126 memcpy(param+len, &af, sizeof(af)); len += sizeof(af);
1127 memcpy(param+len, &n, sizeof(n)); len += sizeof(n);
1128 memcpy(param+len, &p, sizeof(p)); len += sizeof(p);
1129 memcpy(param+len, s, al); len += al;
1130 if(d != NULL)
1131 {
1132 memcpy(param+len, d, al);
1133 len += al;
1134 }
1135 memcpy(param+len, &sp, sizeof(sp)); len += sizeof(sp);
1136 memcpy(param+len, &dp, sizeof(dp)); len += sizeof(dp);
1137
1138 return privsep_dotask(SCAMPER_PRIVSEP_IPFW_ADD, len, param);
1139 }
1140
scamper_privsep_ipfw_del(int n,int af)1141 int scamper_privsep_ipfw_del(int n, int af)
1142 {
1143 uint8_t param[(sizeof(int) * 2)];
1144 uint16_t len = 0;
1145 memcpy(param+len, &n, sizeof(int)); len += sizeof(n);
1146 memcpy(param+len, &af, sizeof(int)); len += sizeof(af);
1147 return privsep_dotask(SCAMPER_PRIVSEP_IPFW_DEL, len, param);
1148 }
1149
scamper_privsep_pf_init(const char * anchor)1150 int scamper_privsep_pf_init(const char *anchor)
1151 {
1152 int len = strlen(anchor) + 1;
1153 return privsep_dotask(SCAMPER_PRIVSEP_PF_INIT, len, (const uint8_t *)anchor);
1154 }
1155
scamper_privsep_pf_cleanup(void)1156 int scamper_privsep_pf_cleanup(void)
1157 {
1158 return privsep_dotask(SCAMPER_PRIVSEP_PF_CLEANUP, 0, NULL);
1159 }
1160
scamper_privsep_pf_add(int n,int af,int p,void * s,void * d,int sp,int dp)1161 int scamper_privsep_pf_add(int n,int af,int p,void *s,void *d,int sp,int dp)
1162 {
1163 uint8_t param[(sizeof(int) * 5) + (16 * 2)];
1164 uint16_t len = 0;
1165 uint16_t al;
1166
1167 if(af == AF_INET)
1168 al = 4;
1169 else if(af == AF_INET6)
1170 al = 16;
1171 else
1172 return -1;
1173
1174 memcpy(param+len, &af, sizeof(af)); len += sizeof(af);
1175 memcpy(param+len, &n, sizeof(n)); len += sizeof(n);
1176 memcpy(param+len, &p, sizeof(p)); len += sizeof(p);
1177 memcpy(param+len, s, al); len += al;
1178 memcpy(param+len, d, al); len += al;
1179 memcpy(param+len, &sp, sizeof(sp)); len += sizeof(sp);
1180 memcpy(param+len, &dp, sizeof(dp)); len += sizeof(dp);
1181
1182 return privsep_dotask(SCAMPER_PRIVSEP_PF_ADD, len, param);
1183 }
1184
scamper_privsep_pf_del(int n)1185 int scamper_privsep_pf_del(int n)
1186 {
1187 uint8_t param[sizeof(int)];
1188 memcpy(param, &n, sizeof(int));
1189 return privsep_dotask(SCAMPER_PRIVSEP_PF_DEL, sizeof(int), param);
1190 }
1191
1192 /*
1193 * scamper_privsep
1194 *
1195 * start a child process that has the root priviledges that scamper starts
1196 * with. then, revoke scamper's priviledges to the minimum scamper can
1197 * obtain
1198 */
scamper_privsep_init()1199 int scamper_privsep_init()
1200 {
1201 struct addrinfo hints, *res0;
1202 struct timeval tv;
1203 struct passwd *pw;
1204 struct stat sb;
1205 mode_t mode;
1206 uid_t uid;
1207 gid_t gid;
1208 int sockets[2];
1209 pid_t pid;
1210 int ret;
1211 time_t t;
1212
1213 /* check to see if the PRIVSEP_DIR exists */
1214 if(stat(PRIVSEP_DIR, &sb) == -1)
1215 {
1216 /* if the directory does not exist, try and create it now */
1217 if(errno == ENOENT)
1218 {
1219 /*
1220 * get the uid of the user who will get ownership of the directory.
1221 * by default, this will be root.
1222 */
1223 if((pw = getpwnam(PRIVSEP_DIR_OWNER)) == NULL)
1224 {
1225 printerror(__func__, "could not getpwnam " PRIVSEP_DIR_OWNER);
1226 #if defined(HAVE_ENDPWENT)
1227 endpwent();
1228 #endif
1229 return -1;
1230 }
1231 uid = pw->pw_uid;
1232
1233 #if defined(HAVE_ENDPWENT)
1234 endpwent();
1235 #endif
1236
1237 gid = 0;
1238
1239 /* create the directory as 555 : no one can write to it */
1240 mode = S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
1241 if(mkdir(PRIVSEP_DIR, mode) == -1)
1242 {
1243 printerror(__func__, "could not mkdir " PRIVSEP_DIR);
1244 return -1;
1245 }
1246
1247 /* assign ownership appropriately */
1248 if(chown(PRIVSEP_DIR, uid, gid) == -1)
1249 {
1250 printerror(__func__, "could not chown " PRIVSEP_DIR);
1251 rmdir(PRIVSEP_DIR);
1252 return -1;
1253 }
1254 }
1255 else
1256 {
1257 printerror(__func__, "could not stat " PRIVSEP_DIR);
1258 return -1;
1259 }
1260 }
1261
1262 /*
1263 * open up the unix domain sockets that will allow the prober to talk
1264 * with the priviledged process
1265 */
1266 if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1)
1267 {
1268 printerror(__func__, "could not socketpair");
1269 return -1;
1270 }
1271
1272 lame_fd = sockets[0];
1273 root_fd = sockets[1];
1274
1275 if((cmsgbuf = malloc_zero(CMSG_SPACE(sizeof(int)))) == NULL)
1276 {
1277 printerror(__func__, "could not malloc cmsgbuf");
1278 return -1;
1279 }
1280
1281 if((pid = fork()) == -1)
1282 {
1283 printerror(__func__, "could not fork");
1284 return -1;
1285 }
1286 else if(pid == 0) /* child */
1287 {
1288 /*
1289 * this is the process that will do the root tasks.
1290 * when this function exits, we call exit() on the forked process.
1291 */
1292 ret = privsep_do();
1293 exit(ret);
1294 }
1295
1296 /* set our copy of the root_pid to the relevant process id */
1297 root_pid = pid;
1298
1299 /*
1300 * we don't need our copy of the file descriptor passed to the priviledged
1301 * process any longer
1302 */
1303 close(root_fd);
1304 root_fd = -1;
1305
1306 /*
1307 * get the details for the PRIVSEP_USER login, which the rest of scamper
1308 * will use to get things done
1309 */
1310 if((pw = getpwnam(PRIVSEP_USER)) == NULL)
1311 {
1312 printerror(__func__, "could not getpwnam " PRIVSEP_USER);
1313 return -1;
1314 }
1315 uid = pw->pw_uid;
1316 gid = pw->pw_gid;
1317 memset(pw->pw_passwd, 0, strlen(pw->pw_passwd));
1318
1319 #if defined(HAVE_ENDPWENT)
1320 endpwent();
1321 #endif
1322
1323 /*
1324 * call localtime now, as then the unpriviledged process will have the
1325 * local time zone information cached in the process, so localtime will
1326 * actually mean something
1327 */
1328 gettimeofday_wrap(&tv);
1329 t = tv.tv_sec;
1330 localtime(&t);
1331
1332 /*
1333 * call getaddrinfo now, as then the unpriviledged process will load
1334 * whatever files it needs to to help resolve IP addresses; the need for
1335 * this was first noticed in SunOS
1336 */
1337 memset(&hints, 0, sizeof(struct addrinfo));
1338 hints.ai_socktype = SOCK_DGRAM;
1339 hints.ai_protocol = IPPROTO_UDP;
1340 hints.ai_family = AF_INET;
1341 getaddrinfo("localhost", NULL, &hints, &res0);
1342 freeaddrinfo(res0);
1343
1344 /* change the root directory of the unpriviledged directory */
1345 if(chroot(PRIVSEP_DIR) == -1)
1346 {
1347 printerror(__func__, "could not chroot to " PRIVSEP_DIR);
1348 return -1;
1349 }
1350
1351 /* go into the chroot environment */
1352 if(chdir("/") == -1)
1353 {
1354 printerror(__func__, "could not chdir /");
1355 return -1;
1356 }
1357
1358 /* change the operating group */
1359 if(setgroups(1, &gid) == -1)
1360 {
1361 printerror(__func__, "could not setgroups");
1362 return -1;
1363 }
1364 if(setgid(gid) == -1)
1365 {
1366 printerror(__func__, "could not setgid");
1367 return -1;
1368 }
1369
1370 /* change the operating user */
1371 if(setuid(uid) == -1)
1372 {
1373 printerror(__func__, "could not setuid");
1374 return -1;
1375 }
1376
1377 return lame_fd;
1378 }
1379
scamper_privsep_cleanup()1380 void scamper_privsep_cleanup()
1381 {
1382 privsep_msg_t msg;
1383
1384 if(root_pid != -1)
1385 {
1386 msg.plen = 0;
1387 msg.type = SCAMPER_PRIVSEP_EXIT;
1388
1389 write_wrap(lame_fd, (uint8_t *)&msg, NULL, sizeof(msg));
1390 root_pid = -1;
1391 }
1392
1393 if(lame_fd != -1)
1394 {
1395 close(lame_fd);
1396 lame_fd = -1;
1397 }
1398
1399 if(cmsgbuf != NULL)
1400 {
1401 free(cmsgbuf);
1402 cmsgbuf = NULL;
1403 }
1404
1405 return;
1406 }
1407
1408 #endif /* ifndef WITHOUT_PRIVSEP */
1409