From decfe44433363002a0ae645b02cf740654828b6e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Dec 2021 11:23:37 +0100 Subject: [PATCH] sec-label: Add class to represent security labels In accordance with SELinux, we include the null-terminator in the encoding for now. --- src/libstrongswan/Android.mk | 3 +- src/libstrongswan/Makefile.am | 6 +- src/libstrongswan/selectors/sec_label.c | 197 ++++++++++++++++++++++++ src/libstrongswan/selectors/sec_label.h | 125 +++++++++++++++ 4 files changed, 328 insertions(+), 3 deletions(-) create mode 100644 src/libstrongswan/selectors/sec_label.c create mode 100644 src/libstrongswan/selectors/sec_label.h diff --git a/src/libstrongswan/Android.mk b/src/libstrongswan/Android.mk index 660382da2e..c1001905cc 100644 --- a/src/libstrongswan/Android.mk +++ b/src/libstrongswan/Android.mk @@ -41,7 +41,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ -selectors/traffic_selector.c settings/settings.c settings/settings_types.c \ +selectors/sec_label.c selectors/traffic_selector.c \ +settings/settings.c settings/settings_types.c \ settings/settings_parser.c settings/settings_lexer.c utils/cpu_feature.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 5d230822b5..37fe4b1e51 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -39,7 +39,8 @@ networking/streams/stream_tcp.c networking/streams/stream_service_tcp.c \ pen/pen.c plugins/plugin_loader.c plugins/plugin_feature.c processing/jobs/job.c \ processing/jobs/callback_job.c processing/processor.c processing/scheduler.c \ processing/watcher.c resolver/resolver_manager.c resolver/rr_set.c \ -selectors/traffic_selector.c settings/settings.c settings/settings_types.c \ +selectors/sec_label.c selectors/traffic_selector.c \ +settings/settings.c settings/settings_types.c \ settings/settings_parser.y settings/settings_lexer.l utils/cpu_feature.c \ utils/utils.c utils/chunk.c utils/debug.c utils/enum.c utils/identification.c \ utils/lexparser.c utils/optionsfrom.c utils/capabilities.c utils/backtrace.c \ @@ -112,7 +113,8 @@ resolver/resolver.h resolver/resolver_response.h resolver/rr_set.h \ resolver/rr.h resolver/resolver_manager.h \ plugins/plugin_loader.h plugins/plugin.h plugins/plugin_feature.h \ processing/jobs/job.h processing/jobs/callback_job.h processing/processor.h \ -processing/scheduler.h processing/watcher.h selectors/traffic_selector.h \ +processing/scheduler.h processing/watcher.h \ +selectors/sec_label.h selectors/traffic_selector.h \ settings/settings.h settings/settings_parser.h threading/thread_value.h \ threading/thread.h threading/windows/thread.h \ threading/mutex.h threading/condvar.h threading/spinlock.h threading/semaphore.h \ diff --git a/src/libstrongswan/selectors/sec_label.c b/src/libstrongswan/selectors/sec_label.c new file mode 100644 index 0000000000..1b85942e9e --- /dev/null +++ b/src/libstrongswan/selectors/sec_label.c @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2021 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define _GNU_SOURCE +#include +#ifdef USE_SELINUX +#include +#endif + +#include "sec_label.h" + +typedef struct private_sec_label_t private_sec_label_t; + +/** + * Private data. + */ +struct private_sec_label_t { + + /** + * Public interface + */ + sec_label_t public; + + /** + * Encoded label value + */ + chunk_t encoding; + + /** + * String representation of the label + */ + char *str; +}; + +static sec_label_t *create_sec_label(chunk_t encoding, char *str); + +METHOD(sec_label_t, get_encoding, chunk_t, + private_sec_label_t *this) +{ + return this->encoding; +} + +METHOD(sec_label_t, get_string, char*, + private_sec_label_t *this) +{ + return this->str; +} + +METHOD(sec_label_t, clone_, sec_label_t*, + private_sec_label_t *this) +{ + return create_sec_label(chunk_clone(this->encoding), strdup(this->str)); +} + +METHOD(sec_label_t, equals, bool, + private_sec_label_t *this, sec_label_t *other_pub) +{ + private_sec_label_t *other = (private_sec_label_t*)other_pub; + + if (!other_pub) + { + return FALSE; + } + return chunk_equals_const(this->encoding, other->encoding); +} + +METHOD(sec_label_t, matches, bool, + private_sec_label_t *this, sec_label_t *other_pub) +{ + if (!other_pub) + { + return FALSE; + } +#ifdef USE_SELINUX + if (is_selinux_enabled()) + { /* if disabled, the following matches anything against anything */ + private_sec_label_t *other = (private_sec_label_t*)other_pub; + return selinux_check_access(other->str, this->str, "association", + "polmatch", NULL) == 0; + } +#endif + return equals(this, other_pub); +} + +METHOD(sec_label_t, hash, u_int, + private_sec_label_t *this, u_int inc) +{ + return chunk_hash_inc(this->encoding, inc); +} + +METHOD(sec_label_t, destroy, void, + private_sec_label_t *this) +{ + chunk_free(&this->encoding); + free(this->str); + free(this); +} + +/** + * Internal constructor, data is adopted + */ +static sec_label_t *create_sec_label(chunk_t encoding, char *str) +{ + private_sec_label_t *this; + + INIT(this, + .public = { + .get_encoding = _get_encoding, + .get_string = _get_string, + .clone = _clone_, + .matches = _matches, + .equals = _equals, + .hash = _hash, + .destroy = _destroy, + }, + .encoding = encoding, + .str = str, + ); + return &this->public; +} + +/* + * Described in header + */ +sec_label_t *sec_label_from_encoding(const chunk_t value) +{ + chunk_t cloned, sanitized = chunk_empty; + char *str; + + if (!value.len || (value.len == 1 && !value.ptr[0])) + { + DBG1(DBG_LIB, "invalid empty security label"); + return NULL; + } + else if (value.ptr[value.len-1]) + { + DBG1(DBG_LIB, "adding null-terminator to security label"); + cloned = chunk_cat("cc", value, chunk_from_chars(0x00)); + } + else + { + cloned = chunk_clone(value); + } + + /* create a sanitized version while ignoring the null-terminator */ + if (!chunk_printable(chunk_create(cloned.ptr, cloned.len-1), &sanitized, '?')) + { +#ifdef USE_SELINUX + /* don't accept labels with non-printable characters if we use SELinux */ + DBG1(DBG_LIB, "invalid security label with non-printable characters %B", + &value); + chunk_free(&sanitized); + chunk_free(&cloned); + return NULL; +#endif + } + if (asprintf(&str, "%.*s", (int)sanitized.len, sanitized.ptr) <= 0) + { + chunk_free(&sanitized); + chunk_free(&cloned); + return NULL; + } + chunk_free(&sanitized); + + return create_sec_label(cloned, str); +} + +/* + * Described in header + */ +sec_label_t *sec_label_from_string(const char *value) +{ + if (!value) + { + return NULL; + } + return sec_label_from_encoding(chunk_create((char*)value, strlen(value)+1)); +} diff --git a/src/libstrongswan/selectors/sec_label.h b/src/libstrongswan/selectors/sec_label.h new file mode 100644 index 0000000000..55feec439d --- /dev/null +++ b/src/libstrongswan/selectors/sec_label.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2021 Tobias Brunner, codelabs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * @defgroup sec_label sec_label + * @{ @ingroup ipsec + */ + +#ifndef SEC_LABEL_H_ +#define SEC_LABEL_H_ + +typedef struct sec_label_t sec_label_t; + +#include + +/** + * Representation of a security label used on policies/SAs. + * + * For example, with SELinux this could be a value like + * system_u:object_r:ipsec_spd_t:s0. + */ +struct sec_label_t { + + /** + * Return a binary encoding of the security label as used for IKE. + * + * @return binary encoding (internal data) + */ + chunk_t (*get_encoding)(sec_label_t *this); + + /** + * Return a string representation of this security label. + * + * @return string representation (internal data) + */ + char *(*get_string)(sec_label_t *this); + + /** + * Clone this security label. + * + * @return clone of it + */ + sec_label_t *(*clone)(sec_label_t *this); + + /** + * Match two security labels. + * + * For SELinux this checks if this security label permits other in terms + * of association { polmatch }. + * + * @param other security label to match against this + * @return TRUE if matching, FALSE otherwise + */ + bool (*matches)(sec_label_t *this, sec_label_t *other); + + /** + * Compare two security labels for equality. + * + * @param other security label to compare with this + * @return TRUE if equal, FALSE otherwise + */ + bool (*equals)(sec_label_t *this, sec_label_t *other); + + /** + * Create a hash value for the security label. + * + * @param inc optional value for incremental hashing + * @return calculated hash value for the security label + */ + u_int (*hash)(sec_label_t *this, u_int inc); + + /** + * Destroys the object. + */ + void (*destroy)(sec_label_t *this); +}; + +/** + * Try to parse a sec_label_t from the given binary encoding. + * + * @param value encoding to parse + * @return security label instance, NULL if invalid + */ +sec_label_t *sec_label_from_encoding(const chunk_t value); + +/** + * Try to parse a sec_label_t from the given string. + * + * @param value string to parse + * @return security label instance, NULL if invalid + */ +sec_label_t *sec_label_from_string(const char *value); + +/** + * Compare two security labels for equality, accept if both are NULL. + * + * @param a first label + * @param b second label + * @return TRUE if labels are equal or both NULL + */ +static inline bool sec_labels_equal(sec_label_t *a, sec_label_t *b) +{ + return (!a && !b) || (a && a->equals(a, b)); +} + +#endif /** SEC_LABEL_H_ @}*/