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