/* Initialization of group */ void sbuxton_set_group(char *group, char *layer) { if (!_client_connection()) { errno = ENOTCONN; return; } saved_errno = errno; int status = 0; /* strcpy the name of the layer and group*/ strncpy(_layer, layer, MAX_LG_LEN-1); strncpy(_group, group, MAX_LG_LEN-1); /* In case a string is longer than MAX_LG_LEN, set the last byte to null */ _layer[MAX_LG_LEN -1] = '\0'; _group[MAX_LG_LEN -1] = '\0'; BuxtonKey g = buxton_key_create(_group, NULL, _layer, BUXTON_TYPE_STRING); buxton_debug("buxton key group = %s\n", buxton_key_get_group(g)); if (buxton_create_group(client, g, _cg_cb, &status, true) || !status) { buxton_debug("Create group call failed.\n"); errno = EBADMSG; } else { buxton_debug("Switched to group: %s, layer: %s.\n", buxton_key_get_group(g), buxton_key_get_layer(g)); errno = saved_errno; } _client_disconnect(); }
int32_t sbuxton_get_int32(char *key) { /* make sure client connection is open */ if (!_client_connection()) { errno = ENOTCONN; return -1; } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, BUXTON_TYPE_INT32); /* return value */ vstatus ret; ret.type = BUXTON_TYPE_INT32; saved_errno = errno; /* get value */ if (buxton_get_value(client, _key, _bg_cb, &ret, true)) { buxton_debug("Get int32_t call failed.\n"); } if (!ret.status) { errno = EACCES; } else { errno = saved_errno; } _client_disconnect(); return ret.val.i32val; }
/* Set and get int32_t value for buxton key with type BUXTON_TYPE_INT32 */ void sbuxton_set_int32(char *key, int32_t value) { /* make sure client connection is open */ if (!_client_connection()) { errno = ENOTCONN; return; } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, BUXTON_TYPE_INT32); /* return value and status */ vstatus ret; ret.type = BUXTON_TYPE_INT32; ret.val.i32val = value; saved_errno = errno; /* call buxton_set_value for type BUXTON_TYPE_INT32 */ if (buxton_set_value(client, _key, &value, _bs_cb, &ret, true)) { buxton_debug("Set int32_t call failed.\n"); return; } if (!ret.status) { errno = EACCES; } else { errno = saved_errno; } _client_disconnect(); }
/* Set and get char * value for buxton key with type BUXTON_TYPE_STRING */ void sbuxton_set_string(char *key, char *value ) { /* make sure client connection is open */ if (!_client_connection()) { errno = ENOTCONN; return; } /* create key */ BuxtonKey _key = buxton_key_create(_group, key, _layer, BUXTON_TYPE_STRING); /* return value and status */ vstatus ret; ret.type = BUXTON_TYPE_STRING; ret.val.sval = value; saved_errno = errno; /* set value */ if (buxton_set_value(client, _key, value, _bs_cb, &ret, true)) { buxton_debug("Set string call failed.\n"); } if (!ret.status) { errno = EACCES; } else { errno = saved_errno; } _client_disconnect(); }
/* Remove group given its name and layer */ void sbuxton_remove_group(char *group_name, char *layer) { /* make sure client connection is open */ if (!_client_connection()) { errno = ENOTCONN; return; } saved_errno = errno; BuxtonKey group = _buxton_group_create(group_name, layer); int status; if (buxton_remove_group(client, group, _rg_cb, &status, true)) { buxton_debug("Remove group call failed.\n"); } if (!status) { errno = EACCES; } else { errno = saved_errno; } _client_disconnect(); }
bool buxton_cache_smack_rules(void) { smack_check(); FILE *load_file = NULL; char *rule_pair = NULL; int ret = true; bool have_rules = false; struct stat buf; if (_smackrules) { hashmap_free(_smackrules); } _smackrules = hashmap_new(string_hash_func, string_compare_func); if (!_smackrules) { abort(); } //FIXME: should check for a proper mount point instead if ((stat(SMACK_MOUNT_DIR, &buf) == -1) || !S_ISDIR(buf.st_mode)) { buxton_log("Smack filesystem not detected; disabling Smack checks\n"); have_smack = false; goto end; } load_file = fopen(buxton_smack_load_file(), "r"); if (!load_file) { switch (errno) { case ENOENT: buxton_log("Smackfs load2 file not found; disabling Smack checks\n"); have_smack = false; goto end; default: buxton_log("fopen(): %m\n"); ret = false; goto end; } } do { int r; int chars; BuxtonKeyAccessType *accesstype; char subject[SMACK_LABEL_LEN+1] = { 0, }; char object[SMACK_LABEL_LEN+1] = { 0, }; char access[ACC_LEN] = { 0, }; /* read contents from load2 */ chars = fscanf(load_file, "%s %s %s\n", subject, object, access); if (ferror(load_file)) { buxton_log("fscanf(): %m\n"); ret = false; goto end; } if (!have_rules && chars == EOF && feof(load_file)) { buxton_debug("No loaded Smack rules found\n"); goto end; } if (chars != 3) { buxton_log("Corrupt load file detected\n"); ret = false; goto end; } have_rules = true; r = asprintf(&rule_pair, "%s %s", subject, object); if (r == -1) { abort(); } accesstype = malloc0(sizeof(BuxtonKeyAccessType)); if (!accesstype) { abort(); } *accesstype = ACCESS_NONE; if (strchr(access, 'r')) { *accesstype |= ACCESS_READ; } if (strchr(access, 'w')) { *accesstype |= ACCESS_WRITE; } hashmap_put(_smackrules, rule_pair, accesstype); } while (!feof(load_file)); end: if (load_file) { fclose(load_file); } return ret; }
bool buxton_check_smack_access(BuxtonString *subject, BuxtonString *object, BuxtonKeyAccessType request) { smack_check(); _cleanup_free_ char *key = NULL; int r; BuxtonKeyAccessType *access; assert(subject); assert(object); assert((request == ACCESS_READ) || (request == ACCESS_WRITE)); assert(_smackrules); buxton_debug("Subject: %s\n", subject->value); buxton_debug("Object: %s\n", object->value); /* check the builtin Smack rules first */ if (streq(subject->value, "*")) { return false; } if (streq(object->value, "@") || streq(subject->value, "@")) { return true; } if (streq(object->value, "*")) { return true; } if (streq(subject->value, object->value)) { return true; } if (request == ACCESS_READ) { if (streq(object->value, "_")) { return true; } if (streq(subject->value, "^")) { return true; } } /* finally, check the loaded rules */ r = asprintf(&key, "%s %s", subject->value, object->value); if (r == -1) { abort(); } buxton_debug("Key: %s\n", key); access = hashmap_get(_smackrules, key); if (!access) { /* A null value is not an error, since clients may try to * read/write keys with labels that are not in the loaded * rule set. In this situation, access is simply denied, * because there are no further rules to consider. */ buxton_debug("Value of key '%s' is NULL\n", key); return false; } /* After debugging, change this code to: */ /* return ((*access) & request); */ if (access) { buxton_debug("Value: %x\n", *access); } if (request == ACCESS_READ && (*access) & request) { buxton_debug("Read access granted!\n"); return true; } if (request == ACCESS_WRITE && ((*access) & ACCESS_READ && (*access) & ACCESS_WRITE)) { buxton_debug("Write access granted!\n"); return true; } buxton_debug("Access denied!\n"); return false; }
/** * Entry point into bt-daemon * @param argc Number of arguments passed * @param argv An array of string arguments * @returns EXIT_SUCCESS if the operation succeeded, otherwise EXIT_FAILURE */ int main(int argc, char *argv[]) { int fd; int smackfd = -1; socklen_t addr_len; struct sockaddr_un remote; int descriptors; int ret; bool manual_start = false; struct sigaction sa; if (!buxton_cache_smack_rules()) exit(EXIT_FAILURE); smackfd = buxton_watch_smack_rules(); if (smackfd < 0 && errno) exit(EXIT_FAILURE); self.nfds_alloc = 0; self.accepting_alloc = 0; self.nfds = 0; self.buxton.client.direct = true; self.buxton.client.uid = geteuid(); if (!buxton_direct_open(&self.buxton)) exit(EXIT_FAILURE); sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sa.sa_handler = my_handler; ret = sigaction(SIGINT, &sa, NULL); if (ret == -1) exit(EXIT_FAILURE); ret = sigaction(SIGTERM, &sa, NULL); if (ret == -1) exit(EXIT_FAILURE); /* For client notifications */ self.notify_mapping = hashmap_new(string_hash_func, string_compare_func); /* Store a list of connected clients */ LIST_HEAD_INIT(client_list_item, self.client_list); descriptors = sd_listen_fds(0); if (descriptors < 0) { buxton_log("sd_listen_fds: %m\n"); exit(EXIT_FAILURE); } else if (descriptors == 0) { /* Manual invocation */ manual_start = true; union { struct sockaddr sa; struct sockaddr_un un; } sa; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { buxton_log("socket(): %m\n"); exit(EXIT_FAILURE); } memset(&sa, 0, sizeof(sa)); sa.un.sun_family = AF_UNIX; strncpy(sa.un.sun_path, BUXTON_SOCKET, sizeof(sa.un.sun_path) - 1); sa.un.sun_path[sizeof(sa.un.sun_path)-1] = 0; ret = unlink(sa.un.sun_path); if (ret == -1 && errno != ENOENT) { exit(EXIT_FAILURE); } if (bind(fd, &sa.sa, sizeof(sa)) < 0) { buxton_log("bind(): %m\n"); exit(EXIT_FAILURE); } chmod(sa.un.sun_path, 0666); if (listen(fd, SOMAXCONN) < 0) { buxton_log("listen(): %m\n"); exit(EXIT_FAILURE); } add_pollfd(&self, fd, POLLIN | POLLPRI, true); } else { /* systemd socket activation */ for (fd = SD_LISTEN_FDS_START + 0; fd < SD_LISTEN_FDS_START + descriptors; fd++) { if (sd_is_fifo(fd, NULL)) { add_pollfd(&self, fd, POLLIN, false); buxton_debug("Added fd %d type FIFO\n", fd); } else if (sd_is_socket_unix(fd, SOCK_STREAM, -1, BUXTON_SOCKET, 0)) { add_pollfd(&self, fd, POLLIN | POLLPRI, true); buxton_debug("Added fd %d type UNIX\n", fd); } else if (sd_is_socket(fd, AF_UNSPEC, 0, -1)) { add_pollfd(&self, fd, POLLIN | POLLPRI, true); buxton_debug("Added fd %d type SOCKET\n", fd); } } } if (smackfd >= 0) { /* add Smack rule fd to pollfds */ add_pollfd(&self, smackfd, POLLIN | POLLPRI, false); } buxton_log("%s: Started\n", argv[0]); /* Enter loop to accept clients */ for (;;) { ret = poll(self.pollfds, self.nfds, -1); if (ret < 0) { buxton_log("poll(): %m\n"); if (errno == EINTR) { if (do_shutdown) break; else continue; } break; } if (ret == 0) continue; for (nfds_t i=0; i<self.nfds; i++) { client_list_item *cl = NULL; char discard[256]; if (self.pollfds[i].revents == 0) continue; if (self.pollfds[i].fd == -1) { /* TODO: Remove client from list */ buxton_debug("Removing / Closing client for fd %d\n", self.pollfds[i].fd); del_pollfd(&self, i); continue; } if (smackfd >= 0) { if (self.pollfds[i].fd == smackfd) { if (!buxton_cache_smack_rules()) exit(EXIT_FAILURE); buxton_log("Reloaded Smack access rules\n"); /* discard inotify data itself */ while (read(smackfd, &discard, 256) == 256); continue; } } if (self.accepting[i] == true) { struct timeval tv; int fd; int on = 1; addr_len = sizeof(remote); if ((fd = accept(self.pollfds[i].fd, (struct sockaddr *)&remote, &addr_len)) == -1) { buxton_log("accept(): %m\n"); break; } buxton_debug("New client fd %d connected through fd %d\n", fd, self.pollfds[i].fd); cl = malloc0(sizeof(client_list_item)); if (!cl) exit(EXIT_FAILURE); LIST_INIT(client_list_item, item, cl); cl->fd = fd; cl->cred = (struct ucred) {0, 0, 0}; LIST_PREPEND(client_list_item, item, self.client_list, cl); /* poll for data on this new client as well */ add_pollfd(&self, cl->fd, POLLIN | POLLPRI, false); /* Mark our packets as high prio */ if (setsockopt(cl->fd, SOL_SOCKET, SO_PRIORITY, &on, sizeof(on)) == -1) buxton_log("setsockopt(SO_PRIORITY): %m\n"); /* Set socket recv timeout */ tv.tv_sec = SOCKET_TIMEOUT; tv.tv_usec = 0; if (setsockopt(cl->fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(struct timeval)) == -1) buxton_log("setsockopt(SO_RCVTIMEO): %m\n"); /* check if this is optimal or not */ break; } assert(self.accepting[i] == 0); if (smackfd >= 0) assert(self.pollfds[i].fd != smackfd); /* handle data on any connection */ /* TODO: Replace with hash table lookup */ LIST_FOREACH(item, cl, self.client_list) if (self.pollfds[i].fd == cl->fd) break; assert(cl); handle_client(&self, cl, i); } } buxton_log("%s: Closing all connections\n", argv[0]); if (manual_start) unlink(BUXTON_SOCKET); for (int i = 0; i < self.nfds; i++) { close(self.pollfds[i].fd); } for (client_list_item *i = self.client_list; i;) { client_list_item *j = i->item_next; free(i); i = j; } hashmap_free(self.notify_mapping); buxton_direct_close(&self.buxton); return EXIT_SUCCESS; }
ssize_t buxton_deserialize_message(uint8_t *data, BuxtonControlMessage *r_message, size_t size, uint32_t *r_msgid, BuxtonData **list) { size_t offset = 0; ssize_t ret = -1; size_t min_length = BUXTON_MESSAGE_HEADER_LENGTH; uint16_t control, message; size_t n_params, c_param, c_length; BuxtonDataType c_type = 0; BuxtonData *k_list = NULL; BuxtonData c_data; uint32_t msgid; assert(data); assert(r_message); assert(list); buxton_debug("Deserializing message...\n"); buxton_debug("size=%lu\n", size); if (size < min_length) { errno = EINVAL; goto end; } /* Copy the control code */ control = *(uint16_t*)data; offset += sizeof(uint16_t); /* Check this is a valid buxton message */ if (control != BUXTON_CONTROL_CODE) { errno = EINVAL; goto end; } /* Obtain the control message */ message = *(BuxtonControlMessage*)(data+offset); offset += sizeof(uint16_t); /* Ensure control message is in valid range */ if (message <= BUXTON_CONTROL_MIN || message >= BUXTON_CONTROL_MAX) { errno = EINVAL; goto end; } /* Skip size since our caller got this already */ offset += sizeof(uint32_t); /* Obtain the message id */ msgid = *(uint32_t*)(data+offset); offset += sizeof(uint32_t); /* Obtain number of parameters */ n_params = *(uint32_t*)(data+offset); offset += sizeof(uint32_t); buxton_debug("total params: %d\n", n_params); if (n_params > BUXTON_MESSAGE_MAX_PARAMS) { errno = EINVAL; goto end; } k_list = malloc0(sizeof(BuxtonData)*n_params); if (n_params && !k_list) { errno = ENOMEM; goto end; } memzero(&c_data, sizeof(BuxtonData)); for (c_param = 0; c_param < n_params; c_param++) { buxton_debug("param: %d\n", c_param + 1); buxton_debug("offset=%lu\n", offset); /* Don't read past the end of the buffer */ if (offset + sizeof(uint16_t) + sizeof(uint32_t) > size) { errno = EINVAL; goto end; } /* Now unpack type */ memcpy(&c_type, data+offset, sizeof(uint16_t)); offset += sizeof(uint16_t); if (c_type >= BUXTON_TYPE_MAX || c_type <= BUXTON_TYPE_MIN) { errno = EINVAL; goto end; } /* Retrieve the length of the value */ c_length = *(uint32_t*)(data+offset); if (c_length == 0 && c_type != BUXTON_TYPE_STRING) { errno = EINVAL; goto end; } offset += sizeof(uint32_t); buxton_debug("value length: %lu\n", c_length); /* Don't try to read past the end of our buffer */ if (offset + c_length > size) { errno = EINVAL; goto end; } switch (c_type) { case BUXTON_TYPE_STRING: if (c_length) { c_data.store.d_string.value = malloc(c_length); if (!c_data.store.d_string.value) { errno = ENOMEM; goto end; } memcpy(c_data.store.d_string.value, data+offset, c_length); c_data.store.d_string.length = (uint32_t)c_length; if (c_data.store.d_string.value[c_length-1] != 0x00) { errno = EINVAL; buxton_debug("buxton_deserialize_message(): Garbage message\n"); free(c_data.store.d_string.value); goto end; } } else { c_data.store.d_string.value = NULL; c_data.store.d_string.length = 0; } break; case BUXTON_TYPE_INT32: c_data.store.d_int32 = *(int32_t*)(data+offset); break; case BUXTON_TYPE_UINT32: c_data.store.d_uint32 = *(uint32_t*)(data+offset); break; case BUXTON_TYPE_INT64: c_data.store.d_int64 = *(int64_t*)(data+offset); break; case BUXTON_TYPE_UINT64: c_data.store.d_uint64 = *(uint64_t*)(data+offset); break; case BUXTON_TYPE_FLOAT: c_data.store.d_float = *(float*)(data+offset); break; case BUXTON_TYPE_DOUBLE: memcpy(&c_data.store.d_double, data + offset, sizeof(double)); break; case BUXTON_TYPE_BOOLEAN: c_data.store.d_boolean = *(bool*)(data+offset); break; default: errno = EINVAL; goto end; } c_data.type = c_type; k_list[c_param] = c_data; memzero(&c_data, sizeof(BuxtonData)); offset += c_length; } *r_message = message; *r_msgid = msgid; if (n_params == 0) { *list = NULL; free(k_list); k_list = NULL; } else { *list = k_list; } ret = (ssize_t)n_params; end: if (ret <= 0) { free(k_list); } buxton_debug("Deserializing returned:%i\n", ret); return ret; }
size_t buxton_serialize_message(uint8_t **dest, BuxtonControlMessage message, uint32_t msgid, BuxtonArray *list) { uint16_t i = 0; uint8_t *data = NULL; size_t ret = 0; size_t offset = 0; size_t size = 0; size_t curSize = 0; uint16_t control, msg; assert(dest); assert(list); buxton_debug("Serializing message...\n"); if (list->len > BUXTON_MESSAGE_MAX_PARAMS) { errno = EINVAL; return ret; } if (message >= BUXTON_CONTROL_MAX || message < BUXTON_CONTROL_SET) { errno = EINVAL; return ret; } /* * initial size = * control code + control message (uint16_t * 2) + * message size (uint32_t) + * message id (uint32_t) + * param count (uint32_t) */ data = malloc0(sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t)); if (!data) { errno = ENOMEM; goto end; } control = BUXTON_CONTROL_CODE; memcpy(data, &control, sizeof(uint16_t)); offset += sizeof(uint16_t); msg = (uint16_t)message; memcpy(data+offset, &msg, sizeof(uint16_t)); offset += sizeof(uint16_t); /* Save room for final size */ offset += sizeof(uint32_t); memcpy(data+offset, &msgid, sizeof(uint32_t)); offset += sizeof(uint32_t); /* Now write the parameter count */ memcpy(data+offset, &(list->len), sizeof(uint32_t)); offset += sizeof(uint32_t); size = offset; /* Deal with parameters */ BuxtonData *param; size_t p_length = 0; for (i=0; i < list->len; i++) { param = buxton_array_get(list, i); if (!param) { errno = EINVAL; goto fail; } switch (param->type) { case BUXTON_TYPE_STRING: p_length = param->store.d_string.length; break; case BUXTON_TYPE_INT32: p_length = sizeof(int32_t); break; case BUXTON_TYPE_UINT32: p_length = sizeof(uint32_t); break; case BUXTON_TYPE_INT64: p_length = sizeof(int64_t); break; case BUXTON_TYPE_UINT64: p_length = sizeof(uint64_t); break; case BUXTON_TYPE_FLOAT: p_length = sizeof(float); break; case BUXTON_TYPE_DOUBLE: p_length = sizeof(double); break; case BUXTON_TYPE_BOOLEAN: p_length = sizeof(bool); break; default: errno = EINVAL; buxton_log("Invalid parameter type %lu\n", param->type); goto fail; }; buxton_debug("offset: %lu\n", offset); buxton_debug("value length: %lu\n", p_length); /* Need to allocate enough room to hold this data */ size += sizeof(uint16_t) + sizeof(uint32_t) + p_length; if (curSize < size) { if (!(data = greedy_realloc((void**)&data, &curSize, size))) { errno = ENOMEM; goto fail; } memzero(data+offset, size - offset); } /* Copy data type */ memcpy(data+offset, &(param->type), sizeof(uint16_t)); offset += sizeof(uint16_t); /* Write out the length of value */ memcpy(data+offset, &p_length, sizeof(uint32_t)); offset += sizeof(uint32_t); switch (param->type) { case BUXTON_TYPE_STRING: memcpy(data+offset, param->store.d_string.value, p_length); break; case BUXTON_TYPE_INT32: memcpy(data+offset, &(param->store.d_int32), sizeof(int32_t)); break; case BUXTON_TYPE_UINT32: memcpy(data+offset, &(param->store.d_uint32), sizeof(uint32_t)); break; case BUXTON_TYPE_INT64: memcpy(data+offset, &(param->store.d_int64), sizeof(int64_t)); break; case BUXTON_TYPE_UINT64: memcpy(data+offset, &(param->store.d_uint64), sizeof(uint64_t)); break; case BUXTON_TYPE_FLOAT: memcpy(data+offset, &(param->store.d_float), sizeof(float)); break; case BUXTON_TYPE_DOUBLE: memcpy(data+offset, &(param->store.d_double), sizeof(double)); break; case BUXTON_TYPE_BOOLEAN: memcpy(data+offset, &(param->store.d_boolean), sizeof(bool)); break; default: /* already tested this above, can't get here * normally */ assert(0); }; offset += p_length; p_length = 0; } memcpy(data+BUXTON_LENGTH_OFFSET, &offset, sizeof(uint32_t)); ret = offset; *dest = data; fail: /* Clean up */ if (ret == 0) { free(data); } end: buxton_debug("Serializing returned:%lu\n", ret); return ret; }
void buxton_deserialize(uint8_t *source, BuxtonData *target, BuxtonString *label) { size_t offset = 0; size_t length = 0; BuxtonDataType type; assert(source); assert(target); assert(label); /* Retrieve the BuxtonDataType */ type = *(BuxtonDataType*)source; offset += sizeof(BuxtonDataType); /* Retrieve the length of the label */ label->length = *(uint32_t*)(source+offset); offset += sizeof(uint32_t); /* Retrieve the length of the value */ length = *(uint32_t*)(source+offset); offset += sizeof(uint32_t); /* Retrieve the label */ label->value = malloc(label->length); if (label->length > 0 && !label->value) { abort(); } memcpy(label->value, source+offset, label->length); offset += label->length; switch (type) { case BUXTON_TYPE_STRING: /* User must free the string */ target->store.d_string.value = malloc(length); if (!target->store.d_string.value) { abort(); } memcpy(target->store.d_string.value, source+offset, length); target->store.d_string.length = (uint32_t)length; break; case BUXTON_TYPE_INT32: target->store.d_int32 = *(int32_t*)(source+offset); break; case BUXTON_TYPE_UINT32: target->store.d_uint32 = *(uint32_t*)(source+offset); break; case BUXTON_TYPE_INT64: target->store.d_int64 = *(int64_t*)(source+offset); break; case BUXTON_TYPE_UINT64: target->store.d_uint64 = *(uint64_t*)(source+offset); break; case BUXTON_TYPE_FLOAT: target->store.d_float = *(float*)(source+offset); break; case BUXTON_TYPE_DOUBLE: memcpy(&target->store.d_double, source + offset, sizeof(double)); break; case BUXTON_TYPE_BOOLEAN: target->store.d_boolean = *(bool*)(source+offset); break; default: buxton_debug("Invalid BuxtonDataType: %lu\n", type); abort(); } /* Successful */ target->type = type; }