1/* 2 * WPA Supplicant - Layer2 packet handling with privilege separation 3 * Copyright (c) 2007, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15#include "includes.h" 16#include <sys/un.h> 17 18#include "common.h" 19#include "eloop.h" 20#include "l2_packet.h" 21#include "privsep_commands.h" 22 23 24struct l2_packet_data { 25 int fd; /* UNIX domain socket for privsep access */ 26 void (*rx_callback)(void *ctx, const u8 *src_addr, 27 const u8 *buf, size_t len); 28 void *rx_callback_ctx; 29 u8 own_addr[ETH_ALEN]; 30 char *own_socket_path; 31 struct sockaddr_un priv_addr; 32}; 33 34 35static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd, 36 const void *data, size_t data_len) 37{ 38 struct msghdr msg; 39 struct iovec io[2]; 40 41 io[0].iov_base = &cmd; 42 io[0].iov_len = sizeof(cmd); 43 io[1].iov_base = (u8 *) data; 44 io[1].iov_len = data_len; 45 46 os_memset(&msg, 0, sizeof(msg)); 47 msg.msg_iov = io; 48 msg.msg_iovlen = data ? 2 : 1; 49 msg.msg_name = &l2->priv_addr; 50 msg.msg_namelen = sizeof(l2->priv_addr); 51 52 if (sendmsg(l2->fd, &msg, 0) < 0) { 53 perror("L2: sendmsg(cmd)"); 54 return -1; 55 } 56 57 return 0; 58} 59 60 61int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) 62{ 63 os_memcpy(addr, l2->own_addr, ETH_ALEN); 64 return 0; 65} 66 67 68int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, 69 const u8 *buf, size_t len) 70{ 71 struct msghdr msg; 72 struct iovec io[4]; 73 int cmd = PRIVSEP_CMD_L2_SEND; 74 75 io[0].iov_base = &cmd; 76 io[0].iov_len = sizeof(cmd); 77 io[1].iov_base = &dst_addr; 78 io[1].iov_len = ETH_ALEN; 79 io[2].iov_base = &proto; 80 io[2].iov_len = 2; 81 io[3].iov_base = (u8 *) buf; 82 io[3].iov_len = len; 83 84 os_memset(&msg, 0, sizeof(msg)); 85 msg.msg_iov = io; 86 msg.msg_iovlen = 4; 87 msg.msg_name = &l2->priv_addr; 88 msg.msg_namelen = sizeof(l2->priv_addr); 89 90 if (sendmsg(l2->fd, &msg, 0) < 0) { 91 perror("L2: sendmsg(packet_send)"); 92 return -1; 93 } 94 95 return 0; 96} 97 98 99static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) 100{ 101 struct l2_packet_data *l2 = eloop_ctx; 102 u8 buf[2300]; 103 int res; 104 struct sockaddr_un from; 105 socklen_t fromlen = sizeof(from); 106 107 os_memset(&from, 0, sizeof(from)); 108 res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, 109 &fromlen); 110 if (res < 0) { 111 perror("l2_packet_receive - recvfrom"); 112 return; 113 } 114 if (res < ETH_ALEN) { 115 wpa_printf(MSG_DEBUG, "L2: Too show packet received"); 116 return; 117 } 118 119 if (from.sun_family != AF_UNIX || 120 os_strncmp(from.sun_path, l2->priv_addr.sun_path, 121 sizeof(from.sun_path)) != 0) { 122 wpa_printf(MSG_DEBUG, "L2: Received message from unexpected " 123 "source"); 124 return; 125 } 126 127 l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN, 128 res - ETH_ALEN); 129} 130 131 132struct l2_packet_data * l2_packet_init( 133 const char *ifname, const u8 *own_addr, unsigned short protocol, 134 void (*rx_callback)(void *ctx, const u8 *src_addr, 135 const u8 *buf, size_t len), 136 void *rx_callback_ctx, int l2_hdr) 137{ 138 struct l2_packet_data *l2; 139 char *own_dir = "/tmp"; 140 char *priv_dir = "/var/run/wpa_priv"; 141 size_t len; 142 static unsigned int counter = 0; 143 struct sockaddr_un addr; 144 fd_set rfds; 145 struct timeval tv; 146 int res; 147 u8 reply[ETH_ALEN + 1]; 148 int reg_cmd[2]; 149 150 l2 = os_zalloc(sizeof(struct l2_packet_data)); 151 if (l2 == NULL) 152 return NULL; 153 l2->rx_callback = rx_callback; 154 l2->rx_callback_ctx = rx_callback_ctx; 155 156 len = os_strlen(own_dir) + 50; 157 l2->own_socket_path = os_malloc(len); 158 if (l2->own_socket_path == NULL) { 159 os_free(l2); 160 return NULL; 161 } 162 os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d", 163 own_dir, getpid(), counter++); 164 165 l2->priv_addr.sun_family = AF_UNIX; 166 os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path), 167 "%s/%s", priv_dir, ifname); 168 169 l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0); 170 if (l2->fd < 0) { 171 perror("socket(PF_UNIX)"); 172 os_free(l2->own_socket_path); 173 l2->own_socket_path = NULL; 174 os_free(l2); 175 return NULL; 176 } 177 178 os_memset(&addr, 0, sizeof(addr)); 179 addr.sun_family = AF_UNIX; 180 os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path)); 181 if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 182 perror("bind(PF_UNIX)"); 183 goto fail; 184 } 185 186 reg_cmd[0] = protocol; 187 reg_cmd[1] = l2_hdr; 188 if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd)) 189 < 0) { 190 wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv"); 191 goto fail; 192 } 193 194 FD_ZERO(&rfds); 195 FD_SET(l2->fd, &rfds); 196 tv.tv_sec = 5; 197 tv.tv_usec = 0; 198 res = select(l2->fd + 1, &rfds, NULL, NULL, &tv); 199 if (res < 0 && errno != EINTR) { 200 perror("select"); 201 goto fail; 202 } 203 204 if (FD_ISSET(l2->fd, &rfds)) { 205 res = recv(l2->fd, reply, sizeof(reply), 0); 206 if (res < 0) { 207 perror("recv"); 208 goto fail; 209 } 210 } else { 211 wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for " 212 "registration reply"); 213 goto fail; 214 } 215 216 if (res != ETH_ALEN) { 217 wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply " 218 "(len=%d)", res); 219 } 220 os_memcpy(l2->own_addr, reply, ETH_ALEN); 221 222 eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); 223 224 return l2; 225 226fail: 227 close(l2->fd); 228 l2->fd = -1; 229 unlink(l2->own_socket_path); 230 os_free(l2->own_socket_path); 231 l2->own_socket_path = NULL; 232 os_free(l2); 233 return NULL; 234} 235 236 237void l2_packet_deinit(struct l2_packet_data *l2) 238{ 239 if (l2 == NULL) 240 return; 241 242 if (l2->fd >= 0) { 243 wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0); 244 eloop_unregister_read_sock(l2->fd); 245 close(l2->fd); 246 } 247 248 if (l2->own_socket_path) { 249 unlink(l2->own_socket_path); 250 os_free(l2->own_socket_path); 251 } 252 253 os_free(l2); 254} 255 256 257int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) 258{ 259 /* TODO */ 260 return -1; 261} 262 263 264void l2_packet_notify_auth_start(struct l2_packet_data *l2) 265{ 266 wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0); 267} 268