/* * * Wireless daemon for Linux * * Copyright (C) 2018-2019 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "linux/nl80211.h" #include "src/nl80211util.h" typedef bool (*attr_handler)(const void *data, uint16_t len, void *o); static bool extract_ifindex(const void *data, uint16_t len, void *o) { uint32_t *out = o; if (len != 4) return false; /* ifindex cannot be 0 */ if (l_get_u32(data) == 0) return false; *out = l_get_u32(data); return true; } static bool extract_name(const void *data, uint16_t len, void *o) { const char **out = o; if (len < 1) return false; if (!memchr(data + 1, 0, len - 1)) return false; *out = data; return true; } static bool extract_2_chars(const void *data, uint16_t len, void *o) { char *out = o; const char *in = data; if (len != 3 || in[2] != 0) return false; out[0] = in[0]; out[1] = in[1]; return true; } static bool extract_mac(const void *data, uint16_t len, void *o) { const uint8_t **out = o; if (len != 6) return false; *out = data; return true; } static bool extract_uint64(const void *data, uint16_t len, void *o) { uint64_t *out = o; if (len != 8) return false; *out = l_get_u64(data); return true; } static bool extract_uint32(const void *data, uint16_t len, void *o) { uint32_t *out = o; if (len != 4) return false; *out = l_get_u32(data); return true; } static bool extract_flag(const void *data, uint16_t len, void *o) { if (len != 0) return false; return true; } static attr_handler handler_for_type(enum nl80211_attrs type) { switch (type) { case NL80211_ATTR_IFINDEX: return extract_ifindex; case NL80211_ATTR_WIPHY: case NL80211_ATTR_IFTYPE: return extract_uint32; case NL80211_ATTR_WDEV: case NL80211_ATTR_COOKIE: return extract_uint64; case NL80211_ATTR_IFNAME: case NL80211_ATTR_WIPHY_NAME: return extract_name; case NL80211_ATTR_REG_ALPHA2: return extract_2_chars; case NL80211_ATTR_MAC: return extract_mac; case NL80211_ATTR_ACK: return extract_flag; default: break; } return NULL; } struct attr_entry { uint16_t type; void *data; attr_handler handler; bool present : 1; }; int nl80211_parse_attrs(struct l_genl_msg *msg, int tag, ...) { struct l_genl_attr attr; va_list args; struct l_queue *entries; const struct l_queue_entry *e; struct attr_entry *entry; uint16_t type; uint16_t len; const void *data; int ret; if (!l_genl_attr_init(&attr, msg)) return -EINVAL; va_start(args, tag); entries = l_queue_new(); ret = -ENOSYS; while (tag != NL80211_ATTR_UNSPEC) { entry = l_new(struct attr_entry, 1); entry->type = tag; entry->data = va_arg(args, void *); entry->handler = handler_for_type(tag); l_queue_push_tail(entries, entry); if (!entry->handler) goto done; tag = va_arg(args, enum nl80211_attrs); } va_end(args); while (l_genl_attr_next(&attr, &type, &len, &data)) { for (e = l_queue_get_entries(entries); e; e = e->next) { entry = e->data; if (entry->type == type) break; } if (!e) continue; if (entry->present) { ret = -EALREADY; goto done; } if (!entry->handler(data, len, entry->data)) { ret = -EINVAL; goto done; } entry->present = true; } ret = -ENOENT; for (e = l_queue_get_entries(entries); e; e = e->next) { entry = e->data; if (entry->handler == extract_flag) { *(bool *) entry->data = entry->present; continue; } if (entry->present) continue; goto done; } ret = 0; done: l_queue_destroy(entries, l_free); return ret; } struct l_genl_msg *nl80211_build_new_key_group(uint32_t ifindex, uint32_t cipher, uint8_t key_id, const uint8_t *key, size_t key_len, const uint8_t *ctr, size_t ctr_len, const uint8_t *addr) { struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_NEW_KEY, 512); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); if (addr) l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY); l_genl_msg_append_attr(msg, NL80211_KEY_DATA, key_len, key); l_genl_msg_append_attr(msg, NL80211_KEY_CIPHER, 4, &cipher); l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &key_id); if (ctr) l_genl_msg_append_attr(msg, NL80211_KEY_SEQ, ctr_len, ctr); if (addr) { uint32_t type = NL80211_KEYTYPE_GROUP; l_genl_msg_append_attr(msg, NL80211_KEY_TYPE, 4, &type); l_genl_msg_enter_nested(msg, NL80211_KEY_DEFAULT_TYPES); l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST, 0, NULL); l_genl_msg_leave_nested(msg); } l_genl_msg_leave_nested(msg); return msg; } static struct l_genl_msg *nl80211_build_set_station(uint32_t ifindex, const uint8_t *addr, struct nl80211_sta_flag_update *flags) { struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_SET_STATION, 512); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); l_genl_msg_append_attr(msg, NL80211_ATTR_STA_FLAGS2, sizeof(struct nl80211_sta_flag_update), flags); return msg; } struct l_genl_msg *nl80211_build_set_station_authorized(uint32_t ifindex, const uint8_t *addr) { struct nl80211_sta_flag_update flags = { .mask = (1 << NL80211_STA_FLAG_AUTHORIZED), .set = (1 << NL80211_STA_FLAG_AUTHORIZED), }; return nl80211_build_set_station(ifindex, addr, &flags); } struct l_genl_msg *nl80211_build_set_station_associated(uint32_t ifindex, const uint8_t *addr) { struct nl80211_sta_flag_update flags = { .mask = (1 << NL80211_STA_FLAG_AUTHENTICATED) | (1 << NL80211_STA_FLAG_ASSOCIATED), .set = (1 << NL80211_STA_FLAG_AUTHENTICATED) | (1 << NL80211_STA_FLAG_ASSOCIATED), }; return nl80211_build_set_station(ifindex, addr, &flags); } struct l_genl_msg *nl80211_build_set_station_unauthorized(uint32_t ifindex, const uint8_t *addr) { struct nl80211_sta_flag_update flags = { .mask = (1 << NL80211_STA_FLAG_AUTHORIZED), .set = 0, }; return nl80211_build_set_station(ifindex, addr, &flags); } struct l_genl_msg *nl80211_build_set_key(uint32_t ifindex, uint8_t key_index) { struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_SET_KEY, 128); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY); l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &key_index); l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT, 0, NULL); l_genl_msg_enter_nested(msg, NL80211_KEY_DEFAULT_TYPES); l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST, 0, NULL); l_genl_msg_leave_nested(msg); l_genl_msg_leave_nested(msg); return msg; } struct l_genl_msg *nl80211_build_get_key(uint32_t ifindex, uint8_t key_index) { struct l_genl_msg *msg; msg = l_genl_msg_new_sized(NL80211_CMD_GET_KEY, 128); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_append_attr(msg, NL80211_ATTR_KEY_IDX, 1, &key_index); return msg; } const void *nl80211_parse_get_key_seq(struct l_genl_msg *msg) { struct l_genl_attr attr, nested; uint16_t type, len; const void *data; if (l_genl_msg_get_error(msg) < 0 || !l_genl_attr_init(&attr, msg)) { l_error("GET_KEY failed for the GTK: %i", l_genl_msg_get_error(msg)); return NULL; } while (l_genl_attr_next(&attr, &type, &len, &data)) { if (type != NL80211_ATTR_KEY) continue; break; } if (type != NL80211_ATTR_KEY || !l_genl_attr_recurse(&attr, &nested)) { l_error("Can't recurse into ATTR_KEY in GET_KEY reply"); return NULL; } while (l_genl_attr_next(&nested, &type, &len, &data)) { if (type != NL80211_KEY_SEQ) continue; break; } if (type != NL80211_KEY_SEQ) { l_error("KEY_SEQ not returned in GET_KEY reply"); return NULL; } if (len != 6) { l_error("KEY_SEQ length != 6 in GET_KEY reply"); return NULL; } return data; } struct l_genl_msg *nl80211_build_cmd_frame(uint32_t ifindex, const uint8_t *addr, const uint8_t *to, uint32_t freq, struct iovec *iov, size_t iov_len) { struct l_genl_msg *msg; struct iovec iovs[iov_len + 1]; const uint16_t frame_type = 0x00d0; uint8_t action_frame[24]; memset(action_frame, 0, 24); l_put_le16(frame_type, action_frame + 0); memcpy(action_frame + 4, to, 6); memcpy(action_frame + 10, addr, 6); memcpy(action_frame + 16, to, 6); iovs[0].iov_base = action_frame; iovs[0].iov_len = sizeof(action_frame); memcpy(iovs + 1, iov, sizeof(*iov) * iov_len); msg = l_genl_msg_new_sized(NL80211_CMD_FRAME, 128 + 512); l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex); l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, 4, &freq); l_genl_msg_append_attrv(msg, NL80211_ATTR_FRAME, iovs, iov_len + 1); return msg; }