1 /* $OpenBSD: control.c,v 1.7 2011/05/09 11:15:18 reyk Exp $ */ 2 /* $vantronix: control.c,v 1.4 2010/05/14 07:35:52 reyk Exp $ */ 3 4 /* 5 * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> 6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 #include <sys/queue.h> 22 #include <sys/param.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <sys/socket.h> 26 #include <sys/un.h> 27 #include <sys/tree.h> 28 29 #include <net/if.h> 30 31 #include <errno.h> 32 #include <event.h> 33 #include <fcntl.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <signal.h> 38 39 #include "iked.h" 40 41 #define CONTROL_BACKLOG 5 42 43 struct ctl_connlist ctl_conns; 44 45 void 46 control_accept(int, short, void *); 47 struct ctl_conn 48 *control_connbyfd(int); 49 void control_close(int); 50 void control_dispatch_imsg(int, short, void *); 51 void control_imsg_forward(struct imsg *); 52 53 int 54 control_init(struct privsep *ps, struct control_sock *cs) 55 { 56 struct iked *env = ps->ps_env; 57 struct sockaddr_un sun; 58 int fd; 59 mode_t old_umask, mode; 60 61 if (cs->cs_name == NULL) 62 return (0); 63 64 if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 65 log_warn("%s: socket", __func__); 66 return (-1); 67 } 68 69 sun.sun_family = AF_UNIX; 70 if (strlcpy(sun.sun_path, cs->cs_name, 71 sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) { 72 log_warn("%s: %s name too long", __func__, cs->cs_name); 73 close(fd); 74 return (-1); 75 } 76 77 if (unlink(cs->cs_name) == -1) 78 if (errno != ENOENT) { 79 log_warn("%s: unlink %s", __func__, cs->cs_name); 80 close(fd); 81 return (-1); 82 } 83 84 if (cs->cs_restricted) { 85 old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH); 86 mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 87 } else { 88 old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 89 mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP; 90 } 91 92 if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 93 log_warn("%s: bind: %s", __func__, cs->cs_name); 94 close(fd); 95 (void)umask(old_umask); 96 return (-1); 97 } 98 (void)umask(old_umask); 99 100 if (chmod(cs->cs_name, mode) == -1) { 101 log_warn("%s: chmod", __func__); 102 close(fd); 103 (void)unlink(cs->cs_name); 104 return (-1); 105 } 106 107 socket_set_blockmode(fd, BM_NONBLOCK); 108 cs->cs_fd = fd; 109 cs->cs_env = env; 110 111 return (0); 112 } 113 114 int 115 control_listen(struct control_sock *cs) 116 { 117 if (cs->cs_name == NULL) 118 return (0); 119 120 if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) { 121 log_warn("%s: listen", __func__); 122 return (-1); 123 } 124 125 event_set(&cs->cs_ev, cs->cs_fd, EV_READ | EV_PERSIST, 126 control_accept, cs->cs_env); 127 event_add(&cs->cs_ev, NULL); 128 129 return (0); 130 } 131 132 void 133 control_cleanup(struct control_sock *cs) 134 { 135 if (cs->cs_name == NULL) 136 return; 137 (void)unlink(cs->cs_name); 138 } 139 140 /* ARGSUSED */ 141 void 142 control_accept(int listenfd, short event, void *arg) 143 { 144 struct iked *env = arg; 145 int connfd; 146 socklen_t len; 147 struct sockaddr_un sun; 148 struct ctl_conn *c; 149 150 len = sizeof(sun); 151 if ((connfd = accept(listenfd, 152 (struct sockaddr *)&sun, &len)) == -1) { 153 if (errno != EWOULDBLOCK && errno != EINTR) 154 log_warn("%s: accept", __func__); 155 return; 156 } 157 158 socket_set_blockmode(connfd, BM_NONBLOCK); 159 160 if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { 161 log_warn("%s", __func__); 162 close(connfd); 163 return; 164 } 165 166 imsg_init(&c->iev.ibuf, connfd); 167 c->iev.handler = control_dispatch_imsg; 168 c->iev.events = EV_READ; 169 c->iev.data = env; 170 event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, 171 c->iev.handler, c->iev.data); 172 event_add(&c->iev.ev, NULL); 173 174 TAILQ_INSERT_TAIL(&ctl_conns, c, entry); 175 } 176 177 struct ctl_conn * 178 control_connbyfd(int fd) 179 { 180 struct ctl_conn *c; 181 182 for (c = TAILQ_FIRST(&ctl_conns); c != NULL && c->iev.ibuf.fd != fd; 183 c = TAILQ_NEXT(c, entry)) 184 ; /* nothing */ 185 186 return (c); 187 } 188 189 void 190 control_close(int fd) 191 { 192 struct ctl_conn *c; 193 194 if ((c = control_connbyfd(fd)) == NULL) { 195 log_warn("%s: fd %d: not found", __func__, fd); 196 return; 197 } 198 199 msgbuf_clear(&c->iev.ibuf.w); 200 TAILQ_REMOVE(&ctl_conns, c, entry); 201 202 event_del(&c->iev.ev); 203 close(c->iev.ibuf.fd); 204 free(c); 205 } 206 207 /* ARGSUSED */ 208 void 209 control_dispatch_imsg(int fd, short event, void *arg) 210 { 211 struct iked *env = arg; 212 struct ctl_conn *c; 213 struct imsg imsg; 214 int n, v; 215 216 if ((c = control_connbyfd(fd)) == NULL) { 217 log_warn("%s: fd %d: not found", __func__, fd); 218 return; 219 } 220 221 switch (event) { 222 case EV_READ: 223 if ((n = imsg_read(&c->iev.ibuf)) == -1 || n == 0) { 224 control_close(fd); 225 return; 226 } 227 break; 228 case EV_WRITE: 229 if (msgbuf_write(&c->iev.ibuf.w) < 0) { 230 control_close(fd); 231 return; 232 } 233 imsg_event_add(&c->iev); 234 return; 235 default: 236 fatalx("unknown event"); 237 } 238 239 for (;;) { 240 if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { 241 control_close(fd); 242 return; 243 } 244 245 if (n == 0) 246 break; 247 248 control_imsg_forward(&imsg); 249 250 switch (imsg.hdr.type) { 251 case IMSG_CTL_NOTIFY: 252 if (c->flags & CTL_CONN_NOTIFY) { 253 log_debug("%s: " 254 "client requested notify more than once", 255 __func__); 256 imsg_compose_event(&c->iev, IMSG_CTL_FAIL, 257 0, 0, -1, NULL, 0); 258 break; 259 } 260 c->flags |= CTL_CONN_NOTIFY; 261 break; 262 case IMSG_CTL_VERBOSE: 263 IMSG_SIZE_CHECK(&imsg, &v); 264 265 memcpy(&v, imsg.data, sizeof(v)); 266 log_verbose(v); 267 268 proc_forward_imsg(env, &imsg, PROC_PARENT); 269 proc_forward_imsg(env, &imsg, PROC_IKEV2); 270 proc_forward_imsg(env, &imsg, PROC_IKEV1); 271 break; 272 case IMSG_CTL_RELOAD: 273 case IMSG_CTL_RESET: 274 case IMSG_CTL_COUPLE: 275 case IMSG_CTL_DECOUPLE: 276 case IMSG_CTL_ACTIVE: 277 case IMSG_CTL_PASSIVE: 278 proc_forward_imsg(env, &imsg, PROC_PARENT); 279 break; 280 default: 281 log_debug("%s: error handling imsg %d", 282 __func__, imsg.hdr.type); 283 break; 284 } 285 imsg_free(&imsg); 286 } 287 288 imsg_event_add(&c->iev); 289 } 290 291 void 292 control_imsg_forward(struct imsg *imsg) 293 { 294 struct ctl_conn *c; 295 296 TAILQ_FOREACH(c, &ctl_conns, entry) 297 if (c->flags & CTL_CONN_NOTIFY) 298 imsg_compose(&c->iev.ibuf, imsg->hdr.type, 299 0, imsg->hdr.pid, -1, imsg->data, 300 imsg->hdr.len - IMSG_HEADER_SIZE); 301 } 302