1/* 2 * $Id: privsep-linux.c,v 1.3 2008/01/24 17:08:46 psavola Exp $ 3 * 4 * Authors: 5 * Jim Paris <jim@jtan.com> 6 * Pedro Roque <roque@di.fc.ul.pt> 7 * Lars Fenneberg <lf@elemental.net> 8 * 9 * This software is Copyright 1996,1997,2008 by the above mentioned author(s), 10 * All Rights Reserved. 11 * 12 * The license which is distributed with this software in the file COPYRIGHT 13 * applies to this software. If your distribution is missing this file, you 14 * may request it from <pekkas@netcore.fi>. 15 * 16 */ 17 18#include <config.h> 19#include <includes.h> 20#include <radvd.h> 21#include <pathnames.h> 22 23int privsep_set(const char *iface, const char *var, uint32_t val); 24void privsep_read_loop(void); 25 26/* For reading or writing, depending on process */ 27static int pfd = -1; 28 29/* Command types */ 30enum privsep_type { 31 SET_INTERFACE_LINKMTU, 32 SET_INTERFACE_CURHLIM, 33 SET_INTERFACE_REACHTIME, 34 SET_INTERFACE_RETRANSTIMER, 35}; 36 37/* Command sent over pipe is a fixed size binary structure. */ 38struct privsep_command { 39 int type; 40 char iface[IFNAMSIZ]; 41 uint32_t val; 42}; 43 44/* Privileged read loop */ 45void 46privsep_read_loop(void) 47{ 48 struct privsep_command cmd; 49 int ret; 50 51 while (1) { 52 ret = readn(pfd, &cmd, sizeof(cmd)); 53 if (ret <= 0) { 54 /* Error or EOF, give up */ 55 close(pfd); 56 _exit(0); 57 } 58 if (ret != sizeof(cmd)) { 59 /* Short read, ignore */ 60 continue; 61 } 62 63 cmd.iface[IFNAMSIZ-1] = '\0'; 64 65 switch(cmd.type) { 66 67 case SET_INTERFACE_LINKMTU: 68 if (cmd.val < MIN_AdvLinkMTU || cmd.val > MAX_AdvLinkMTU) { 69 flog(LOG_ERR, "(privsep) %s: LinkMTU (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val); 70 break; 71 } 72 ret = set_interface_var(cmd.iface, PROC_SYS_IP6_LINKMTU, "LinkMTU", cmd.val); 73 break; 74 75 case SET_INTERFACE_CURHLIM: 76 if (cmd.val < MIN_AdvCurHopLimit || cmd.val > MAX_AdvCurHopLimit) { 77 flog(LOG_ERR, "(privsep) %s: CurHopLimit (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val); 78 break; 79 } 80 ret = set_interface_var(cmd.iface, PROC_SYS_IP6_CURHLIM, "CurHopLimit", cmd.val); 81 break; 82 83 case SET_INTERFACE_REACHTIME: 84 if (cmd.val < MIN_AdvReachableTime || cmd.val > MAX_AdvReachableTime) { 85 flog(LOG_ERR, "(privsep) %s: BaseReachableTimer (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val); 86 break; 87 } 88 ret = set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME_MS, "BaseReachableTimer (ms)", cmd.val); 89 if (ret == 0) 90 break; 91 set_interface_var(cmd.iface, PROC_SYS_IP6_BASEREACHTIME, "BaseReachableTimer", cmd.val / 1000); 92 break; 93 94 case SET_INTERFACE_RETRANSTIMER: 95 if (cmd.val < MIN_AdvRetransTimer || cmd.val > MAX_AdvRetransTimer) { 96 flog(LOG_ERR, "(privsep) %s: RetransTimer (%u) is not within the defined bounds, ignoring", cmd.iface, cmd.val); 97 break; 98 } 99 ret = set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER_MS, "RetransTimer (ms)", cmd.val); 100 if (ret == 0) 101 break; 102 set_interface_var(cmd.iface, PROC_SYS_IP6_RETRANSTIMER, "RetransTimer", cmd.val / 1000 * USER_HZ); /* XXX user_hz */ 103 break; 104 105 default: 106 /* Bad command */ 107 break; 108 } 109 } 110} 111 112/* Return 1 if privsep is currently enabled */ 113int 114privsep_enabled(void) 115{ 116 if (pfd < 0) 117 return 0; 118 return 1; 119} 120 121/* Fork to create privileged process connected by a pipe */ 122int 123privsep_init(void) 124{ 125 int pipefds[2]; 126 pid_t pid; 127 128 if (privsep_enabled()) 129 return 0; 130 131 if (pipe(pipefds) != 0) { 132 flog(LOG_ERR, "Couldn't create privsep pipe."); 133 return (-1); 134 } 135 136 pid = fork(); 137 if (pid == -1) { 138 flog(LOG_ERR, "Couldn't fork for privsep."); 139 return (-1); 140 } 141 142 if (pid == 0) { 143 int nullfd; 144 145 /* This will be the privileged child */ 146 close(pipefds[1]); 147 pfd = pipefds[0]; 148 149 /* Detach from stdio */ 150 nullfd = open("/dev/null", O_RDONLY); 151 if (nullfd < 0) { 152 perror("/dev/null"); 153 close(pfd); 154 _exit(1); 155 } 156 dup2(nullfd, 0); 157 dup2(nullfd, 1); 158 /* XXX: we'll keep stderr open in debug mode for better logging */ 159 if (get_debuglevel() == 0) 160 dup2(nullfd, 2); 161 162 privsep_read_loop(); 163 close(pfd); 164 _exit(0); 165 } 166 167 /* Continue execution (will drop privileges soon) */ 168 close(pipefds[0]); 169 pfd = pipefds[1]; 170 171 return 0; 172} 173 174/* Interface calls for the unprivileged process */ 175int 176privsep_interface_linkmtu(const char *iface, uint32_t mtu) 177{ 178 struct privsep_command cmd; 179 cmd.type = SET_INTERFACE_LINKMTU; 180 strncpy(cmd.iface, iface, sizeof(cmd.iface)); 181 cmd.val = mtu; 182 183 if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 184 return (-1); 185 return 0; 186} 187 188int 189privsep_interface_curhlim(const char *iface, uint32_t hlim) 190{ 191 struct privsep_command cmd; 192 cmd.type = SET_INTERFACE_CURHLIM; 193 strncpy(cmd.iface, iface, sizeof(cmd.iface)); 194 cmd.val = hlim; 195 if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 196 return (-1); 197 return 0; 198} 199 200int 201privsep_interface_reachtime(const char *iface, uint32_t rtime) 202{ 203 struct privsep_command cmd; 204 cmd.type = SET_INTERFACE_REACHTIME; 205 strncpy(cmd.iface, iface, sizeof(cmd.iface)); 206 cmd.val = rtime; 207 if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 208 return (-1); 209 return 0; 210} 211 212int 213privsep_interface_retranstimer(const char *iface, uint32_t rettimer) 214{ 215 struct privsep_command cmd; 216 cmd.type = SET_INTERFACE_RETRANSTIMER; 217 strncpy(cmd.iface, iface, sizeof(cmd.iface)); 218 cmd.val = rettimer; 219 if (writen(pfd, &cmd, sizeof(cmd)) != sizeof(cmd)) 220 return (-1); 221 return 0; 222} 223