/* Load layer configurations from disk */ bool buxton_init_layers(void) { bool ret = false; dictionary *ini; char *path; int nlayers = 0; path = DEFAULT_CONFIGURATION_FILE; ini = iniparser_load(path); if (ini == NULL) { buxton_log("Failed to load buxton conf file: %s\n", path); goto finish; } nlayers = iniparser_getnsec(ini); if (nlayers <= 0) { buxton_log("No layers defined in buxton conf file: %s\n", path); goto end; } _layers = hashmap_new(string_hash_func, string_compare_func); if (!_layers) goto end; for (int n = 0; n < nlayers; n++) { BuxtonLayer *layer; char *section_name; layer = malloc0(sizeof(BuxtonLayer)); if (!layer) continue; section_name = iniparser_getsecname(ini, n); if (!section_name) { buxton_log("Failed to find section number: %d\n", n); continue; } if (!parse_layer(ini, section_name, layer)) { free(layer); buxton_log("Failed to load layer: %s\n", section_name); continue; } hashmap_put(_layers, layer->name, layer); } ret = true; end: iniparser_freedict(ini); finish: return ret; }
static BuxtonLayer *buxton_layer_new(ConfigLayer *conf_layer) { BuxtonLayer *out; assert(conf_layer); out= malloc0(sizeof(BuxtonLayer)); if (!out) { abort(); } if (conf_layer->priority < 0) { goto fail; } out->name.value = strdup(conf_layer->name); if (!out->name.value) { abort(); } out->name.length = (uint32_t)strlen(conf_layer->name); if (strcmp(conf_layer->type, "System") == 0) { out->type = LAYER_SYSTEM; } else if (strcmp(conf_layer->type, "User") == 0) { out->type = LAYER_USER; } else { buxton_log("Layer %s has unknown type: %s\n", conf_layer->name, conf_layer->type); goto fail; } if (strcmp(conf_layer->backend, "gdbm") == 0) { out->backend = BACKEND_GDBM; } else if (strcmp(conf_layer->backend, "memory") == 0) { out->backend = BACKEND_MEMORY; } else { buxton_log("Layer %s has unknown database: %s\n", conf_layer->name, conf_layer->backend); goto fail; } if (conf_layer->description != NULL) { out->description = strdup(conf_layer->description); if (!out->description) { abort(); } } out->priority = conf_layer->priority; return out; fail: free(out->name.value); free(out->description); free(out); return NULL; }
END_TEST static Suite * daemon_suite(void) { Suite *s; TCase *tc; s = suite_create("smack"); buxton_cache_smack_rules(); if (buxton_smack_enabled()) { tc = tcase_create("smack access test functions"); tcase_add_checked_fixture(tc, setup, teardown); /* TODO: add tests that use actual client Smack labels */ suite_add_tcase(s, tc); tc = tcase_create("smack libsecurity functions"); tcase_add_test(tc, smack_access_check); suite_add_tcase(s, tc); } else { buxton_log("Smack support not detected; skipping this test suite\n"); } return s; }
int buxton_watch_smack_rules(void) { if (!have_smack) { errno = 0; return -1; } int fd; fd = inotify_init1(IN_NONBLOCK); if (fd < 0) { buxton_log("inotify_init(): %m\n"); return -1; } if (inotify_add_watch(fd, buxton_smack_load_file(), IN_CLOSE_WRITE) < 0) { buxton_log("inotify_add_watch(): %m\n"); return -1; } return fd; }
int buxton_open(BuxtonClient *client) { _BuxtonClient **c = (_BuxtonClient **)client; _BuxtonClient *cl = NULL; int bx_socket, r; struct sockaddr_un remote; size_t sock_name_len; if ((bx_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { return -1; } remote.sun_family = AF_UNIX; sock_name_len = strlen(buxton_socket()) + 1; if (sock_name_len >= sizeof(remote.sun_path)) { buxton_log("Provided socket name: %s is too long, maximum allowed length is %d bytes\n", buxton_socket(), sizeof(remote.sun_path)); return -1; } strncpy(remote.sun_path, buxton_socket(), sock_name_len); r = connect(bx_socket, (struct sockaddr *)&remote, sizeof(remote)); if ( r == -1) { close(bx_socket); return -1; } if (fcntl(bx_socket, F_SETFL, O_NONBLOCK)) { close(bx_socket); return -1; } if (!setup_callbacks()) { close(bx_socket); return -1; } cl = malloc0(sizeof(_BuxtonClient)); if (!cl) { close(bx_socket); return -1; } cl->fd = bx_socket; *c = cl; return bx_socket; }
BuxtonBackend* backend_for_layer(BuxtonLayer *layer) { BuxtonBackend *backend; assert(layer); if (!_databases) _databases = hashmap_new(string_hash_func, string_compare_func); if ((backend = (BuxtonBackend*)hashmap_get(_databases, layer->name)) == NULL) { /* attempt load of backend */ backend = malloc0(sizeof(BuxtonBackend)); if (!backend) return NULL; if (!init_backend(layer, backend)) { buxton_log("backend_for_layer(): failed to initialise backend for layer: %s\n", layer->name); free(backend); return NULL; } hashmap_put(_databases, layer->name, backend); } return (BuxtonBackend*)hashmap_get(_databases, layer->name); }
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 parse_layer(dictionary *ini, char *name, BuxtonLayer *out) { bool ret = false; int r; char *k_desc = NULL; char *k_backend = NULL; char *k_type = NULL; char *k_priority = NULL; char *_desc = NULL; char *_backend = NULL; char *_type = NULL; char *_priority = NULL; assert(ini); assert(name); assert(out); r = asprintf(&k_desc, "%s:description", name); if (r == -1) goto end; r = asprintf(&k_backend, "%s:backend", name); if (r == -1) goto end; r = asprintf(&k_type, "%s:type", name); if (r == -1) goto end; r = asprintf(&k_priority, "%s:priority", name); if (r == -1) goto end; _type = iniparser_getstring(ini, k_type, NULL); _backend = iniparser_getstring(ini, k_backend, NULL); _priority = iniparser_getstring(ini, k_priority, NULL); _desc = iniparser_getstring(ini, k_desc, NULL); if (!_type || !name || !_backend || !_priority) goto end; out->name = strdup(name); if (!out->name) goto fail; if (strcmp(_type, "System") == 0) out->type = LAYER_SYSTEM; else if (strcmp(_type, "User") == 0) out->type = LAYER_USER; else { buxton_log("Layer %s has unknown type: %s\n", name, _type); goto fail; } if (strcmp(_backend, "gdbm") == 0) out->backend = BACKEND_GDBM; else if(strcmp(_backend, "memory") == 0) out->backend = BACKEND_MEMORY; else goto fail; out->priority = strdup(_backend); if (!out->priority) goto fail; if (_desc != NULL) out->description = strdup(_desc); ret = true; goto end; fail: if (out->name) free(out->name); if (out->description) free(out->description); if (out->priority) free(out->priority); end: if (k_desc) free(k_desc); if (k_backend) free(k_backend); if (k_type) free(k_type); if (k_priority) free(k_priority); return ret; }
bool init_backend(BuxtonLayer *layer, BuxtonBackend* backend) { void *handle, *cast; char *path; const char *name; char *error; int r; module_init_func i_func; module_destroy_func d_func; assert(layer); assert(backend); if (layer->backend == BACKEND_GDBM) name = "gdbm"; else if (layer->backend == BACKEND_MEMORY) name = "memory"; else return false; r = asprintf(&path, "%s/%s.so", MODULE_DIRECTORY, name); if (r == -1) return false; /* Load the module */ handle = dlopen(path, RTLD_LAZY); free(path); if (!handle) { buxton_log("dlopen(): %s\n", dlerror()); return false; } dlerror(); cast = dlsym(handle, "buxton_module_init"); if ((error = dlerror()) != NULL || !cast) { buxton_log("dlsym(): %s", error); dlclose(handle); return false; } memcpy(&i_func, &cast, sizeof(i_func)); dlerror(); cast = dlsym(handle, "buxton_module_destroy"); if ((error = dlerror()) != NULL || !cast) { buxton_log("dlsym(): %s", error); dlclose(handle); return false; } memcpy(&d_func, &cast, sizeof(d_func)); i_func(backend); if (backend == NULL) { buxton_log("buxton_module_init returned NULL"); dlclose(handle); return false; } backend->module = handle; backend->destroy = d_func; return true; }
/** * 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; }
static void init_backend(BuxtonConfig *config, BuxtonLayer *layer, BuxtonBackend **backend) { void *handle, *cast; _cleanup_free_ char *path = NULL; const char *name; char *error; int r; bool rb; module_init_func i_func; module_destroy_func d_func; BuxtonBackend *backend_tmp; assert(layer); assert(backend); if (layer->backend == BACKEND_GDBM) { name = "gdbm"; } else if (layer->backend == BACKEND_MEMORY) { name = "memory"; } else { buxton_log("Invalid backend type for layer: %s\n", layer->name); abort(); } backend_tmp = hashmap_get(config->backends, name); if (backend_tmp) { *backend = backend_tmp; return; } backend_tmp = malloc0(sizeof(BuxtonBackend)); if (!backend_tmp) { abort(); } r = asprintf(&path, "%s/%s.so", buxton_module_dir(), name); if (r == -1) { abort(); } /* Load the module */ handle = dlopen(path, RTLD_LAZY); if (!handle) { buxton_log("dlopen(): %s\n", dlerror()); abort(); } dlerror(); cast = dlsym(handle, "buxton_module_init"); if ((error = dlerror()) != NULL || !cast) { buxton_log("dlsym(): %s\n", error); abort(); } memcpy(&i_func, &cast, sizeof(i_func)); dlerror(); cast = dlsym(handle, "buxton_module_destroy"); if ((error = dlerror()) != NULL || !cast) { buxton_log("dlsym(): %s\n", error); abort(); } memcpy(&d_func, &cast, sizeof(d_func)); rb = i_func(backend_tmp); if (!rb) { buxton_log("buxton_module_init failed\n"); abort(); } if (!config->backends) { config->backends = hashmap_new(trivial_hash_func, trivial_compare_func); if (!config->backends) { abort(); } } r = hashmap_put(config->backends, name, backend_tmp); if (r != 1) { abort(); } backend_tmp->module = handle; backend_tmp->destroy = d_func; *backend = backend_tmp; }
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; }