From 96aaf1f7f21dc81396eb1c78e6f0fca661916cc6 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Sat, 17 Aug 2024 17:43:05 +0200 Subject: [PATCH 01/18] feat: Initial Kerberos integration proposal This commit introduces the initial proposal for integrating Kerberos authentication across: - openvas - scannerctl - openvasd The goal is to provide a common header and implementation that can be shared among these components. The proposed common header will define the following NASL functions: 1. `krb5_authenticate(realm, kdc, user, password)` - All parameters are optional, with `realm`, `user`, and `password` ideally set via OSP/HTTP credentials. However, for testing purposes, these parameters can be provided programmatically. - Returns an identifier for the internally stored element or a negative value on failure. 2. `krb5_end_time(connection)` - Returns the `end_time` of the given connection. While this is typically handled internally, it is exposed for debugging purposes. 3. `krb5_start_time(connection)` - Returns the `start_time` of the given connection, primarily for debugging purposes. 4. `krb5_renew_till(connection)` - Returns the `renew_till` value of the given connection, also for debugging purposes. 5. `krb5_create_request(connection, data)` - Creates a Kerberos request based on the connection and provided data. Returns the request data or `NULL` on error. 6. `krb5_error(connection)` - Returns `NULL` if there is no error, otherwise an error string. This is useful for debugging and forces the underlying implementation to track connection errors. This proposal is the foundation for implementing consistent Kerberos authentication across various components while offering debugging and testing flexibility. --- keberos/.gitignore | 2 + keberos/authenticate_test.c | 68 ++++++++ keberos/openvas-krb5.c | 327 ++++++++++++++++++++++++++++++++++++ keberos/openvas-krb5.h | 92 ++++++++++ 4 files changed, 489 insertions(+) create mode 100644 keberos/.gitignore create mode 100644 keberos/authenticate_test.c create mode 100644 keberos/openvas-krb5.c create mode 100644 keberos/openvas-krb5.h diff --git a/keberos/.gitignore b/keberos/.gitignore new file mode 100644 index 000000000..eb201f9bb --- /dev/null +++ b/keberos/.gitignore @@ -0,0 +1,2 @@ +a.out +test.sh diff --git a/keberos/authenticate_test.c b/keberos/authenticate_test.c new file mode 100644 index 000000000..26c2e570d --- /dev/null +++ b/keberos/authenticate_test.c @@ -0,0 +1,68 @@ +#include "openvas-krb5.h" + +#include +#include +#include + +#define REALM "GBKERB.LOCAL" + +#define GUARD_ENV_SET(var, env) \ + do \ + { \ + if ((var = getenv (env)) == NULL) \ + { \ + fprintf (stderr, env " is not set\n"); \ + return 1; \ + } \ + } \ + while (0) +int +main () +{ + char *kdc = NULL; + OKrb5ErrorCode result = O_KRB5_SUCCESS; + OKrb5Credential credentials; + OKrb5Element *element = NULL; + OKrb5Data *data = NULL; + GUARD_ENV_SET (credentials.config_path, "KRB5_CONFIG"); + GUARD_ENV_SET (credentials.realm, "KRB5_REALM"); + GUARD_ENV_SET (credentials.user, "KRB5_USER"); + GUARD_ENV_SET (credentials.password, "KRB5_PASSWORD"); + + if (o_krb5_find_kdc (&credentials, &kdc)) + { + GUARD_ENV_SET (kdc, "KRB5_KDC"); + if (o_krb5_add_realm (&credentials, kdc)) + { + fprintf (stderr, "Unable to add kdc\n"); + return 1; + } + } + else + { + printf ("Using kdc: %s\n", kdc); + free (kdc); + } + if ((result = o_krb5_authenticate (credentials, &element))) + { + fprintf (stderr, "Error: %d: %s\n", result, + krb5_get_error_message (element->ctx, result - O_KRB5_ERROR)); + return result; + } + + printf ("Authentication Token:\n"); + printf ("--------------------\n"); + printf ("End time: %d\n", element->creds.times.endtime); + printf ("start time: %d\n", element->creds.times.starttime); + printf ("Renew till: %d\n", element->creds.times.renew_till); + if ((result = o_krb5_request (element, "test", 5, &data))) + { + fprintf (stderr, "unable to create request: %d", result); + } + if ((result = o_krb5_free_data (element, data))) + { + fprintf (stderr, "unable to free request: %d", result); + } + + return 0; +} diff --git a/keberos/openvas-krb5.c b/keberos/openvas-krb5.c new file mode 100644 index 000000000..25f4fc3d4 --- /dev/null +++ b/keberos/openvas-krb5.c @@ -0,0 +1,327 @@ +#include "openvas-krb5.h" + +#include +#include +#include +#include +#include + +#define GUARD_NULL(var, return_var) \ + do \ + { \ + if (var != NULL) \ + { \ + return_var = O_KRB5_EXPECTED_NULL; \ + goto result; \ + } \ + } \ + while (0) + +#define GUARD_NOT_NULL(var, return_var) \ + do \ + { \ + if (var == NULL) \ + { \ + return_var = O_KRB5_EXPECTED_NOT_NULL; \ + goto result; \ + } \ + } \ + while (0) + +#define ALLOCATE_AND_CHECK(var, type, n, return_var) \ + do \ + { \ + var = (type *) calloc (n, sizeof (type)); \ + if (var == NULL) \ + { \ + return_var = O_KRB5_NOMEM; \ + goto result; \ + } \ + } \ + while (0) + +#define SKIP_WS(line, line_len, start, i) \ + do \ + { \ + for (i = start; i < line_len; i++) \ + { \ + if (line[i] != ' ' && line[i] != '\t') \ + { \ + break; \ + } \ + } \ + } \ + while (0) + +#define IS_STR_EQUAL(line, line_len, start, cmp, cmp_len) \ + ((line_len - start < cmp_len) ? 0 \ + : (line_len == 0 && cmp_len == 0) \ + ? 1 \ + : (memcmp (line + start, cmp, cmp_len) == 0)) + +#define MAX_LINE_LENGTH 1024 +// Finds the kdc defined for the given realm. +OKrb5ErrorCode +o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc) +{ + OKrb5ErrorCode result = O_KRB5_REALM_NOT_FOUND; + char line[MAX_LINE_LENGTH]; + int state = 0; + int last_element; + int i, j; + FILE *file; + + int realm_len = strlen (creds->realm); + + // we don't know if we should free it or just override it. + // aborting instead. + GUARD_NULL (*kdc, result); + if ((file = fopen (creds->config_path, "r")) == NULL) + { + result = O_KRB5_CONF_NOT_FOUND; + goto result; + } + + while (fgets (line, MAX_LINE_LENGTH, file)) + { + line[strcspn (line, "\n")] = 0; + last_element = strlen (line) - 1; + SKIP_WS (line, last_element, 0, i); + if (line[i] == '[' && line[last_element] == ']') + { + if (state != 0) + { + result = O_KRB5_REALM_NOT_FOUND; + goto result; + } + if (IS_STR_EQUAL (line, last_element + 1, i, "[realms]", 8) == 1) + { + state = 1; + } + } + else + { + if (line[i] == '}' || line[last_element] == '}') + { + state = 1; + } + else if (state == 1) + { + for (j = i; j <= last_element; j++) + { + if (line[j] != creds->realm[j - i]) + { + state = 2; + break; + } + if (j - i >= realm_len) + { + break; + } + } + if (j - i == realm_len) + { + state = 3; + } + } + else if (state == 3) + { + if (IS_STR_EQUAL (line, last_element + 1, i, "kdc", 3)) + { + SKIP_WS (line, last_element, i + 3, i); + if (line[i] == '=') + { + SKIP_WS (line, last_element, i + 1, i); + ALLOCATE_AND_CHECK (*kdc, char, (last_element - i) + 1, + result); + for (j = i; j <= last_element; j++) + { + (*kdc)[j - i] = line[j]; + } + + result = O_KRB5_SUCCESS; + goto result; + } + } + } + } + } + +result: + if (result != O_KRB5_CONF_NOT_FOUND) + { + fclose (file); + } + return result; +} +// Adds realm with the given kdc into krb5.conf +OKrb5ErrorCode +o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + FILE *file = NULL, *tmp = NULL; + char line[MAX_LINE_LENGTH] = {0}; + char tmpfn[MAX_LINE_LENGTH] = {0}; + int state, i; + // TODO: validate output of fprintf when writing and abort + + if ((file = fopen (creds->config_path, "r")) == NULL) + { + if ((file = fopen (creds->config_path, "w")) == NULL) + { + result = O_KRB5_CONF_NOT_CREATED; + goto result; + } + fprintf (file, "[realms]\n%s = {\n kdc = %s\n}\n", creds->realm, kdc); + goto close_config; + } + snprintf (tmpfn, MAX_LINE_LENGTH, "%s.tmp", creds->config_path); + if ((tmp = fopen (tmpfn, "w")) == NULL) + { + result = O_KRB5_TMP_CONF_NOT_CREATED; + goto close_config; + } + state = 0; + while (fgets (line, MAX_LINE_LENGTH, file)) + { + fputs (line, tmp); + if (state == 0) + { + SKIP_WS (line, MAX_LINE_LENGTH, 0, i); + if (IS_STR_EQUAL (line, MAX_LINE_LENGTH, i, "[realms]", 8) == 1) + { + fprintf (tmp, "%s = {\n kdc = %s\n}\n", creds->realm, kdc); + state = 1; + } + } + } + + if (rename (tmpfn, creds->config_path) != 0) + { + result = O_KRB5_TMP_CONF_NOT_MOVED; + } + +close_tmp: + fclose (tmp); +close_config: + fclose (file); +result: + return result; +} + +OKrb5ErrorCode +o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + // probably better to use heap instead? + krb5_context ctx; + krb5_principal me; + krb5_creds creds; + + GUARD_NULL (*element, result); + if ((result = krb5_init_context (&ctx))) + { + result = result + O_KRB5_ERROR; + goto result; + } + + if ((result = + krb5_build_principal (ctx, &me, strlen (credentials.realm), + credentials.realm, credentials.user, NULL))) + { + result = result + O_KRB5_ERROR; + goto result; + }; + + if ((result = krb5_get_init_creds_password ( + ctx, &creds, me, credentials.password, NULL, NULL, 0, NULL, NULL))) + { + result = result + O_KRB5_ERROR; + goto result; + } + ALLOCATE_AND_CHECK (*element, OKrb5Element, 1, result); + (*element)->me = me; + (*element)->creds = creds; + (*element)->ctx = ctx; + +result: + if (result != O_KRB5_SUCCESS) + { + krb5_free_cred_contents (ctx, &creds); + krb5_free_principal (ctx, me); + krb5_free_context (ctx); + if (*element != NULL) + { + free (*element); + *element = NULL; + } + } + + return result; +} + +OKrb5ErrorCode +o_krb5_free_element (OKrb5Element *element) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + if (element != NULL) + { + krb5_free_cred_contents (element->ctx, &element->creds); + krb5_free_principal (element->ctx, element->me); + krb5_free_context (element->ctx); + free (element); + } + return result; +} + +OKrb5ErrorCode +o_krb5_request (const OKrb5Element *element, const char *data, + const size_t data_len, OKrb5Data **out) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + GUARD_NOT_NULL (out, result); + GUARD_NULL (*out, result); + GUARD_NOT_NULL (element, result); + ALLOCATE_AND_CHECK (*out, OKrb5Data, 1, result); + krb5_data in_data; + int ap_req_options = 0; + + in_data.length = data_len; + in_data.data = (char *) data; + + if ((result = krb5_auth_con_init (element->ctx, &(*out)->auth_context))) + { + result = result + O_KRB5_ERROR; + goto result; + }; + + if ((result = krb5_mk_req_extended ( + element->ctx, &(*out)->auth_context, ap_req_options, &in_data, + (krb5_creds *) &(element->creds), &(*out)->data))) + { + result = result + O_KRB5_ERROR; + }; +result: + return result; +} + +OKrb5ErrorCode +o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + GUARD_NOT_NULL (element, result); + + if (data != NULL) + { + if ((result = krb5_auth_con_free (element->ctx, data->auth_context))) + { + result += O_KRB5_ERROR; + goto result; + }; + free (data); + } +result: + return result; +} diff --git a/keberos/openvas-krb5.h b/keberos/openvas-krb5.h new file mode 100644 index 000000000..88c927bca --- /dev/null +++ b/keberos/openvas-krb5.h @@ -0,0 +1,92 @@ +#include +#ifndef OPENVAS_KRB5 +#define OPENVAS_KRB5 1 +#include +// Enables or disables the cache implementation. +// +// When using the cached functions it will store each credential in a memory +// list and refresh a ticket when required or reauthenticate depending on the +// requirements in the background. +#define OPENVAS_KRB5_CACHED 1 + +typedef enum +{ + O_KRB5_SUCCESS, + // Is returned when the krb5.conf was not found + O_KRB5_CONF_NOT_FOUND, + O_KRB5_CONF_NOT_CREATED, + O_KRB5_TMP_CONF_NOT_CREATED, + O_KRB5_TMP_CONF_NOT_MOVED, + O_KRB5_REALM_NOT_FOUND, + O_KRB5_EXPECTED_NULL, + O_KRB5_EXPECTED_NOT_NULL, + // can only happen when GFP_ATOMIC is set on the kernel. + O_KRB5_NOMEM, + + // Is an transitive error code to indicate an error originating from the + // underlying krb5 implementation. It must be last and can not check by equals + // operation as each krb5 error return will be added with that number + // representation, + O_KRB5_ERROR, +} OKrb5ErrorCode; + +typedef struct +{ + krb5_context ctx; + krb5_principal me; + krb5_creds creds; +} OKrb5Element; + +typedef struct +{ + const char *config_path; + const char *realm; + const char *user; + const char *password; +} OKrb5Credential; + +typedef struct +{ + krb5_data data; + krb5_auth_context auth_context; +} OKrb5Data; + +// Finds the kdc defined for the given realm. +OKrb5ErrorCode +o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc); +// Adds realm with the given kdc into krb5.conf +OKrb5ErrorCode +o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc); + +OKrb5ErrorCode +o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element); + +OKrb5ErrorCode +o_krb5_request (const OKrb5Element *element, const char *data, + const size_t data_len, OKrb5Data **out); + +OKrb5ErrorCode +o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data); + +OKrb5ErrorCode +o_krb5_free_element (OKrb5Element *element); + +#if OPENVAS_KRB5_CACHED == 1 + +typedef struct +{ + OKrb5Credential credentials; + OKrb5Element element; + OKrb5ErrorCode last_error_code; +} OKrb5CacheElement; + +typedef struct +{ + size_t cap; + size_t len; + OKrb5CacheElement **elements; +} OKrb5CacheList; + +#endif + +#endif From 2a02eaf58cededf41fcb30060d22f1bce9ef5837 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Sun, 18 Aug 2024 06:38:31 +0200 Subject: [PATCH 02/18] Implement caching For easier handling a cache implementation is added. This allows us to reuse tickets when they are available. --- keberos/.gitignore | 1 + keberos/authenticate_test.c | 24 ++++- keberos/openvas-krb5.c | 172 +++++++++++++++++++++++++++++++++++- keberos/openvas-krb5.h | 12 ++- 4 files changed, 202 insertions(+), 7 deletions(-) diff --git a/keberos/.gitignore b/keberos/.gitignore index eb201f9bb..bb1351e23 100644 --- a/keberos/.gitignore +++ b/keberos/.gitignore @@ -1,2 +1,3 @@ a.out test.sh +krb5.conf diff --git a/keberos/authenticate_test.c b/keberos/authenticate_test.c index 26c2e570d..e86baa1f8 100644 --- a/keberos/authenticate_test.c +++ b/keberos/authenticate_test.c @@ -28,7 +28,6 @@ main () GUARD_ENV_SET (credentials.realm, "KRB5_REALM"); GUARD_ENV_SET (credentials.user, "KRB5_USER"); GUARD_ENV_SET (credentials.password, "KRB5_PASSWORD"); - if (o_krb5_find_kdc (&credentials, &kdc)) { GUARD_ENV_SET (kdc, "KRB5_KDC"); @@ -43,6 +42,9 @@ main () printf ("Using kdc: %s\n", kdc); free (kdc); } + +#if OPENVAS_KRB5_CACHED != 1 + if ((result = o_krb5_authenticate (credentials, &element))) { fprintf (stderr, "Error: %d: %s\n", result, @@ -65,4 +67,24 @@ main () } return 0; +#else + + if ((result = o_krb5_cache_request (credentials, "test", 5, &data))) + { + fprintf (stderr, "unable to create request: %d\n", result); + return 1; + } + element = o_krb5_cache_find(&credentials)->element; + if (element == NULL) + { + fprintf (stderr, "element not found: %d", result); + return 1; + } + printf ("Authentication Token:\n"); + printf ("--------------------\n"); + printf ("End time: %d\n", element->creds.times.endtime); + printf ("start time: %d\n", element->creds.times.starttime); + printf ("Renew till: %d\n", element->creds.times.renew_till); + o_krb5_cache_clear(); +#endif } diff --git a/keberos/openvas-krb5.c b/keberos/openvas-krb5.c index 25f4fc3d4..64747600f 100644 --- a/keberos/openvas-krb5.c +++ b/keberos/openvas-krb5.c @@ -5,6 +5,7 @@ #include #include #include +#include #define GUARD_NULL(var, return_var) \ do \ @@ -261,11 +262,9 @@ o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element) return result; } -OKrb5ErrorCode +void o_krb5_free_element (OKrb5Element *element) { - OKrb5ErrorCode result = O_KRB5_SUCCESS; - if (element != NULL) { krb5_free_cred_contents (element->ctx, &element->creds); @@ -273,7 +272,6 @@ o_krb5_free_element (OKrb5Element *element) krb5_free_context (element->ctx); free (element); } - return result; } OKrb5ErrorCode @@ -325,3 +323,169 @@ o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data) result: return result; } + +#if OPENVAS_KRB5_CACHED == 1 +// we use FNV-1a to generate the id so that we don't need to introduce artifical +// numbers but can just reuse credentials to find connections that way we can +// simply reconnect when we either don't find an entry or when the ticket is +// invalid witout having the caller to remember artifical identifier. +unsigned long +o_krb5_cach_credential_id (const OKrb5Credential *cred) +{ + unsigned long hash = 2166136261; + unsigned int prime = 16777219; + + for (const char *str = cred->realm; *str; str++) + { + hash = (hash ^ *str) * prime; + } + for (const char *str = cred->user; *str; str++) + { + hash = (hash ^ *str) * prime; + } + for (const char *str = cred->password; *str; str++) + { + hash = (hash ^ *str) * prime; + } + return hash; +} + +static OKrb5CacheList *element_cache = NULL; + +OKrb5ErrorCode +o_krb5_init_cache (void) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + GUARD_NULL (element_cache, result); + ALLOCATE_AND_CHECK (element_cache, OKrb5CacheList, 1, result); + element_cache->cap = 2; + ALLOCATE_AND_CHECK (element_cache->elements, OKrb5CacheElement *, + element_cache->cap, result); +result: + return result; +} + +OKrb5ErrorCode +o_krb5_clear_cache (void) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + if (element_cache == NULL) + goto result; + int i; + for (i = 0; i < element_cache->len; i++) + { + o_krb5_free_element ((element_cache->elements[i])->element); + free (element_cache->elements[i]); + } + free (element_cache); + element_cache = NULL; + +result: + return result; +} + +OKrb5CacheElement * +o_krb5_find_element (const OKrb5Credential *cred) +{ + if (element_cache == NULL) + { + return NULL; + } + int i; + unsigned long id = o_krb5_cach_credential_id (cred); + + for (i = 0; i < element_cache->len; i++) + { + if (element_cache->elements[i]->id == id) + { + return element_cache->elements[i]; + } + } + return NULL; +} + + +OKrb5ErrorCode +o_krb5_cache_add_element (const OKrb5Credential credentials, + OKrb5CacheElement **out) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + OKrb5CacheElement **new_elements; + if (element_cache->len == element_cache->cap) + { + if ((new_elements = + realloc (element_cache->elements, + element_cache->cap * 2 * sizeof (OKrb5CacheElement *))) + == NULL) + { + result = O_KRB5_NOMEM; + goto result; + } + memset (new_elements + element_cache->cap, 0, + element_cache->cap * sizeof (OKrb5CacheElement *)); + element_cache->cap = element_cache->cap * 2; + element_cache->elements = new_elements; + } + + ALLOCATE_AND_CHECK (*out, OKrb5CacheElement, 1, result); + (*out)->credentials = credentials; + (*out)->id = o_krb5_cach_credential_id (&credentials); + element_cache->elements[element_cache->len] = *out; + + if ((result = o_krb5_authenticate (credentials, &(*out)->element))) + { + (*out)->last_error_code = result; + goto result; + } + element_cache->len += 1; + +result: + return result; +} + +OKrb5ErrorCode +o_krb5_cached_request (const OKrb5Credential credentials, const char *data, + const size_t data_len, OKrb5Data **out) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + if (element_cache == NULL) + o_krb5_init_cache (); + OKrb5CacheElement *element = o_krb5_find_element (&credentials); + + // TODO: check if ticket is valid, if not valid try to get new one without + // reauthenticate (till_new) or free and reauthenticate + if (element == NULL) + { + if ((result = o_krb5_cache_add_element (credentials, &element))) + { + goto result; + } + } + else + { + time_t systime; + systime = time (NULL); + if (systime >= element->element->creds.times.endtime) + { + // TODO: add renew when till is lower than systime + o_krb5_free_element (element->element); + element->element = NULL; + if ((result = o_krb5_authenticate (credentials, &element->element))) + { + element->last_error_code = result; + goto result; + } + } + } + if ((result = o_krb5_request (element->element, data, data_len, out))) + { + goto result; + } + +result: + return result; +} + +#endif diff --git a/keberos/openvas-krb5.h b/keberos/openvas-krb5.h index 88c927bca..dfcc0341e 100644 --- a/keberos/openvas-krb5.h +++ b/keberos/openvas-krb5.h @@ -68,7 +68,7 @@ o_krb5_request (const OKrb5Element *element, const char *data, OKrb5ErrorCode o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data); -OKrb5ErrorCode +void o_krb5_free_element (OKrb5Element *element); #if OPENVAS_KRB5_CACHED == 1 @@ -76,8 +76,9 @@ o_krb5_free_element (OKrb5Element *element); typedef struct { OKrb5Credential credentials; - OKrb5Element element; + OKrb5Element *element; OKrb5ErrorCode last_error_code; + unsigned long id; } OKrb5CacheElement; typedef struct @@ -87,6 +88,13 @@ typedef struct OKrb5CacheElement **elements; } OKrb5CacheList; + +OKrb5ErrorCode o_krb5_cache_init(void); +OKrb5ErrorCode o_krb5_cache_clear(void); + +OKrb5CacheElement *o_krb5_cache_find(const OKrb5Credential *cred); + +OKrb5ErrorCode o_krb5_cache_request(const OKrb5Credential credentials, const char *data, const size_t data_len, OKrb5Data **out); #endif #endif From 8754b824aa91e1cd810158cf4b7d624f19feaade Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 28 Aug 2024 12:18:11 +0200 Subject: [PATCH 03/18] Add check for writing krb5.conf --- keberos/authenticate_test.c | 1 - keberos/openvas-krb5.c | 52 ++++++++++++++++++++++--------------- keberos/openvas-krb5.h | 9 ++++++- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/keberos/authenticate_test.c b/keberos/authenticate_test.c index e86baa1f8..3ea302e2d 100644 --- a/keberos/authenticate_test.c +++ b/keberos/authenticate_test.c @@ -1,6 +1,5 @@ #include "openvas-krb5.h" -#include #include #include diff --git a/keberos/openvas-krb5.c b/keberos/openvas-krb5.c index 64747600f..98d9174af 100644 --- a/keberos/openvas-krb5.c +++ b/keberos/openvas-krb5.c @@ -155,6 +155,17 @@ o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc) } return result; } + +#define CHECK_FPRINTF(result, writer, fmt, ...) \ + do \ + { \ + if (fprintf (writer, fmt, __VA_ARGS__) < 0) \ + { \ + result = O_KRB5_UNABLE_TO_WRITE; \ + goto result; \ + } \ + } \ + while (0) // Adds realm with the given kdc into krb5.conf OKrb5ErrorCode o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) @@ -164,8 +175,6 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) char line[MAX_LINE_LENGTH] = {0}; char tmpfn[MAX_LINE_LENGTH] = {0}; int state, i; - // TODO: validate output of fprintf when writing and abort - if ((file = fopen (creds->config_path, "r")) == NULL) { if ((file = fopen (creds->config_path, "w")) == NULL) @@ -173,14 +182,15 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) result = O_KRB5_CONF_NOT_CREATED; goto result; } - fprintf (file, "[realms]\n%s = {\n kdc = %s\n}\n", creds->realm, kdc); - goto close_config; + CHECK_FPRINTF (result, file, "[realms]\n%s = {\n kdc = %s\n}\n", + creds->realm, kdc); + goto result; } snprintf (tmpfn, MAX_LINE_LENGTH, "%s.tmp", creds->config_path); if ((tmp = fopen (tmpfn, "w")) == NULL) { result = O_KRB5_TMP_CONF_NOT_CREATED; - goto close_config; + goto result; } state = 0; while (fgets (line, MAX_LINE_LENGTH, file)) @@ -191,7 +201,8 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) SKIP_WS (line, MAX_LINE_LENGTH, 0, i); if (IS_STR_EQUAL (line, MAX_LINE_LENGTH, i, "[realms]", 8) == 1) { - fprintf (tmp, "%s = {\n kdc = %s\n}\n", creds->realm, kdc); + CHECK_FPRINTF (result, tmp, "%s = {\n kdc = %s\n}\n", + creds->realm, kdc); state = 1; } } @@ -202,11 +213,11 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) result = O_KRB5_TMP_CONF_NOT_MOVED; } -close_tmp: - fclose (tmp); -close_config: - fclose (file); result: + if (tmp != NULL) + fclose (tmp); + if (file != NULL) + fclose (file); return result; } @@ -330,7 +341,7 @@ o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data) // simply reconnect when we either don't find an entry or when the ticket is // invalid witout having the caller to remember artifical identifier. unsigned long -o_krb5_cach_credential_id (const OKrb5Credential *cred) +o_krb5_cache_credential_id (const OKrb5Credential *cred) { unsigned long hash = 2166136261; unsigned int prime = 16777219; @@ -353,7 +364,7 @@ o_krb5_cach_credential_id (const OKrb5Credential *cred) static OKrb5CacheList *element_cache = NULL; OKrb5ErrorCode -o_krb5_init_cache (void) +o_krb5_cache_init (void) { OKrb5ErrorCode result = O_KRB5_SUCCESS; GUARD_NULL (element_cache, result); @@ -366,7 +377,7 @@ o_krb5_init_cache (void) } OKrb5ErrorCode -o_krb5_clear_cache (void) +o_krb5_cache_clear (void) { OKrb5ErrorCode result = O_KRB5_SUCCESS; if (element_cache == NULL) @@ -385,14 +396,14 @@ o_krb5_clear_cache (void) } OKrb5CacheElement * -o_krb5_find_element (const OKrb5Credential *cred) +o_krb5_cache_find(const OKrb5Credential *cred) { if (element_cache == NULL) { return NULL; } int i; - unsigned long id = o_krb5_cach_credential_id (cred); + unsigned long id = o_krb5_cache_credential_id (cred); for (i = 0; i < element_cache->len; i++) { @@ -404,10 +415,9 @@ o_krb5_find_element (const OKrb5Credential *cred) return NULL; } - OKrb5ErrorCode o_krb5_cache_add_element (const OKrb5Credential credentials, - OKrb5CacheElement **out) + OKrb5CacheElement **out) { OKrb5ErrorCode result = O_KRB5_SUCCESS; @@ -430,7 +440,7 @@ o_krb5_cache_add_element (const OKrb5Credential credentials, ALLOCATE_AND_CHECK (*out, OKrb5CacheElement, 1, result); (*out)->credentials = credentials; - (*out)->id = o_krb5_cach_credential_id (&credentials); + (*out)->id = o_krb5_cache_credential_id (&credentials); element_cache->elements[element_cache->len] = *out; if ((result = o_krb5_authenticate (credentials, &(*out)->element))) @@ -445,14 +455,14 @@ o_krb5_cache_add_element (const OKrb5Credential credentials, } OKrb5ErrorCode -o_krb5_cached_request (const OKrb5Credential credentials, const char *data, +o_krb5_cache_request (const OKrb5Credential credentials, const char *data, const size_t data_len, OKrb5Data **out) { OKrb5ErrorCode result = O_KRB5_SUCCESS; if (element_cache == NULL) - o_krb5_init_cache (); - OKrb5CacheElement *element = o_krb5_find_element (&credentials); + o_krb5_cache_init (); + OKrb5CacheElement *element = o_krb5_cache_find(&credentials); // TODO: check if ticket is valid, if not valid try to get new one without // reauthenticate (till_new) or free and reauthenticate diff --git a/keberos/openvas-krb5.h b/keberos/openvas-krb5.h index dfcc0341e..588104990 100644 --- a/keberos/openvas-krb5.h +++ b/keberos/openvas-krb5.h @@ -20,6 +20,7 @@ typedef enum O_KRB5_REALM_NOT_FOUND, O_KRB5_EXPECTED_NULL, O_KRB5_EXPECTED_NOT_NULL, + O_KRB5_UNABLE_TO_WRITE, // can only happen when GFP_ATOMIC is set on the kernel. O_KRB5_NOMEM, @@ -52,12 +53,18 @@ typedef struct } OKrb5Data; // Finds the kdc defined for the given realm. +// +// It returns the found kdc into `kdc` when `kdc` is not NULL. +// If kdc is not NULL it requires that the value pointer is NULL otherwise an error code is returned. OKrb5ErrorCode o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc); -// Adds realm with the given kdc into krb5.conf +// Adds realm with the given kdc into krb5.conf if the krc5.conf is not found it will create a new one OKrb5ErrorCode o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc); +// Is used to get a ticket based on the given credentials. +// +// It will store the ticket into element, it requires that elemenet is not NULL but that the value of element is NULL. Otherwise an error code is returned. OKrb5ErrorCode o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element); From 4e96134fe042438770df45a958eeb8a6be5e92cf Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 28 Aug 2024 12:29:30 +0200 Subject: [PATCH 04/18] Fix: remove unnecessary creds free --- keberos/openvas-krb5.c | 1 - 1 file changed, 1 deletion(-) diff --git a/keberos/openvas-krb5.c b/keberos/openvas-krb5.c index 98d9174af..0d51c76c1 100644 --- a/keberos/openvas-krb5.c +++ b/keberos/openvas-krb5.c @@ -260,7 +260,6 @@ o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element) result: if (result != O_KRB5_SUCCESS) { - krb5_free_cred_contents (ctx, &creds); krb5_free_principal (ctx, me); krb5_free_context (ctx); if (*element != NULL) From ba72f24a2cda58aaec42d413ba8cb93750c44809 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 28 Aug 2024 12:38:58 +0200 Subject: [PATCH 05/18] Seperare cache_authentication into an own function --- keberos/openvas-krb5.c | 31 +++++++++++++++++++++++++------ keberos/openvas-krb5.h | 28 +++++++++++++++++++--------- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/keberos/openvas-krb5.c b/keberos/openvas-krb5.c index 0d51c76c1..117327664 100644 --- a/keberos/openvas-krb5.c +++ b/keberos/openvas-krb5.c @@ -395,7 +395,7 @@ o_krb5_cache_clear (void) } OKrb5CacheElement * -o_krb5_cache_find(const OKrb5Credential *cred) +o_krb5_cache_find (const OKrb5Credential *cred) { if (element_cache == NULL) { @@ -454,17 +454,17 @@ o_krb5_cache_add_element (const OKrb5Credential credentials, } OKrb5ErrorCode -o_krb5_cache_request (const OKrb5Credential credentials, const char *data, - const size_t data_len, OKrb5Data **out) +o_krb5_cache_authenticate (const OKrb5Credential credentials, + OKrb5CacheElement **out) { OKrb5ErrorCode result = O_KRB5_SUCCESS; + GUARD_NOT_NULL (out, result); + GUARD_NULL (*out, result); if (element_cache == NULL) o_krb5_cache_init (); - OKrb5CacheElement *element = o_krb5_cache_find(&credentials); + OKrb5CacheElement *element = o_krb5_cache_find (&credentials); - // TODO: check if ticket is valid, if not valid try to get new one without - // reauthenticate (till_new) or free and reauthenticate if (element == NULL) { if ((result = o_krb5_cache_add_element (credentials, &element))) @@ -488,6 +488,25 @@ o_krb5_cache_request (const OKrb5Credential credentials, const char *data, } } } + *out = element; +result: + return result; +} + +OKrb5ErrorCode +o_krb5_cache_request (const OKrb5Credential credentials, const char *data, + const size_t data_len, OKrb5Data **out) +{ + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + if (element_cache == NULL) + o_krb5_cache_init (); + OKrb5CacheElement *element = NULL; + if ((result = o_krb5_cache_authenticate (credentials, &element))) + { + goto result; + } + if ((result = o_krb5_request (element->element, data, data_len, out))) { goto result; diff --git a/keberos/openvas-krb5.h b/keberos/openvas-krb5.h index 588104990..a7f82e370 100644 --- a/keberos/openvas-krb5.h +++ b/keberos/openvas-krb5.h @@ -53,18 +53,21 @@ typedef struct } OKrb5Data; // Finds the kdc defined for the given realm. -// +// // It returns the found kdc into `kdc` when `kdc` is not NULL. -// If kdc is not NULL it requires that the value pointer is NULL otherwise an error code is returned. +// If kdc is not NULL it requires that the value pointer is NULL otherwise an +// error code is returned. OKrb5ErrorCode o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc); -// Adds realm with the given kdc into krb5.conf if the krc5.conf is not found it will create a new one +// Adds realm with the given kdc into krb5.conf if the krc5.conf is not found it +// will create a new one OKrb5ErrorCode o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc); // Is used to get a ticket based on the given credentials. // -// It will store the ticket into element, it requires that elemenet is not NULL but that the value of element is NULL. Otherwise an error code is returned. +// It will store the ticket into element, it requires that elemenet is not NULL +// but that the value of element is NULL. Otherwise an error code is returned. OKrb5ErrorCode o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element); @@ -95,13 +98,20 @@ typedef struct OKrb5CacheElement **elements; } OKrb5CacheList; +OKrb5ErrorCode +o_krb5_cache_init (void); +OKrb5ErrorCode +o_krb5_cache_clear (void); -OKrb5ErrorCode o_krb5_cache_init(void); -OKrb5ErrorCode o_krb5_cache_clear(void); - -OKrb5CacheElement *o_krb5_cache_find(const OKrb5Credential *cred); +OKrb5CacheElement * +o_krb5_cache_find (const OKrb5Credential *cred); +OKrb5ErrorCode +o_krb5_cache_authenticate (const OKrb5Credential credentials, + OKrb5CacheElement **out); -OKrb5ErrorCode o_krb5_cache_request(const OKrb5Credential credentials, const char *data, const size_t data_len, OKrb5Data **out); +OKrb5ErrorCode +o_krb5_cache_request (const OKrb5Credential credentials, const char *data, + const size_t data_len, OKrb5Data **out); #endif #endif From b24d4563067f0c9556d41d7f18163ff35915efcc Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Mon, 30 Sep 2024 07:45:11 +0000 Subject: [PATCH 06/18] Renamed keberos to kerberos. --- {keberos => kerberos}/.gitignore | 0 {keberos => kerberos}/authenticate_test.c | 0 {keberos => kerberos}/openvas-krb5.c | 0 {keberos => kerberos}/openvas-krb5.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {keberos => kerberos}/.gitignore (100%) rename {keberos => kerberos}/authenticate_test.c (100%) rename {keberos => kerberos}/openvas-krb5.c (100%) rename {keberos => kerberos}/openvas-krb5.h (100%) diff --git a/keberos/.gitignore b/kerberos/.gitignore similarity index 100% rename from keberos/.gitignore rename to kerberos/.gitignore diff --git a/keberos/authenticate_test.c b/kerberos/authenticate_test.c similarity index 100% rename from keberos/authenticate_test.c rename to kerberos/authenticate_test.c diff --git a/keberos/openvas-krb5.c b/kerberos/openvas-krb5.c similarity index 100% rename from keberos/openvas-krb5.c rename to kerberos/openvas-krb5.c diff --git a/keberos/openvas-krb5.h b/kerberos/openvas-krb5.h similarity index 100% rename from keberos/openvas-krb5.h rename to kerberos/openvas-krb5.h From 51a083b41ecff297b901b0de46bc3c74fc35a3d4 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Tue, 1 Oct 2024 12:55:08 +0000 Subject: [PATCH 07/18] Added c-nasl-binding, moved implementation to misc To not have to deal with cmake the implementation of krb5 moved from kerberos to misc. Additionally the bindings to add it into openvas are created. --- kerberos/authenticate_test.c | 4 +- {kerberos => misc}/.gitignore | 0 misc/CMakeLists.txt | 14 +-- {kerberos => misc}/openvas-krb5.c | 8 +- {kerberos => misc}/openvas-krb5.h | 0 nasl/CMakeLists.txt | 3 +- nasl/nasl_init.c | 4 + nasl/nasl_krb5.c | 160 ++++++++++++++++++++++++++++++ nasl/nasl_krb5.h | 83 ++++++++++++++++ nasl/nasl_var.c | 3 + 10 files changed, 266 insertions(+), 13 deletions(-) rename {kerberos => misc}/.gitignore (100%) rename {kerberos => misc}/openvas-krb5.c (99%) rename {kerberos => misc}/openvas-krb5.h (100%) create mode 100644 nasl/nasl_krb5.c create mode 100644 nasl/nasl_krb5.h diff --git a/kerberos/authenticate_test.c b/kerberos/authenticate_test.c index 3ea302e2d..f0946e32d 100644 --- a/kerberos/authenticate_test.c +++ b/kerberos/authenticate_test.c @@ -1,9 +1,8 @@ -#include "openvas-krb5.h" +#include "../misc/openvas-krb5.h" #include #include -#define REALM "GBKERB.LOCAL" #define GUARD_ENV_SET(var, env) \ do \ @@ -15,6 +14,7 @@ } \ } \ while (0) + int main () { diff --git a/kerberos/.gitignore b/misc/.gitignore similarity index 100% rename from kerberos/.gitignore rename to misc/.gitignore diff --git a/misc/CMakeLists.txt b/misc/CMakeLists.txt index bd647ff24..ec939a385 100644 --- a/misc/CMakeLists.txt +++ b/misc/CMakeLists.txt @@ -15,6 +15,8 @@ pkg_check_modules (GLIB REQUIRED glib-2.0>=2.42) pkg_check_modules (GLIB_JSON REQUIRED json-glib-1.0>=1.4.4) pkg_check_modules (GNUTLS REQUIRED gnutls>=3.6.4) pkg_check_modules (CURL REQUIRED libcurl>=7.74.0) +pkg_check_modules (KRB5 REQUIRED krb5) +pkg_check_modules (KRB5_GSSAPI REQUIRED krb5-gssapi) pkg_check_modules (LIBGVM_BASE REQUIRED libgvm_base>=22.4) pkg_check_modules (LIBGVM_UTIL REQUIRED libgvm_util>=22.4) @@ -69,13 +71,13 @@ add_definitions (-DOPENVAS_MISC_VERSION="${PROJECT_VERSION_STRING}") include_directories (${GLIB_INCLUDE_DIRS} ${GLIB_JSON_INCLUDE_DIRS} ${LIBGVM_BASE_INCLUDE_DIRS} - ${GNUTLS_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS}) + ${GNUTLS_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${KRB5_INCLUDE_DIRS} ${KRB5_GSSAPI_INCLUDE_DIRS}) # Library set (FILES bpf_share.c ftp_funcs.c vendorversion.c network.c plugutils.c pcap.c scan_id.c strutils.c table_driven_lsc.c ipc.c ipc_openvas.c ipc_pipe.c - user_agent.c scanneraux.c kb_cache.c heartbeat.c) + user_agent.c scanneraux.c kb_cache.c heartbeat.c openvas-krb5.c) # On windows we are always PIC and stack-protector is replaces by DEP # Also stack protection needs a shared library to work @@ -93,7 +95,7 @@ set_target_properties (openvas_misc_shared PROPERTIES VERSION "${PROJECT_VERSION target_link_libraries (openvas_misc_shared LINK_PRIVATE ${GNUTLS_LDFLAGS} ${UUID_LDFLAGS} ${GLIB_LDFLAGS} ${GLIB_JSON_LDFLAGS} - ${PCAP_LDFLAGS} ${LIBGVM_BOREAS_LDFLAGS} ${CURL_LDFLAGS} + ${PCAP_LDFLAGS} ${LIBGVM_BOREAS_LDFLAGS} ${CURL_LDFLAGS} ${KRB5_LDFLAGS} ${KRB5_GSSAPI_LDFLAGS} ${LINKER_HARDENING_FLAGS}) if (OPENVAS_STATE_DIR) @@ -136,7 +138,7 @@ set (LINK_LIBS_FOR_TESTS cgreen ${LIBGVM_BASE_LDFLAGS} ${GLIB_LDFLAGS} ${PCAP_LDFLAGS} - ${CURL_LDFLAGS} + ${CURL_LDFLAGS} ${KRB5_LDFLAGS} ${KRB5_GSSAPI_LDFLAGS} ${LINKER_HARDENING_FLAGS} ${CMAKE_THREAD_LIBS_INIT} ${ALIVEDETECTION_TEST_LINKER_WRAP_OPTIONS}) @@ -163,7 +165,7 @@ target_include_directories (ipc-openvas-test PRIVATE ${CGREEN_INCLUDE_DIRS}) target_link_libraries (ipc-openvas-test cgreen ${GLIB_LDFLAGS} ${GLIB_JSON_LDFLAGS} - ${CURL_LDFLAGS} + ${CURL_LDFLAGS} ${KRB5_LDFLAGS} ${KRB5_GSSAPI_LDFLAGS} ${LINKER_HARDENING_FLAGS}) add_custom_target (tests-ipc-openvas @@ -178,7 +180,7 @@ target_link_libraries (lsc-test cgreen ${LIBGVM_UTIL_LDFLAGS} ${GLIB_LDFLAGS} ${GLIB_JSON_LDFLAGS} - ${CURL_LDFLAGS} + ${CURL_LDFLAGS} ${KRB5_LDFLAGS} ${KRB5_GSSAPI_LDFLAGS} ${LINKER_HARDENING_FLAGS}) add_custom_target (tests-lsc diff --git a/kerberos/openvas-krb5.c b/misc/openvas-krb5.c similarity index 99% rename from kerberos/openvas-krb5.c rename to misc/openvas-krb5.c index 117327664..d3238ef94 100644 --- a/kerberos/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -339,7 +339,7 @@ o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data) // numbers but can just reuse credentials to find connections that way we can // simply reconnect when we either don't find an entry or when the ticket is // invalid witout having the caller to remember artifical identifier. -unsigned long +static unsigned long o_krb5_cache_credential_id (const OKrb5Credential *cred) { unsigned long hash = 2166136261; @@ -379,9 +379,9 @@ OKrb5ErrorCode o_krb5_cache_clear (void) { OKrb5ErrorCode result = O_KRB5_SUCCESS; +size_t i; if (element_cache == NULL) goto result; - int i; for (i = 0; i < element_cache->len; i++) { o_krb5_free_element ((element_cache->elements[i])->element); @@ -401,8 +401,8 @@ o_krb5_cache_find (const OKrb5Credential *cred) { return NULL; } - int i; unsigned long id = o_krb5_cache_credential_id (cred); + size_t i; for (i = 0; i < element_cache->len; i++) { @@ -414,7 +414,7 @@ o_krb5_cache_find (const OKrb5Credential *cred) return NULL; } -OKrb5ErrorCode +static OKrb5ErrorCode o_krb5_cache_add_element (const OKrb5Credential credentials, OKrb5CacheElement **out) { diff --git a/kerberos/openvas-krb5.h b/misc/openvas-krb5.h similarity index 100% rename from kerberos/openvas-krb5.h rename to misc/openvas-krb5.h diff --git a/nasl/CMakeLists.txt b/nasl/CMakeLists.txt index 2e888f1a4..b9df861d1 100644 --- a/nasl/CMakeLists.txt +++ b/nasl/CMakeLists.txt @@ -127,7 +127,8 @@ set (FILES arc4.c capture_packet.c charcnv.c exec.c genrand.c hmacmd5.c nasl_http.c nasl_http2.c nasl_init.c nasl_lex_ctxt.c nasl_misc_funcs.c nasl_scanner_glue.c nasl_packet_forgery.c nasl_packet_forgery_v6.c nasl_signature.c nasl_smb.c nasl_socket.c nasl_text_utils.c nasl_tree.c nasl_var.c nasl_wmi.c - nasl_isotime.c ntlmssp.c smb_crypt.c smb_crypt2.c smb_signing.c time.c) + nasl_isotime.c ntlmssp.c smb_crypt.c smb_crypt2.c smb_signing.c time.c + nasl_krb5.c) if (NOT OPENVAS_WMICLIENT_FOUND) diff --git a/nasl/nasl_init.c b/nasl/nasl_init.c index 0c8dab42d..db4bdfd48 100644 --- a/nasl/nasl_init.c +++ b/nasl/nasl_init.c @@ -27,6 +27,8 @@ #include "nasl_misc_funcs.h" #include "nasl_packet_forgery.h" #include "nasl_packet_forgery_v6.h" +#include "nasl_krb5.h" + #include /* for getenv. */ #include /* for memset */ @@ -414,6 +416,8 @@ static init_func libfuncs[] = { {"isotime_scan", nasl_isotime_scan}, {"isotime_print", nasl_isotime_print}, {"isotime_add", nasl_isotime_add}, + // krb5 + {"krb5_find_kdc", nasl_okrb5_find_kdc }, {NULL, NULL}}; /* String variables */ diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c new file mode 100644 index 000000000..7b4282598 --- /dev/null +++ b/nasl/nasl_krb5.c @@ -0,0 +1,160 @@ +#include "nasl_krb5.h" + +#include "../misc/openvas-krb5.h" +#include "nasl_debug.h" +#include "nasl_func.h" +#include "nasl_global_ctxt.h" +#include "nasl_lex_ctxt.h" +#include "nasl_tree.h" +#include "nasl_var.h" +// TODO: add string function for result +#define nasl_print_krb_error(lexic, credential, result) \ + nasl_perror (lexic, "%s[config_path: %s realm: %s user: %s] => %d", \ + __func__, credential.config_path, credential.realm, \ + credential.user, result); + +OKrb5ErrorCode last_okrb5_result; +static OKrb5Credential +build_krb5_credential (lex_ctxt *lexic) +{ + OKrb5Credential credential; + credential.user = NULL; + credential.password = NULL; + // neither values from get_str_var_by_name nor getenv must be freed + if ((credential.config_path = get_str_var_by_name (lexic, "config_path")) + == NULL) + { + credential.config_path = getenv ("KRB5_CONFIG"); + if (credential.config_path == NULL) + { + credential.config_path = "/etc/krb5.conf"; + } + } + if ((credential.realm = get_str_var_by_name (lexic, "realm")) == NULL) + { + credential.realm = getenv ("KRB5_REALM"); + if (credential.realm == NULL) + { + nasl_print_krb_error (lexic, credential, O_KRB5_REALM_NOT_FOUND); + } + } + + return credential; +} + +/** + * @brief Returns the defined KDC of a given Realm + * + * This function returns the KDC of a given Realm. The Realm is defined in the + * krb5.conf file. If there is no KDC for the given Realm, the function returns + * NULL within the tree_cell to the script. + * + * The nasl function has two optional parameter: + * - realm: The realm for which the KDC should be returned. If the realm is not + * defined, then the env parameter `KRB5_REALM` is used. + * - config_path: The path to the krb5.conf file. If the path is not defined, + * then the env parameter `KRB5_CONFIG` is used. + * + * This function should only be used for debug purposes. + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing the KDC as a string. + */ +tree_cell * +nasl_okrb5_find_kdc (lex_ctxt *lexic) +{ + tree_cell *retc; + char *kdc = NULL; + OKrb5Credential credential; + + credential = build_krb5_credential (lexic); + + if ((last_okrb5_result = o_krb5_find_kdc (&credential, &kdc))) + { + nasl_print_krb_error (lexic, credential, last_okrb5_result); + return NULL; + } + + retc = alloc_typed_cell (CONST_DATA); + retc->x.str_val = kdc; + retc->size = strlen (kdc); + return retc; +} + +tree_cell * +nasl_okrb5_add_realm (lex_ctxt *lexic) +{ + tree_cell *retc; + OKrb5Credential credential; + // TODO: create macro for that + char *kdc = get_str_var_by_name (lexic, "kdc"); + if (kdc == NULL) + { + kdc = getenv ("KRB5_KDC"); + if (kdc == NULL) + { + last_okrb5_result = O_KRB5_EXPECTED_NOT_NULL; + nasl_print_krb_error (lexic, credential, last_okrb5_result); + goto exit; + } + } + + credential = build_krb5_credential (lexic); + + if ((last_okrb5_result = o_krb5_add_realm (&credential, kdc))) + { + nasl_print_krb_error (lexic, credential, last_okrb5_result); + } + +exit: + retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = last_okrb5_result; + return retc; +} + +tree_cell * +nasl_okrb5_result (lex_ctxt *lexic) { + (void) lexic; + // TODO: implement function to return string representation of result + return NULL; +} + +/** + * @brief Returns 1 if the krb5 function was successful 0 otherwise + * + * The nasl function has one optional parameter: + * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success. + */ +tree_cell * +nasl_okrb5_is_success (lex_ctxt *lexic) { + OKrb5ErrorCode result = get_int_var_by_name (lexic, "retval", last_okrb5_result); + tree_cell *retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = result == O_KRB5_SUCCESS; + return retc; +} + +/** + * @brief Returns 0 if the krb5 function was successful and 1 if it failed + * + * The nasl function has one optional parameter: + * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success. + */ +tree_cell * +nasl_okrb5_is_failure (lex_ctxt *lexic) { + OKrb5ErrorCode result = get_int_var_by_name (lexic, "retval", last_okrb5_result); + tree_cell *retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = result != O_KRB5_SUCCESS; + return retc; +} + diff --git a/nasl/nasl_krb5.h b/nasl/nasl_krb5.h new file mode 100644 index 000000000..5095b2794 --- /dev/null +++ b/nasl/nasl_krb5.h @@ -0,0 +1,83 @@ +#include "nasl_lex_ctxt.h" +#include "nasl_tree.h" + +/** + * @brief Returns the defined KDC of a given Realm + * + * This function returns the KDC of a given Realm. The Realm is defined in the krb5.conf file. + * If there is no KDC for the given Realm, the function returns NULL within the tree_cell to the script. + * + * The nasl function has two optional parameter: + * - realm: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_REALM` is used. + * - config_path: The path to the krb5.conf file. If the path is not defined, then the env parameter `KRB5_CONFIG` is used. + * + * This function should only be used for debug purposes. + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing the KDC as a string. + */ +tree_cell * +nasl_okrb5_find_kdc (lex_ctxt *lexic); + +/** + * @brief Adds the given KDC to the given Realm + * + * This function returns 0 on success. To retrieve a human readable error message, the function `okrb5_result` can be used. + * + * The nasl function has three optional parameter: + * - realm: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_REALM` is used. + * - kdc: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_KDC` is used. + * - config_path: The path to the krb5.conf file. If the path is not defined, then the env parameter `KRB5_CONFIG` is used. + * + * This function should only be used for debug purposes. + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success or failure. + */ +tree_cell * +nasl_okrb5_add_realm (lex_ctxt *lexic); + +/** + * @brief Returns the last result of the krb5 functions as a string + * + * The nasl function has one optional parameter: + * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success or failure. + */ +tree_cell * +nasl_okrb5_result (lex_ctxt *lexic); + +/** + * @brief Returns 1 if the krb5 function was successful 0 otherwise + * + * The nasl function has one optional parameter: + * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success. + */ +tree_cell * +nasl_okrb5_is_success (lex_ctxt *lexic); + +/** + * @brief Returns 0 if the krb5 function was successful and 1 if it failed + * + * The nasl function has one optional parameter: + * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * + * + * @param[in] lexic NASL lexer. + * + * @return lex cell containing a number indicating success. + */ +tree_cell * +nasl_okrb5_is_failure (lex_ctxt *lexic); + diff --git a/nasl/nasl_var.c b/nasl/nasl_var.c index 80b9c6570..81e1061f8 100644 --- a/nasl/nasl_var.c +++ b/nasl/nasl_var.c @@ -1114,6 +1114,9 @@ get_variable_by_name (lex_ctxt *ctxt, const char *name) return (char *) var2str (v); } + // TODO: this is very confusing that it returns char * instead of const char * + // because it is not supposed to be modified as str_form is freed later on and + // may double free on misuse char * get_str_var_by_name (lex_ctxt *lexic, const char *name) { From 682a7beb972c21067b45a416e2a421907b299edc Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 2 Oct 2024 22:05:11 +0000 Subject: [PATCH 08/18] Adds functionality to create gss context into openvas-krb5 --- kerberos/.gitignore | 3 + kerberos/authenticate_test.c | 102 +++++------ misc/openvas-krb5.c | 341 ++++++++++++++++++++++++++++++++--- misc/openvas-krb5.h | 72 +++++++- nasl/nasl_krb5.c | 64 ++++--- 5 files changed, 468 insertions(+), 114 deletions(-) create mode 100644 kerberos/.gitignore diff --git a/kerberos/.gitignore b/kerberos/.gitignore new file mode 100644 index 000000000..bb1351e23 --- /dev/null +++ b/kerberos/.gitignore @@ -0,0 +1,3 @@ +a.out +test.sh +krb5.conf diff --git a/kerberos/authenticate_test.c b/kerberos/authenticate_test.c index f0946e32d..30ed91240 100644 --- a/kerberos/authenticate_test.c +++ b/kerberos/authenticate_test.c @@ -2,12 +2,13 @@ #include #include - +#include #define GUARD_ENV_SET(var, env) \ do \ { \ - if ((var = getenv (env)) == NULL) \ + var = okrb5_slice_from_str (getenv (env)); \ + if (var.len == 0) \ { \ fprintf (stderr, env " is not set\n"); \ return 1; \ @@ -21,69 +22,54 @@ main () char *kdc = NULL; OKrb5ErrorCode result = O_KRB5_SUCCESS; OKrb5Credential credentials; - OKrb5Element *element = NULL; - OKrb5Data *data = NULL; + OKrb5GSSContext *context = NULL; + struct OKrb5Slice from_application = {.data = NULL, .len = 0}; + struct OKrb5Slice *to_application = NULL; + bool more = false; GUARD_ENV_SET (credentials.config_path, "KRB5_CONFIG"); GUARD_ENV_SET (credentials.realm, "KRB5_REALM"); - GUARD_ENV_SET (credentials.user, "KRB5_USER"); - GUARD_ENV_SET (credentials.password, "KRB5_PASSWORD"); - if (o_krb5_find_kdc (&credentials, &kdc)) - { - GUARD_ENV_SET (kdc, "KRB5_KDC"); - if (o_krb5_add_realm (&credentials, kdc)) - { - fprintf (stderr, "Unable to add kdc\n"); - return 1; - } - } - else + GUARD_ENV_SET (credentials.user.user, "KRB5_USER"); + GUARD_ENV_SET (credentials.user.password, "KRB5_PASSWORD"); + GUARD_ENV_SET (credentials.target.host_name, "KRB5_TARGET_HOST"); + GUARD_ENV_SET (credentials.kdc, "KRB5_KDC"); + credentials.target.service = okrb5_slice_from_str ("cifs"); + memset (&credentials.target.domain, 0, sizeof (struct OKrb5Slice)); + printf ("Using realm: %s\n", (char *) credentials.realm.data); + // TODO: move to overall function + // TODO: refactor signature to use slice + // if (o_krb5_find_kdc (&credentials, &kdc)) + // { + // if (o_krb5_add_realm (&credentials, credentials.kdc.data)) + // { + // fprintf (stderr, "Unable to add kdc\n"); + // return 1; + // } + // } + // else + // { + // printf ("Using kdc: %s\n", kdc); + // free (kdc); + // } + context = okrb5_gss_init_context (); + printf ("Using realm: %s\n", (char *) credentials.realm.data); + if ((result = o_krb5_gss_prepare_context (&credentials, context))) { - printf ("Using kdc: %s\n", kdc); - free (kdc); - } - -#if OPENVAS_KRB5_CACHED != 1 - - if ((result = o_krb5_authenticate (credentials, &element))) - { - fprintf (stderr, "Error: %d: %s\n", result, - krb5_get_error_message (element->ctx, result - O_KRB5_ERROR)); - return result; - } - - printf ("Authentication Token:\n"); - printf ("--------------------\n"); - printf ("End time: %d\n", element->creds.times.endtime); - printf ("start time: %d\n", element->creds.times.starttime); - printf ("Renew till: %d\n", element->creds.times.renew_till); - if ((result = o_krb5_request (element, "test", 5, &data))) - { - fprintf (stderr, "unable to create request: %d", result); - } - if ((result = o_krb5_free_data (element, data))) - { - fprintf (stderr, "unable to free request: %d", result); + fprintf (stderr, "Unable to prepare context: %d\n", result); + return 1; } - - return 0; -#else - - if ((result = o_krb5_cache_request (credentials, "test", 5, &data))) + printf ("Using realm: %s\n", (char *) credentials.realm.data); + // first call always empty + if ((result = o_krb5_gss_update_context (context, &from_application, + &to_application, &more))) { - fprintf (stderr, "unable to create request: %d\n", result); + fprintf (stderr, "Unable to update context: %d\n", result); return 1; } - element = o_krb5_cache_find(&credentials)->element; - if (element == NULL) + printf ("success: %d: outdata_len: %zu\n", result, to_application->len); + + for (size_t i = 0; i < to_application->len; i++) { - fprintf (stderr, "element not found: %d", result); - return 1; + printf ("%02x", ((char *) to_application->data)[i]); } - printf ("Authentication Token:\n"); - printf ("--------------------\n"); - printf ("End time: %d\n", element->creds.times.endtime); - printf ("start time: %d\n", element->creds.times.starttime); - printf ("Renew till: %d\n", element->creds.times.renew_till); - o_krb5_cache_clear(); -#endif + printf ("\n"); } diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index d3238ef94..8517f46eb 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -1,7 +1,10 @@ #include "openvas-krb5.h" #include +#include +#include #include +#include #include #include #include @@ -68,16 +71,14 @@ o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc) OKrb5ErrorCode result = O_KRB5_REALM_NOT_FOUND; char line[MAX_LINE_LENGTH]; int state = 0; - int last_element; - int i, j; + size_t last_element; + size_t i, j; FILE *file; - int realm_len = strlen (creds->realm); - // we don't know if we should free it or just override it. // aborting instead. GUARD_NULL (*kdc, result); - if ((file = fopen (creds->config_path, "r")) == NULL) + if ((file = fopen ((char *) &creds->config_path.data, "r")) == NULL) { result = O_KRB5_CONF_NOT_FOUND; goto result; @@ -110,17 +111,17 @@ o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc) { for (j = i; j <= last_element; j++) { - if (line[j] != creds->realm[j - i]) + if (line[j] != ((char *) creds->realm.data)[j - i]) { state = 2; break; } - if (j - i >= realm_len) + if (j - i >= creds->realm.len) { break; } } - if (j - i == realm_len) + if (j - i == creds->realm.len) { state = 3; } @@ -175,18 +176,20 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) char line[MAX_LINE_LENGTH] = {0}; char tmpfn[MAX_LINE_LENGTH] = {0}; int state, i; - if ((file = fopen (creds->config_path, "r")) == NULL) + char *cp = (char *) creds->config_path.data; + char *realm = (char *) creds->realm.data; + if ((file = fopen (cp, "r")) == NULL) { - if ((file = fopen (creds->config_path, "w")) == NULL) + if ((file = fopen (cp, "w")) == NULL) { result = O_KRB5_CONF_NOT_CREATED; goto result; } - CHECK_FPRINTF (result, file, "[realms]\n%s = {\n kdc = %s\n}\n", - creds->realm, kdc); + CHECK_FPRINTF (result, file, "[realms]\n%s = {\n kdc = %s\n}\n", realm, + kdc); goto result; } - snprintf (tmpfn, MAX_LINE_LENGTH, "%s.tmp", creds->config_path); + snprintf (tmpfn, MAX_LINE_LENGTH, "%s.tmp", cp); if ((tmp = fopen (tmpfn, "w")) == NULL) { result = O_KRB5_TMP_CONF_NOT_CREATED; @@ -201,14 +204,14 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) SKIP_WS (line, MAX_LINE_LENGTH, 0, i); if (IS_STR_EQUAL (line, MAX_LINE_LENGTH, i, "[realms]", 8) == 1) { - CHECK_FPRINTF (result, tmp, "%s = {\n kdc = %s\n}\n", - creds->realm, kdc); + CHECK_FPRINTF (result, tmp, "%s = {\n kdc = %s\n}\n", realm, + kdc); state = 1; } } } - if (rename (tmpfn, creds->config_path) != 0) + if (rename (tmpfn, cp) != 0) { result = O_KRB5_TMP_CONF_NOT_MOVED; } @@ -238,16 +241,17 @@ o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element) goto result; } - if ((result = - krb5_build_principal (ctx, &me, strlen (credentials.realm), - credentials.realm, credentials.user, NULL))) + if ((result = krb5_build_principal (ctx, &me, credentials.realm.len, + (char *) credentials.realm.data, + credentials.user, NULL))) { result = result + O_KRB5_ERROR; goto result; }; if ((result = krb5_get_init_creds_password ( - ctx, &creds, me, credentials.password, NULL, NULL, 0, NULL, NULL))) + ctx, &creds, me, (char *) credentials.user.password.data, NULL, NULL, + 0, NULL, NULL))) { result = result + O_KRB5_ERROR; goto result; @@ -345,15 +349,15 @@ o_krb5_cache_credential_id (const OKrb5Credential *cred) unsigned long hash = 2166136261; unsigned int prime = 16777219; - for (const char *str = cred->realm; *str; str++) + for (const char *str = cred->realm.data; *str; str++) { hash = (hash ^ *str) * prime; } - for (const char *str = cred->user; *str; str++) + for (const char *str = cred->user.user.data; *str; str++) { hash = (hash ^ *str) * prime; } - for (const char *str = cred->password; *str; str++) + for (const char *str = cred->user.password.data; *str; str++) { hash = (hash ^ *str) * prime; } @@ -379,7 +383,7 @@ OKrb5ErrorCode o_krb5_cache_clear (void) { OKrb5ErrorCode result = O_KRB5_SUCCESS; -size_t i; + size_t i; if (element_cache == NULL) goto result; for (i = 0; i < element_cache->len; i++) @@ -438,7 +442,7 @@ o_krb5_cache_add_element (const OKrb5Credential credentials, } ALLOCATE_AND_CHECK (*out, OKrb5CacheElement, 1, result); - (*out)->credentials = credentials; + (*out)->credentials = &credentials; (*out)->id = o_krb5_cache_credential_id (&credentials); element_cache->elements[element_cache->len] = *out; @@ -499,21 +503,308 @@ o_krb5_cache_request (const OKrb5Credential credentials, const char *data, { OKrb5ErrorCode result = O_KRB5_SUCCESS; + printf ("hi: %s %d\n", __func__, __LINE__); if (element_cache == NULL) o_krb5_cache_init (); + printf ("hi: %s %d\n", __func__, __LINE__); OKrb5CacheElement *element = NULL; if ((result = o_krb5_cache_authenticate (credentials, &element))) { goto result; } + printf ("hi: %s %d\n", __func__, __LINE__); if ((result = o_krb5_request (element->element, data, data_len, out))) { goto result; } + printf ("hi: %s %d\n", __func__, __LINE__); result: return result; } #endif + +// GSS stuff, remove rest except for REALM handling +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID \ + "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" +#ifndef gss_mech_spnego +gss_OID_desc spnego_mech_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; +#define gss_mech_spnego (&spnego_mech_oid_desc) +#endif + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) + +struct OKrb5GSSCredentials +{ + gss_cred_id_t gss_creds; +}; + +struct OKrb5GSSContext +{ + gss_cred_id_t gss_creds; + gss_ctx_id_t gss_ctx; + gss_name_t gss_target; + gss_OID gss_mech; + OM_uint32 gss_want_flags; + OM_uint32 gss_time_req; + gss_channel_bindings_t gss_channel_bindings; + gss_OID gss_actual_mech_type; + OM_uint32 gss_got_flags; + OM_uint32 gss_time_rec; +}; + +static OKrb5ErrorCode +okrb5_gss_authenticate (const OKrb5Credential *creds, + struct OKrb5GSSContext *gss_creds) +{ + char *user_principal; + const struct OKrb5User *user = &creds->user; + + OKrb5ErrorCode result = O_KRB5_SUCCESS; + ALLOCATE_AND_CHECK (user_principal, char, + user->user.len + creds->realm.len + 2, result); + sprintf (user_principal, "%s@%s", (char *) user->user.data, + (char *) creds->realm.data); + + gss_name_t gss_username = GSS_C_NO_NAME; + OM_uint32 maj_stat; + OM_uint32 min_stat; + // OM_uint32 dummy_min_stat; + gss_buffer_desc userbuf = { + .value = user_principal, + .length = strlen (user_principal), + }; + gss_buffer_desc pwbuf = { + .value = user->password.data, + .length = user->password.len, + }; + gss_OID_desc elements[] = { + *gss_mech_krb5, +#ifdef __USE_IAKERB + *gss_mech_iakerb, +#endif /* __USE_IAKERB */ + *gss_mech_spnego, + }; + gss_OID_set_desc creds_mechs = { + .elements = elements, + .count = ARRAY_SIZE (elements), + }; + gss_OID_set_desc spnego_mechs = { + .elements = elements, + .count = ARRAY_SIZE (elements) - 1, /* without gss_mech_spnego */ + }; + gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; + + maj_stat = + gss_import_name (&min_stat, &userbuf, GSS_C_NT_USER_NAME, &gss_username); + if (maj_stat != GSS_S_COMPLETE) + { + return O_KRB5_ERROR + maj_stat; + } + + maj_stat = gss_acquire_cred_with_password (&min_stat, gss_username, &pwbuf, 0, + &creds_mechs, GSS_C_INITIATE, + &cred, NULL, NULL); + + //(void) gss_release_name (&dummy_min_stat, &gss_username); + if (maj_stat != GSS_S_COMPLETE) + { + // return NT_STATUS_LOGON_FAILURE; + return O_KRB5_ERROR + maj_stat; + } + + // let spnego only use the desired mechs + maj_stat = gss_set_neg_mechs (&min_stat, cred, &spnego_mechs); + if (maj_stat != GSS_S_COMPLETE) + { + // failed setting neg mechs + return O_KRB5_ERROR + maj_stat; + } + gss_creds->gss_creds = cred; +result: + // TODO: free user_principal on failure? + return result; +} + +struct OKrb5GSSContext * +okrb5_gss_init_context (void) +{ + struct OKrb5GSSContext *context = calloc (1, sizeof (struct OKrb5GSSContext)); + if (context == NULL) + { + return NULL; + } + context->gss_creds = GSS_C_NO_CREDENTIAL; + context->gss_ctx = GSS_C_NO_CONTEXT; + return context; +} + +void +okrb5_gss_free_context (struct OKrb5GSSContext *context) +{ + if (context != NULL) + { + if (context->gss_creds != GSS_C_NO_CREDENTIAL) + { + gss_release_cred (NULL, &context->gss_creds); + } + // TODO: clean rest + free (context); + } +} + +OKrb5ErrorCode +o_krb5_gss_prepare_context (const OKrb5Credential *creds, + struct OKrb5GSSContext *gss_context) +{ + char *target_principal_str = NULL; + OKrb5ErrorCode result = O_KRB5_SUCCESS; + + gss_name_t gss_target = GSS_C_NO_NAME; + OM_uint32 maj_stat; + OM_uint32 min_stat; + gss_buffer_desc targetbuf = GSS_C_EMPTY_BUFFER; + const struct OKrb5Target *target = &creds->target; + + if (gss_context->gss_creds == GSS_C_NO_CREDENTIAL) + { + if ((result = okrb5_gss_authenticate (creds, gss_context))) + { + goto result; + } + } + + if (target->domain.len != 0) + { + ALLOCATE_AND_CHECK (target_principal_str, char, + target->host_name.len + target->domain.len + + target->service.len + creds->realm.len + 4, + result); + sprintf (target_principal_str, "%s/%s/%s@%s", + (char *) target->service.data, (char *) target->host_name.data, + (char *) target->domain.data, (char *) creds->realm.data); + } + else + { + ALLOCATE_AND_CHECK (target_principal_str, char, + target->host_name.len + target->service.len + + creds->realm.len + 3, + result); + sprintf (target_principal_str, "%s/%s@%s", (char *) target->service.data, + (char *) target->host_name.data, (char *) creds->realm.data); + } + + targetbuf = (gss_buffer_desc){ + .value = target_principal_str, + .length = strlen (target_principal_str), + }; + + maj_stat = gss_import_name (&min_stat, &targetbuf, + // might also be GSS_C_NT_HOSTBASED_SERVICE, + // but samba uses GSS_C_NT_USER_NAME + GSS_C_NT_USER_NAME, &gss_target); + if (maj_stat != GSS_S_COMPLETE) + { + result = O_KRB5_ERROR + maj_stat; + goto result; + } + + gss_context->gss_target = gss_target; + // gss_set_neg_mechs() already specified that we want gss_mech_krb5 + // and/or gss_mech_iakerb + // so we use spnego to do the negotiation + gss_context->gss_mech = gss_mech_spnego; + gss_context->gss_want_flags = GSS_C_MUTUAL_FLAG | GSS_C_DELEG_POLICY_FLAG + | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG + | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG; + gss_context->gss_got_flags = 0; + gss_context->gss_channel_bindings = GSS_C_NO_CHANNEL_BINDINGS; + gss_context->gss_time_req = 0; + gss_context->gss_time_rec = 0; + gss_context->gss_actual_mech_type = NULL; +result: + // TODO: cleanup target_principal_str on failure? + + return result; +} + +// TODO: this signature feels unintuitive based on the mix of in and out and +// changed gss_context as well... +OKrb5ErrorCode +o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, + const struct OKrb5Slice *in_data, + struct OKrb5Slice **out_data, bool *more) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + OKrb5ErrorCode result = O_KRB5_SUCCESS; + // TODO: validate in data + gss_buffer_desc in_buf = { + .length = in_data->len, + .value = in_data->data, + }; + gss_buffer_desc out_buf = GSS_C_EMPTY_BUFFER; + + maj_stat = gss_init_sec_context ( + &min_stat, gss_context->gss_creds, &gss_context->gss_ctx, + gss_context->gss_target, gss_context->gss_mech, gss_context->gss_want_flags, + gss_context->gss_time_req, gss_context->gss_channel_bindings, &in_buf, + &gss_context->gss_actual_mech_type, &out_buf, &gss_context->gss_got_flags, + &gss_context->gss_time_rec); + if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) + { + result = O_KRB5_ERROR + maj_stat; + goto result; + } + *out_data = malloc (sizeof (struct OKrb5Slice)); + (*out_data)->data = calloc (1, out_buf.length); + memcpy ((*out_data)->data, out_buf.value, out_buf.length); + printf ("out_buf.length: %lu\n", out_buf.length); + (*out_data)->len = out_buf.length; + + gss_release_buffer (&min_stat, &out_buf); + *more = maj_stat == GSS_S_CONTINUE_NEEDED; +result: + return result; +} + +OKrb5ErrorCode +o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, + struct OKrb5Slice **out) +{ + OM_uint32 maj_stat; + OM_uint32 min_stat; + OKrb5ErrorCode result = O_KRB5_SUCCESS; + gss_OID_desc gse_sesskey_inq_oid = { + GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH, + (void *) GSS_KRB5_INQ_SSPI_SESSION_KEY_OID, + }; + gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; + + maj_stat = gss_inquire_sec_context_by_oid (&min_stat, gss_context->gss_ctx, + &gse_sesskey_inq_oid, &set); + if (maj_stat != GSS_S_COMPLETE) + { + result = O_KRB5_ERROR + maj_stat; + goto result; + } + + if ((set == GSS_C_NO_BUFFER_SET) || (set->count == 0) + || (set->elements[0].length == 0)) + { + result = O_KRB5_ERROR + GSS_S_BAD_SIG; + goto result; + } + + // TODO: verify out + *out = calloc (1, sizeof (struct OKrb5Slice)); + (*out)->data = malloc (set->elements[0].length); + memcpy ((*out)->data, set->elements[0].value, set->elements[0].length); + (*out)->len = set->elements[0].length; + gss_release_buffer_set (&min_stat, &set); +result: + return result; +} diff --git a/misc/openvas-krb5.h b/misc/openvas-krb5.h index a7f82e370..cda1596fa 100644 --- a/misc/openvas-krb5.h +++ b/misc/openvas-krb5.h @@ -1,4 +1,5 @@ #include +#include #ifndef OPENVAS_KRB5 #define OPENVAS_KRB5 1 #include @@ -38,14 +39,40 @@ typedef struct krb5_creds creds; } OKrb5Element; +struct OKrb5Slice +{ + void *data; + size_t len; +}; + +struct OKrb5User +{ + struct OKrb5Slice user; + struct OKrb5Slice password; +}; + +struct OKrb5Target +{ + struct OKrb5Slice host_name; + struct OKrb5Slice service; + struct OKrb5Slice domain; +}; + typedef struct { - const char *config_path; - const char *realm; - const char *user; - const char *password; + struct OKrb5Slice config_path; + struct OKrb5Slice realm; + // required for the case that the realm is not configured in the krb5.conf + // will be ignored otherwise. + struct OKrb5Slice kdc; + struct OKrb5User user; + struct OKrb5Target target; } OKrb5Credential; + + +// TODO: initializer with default values and NULL + typedef struct { krb5_data data; @@ -85,7 +112,7 @@ o_krb5_free_element (OKrb5Element *element); typedef struct { - OKrb5Credential credentials; + const OKrb5Credential *credentials; OKrb5Element *element; OKrb5ErrorCode last_error_code; unsigned long id; @@ -114,4 +141,39 @@ o_krb5_cache_request (const OKrb5Credential credentials, const char *data, const size_t data_len, OKrb5Data **out); #endif +#define okrb5_slice_from_str(str) \ + (struct OKrb5Slice) \ + { \ + .data = (void *) str, .len = str == NULL ? 0 : strlen (str) \ + } + +#define okrb5_set_slice_from_str(slice, str) \ + do \ + { \ + slice.data = (void *) str; \ + slice.len = str == NULL ? 0 : strlen (str); \ + } \ + while (0) + + +typedef struct OKrb5GSSContext OKrb5GSSContext; +// Unsure about bool type + +OKrb5ErrorCode +o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, + struct OKrb5Slice **out); + +struct OKrb5GSSContext *okrb5_gss_init_context (void); +void okrb5_gss_free_context (struct OKrb5GSSContext *context); + +OKrb5ErrorCode +o_krb5_gss_prepare_context (const OKrb5Credential *creds, + struct OKrb5GSSContext *gss_context); + +OKrb5ErrorCode +o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, + const struct OKrb5Slice *in_data, + struct OKrb5Slice **out_data, bool *more); + + #endif diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index 7b4282598..585a0e396 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -14,29 +14,35 @@ credential.user, result); OKrb5ErrorCode last_okrb5_result; + +#define set_slice_from_lex_or_env(lexic, slice, name, env_name) \ + do \ + { \ + okrb5_set_slice_from_str (slice, get_str_var_by_name (lexic, name)); \ + if (slice.len == 0) \ + { \ + okrb5_set_slice_from_str (slice, getenv (env_name)); \ + } \ + } \ + while (0) + static OKrb5Credential build_krb5_credential (lex_ctxt *lexic) { OKrb5Credential credential; - credential.user = NULL; - credential.password = NULL; - // neither values from get_str_var_by_name nor getenv must be freed - if ((credential.config_path = get_str_var_by_name (lexic, "config_path")) - == NULL) + memset (&credential, 0, sizeof (OKrb5Credential)); + + set_slice_from_lex_or_env (lexic, credential.config_path, "config_path", + "KRB5_CONFIG"); + if (credential.config_path.len == 0) { - credential.config_path = getenv ("KRB5_CONFIG"); - if (credential.config_path == NULL) - { - credential.config_path = "/etc/krb5.conf"; - } + okrb5_set_slice_from_str (credential.config_path, "/etc/krb5.conf"); } - if ((credential.realm = get_str_var_by_name (lexic, "realm")) == NULL) + // TODO: enhance with redis check? maybe. + set_slice_from_lex_or_env (lexic, credential.realm, "realm", "KRB5_REALM"); + if (credential.realm.len == 0) { - credential.realm = getenv ("KRB5_REALM"); - if (credential.realm == NULL) - { - nasl_print_krb_error (lexic, credential, O_KRB5_REALM_NOT_FOUND); - } + nasl_print_krb_error (lexic, credential, O_KRB5_REALM_NOT_FOUND); } return credential; @@ -114,7 +120,8 @@ nasl_okrb5_add_realm (lex_ctxt *lexic) } tree_cell * -nasl_okrb5_result (lex_ctxt *lexic) { +nasl_okrb5_result (lex_ctxt *lexic) +{ (void) lexic; // TODO: implement function to return string representation of result return NULL; @@ -124,16 +131,19 @@ nasl_okrb5_result (lex_ctxt *lexic) { * @brief Returns 1 if the krb5 function was successful 0 otherwise * * The nasl function has one optional parameter: - * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * - retval: the return value of the krb5 function. If the value is not defined, + * the return value of the last krb5 function is used. * * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success. */ tree_cell * -nasl_okrb5_is_success (lex_ctxt *lexic) { - OKrb5ErrorCode result = get_int_var_by_name (lexic, "retval", last_okrb5_result); +nasl_okrb5_is_success (lex_ctxt *lexic) +{ + OKrb5ErrorCode result = + get_int_var_by_name (lexic, "retval", last_okrb5_result); tree_cell *retc = alloc_typed_cell (CONST_INT); retc->x.i_val = result == O_KRB5_SUCCESS; return retc; @@ -143,18 +153,20 @@ nasl_okrb5_is_success (lex_ctxt *lexic) { * @brief Returns 0 if the krb5 function was successful and 1 if it failed * * The nasl function has one optional parameter: - * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * - retval: the return value of the krb5 function. If the value is not defined, + * the return value of the last krb5 function is used. * * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success. */ tree_cell * -nasl_okrb5_is_failure (lex_ctxt *lexic) { - OKrb5ErrorCode result = get_int_var_by_name (lexic, "retval", last_okrb5_result); +nasl_okrb5_is_failure (lex_ctxt *lexic) +{ + OKrb5ErrorCode result = + get_int_var_by_name (lexic, "retval", last_okrb5_result); tree_cell *retc = alloc_typed_cell (CONST_INT); retc->x.i_val = result != O_KRB5_SUCCESS; return retc; } - From befcfa8426ad66a3608c89ee69e7ea1c1fa36000 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Thu, 3 Oct 2024 15:31:19 +0000 Subject: [PATCH 09/18] Add additional functions for krb5 This allows to authenticate against a kdc as well as creating data to send to and from the target. --- kerberos/authenticate_test.c | 32 +++--- misc/openvas-krb5.c | 1 + misc/openvas-krb5.h | 1 + nasl/nasl_init.c | 14 ++- nasl/nasl_krb5.c | 194 ++++++++++++++++++++++++++++++++--- nasl/nasl_krb5.h | 17 +++ nasl/nasl_scanner_glue.c | 3 +- nasl/nasl_scanner_glue.h | 2 +- rust/examples/.gitignore | 1 + rust/examples/gss.nasl | 31 ++++++ 10 files changed, 263 insertions(+), 33 deletions(-) create mode 100644 rust/examples/.gitignore create mode 100644 rust/examples/gss.nasl diff --git a/kerberos/authenticate_test.c b/kerberos/authenticate_test.c index 30ed91240..e35c008ad 100644 --- a/kerberos/authenticate_test.c +++ b/kerberos/authenticate_test.c @@ -4,25 +4,27 @@ #include #include -#define GUARD_ENV_SET(var, env) \ - do \ - { \ - var = okrb5_slice_from_str (getenv (env)); \ - if (var.len == 0) \ - { \ - fprintf (stderr, env " is not set\n"); \ - return 1; \ - } \ - } \ +#define GUARD_ENV_SET(var, env) \ + do \ + { \ + okrb5_set_slice_from_str (var, getenv (env)); \ + if (var.len == 0) \ + { \ + fprintf (stderr, env " is not set\n"); \ + return 1; \ + } \ + } \ while (0) +struct OKrb5GSSContext *cached_gss_context = NULL; int main () { char *kdc = NULL; OKrb5ErrorCode result = O_KRB5_SUCCESS; OKrb5Credential credentials; - OKrb5GSSContext *context = NULL; + + memset (&credentials, 0, sizeof (OKrb5Credential)); struct OKrb5Slice from_application = {.data = NULL, .len = 0}; struct OKrb5Slice *to_application = NULL; bool more = false; @@ -50,17 +52,17 @@ main () // printf ("Using kdc: %s\n", kdc); // free (kdc); // } - context = okrb5_gss_init_context (); + cached_gss_context = okrb5_gss_init_context (); printf ("Using realm: %s\n", (char *) credentials.realm.data); - if ((result = o_krb5_gss_prepare_context (&credentials, context))) + if ((result = o_krb5_gss_prepare_context (&credentials, cached_gss_context))) { fprintf (stderr, "Unable to prepare context: %d\n", result); return 1; } printf ("Using realm: %s\n", (char *) credentials.realm.data); // first call always empty - if ((result = o_krb5_gss_update_context (context, &from_application, - &to_application, &more))) + if ((result = o_krb5_gss_update_context ( + cached_gss_context, &from_application, &to_application, &more))) { fprintf (stderr, "Unable to update context: %d\n", result); return 1; diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index 8517f46eb..e747d2dde 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -669,6 +669,7 @@ o_krb5_gss_prepare_context (const OKrb5Credential *creds, gss_buffer_desc targetbuf = GSS_C_EMPTY_BUFFER; const struct OKrb5Target *target = &creds->target; + if (gss_context->gss_creds == GSS_C_NO_CREDENTIAL) { if ((result = okrb5_gss_authenticate (creds, gss_context))) diff --git a/misc/openvas-krb5.h b/misc/openvas-krb5.h index cda1596fa..a61952343 100644 --- a/misc/openvas-krb5.h +++ b/misc/openvas-krb5.h @@ -164,6 +164,7 @@ o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, struct OKrb5Slice **out); struct OKrb5GSSContext *okrb5_gss_init_context (void); + void okrb5_gss_free_context (struct OKrb5GSSContext *context); OKrb5ErrorCode diff --git a/nasl/nasl_init.c b/nasl/nasl_init.c index db4bdfd48..c09375510 100644 --- a/nasl/nasl_init.c +++ b/nasl/nasl_init.c @@ -23,12 +23,11 @@ #include "nasl_http.h" #include "nasl_http2.h" #include "nasl_isotime.h" +#include "nasl_krb5.h" #include "nasl_lex_ctxt.h" #include "nasl_misc_funcs.h" #include "nasl_packet_forgery.h" #include "nasl_packet_forgery_v6.h" -#include "nasl_krb5.h" - #include /* for getenv. */ #include /* for memset */ @@ -91,7 +90,7 @@ static init_func libfuncs[] = { {"get_host_kb_index", get_host_kb_index}, {"security_message", security_message}, {"log_message", log_message}, - {"error_message", error_message}, + {"error_message", error_message2}, {"open_sock_tcp", nasl_open_sock_tcp}, {"open_sock_udp", nasl_open_sock_udp}, {"open_priv_sock_tcp", nasl_open_priv_sock_tcp}, @@ -417,7 +416,14 @@ static init_func libfuncs[] = { {"isotime_print", nasl_isotime_print}, {"isotime_add", nasl_isotime_add}, // krb5 - {"krb5_find_kdc", nasl_okrb5_find_kdc }, + {"krb5_find_kdc", nasl_okrb5_find_kdc}, + {"krb5_is_success", nasl_okrb5_is_success}, + {"krb5_is_failure", nasl_okrb5_is_failure}, + {"krb5_gss_init", nasl_okrb5_gss_init}, + {"krb5_gss_prepare_context", nasl_okrb5_gss_prepare_context}, + {"krb5_gss_update_context", nasl_okrb5_gss_update_context}, + {"krb5_gss_update_context_needs_more", nasl_okrb5_gss_update_context_needs_more}, + {"krb5_gss_update_context_out", nasl_okrb5_gss_update_context_out}, {NULL, NULL}}; /* String variables */ diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index 585a0e396..e8b8e2af7 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -7,11 +7,13 @@ #include "nasl_lex_ctxt.h" #include "nasl_tree.h" #include "nasl_var.h" + +#include // TODO: add string function for result -#define nasl_print_krb_error(lexic, credential, result) \ - nasl_perror (lexic, "%s[config_path: %s realm: %s user: %s] => %d", \ - __func__, credential.config_path, credential.realm, \ - credential.user, result); +#define nasl_print_krb_error(lexic, credential, result) \ + nasl_perror (lexic, "%s[config_path: '%s' realm: '%s' user: '%s'] => %d", \ + __func__, credential.config_path.data, credential.realm.data, \ + credential.user.user.data, result); OKrb5ErrorCode last_okrb5_result; @@ -26,6 +28,18 @@ OKrb5ErrorCode last_okrb5_result; } \ while (0) +#define perror_set_slice_from_lex_or_env(lexic, slice, name, env_name) \ + do \ + { \ + set_slice_from_lex_or_env (lexic, slice, name, env_name); \ + if (slice.len == 0) \ + { \ + nasl_perror (lexic, "Expected %s or env variable %s", name, \ + env_name); \ + } \ + } \ + while (0) + static OKrb5Credential build_krb5_credential (lex_ctxt *lexic) { @@ -39,11 +53,24 @@ build_krb5_credential (lex_ctxt *lexic) okrb5_set_slice_from_str (credential.config_path, "/etc/krb5.conf"); } // TODO: enhance with redis check? maybe. - set_slice_from_lex_or_env (lexic, credential.realm, "realm", "KRB5_REALM"); - if (credential.realm.len == 0) + + perror_set_slice_from_lex_or_env (lexic, credential.realm, "realm", + "KRB5_REALM"); + perror_set_slice_from_lex_or_env (lexic, credential.user.user, "user", + "KRB5_USER"); + perror_set_slice_from_lex_or_env (lexic, credential.user.password, "password", + "KRB5_PASSWORD"); + perror_set_slice_from_lex_or_env (lexic, credential.target.host_name, "host", + "KRB5_TARGET_HOST"); + // set_slice_from_lex_or_env (lexic, credential.target.service, "service", + // "KRB5_TARGET_SERVICE"); + if (credential.target.service.len == 0) { - nasl_print_krb_error (lexic, credential, O_KRB5_REALM_NOT_FOUND); + okrb5_set_slice_from_str (credential.target.service, "cifs"); } + set_slice_from_lex_or_env (lexic, credential.kdc, "kdc", "KRB5_KDC"); + + memset (&credential.target.domain, 0, sizeof (struct OKrb5Slice)); return credential; } @@ -79,7 +106,7 @@ nasl_okrb5_find_kdc (lex_ctxt *lexic) if ((last_okrb5_result = o_krb5_find_kdc (&credential, &kdc))) { nasl_print_krb_error (lexic, credential, last_okrb5_result); - return NULL; + return FAKE_CELL; } retc = alloc_typed_cell (CONST_DATA); @@ -142,8 +169,7 @@ nasl_okrb5_result (lex_ctxt *lexic) tree_cell * nasl_okrb5_is_success (lex_ctxt *lexic) { - OKrb5ErrorCode result = - get_int_var_by_name (lexic, "retval", last_okrb5_result); + OKrb5ErrorCode result = get_int_var_by_num (lexic, 0, last_okrb5_result); tree_cell *retc = alloc_typed_cell (CONST_INT); retc->x.i_val = result == O_KRB5_SUCCESS; return retc; @@ -164,9 +190,153 @@ nasl_okrb5_is_success (lex_ctxt *lexic) tree_cell * nasl_okrb5_is_failure (lex_ctxt *lexic) { - OKrb5ErrorCode result = - get_int_var_by_name (lexic, "retval", last_okrb5_result); + OKrb5ErrorCode result = get_int_var_by_num (lexic, 0, last_okrb5_result); tree_cell *retc = alloc_typed_cell (CONST_INT); retc->x.i_val = result != O_KRB5_SUCCESS; return retc; } + +// TODO: may need a cacing mechanism for different configurations +// for now we just use one +struct OKrb5GSSContext *cached_gss_context = NULL; + +tree_cell * +nasl_okrb5_gss_init (lex_ctxt *lexic) +{ + (void) lexic; + if (cached_gss_context != NULL) + { + okrb5_gss_free_context (cached_gss_context); + } + cached_gss_context = okrb5_gss_init_context (); + if (cached_gss_context == NULL) + { + last_okrb5_result = O_KRB5_EXPECTED_NOT_NULL; + } + else + { + last_okrb5_result = O_KRB5_SUCCESS; + }; + tree_cell *retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = last_okrb5_result; + return retc; +} +tree_cell * +nasl_okrb5_gss_prepare_context (lex_ctxt *lexic) +{ + (void) lexic; + + OKrb5Credential credential; + credential = build_krb5_credential (lexic); + OKrb5ErrorCode result = O_KRB5_SUCCESS; + if (cached_gss_context == NULL) + { + cached_gss_context = okrb5_gss_init_context (); + } + result = o_krb5_gss_prepare_context (&credential, cached_gss_context); + tree_cell *retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = result; + last_okrb5_result = result; + return retc; +} + +struct OKrb5Slice *to_application = NULL; +bool gss_update_context_more = false; + +tree_cell * +nasl_okrb5_gss_update_context (lex_ctxt *lexic) +{ + (void) lexic; + OKrb5ErrorCode result = O_KRB5_SUCCESS; + tree_cell *retc; + struct OKrb5Slice from_application; + + if (to_application != NULL) + { + free (to_application->data); + free (to_application); + to_application = NULL; + } + + okrb5_set_slice_from_str (from_application, get_str_var_by_num (lexic, 0)); + + if (cached_gss_context == NULL) + { + last_okrb5_result = O_KRB5_EXPECTED_NOT_NULL; + goto result; + } + result = + o_krb5_gss_update_context (cached_gss_context, &from_application, + &to_application, &gss_update_context_more); +result: + retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = result; + last_okrb5_result = result; + return retc; +} + +tree_cell * +nasl_okrb5_gss_update_context_needs_more (lex_ctxt *lexic) +{ + (void) lexic; + tree_cell *retc = alloc_typed_cell (CONST_INT); + retc->x.i_val = gss_update_context_more; + return retc; +} + +tree_cell * +nasl_okrb5_gss_update_context_out (lex_ctxt *lexic) +{ + (void) lexic; + if (to_application == NULL) + { + return FAKE_CELL; + } + tree_cell *retc = alloc_typed_cell (CONST_DATA); + retc->x.str_val = to_application->data; + retc->size = to_application->len; + return retc; +} + +/* + context = okrb5_gss_init_context (); + printf ("Using realm: %s\n", (char *) credentials.realm.data); + if ((result = o_krb5_gss_prepare_context (&credentials, context))) + { + return 1; + } + printf ("Using realm: %s\n", (char *) credentials.realm.data); + // first call always empty + if ((result = o_krb5_gss_update_context (context, &from_application, + &to_application, &more))) + { + return 1; + } + printf ("success: %d: outdata_len: %zu\n", result, to_application->len); + + for (size_t i = 0; i < to_application->len; i++) + { + printf ("%02x", ((char *) to_application->data)[i]); + } + printf ("\n"); + +*/ + +/* +*OKrb5ErrorCode +o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, + struct OKrb5Slice **out); + +struct OKrb5GSSContext *okrb5_gss_init_context (void); + +void okrb5_gss_free_context (struct OKrb5GSSContext *context); + +OKrb5ErrorCode +o_krb5_gss_prepare_context (const OKrb5Credential *creds, + struct OKrb5GSSContext *gss_context); + +OKrb5ErrorCode +o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, + const struct OKrb5Slice *in_data, + struct OKrb5Slice **out_data, bool *more); +*/ diff --git a/nasl/nasl_krb5.h b/nasl/nasl_krb5.h index 5095b2794..6464ef9be 100644 --- a/nasl/nasl_krb5.h +++ b/nasl/nasl_krb5.h @@ -81,3 +81,20 @@ nasl_okrb5_is_success (lex_ctxt *lexic); tree_cell * nasl_okrb5_is_failure (lex_ctxt *lexic); +tree_cell * +nasl_okrb5_gss_init (lex_ctxt *lexic); + + +tree_cell * +nasl_okrb5_gss_prepare_context (lex_ctxt *lexic); + + +tree_cell * +nasl_okrb5_gss_update_context (lex_ctxt *lexic); + + +tree_cell * +nasl_okrb5_gss_update_context_needs_more(lex_ctxt *lexic); + +tree_cell * +nasl_okrb5_gss_update_context_out (lex_ctxt *lexic); diff --git a/nasl/nasl_scanner_glue.c b/nasl/nasl_scanner_glue.c index 0338c7420..34abfe2a2 100644 --- a/nasl/nasl_scanner_glue.c +++ b/nasl/nasl_scanner_glue.c @@ -1024,8 +1024,9 @@ log_message (lex_ctxt *lexic) return security_something (lexic, proto_post_log, post_log_with_uri); } +// FIXME: the name of the function is too broad, krb5 people also hate prefixes tree_cell * -error_message (lex_ctxt *lexic) +error_message2 (lex_ctxt *lexic) { return security_something (lexic, proto_post_error, post_error); } diff --git a/nasl/nasl_scanner_glue.h b/nasl/nasl_scanner_glue.h index 5ea732f19..113efb9f9 100644 --- a/nasl/nasl_scanner_glue.h +++ b/nasl/nasl_scanner_glue.h @@ -101,7 +101,7 @@ tree_cell * log_message (lex_ctxt *); tree_cell * -error_message (lex_ctxt *); +error_message2 (lex_ctxt *); tree_cell * nasl_scanner_get_port (lex_ctxt *); diff --git a/rust/examples/.gitignore b/rust/examples/.gitignore new file mode 100644 index 000000000..73d41de61 --- /dev/null +++ b/rust/examples/.gitignore @@ -0,0 +1 @@ +test.sh diff --git a/rust/examples/gss.nasl b/rust/examples/gss.nasl new file mode 100644 index 000000000..301f5e27d --- /dev/null +++ b/rust/examples/gss.nasl @@ -0,0 +1,31 @@ +display('do more hate-driven development'); +result = krb5_gss_init(); +if (krb5_is_failure(result)) { + display('oh nooo, unable to init gss context'); + exit(42); +} +display("Got context, the easiest part is done."); +display("Keep in mind that gss_init does override previous context."); +#result = krb5_gss_prepare_context(realm: 'KBKERB.LOCAL', host: 'WIN-MPDRO9RF6Q8.gbkerb.local', service: 'cifs', user: 'gbadmin', password: '*********'); +result = krb5_gss_prepare_context(); +if (krb5_is_failure()) { + display('oh nooo, unable to authenticate, did you check vpn? Yes, oh.'); + exit(42); +} +display("We got authenticated.... keep in mint that the first update context must be without data ..."); +result = krb5_gss_update_context(); +if (krb5_is_failure()) { + display('oh nooo, unable to initially update context, did you check vpn? Yes, oh.'); + exit(42); +} +if (krb5_gss_update_context_needs_more()) { + display('continue to send data to update_context...'); +} + +out = krb5_gss_update_context_out(); +if (out) { + display(hexstr(out)); +} else { + display('no data?!'); +} + From 7af498dc018484f138bd3c38d931e4b6edd9b32e Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Mon, 7 Oct 2024 09:15:17 +0000 Subject: [PATCH 10/18] Mhm --- rust/examples/gss.nasl | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/rust/examples/gss.nasl b/rust/examples/gss.nasl index 301f5e27d..ff9e68711 100644 --- a/rust/examples/gss.nasl +++ b/rust/examples/gss.nasl @@ -1,20 +1,20 @@ -display('do more hate-driven development'); -result = krb5_gss_init(); -if (krb5_is_failure(result)) { - display('oh nooo, unable to init gss context'); - exit(42); -} -display("Got context, the easiest part is done."); -display("Keep in mind that gss_init does override previous context."); +#display('do more hate-driven-development'); +#result = krb5_gss_init(); +#if (krb5_is_failure(result)) { +# display('oh nooo, unable to init gss context'); +# exit(42); +#} +#display("Got context, the easiest part is done."); +#display("Keep in mind that gss_init does override previous context."); #result = krb5_gss_prepare_context(realm: 'KBKERB.LOCAL', host: 'WIN-MPDRO9RF6Q8.gbkerb.local', service: 'cifs', user: 'gbadmin', password: '*********'); result = krb5_gss_prepare_context(); -if (krb5_is_failure()) { +if (krb5_is_failure(result)) { display('oh nooo, unable to authenticate, did you check vpn? Yes, oh.'); exit(42); } display("We got authenticated.... keep in mint that the first update context must be without data ..."); result = krb5_gss_update_context(); -if (krb5_is_failure()) { +if (krb5_is_failure(result)) { display('oh nooo, unable to initially update context, did you check vpn? Yes, oh.'); exit(42); } @@ -29,3 +29,4 @@ if (out) { display('no data?!'); } +# TODO: provide clean up function From 573198182b708d352534574570efefd97202375d Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Thu, 24 Oct 2024 12:59:03 +0000 Subject: [PATCH 11/18] Add: krb5_gss_session_key, krb5_error_code_to_string Adds new nasl functions: - krb5_gss_session_key: to extract the session key - krb5_error_code_to_string: to translate the stored error code into a string. Usage: ``` sk = krb5_gss_session_key(); display("Error code: " + krb5_error_code_to_string()); ``` --- misc/openvas-krb5.c | 57 ++++++++++++++++++++++++++++++- misc/openvas-krb5.h | 14 +++++--- nasl/nasl_init.c | 2 ++ nasl/nasl_krb5.c | 76 ++++++++++++++++++++---------------------- nasl/nasl_krb5.h | 7 ++++ rust/examples/gss.nasl | 12 +++++++ 6 files changed, 122 insertions(+), 46 deletions(-) diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index e747d2dde..549698155 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -669,7 +669,6 @@ o_krb5_gss_prepare_context (const OKrb5Credential *creds, gss_buffer_desc targetbuf = GSS_C_EMPTY_BUFFER; const struct OKrb5Target *target = &creds->target; - if (gss_context->gss_creds == GSS_C_NO_CREDENTIAL) { if ((result = okrb5_gss_authenticate (creds, gss_context))) @@ -809,3 +808,59 @@ o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, result: return result; } + +char * +okrb5_error_code_to_string (const OKrb5ErrorCode code) +{ +#define HEAP_STRING(var, s) \ + do \ + { \ + var = calloc (1, strlen (s) + 1); \ + goto result; \ + } \ + while (0) + + char *result = NULL; + switch (code) + { + case O_KRB5_SUCCESS: + HEAP_STRING (result, "success"); + case O_KRB5_CONF_NOT_FOUND: + HEAP_STRING (result, "krb5.conf not found"); + case O_KRB5_CONF_NOT_CREATED: + HEAP_STRING (result, "krb5.conf not created"); + case O_KRB5_TMP_CONF_NOT_CREATED: + HEAP_STRING (result, "tmp krb5.conf not created"); + case O_KRB5_TMP_CONF_NOT_MOVED: + HEAP_STRING (result, "tmp krb5.conf not moved"); + case O_KRB5_REALM_NOT_FOUND: + HEAP_STRING (result, "realm not found"); + case O_KRB5_EXPECTED_NULL: + HEAP_STRING (result, "expected null"); + case O_KRB5_EXPECTED_NOT_NULL: + HEAP_STRING (result, "expected not null"); + case O_KRB5_UNABLE_TO_WRITE: + HEAP_STRING (result, "unable to write"); + case O_KRB5_NOMEM: + HEAP_STRING (result, "no memory"); + default: + if (code >= O_KRB5_ERROR) + { + int maj_stat = code - O_KRB5_ERROR; + OM_uint32 min_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + + (void) gss_display_status (&min_stat, maj_stat, GSS_C_GSS_CODE, + GSS_C_NULL_OID, &msg_ctx, &msg); + result = msg.value; + } + else + { + result = NULL; + goto result; + } + } +result: + return result; +} diff --git a/misc/openvas-krb5.h b/misc/openvas-krb5.h index a61952343..59a3c506a 100644 --- a/misc/openvas-krb5.h +++ b/misc/openvas-krb5.h @@ -69,8 +69,6 @@ typedef struct struct OKrb5Target target; } OKrb5Credential; - - // TODO: initializer with default values and NULL typedef struct @@ -155,7 +153,6 @@ o_krb5_cache_request (const OKrb5Credential credentials, const char *data, } \ while (0) - typedef struct OKrb5GSSContext OKrb5GSSContext; // Unsure about bool type @@ -163,9 +160,11 @@ OKrb5ErrorCode o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, struct OKrb5Slice **out); -struct OKrb5GSSContext *okrb5_gss_init_context (void); +struct OKrb5GSSContext * +okrb5_gss_init_context (void); -void okrb5_gss_free_context (struct OKrb5GSSContext *context); +void +okrb5_gss_free_context (struct OKrb5GSSContext *context); OKrb5ErrorCode o_krb5_gss_prepare_context (const OKrb5Credential *creds, @@ -176,5 +175,10 @@ o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, const struct OKrb5Slice *in_data, struct OKrb5Slice **out_data, bool *more); +// Returns NULL if the error code is not found. The returned string must be +// freed by the caller. +char * +okrb5_error_code_to_string (const OKrb5ErrorCode code); + #endif diff --git a/nasl/nasl_init.c b/nasl/nasl_init.c index c09375510..e004a867e 100644 --- a/nasl/nasl_init.c +++ b/nasl/nasl_init.c @@ -424,6 +424,8 @@ static init_func libfuncs[] = { {"krb5_gss_update_context", nasl_okrb5_gss_update_context}, {"krb5_gss_update_context_needs_more", nasl_okrb5_gss_update_context_needs_more}, {"krb5_gss_update_context_out", nasl_okrb5_gss_update_context_out}, + {"krb5_gss_session_key", nasl_okrb5_gss_session_key_context}, + {"krb5_error_code_to_string", nasl_okrb5_error_code_to_string}, {NULL, NULL}}; /* String variables */ diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index e8b8e2af7..2bb934115 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -284,6 +284,16 @@ nasl_okrb5_gss_update_context_needs_more (lex_ctxt *lexic) return retc; } +static inline tree_cell * +okrb5_slice_to_tree_cell (struct OKrb5Slice *slice) +{ + tree_cell *retc = alloc_typed_cell (CONST_DATA); + printf ("slice->data: %s\n", (char *) slice->data); + retc->x.str_val = slice->data; + retc->size = slice->len; + return retc; +} + tree_cell * nasl_okrb5_gss_update_context_out (lex_ctxt *lexic) { @@ -292,51 +302,37 @@ nasl_okrb5_gss_update_context_out (lex_ctxt *lexic) { return FAKE_CELL; } - tree_cell *retc = alloc_typed_cell (CONST_DATA); - retc->x.str_val = to_application->data; - retc->size = to_application->len; - return retc; + return okrb5_slice_to_tree_cell (to_application); } -/* - context = okrb5_gss_init_context (); - printf ("Using realm: %s\n", (char *) credentials.realm.data); - if ((result = o_krb5_gss_prepare_context (&credentials, context))) - { - return 1; - } - printf ("Using realm: %s\n", (char *) credentials.realm.data); - // first call always empty - if ((result = o_krb5_gss_update_context (context, &from_application, - &to_application, &more))) +tree_cell * +nasl_okrb5_gss_session_key_context (lex_ctxt *lexic) +{ + (void) lexic; + struct OKrb5Slice *session_key = NULL; + if (cached_gss_context == NULL) { - return 1; + printf ("cached_gss_context is NULL\n"); + last_okrb5_result = O_KRB5_EXPECTED_NOT_NULL; + return FAKE_CELL; } - printf ("success: %d: outdata_len: %zu\n", result, to_application->len); - - for (size_t i = 0; i < to_application->len; i++) + if ((last_okrb5_result = + o_krb5_gss_session_key_context (cached_gss_context, &session_key)) + != O_KRB5_SUCCESS) { - printf ("%02x", ((char *) to_application->data)[i]); + printf ("o_krb5_gss_session_key_context failed\n"); + return FAKE_CELL; } - printf ("\n"); - -*/ - -/* -*OKrb5ErrorCode -o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, - struct OKrb5Slice **out); - -struct OKrb5GSSContext *okrb5_gss_init_context (void); - -void okrb5_gss_free_context (struct OKrb5GSSContext *context); + return okrb5_slice_to_tree_cell (session_key); +} -OKrb5ErrorCode -o_krb5_gss_prepare_context (const OKrb5Credential *creds, - struct OKrb5GSSContext *gss_context); +tree_cell * -OKrb5ErrorCode -o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, - const struct OKrb5Slice *in_data, - struct OKrb5Slice **out_data, bool *more); -*/ +nasl_okrb5_error_code_to_string (lex_ctxt *lexic) +{ + (void) lexic; + tree_cell *retc = alloc_typed_cell (CONST_STR); + retc->x.str_val = okrb5_error_code_to_string (last_okrb5_result); + retc->size = strlen (retc->x.str_val); + return retc; +} diff --git a/nasl/nasl_krb5.h b/nasl/nasl_krb5.h index 6464ef9be..ab5844a13 100644 --- a/nasl/nasl_krb5.h +++ b/nasl/nasl_krb5.h @@ -98,3 +98,10 @@ nasl_okrb5_gss_update_context_needs_more(lex_ctxt *lexic); tree_cell * nasl_okrb5_gss_update_context_out (lex_ctxt *lexic); + + +tree_cell * +nasl_okrb5_gss_session_key_context (lex_ctxt *lexic); + +tree_cell * +nasl_okrb5_error_code_to_string (lex_ctxt *lexic); diff --git a/rust/examples/gss.nasl b/rust/examples/gss.nasl index ff9e68711..d3e6e3d00 100644 --- a/rust/examples/gss.nasl +++ b/rust/examples/gss.nasl @@ -29,4 +29,16 @@ if (out) { display('no data?!'); } + +soc = open_sock_tcp( 445 ); +if( ! soc ) { + return -1; +} + +display("Forking"); +sk = krb5_gss_session_key(); +display("Error code: " + krb5_error_code_to_string()); +display("Session key: " + hexstr(sk)); + + # TODO: provide clean up function From 1804078ad94433c2d4de6087efa4142cebc99161 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Mon, 4 Nov 2024 08:54:26 +0000 Subject: [PATCH 12/18] example/gss.nasl: send data to target and update context As long as krb5_gss_update_context_needs_more send the krb5 data to the target and update the context with the result of that. --- nasl/nasl_krb5.c | 3 --- rust/examples/gss.nasl | 31 +++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index 2bb934115..465287cf3 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -288,7 +288,6 @@ static inline tree_cell * okrb5_slice_to_tree_cell (struct OKrb5Slice *slice) { tree_cell *retc = alloc_typed_cell (CONST_DATA); - printf ("slice->data: %s\n", (char *) slice->data); retc->x.str_val = slice->data; retc->size = slice->len; return retc; @@ -312,7 +311,6 @@ nasl_okrb5_gss_session_key_context (lex_ctxt *lexic) struct OKrb5Slice *session_key = NULL; if (cached_gss_context == NULL) { - printf ("cached_gss_context is NULL\n"); last_okrb5_result = O_KRB5_EXPECTED_NOT_NULL; return FAKE_CELL; } @@ -320,7 +318,6 @@ nasl_okrb5_gss_session_key_context (lex_ctxt *lexic) o_krb5_gss_session_key_context (cached_gss_context, &session_key)) != O_KRB5_SUCCESS) { - printf ("o_krb5_gss_session_key_context failed\n"); return FAKE_CELL; } return okrb5_slice_to_tree_cell (session_key); diff --git a/rust/examples/gss.nasl b/rust/examples/gss.nasl index d3e6e3d00..a9479617c 100644 --- a/rust/examples/gss.nasl +++ b/rust/examples/gss.nasl @@ -18,11 +18,31 @@ if (krb5_is_failure(result)) { display('oh nooo, unable to initially update context, did you check vpn? Yes, oh.'); exit(42); } -if (krb5_gss_update_context_needs_more()) { - display('continue to send data to update_context...'); +while (krb5_gss_update_context_needs_more()) { + display('continue to send data to update_context...2'); + out = krb5_gss_update_context_out(); + soc = open_sock_tcp( 445 ); + if( ! soc ) { + display('no socket, exiting'); + exit(42); + } + display('sending data to update context...'); + send(socket:soc, data:out); + rec = recv(socket: sock); + if (!rec) { + display('no data received, exiting'); + # trying out ... it seems wrong, but who knows? + exit(42); + } + display('received data: ' + hexstr(rec)); + result = krb5_gss_update_context(rec); + if (krb5_is_failure(result)) { + display('oh nooo, unable to update context, did you check vpn? Yes, oh.'); + exit(42); + } + display('context updated'); } -out = krb5_gss_update_context_out(); if (out) { display(hexstr(out)); } else { @@ -30,11 +50,6 @@ if (out) { } -soc = open_sock_tcp( 445 ); -if( ! soc ) { - return -1; -} - display("Forking"); sk = krb5_gss_session_key(); display("Error code: " + krb5_error_code_to_string()); From d32542a1a697be10a4bbaaea00a27c2e29057cc0 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Tue, 5 Nov 2024 10:39:05 +0000 Subject: [PATCH 13/18] Add: find or add realm, fixes error code handling When creating the credentials nasl_krb5 checks if the provided realm is configured within the provided krb5.conf. If not it adds the provided kdc to the realm within krb5.conf. Additionally this commit fixes the error handling by first setting msg_ctx to 0. --- misc/openvas-krb5.c | 6 ++---- nasl/nasl_krb5.c | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index 549698155..0e8c60867 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -1,6 +1,5 @@ #include "openvas-krb5.h" -#include #include #include #include @@ -78,7 +77,7 @@ o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc) // we don't know if we should free it or just override it. // aborting instead. GUARD_NULL (*kdc, result); - if ((file = fopen ((char *) &creds->config_path.data, "r")) == NULL) + if ((file = fopen ((char *) creds->config_path.data, "r")) == NULL) { result = O_KRB5_CONF_NOT_FOUND; goto result; @@ -762,7 +761,6 @@ o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, *out_data = malloc (sizeof (struct OKrb5Slice)); (*out_data)->data = calloc (1, out_buf.length); memcpy ((*out_data)->data, out_buf.value, out_buf.length); - printf ("out_buf.length: %lu\n", out_buf.length); (*out_data)->len = out_buf.length; gss_release_buffer (&min_stat, &out_buf); @@ -849,7 +847,7 @@ okrb5_error_code_to_string (const OKrb5ErrorCode code) int maj_stat = code - O_KRB5_ERROR; OM_uint32 min_stat; gss_buffer_desc msg; - OM_uint32 msg_ctx; + OM_uint32 msg_ctx = 0; (void) gss_display_status (&min_stat, maj_stat, GSS_C_GSS_CODE, GSS_C_NULL_OID, &msg_ctx, &msg); diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index 465287cf3..7c19a7e21 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -10,10 +10,17 @@ #include // TODO: add string function for result -#define nasl_print_krb_error(lexic, credential, result) \ - nasl_perror (lexic, "%s[config_path: '%s' realm: '%s' user: '%s'] => %d", \ - __func__, credential.config_path.data, credential.realm.data, \ - credential.user.user.data, result); +#define nasl_print_krb_error(lexic, credential, result) \ + do \ + { \ + char *error_str = okrb5_error_code_to_string (result); \ + nasl_perror (lexic, \ + "%s[config_path: '%s' realm: '%s' user: '%s'] => %s (%d)", \ + __func__, credential.config_path.data, \ + credential.realm.data, credential.user.user.data, result); \ + free (error_str); \ + } \ + while (0) OKrb5ErrorCode last_okrb5_result; @@ -44,6 +51,9 @@ static OKrb5Credential build_krb5_credential (lex_ctxt *lexic) { OKrb5Credential credential; + OKrb5ErrorCode code; + + char *kdc = NULL; memset (&credential, 0, sizeof (OKrb5Credential)); set_slice_from_lex_or_env (lexic, credential.config_path, "config_path", @@ -56,6 +66,7 @@ build_krb5_credential (lex_ctxt *lexic) perror_set_slice_from_lex_or_env (lexic, credential.realm, "realm", "KRB5_REALM"); + perror_set_slice_from_lex_or_env (lexic, credential.kdc, "kdc", "KRB5_KDC"); perror_set_slice_from_lex_or_env (lexic, credential.user.user, "user", "KRB5_USER"); perror_set_slice_from_lex_or_env (lexic, credential.user.password, "password", @@ -64,6 +75,25 @@ build_krb5_credential (lex_ctxt *lexic) "KRB5_TARGET_HOST"); // set_slice_from_lex_or_env (lexic, credential.target.service, "service", // "KRB5_TARGET_SERVICE"); + + if ((code = o_krb5_find_kdc (&credential, &kdc))) + { + if (code != O_KRB5_REALM_NOT_FOUND) + { + nasl_print_krb_error (lexic, credential, code); + } + else + { + if ((code = o_krb5_add_realm (&credential, credential.kdc.data))) + { + nasl_print_krb_error (lexic, credential, code); + } + } + } + else + { + free (kdc); + } if (credential.target.service.len == 0) { okrb5_set_slice_from_str (credential.target.service, "cifs"); @@ -301,7 +331,11 @@ nasl_okrb5_gss_update_context_out (lex_ctxt *lexic) { return FAKE_CELL; } - return okrb5_slice_to_tree_cell (to_application); + tree_cell *out = okrb5_slice_to_tree_cell (to_application); + // we need to prevent accidental free it as it is freed when the tree_cell is + // cleaned up + to_application = NULL; + return out; } tree_cell * From a03ab026f4e2df8337aca97fe003d5aaad79834f Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 6 Nov 2024 08:39:10 +0000 Subject: [PATCH 14/18] Refactor: remove old krb5 implementation Since we made the decision to rather use GSS instead of KRB5 directly and accepting that we lose controll in favor of convencience. --- misc/openvas-krb5.c | 304 +------------------------------------------- misc/openvas-krb5.h | 60 --------- 2 files changed, 2 insertions(+), 362 deletions(-) diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index 0e8c60867..f54379602 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -223,312 +223,12 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) return result; } -OKrb5ErrorCode -o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - - // probably better to use heap instead? - krb5_context ctx; - krb5_principal me; - krb5_creds creds; - - GUARD_NULL (*element, result); - if ((result = krb5_init_context (&ctx))) - { - result = result + O_KRB5_ERROR; - goto result; - } - - if ((result = krb5_build_principal (ctx, &me, credentials.realm.len, - (char *) credentials.realm.data, - credentials.user, NULL))) - { - result = result + O_KRB5_ERROR; - goto result; - }; - - if ((result = krb5_get_init_creds_password ( - ctx, &creds, me, (char *) credentials.user.password.data, NULL, NULL, - 0, NULL, NULL))) - { - result = result + O_KRB5_ERROR; - goto result; - } - ALLOCATE_AND_CHECK (*element, OKrb5Element, 1, result); - (*element)->me = me; - (*element)->creds = creds; - (*element)->ctx = ctx; - -result: - if (result != O_KRB5_SUCCESS) - { - krb5_free_principal (ctx, me); - krb5_free_context (ctx); - if (*element != NULL) - { - free (*element); - *element = NULL; - } - } - - return result; -} - -void -o_krb5_free_element (OKrb5Element *element) -{ - if (element != NULL) - { - krb5_free_cred_contents (element->ctx, &element->creds); - krb5_free_principal (element->ctx, element->me); - krb5_free_context (element->ctx); - free (element); - } -} - -OKrb5ErrorCode -o_krb5_request (const OKrb5Element *element, const char *data, - const size_t data_len, OKrb5Data **out) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - GUARD_NOT_NULL (out, result); - GUARD_NULL (*out, result); - GUARD_NOT_NULL (element, result); - ALLOCATE_AND_CHECK (*out, OKrb5Data, 1, result); - krb5_data in_data; - int ap_req_options = 0; - - in_data.length = data_len; - in_data.data = (char *) data; - - if ((result = krb5_auth_con_init (element->ctx, &(*out)->auth_context))) - { - result = result + O_KRB5_ERROR; - goto result; - }; - - if ((result = krb5_mk_req_extended ( - element->ctx, &(*out)->auth_context, ap_req_options, &in_data, - (krb5_creds *) &(element->creds), &(*out)->data))) - { - result = result + O_KRB5_ERROR; - }; -result: - return result; -} - -OKrb5ErrorCode -o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - GUARD_NOT_NULL (element, result); - - if (data != NULL) - { - if ((result = krb5_auth_con_free (element->ctx, data->auth_context))) - { - result += O_KRB5_ERROR; - goto result; - }; - free (data); - } -result: - return result; -} - -#if OPENVAS_KRB5_CACHED == 1 -// we use FNV-1a to generate the id so that we don't need to introduce artifical -// numbers but can just reuse credentials to find connections that way we can -// simply reconnect when we either don't find an entry or when the ticket is -// invalid witout having the caller to remember artifical identifier. -static unsigned long -o_krb5_cache_credential_id (const OKrb5Credential *cred) -{ - unsigned long hash = 2166136261; - unsigned int prime = 16777219; - - for (const char *str = cred->realm.data; *str; str++) - { - hash = (hash ^ *str) * prime; - } - for (const char *str = cred->user.user.data; *str; str++) - { - hash = (hash ^ *str) * prime; - } - for (const char *str = cred->user.password.data; *str; str++) - { - hash = (hash ^ *str) * prime; - } - return hash; -} - -static OKrb5CacheList *element_cache = NULL; - -OKrb5ErrorCode -o_krb5_cache_init (void) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - GUARD_NULL (element_cache, result); - ALLOCATE_AND_CHECK (element_cache, OKrb5CacheList, 1, result); - element_cache->cap = 2; - ALLOCATE_AND_CHECK (element_cache->elements, OKrb5CacheElement *, - element_cache->cap, result); -result: - return result; -} - -OKrb5ErrorCode -o_krb5_cache_clear (void) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - size_t i; - if (element_cache == NULL) - goto result; - for (i = 0; i < element_cache->len; i++) - { - o_krb5_free_element ((element_cache->elements[i])->element); - free (element_cache->elements[i]); - } - free (element_cache); - element_cache = NULL; - -result: - return result; -} - -OKrb5CacheElement * -o_krb5_cache_find (const OKrb5Credential *cred) -{ - if (element_cache == NULL) - { - return NULL; - } - unsigned long id = o_krb5_cache_credential_id (cred); - size_t i; - - for (i = 0; i < element_cache->len; i++) - { - if (element_cache->elements[i]->id == id) - { - return element_cache->elements[i]; - } - } - return NULL; -} -static OKrb5ErrorCode -o_krb5_cache_add_element (const OKrb5Credential credentials, - OKrb5CacheElement **out) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - - OKrb5CacheElement **new_elements; - if (element_cache->len == element_cache->cap) - { - if ((new_elements = - realloc (element_cache->elements, - element_cache->cap * 2 * sizeof (OKrb5CacheElement *))) - == NULL) - { - result = O_KRB5_NOMEM; - goto result; - } - memset (new_elements + element_cache->cap, 0, - element_cache->cap * sizeof (OKrb5CacheElement *)); - element_cache->cap = element_cache->cap * 2; - element_cache->elements = new_elements; - } - - ALLOCATE_AND_CHECK (*out, OKrb5CacheElement, 1, result); - (*out)->credentials = &credentials; - (*out)->id = o_krb5_cache_credential_id (&credentials); - element_cache->elements[element_cache->len] = *out; - - if ((result = o_krb5_authenticate (credentials, &(*out)->element))) - { - (*out)->last_error_code = result; - goto result; - } - element_cache->len += 1; - -result: - return result; -} - -OKrb5ErrorCode -o_krb5_cache_authenticate (const OKrb5Credential credentials, - OKrb5CacheElement **out) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - GUARD_NOT_NULL (out, result); - GUARD_NULL (*out, result); - - if (element_cache == NULL) - o_krb5_cache_init (); - OKrb5CacheElement *element = o_krb5_cache_find (&credentials); - - if (element == NULL) - { - if ((result = o_krb5_cache_add_element (credentials, &element))) - { - goto result; - } - } - else - { - time_t systime; - systime = time (NULL); - if (systime >= element->element->creds.times.endtime) - { - // TODO: add renew when till is lower than systime - o_krb5_free_element (element->element); - element->element = NULL; - if ((result = o_krb5_authenticate (credentials, &element->element))) - { - element->last_error_code = result; - goto result; - } - } - } - *out = element; -result: - return result; -} - -OKrb5ErrorCode -o_krb5_cache_request (const OKrb5Credential credentials, const char *data, - const size_t data_len, OKrb5Data **out) -{ - OKrb5ErrorCode result = O_KRB5_SUCCESS; - - printf ("hi: %s %d\n", __func__, __LINE__); - if (element_cache == NULL) - o_krb5_cache_init (); - printf ("hi: %s %d\n", __func__, __LINE__); - OKrb5CacheElement *element = NULL; - if ((result = o_krb5_cache_authenticate (credentials, &element))) - { - goto result; - } - printf ("hi: %s %d\n", __func__, __LINE__); - - if ((result = o_krb5_request (element->element, data, data_len, out))) - { - goto result; - } - printf ("hi: %s %d\n", __func__, __LINE__); - -result: - return result; -} - -#endif - -// GSS stuff, remove rest except for REALM handling #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +// TODO: make GSS_KRB5_INQ_SSPI_SESSION_KEY_OID dynamic #define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID \ "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" + #ifndef gss_mech_spnego gss_OID_desc spnego_mech_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; #define gss_mech_spnego (&spnego_mech_oid_desc) diff --git a/misc/openvas-krb5.h b/misc/openvas-krb5.h index 59a3c506a..9da377907 100644 --- a/misc/openvas-krb5.h +++ b/misc/openvas-krb5.h @@ -32,13 +32,6 @@ typedef enum O_KRB5_ERROR, } OKrb5ErrorCode; -typedef struct -{ - krb5_context ctx; - krb5_principal me; - krb5_creds creds; -} OKrb5Element; - struct OKrb5Slice { void *data; @@ -69,8 +62,6 @@ typedef struct struct OKrb5Target target; } OKrb5Credential; -// TODO: initializer with default values and NULL - typedef struct { krb5_data data; @@ -89,56 +80,6 @@ o_krb5_find_kdc (const OKrb5Credential *creds, char **kdc); OKrb5ErrorCode o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc); -// Is used to get a ticket based on the given credentials. -// -// It will store the ticket into element, it requires that elemenet is not NULL -// but that the value of element is NULL. Otherwise an error code is returned. -OKrb5ErrorCode -o_krb5_authenticate (const OKrb5Credential credentials, OKrb5Element **element); - -OKrb5ErrorCode -o_krb5_request (const OKrb5Element *element, const char *data, - const size_t data_len, OKrb5Data **out); - -OKrb5ErrorCode -o_krb5_free_data (const OKrb5Element *element, OKrb5Data *data); - -void -o_krb5_free_element (OKrb5Element *element); - -#if OPENVAS_KRB5_CACHED == 1 - -typedef struct -{ - const OKrb5Credential *credentials; - OKrb5Element *element; - OKrb5ErrorCode last_error_code; - unsigned long id; -} OKrb5CacheElement; - -typedef struct -{ - size_t cap; - size_t len; - OKrb5CacheElement **elements; -} OKrb5CacheList; - -OKrb5ErrorCode -o_krb5_cache_init (void); -OKrb5ErrorCode -o_krb5_cache_clear (void); - -OKrb5CacheElement * -o_krb5_cache_find (const OKrb5Credential *cred); -OKrb5ErrorCode -o_krb5_cache_authenticate (const OKrb5Credential credentials, - OKrb5CacheElement **out); - -OKrb5ErrorCode -o_krb5_cache_request (const OKrb5Credential credentials, const char *data, - const size_t data_len, OKrb5Data **out); -#endif - #define okrb5_slice_from_str(str) \ (struct OKrb5Slice) \ { \ @@ -180,5 +121,4 @@ o_krb5_gss_update_context (struct OKrb5GSSContext *gss_context, char * okrb5_error_code_to_string (const OKrb5ErrorCode code); - #endif From c86cbd8aa73bc9c2daa3d081ab394db2e4517a38 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Wed, 6 Nov 2024 13:11:31 +0000 Subject: [PATCH 15/18] Minor refacting Reduce cognitive load by using a macro for testing maj stat within `orkb5_gss_authenticate`, Free user_principal as it is not needed after the authentication is done. --- misc/openvas-krb5.c | 65 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index f54379602..6f08ad82f 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -62,6 +62,18 @@ ? 1 \ : (memcmp (line + start, cmp, cmp_len) == 0)) +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 +// TODO: make GSS_KRB5_INQ_SSPI_SESSION_KEY_OID dynamic +#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID \ + "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" + +#ifndef gss_mech_spnego +gss_OID_desc spnego_mech_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; +#define gss_mech_spnego (&spnego_mech_oid_desc) +#endif + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) + #define MAX_LINE_LENGTH 1024 // Finds the kdc defined for the given realm. OKrb5ErrorCode @@ -223,19 +235,6 @@ o_krb5_add_realm (const OKrb5Credential *creds, const char *kdc) return result; } - -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID_LENGTH 11 -// TODO: make GSS_KRB5_INQ_SSPI_SESSION_KEY_OID dynamic -#define GSS_KRB5_INQ_SSPI_SESSION_KEY_OID \ - "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x05" - -#ifndef gss_mech_spnego -gss_OID_desc spnego_mech_oid_desc = {6, (void *) "\x2b\x06\x01\x05\x05\x02"}; -#define gss_mech_spnego (&spnego_mech_oid_desc) -#endif - -#define ARRAY_SIZE(a) (sizeof (a) / sizeof (a[0])) - struct OKrb5GSSCredentials { gss_cred_id_t gss_creds; @@ -259,14 +258,21 @@ static OKrb5ErrorCode okrb5_gss_authenticate (const OKrb5Credential *creds, struct OKrb5GSSContext *gss_creds) { +#define check_major_stat() \ + if (maj_stat != GSS_S_COMPLETE) \ + { \ + result = O_KRB5_ERROR + maj_stat; \ + goto result; \ + } char *user_principal; const struct OKrb5User *user = &creds->user; + size_t user_principal_len = user->user.len + creds->realm.len + 1; + size_t user_principal_cap = user_principal_len + 1; OKrb5ErrorCode result = O_KRB5_SUCCESS; - ALLOCATE_AND_CHECK (user_principal, char, - user->user.len + creds->realm.len + 2, result); - sprintf (user_principal, "%s@%s", (char *) user->user.data, - (char *) creds->realm.data); + ALLOCATE_AND_CHECK (user_principal, char, user_principal_cap, result); + snprintf (user_principal, user_principal_cap, "%s@%s", + (char *) user->user.data, (char *) creds->realm.data); gss_name_t gss_username = GSS_C_NO_NAME; OM_uint32 maj_stat; @@ -274,7 +280,7 @@ okrb5_gss_authenticate (const OKrb5Credential *creds, // OM_uint32 dummy_min_stat; gss_buffer_desc userbuf = { .value = user_principal, - .length = strlen (user_principal), + .length = user_principal_len, }; gss_buffer_desc pwbuf = { .value = user->password.data, @@ -299,32 +305,22 @@ okrb5_gss_authenticate (const OKrb5Credential *creds, maj_stat = gss_import_name (&min_stat, &userbuf, GSS_C_NT_USER_NAME, &gss_username); - if (maj_stat != GSS_S_COMPLETE) - { - return O_KRB5_ERROR + maj_stat; - } + check_major_stat (); maj_stat = gss_acquire_cred_with_password (&min_stat, gss_username, &pwbuf, 0, &creds_mechs, GSS_C_INITIATE, &cred, NULL, NULL); - //(void) gss_release_name (&dummy_min_stat, &gss_username); - if (maj_stat != GSS_S_COMPLETE) - { - // return NT_STATUS_LOGON_FAILURE; - return O_KRB5_ERROR + maj_stat; - } + (void) gss_release_name (&min_stat, &gss_username); + check_major_stat (); // let spnego only use the desired mechs maj_stat = gss_set_neg_mechs (&min_stat, cred, &spnego_mechs); - if (maj_stat != GSS_S_COMPLETE) - { - // failed setting neg mechs - return O_KRB5_ERROR + maj_stat; - } + check_major_stat (); gss_creds->gss_creds = cred; result: - // TODO: free user_principal on failure? + if (user_principal != NULL) + free (user_principal); return result; } @@ -497,7 +493,6 @@ o_krb5_gss_session_key_context (struct OKrb5GSSContext *gss_context, goto result; } - // TODO: verify out *out = calloc (1, sizeof (struct OKrb5Slice)); (*out)->data = malloc (set->elements[0].length); memcpy ((*out)->data, set->elements[0].value, set->elements[0].length); From 421f68699e101b1b198200efd878baf4a0f858c7 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Thu, 7 Nov 2024 11:15:46 +0000 Subject: [PATCH 16/18] Fix: krb5_gss_update_context and okrb5_gss_free_context In krb5_gss_update_context the data len was retrived by using strlen, this is incorrect as the data is not a str, although the name indicates it, but is actually a pointer to raw data and therefore `get_var_size_by_num` must be used instead. The freeing method `okrb5_gss_free_context` did not release gss specific data this is changed. --- misc/openvas-krb5.c | 27 ++++++++++++++++++++- nasl/nasl_krb5.c | 5 ++-- nasl/nasl_var.c | 1 + rust/examples/gss.nasl | 55 ++++++++++++++++++++++++------------------ 4 files changed, 61 insertions(+), 27 deletions(-) diff --git a/misc/openvas-krb5.c b/misc/openvas-krb5.c index 6f08ad82f..60079dfb4 100644 --- a/misc/openvas-krb5.c +++ b/misc/openvas-krb5.c @@ -346,7 +346,32 @@ okrb5_gss_free_context (struct OKrb5GSSContext *context) { gss_release_cred (NULL, &context->gss_creds); } - // TODO: clean rest + if (context->gss_ctx != GSS_C_NO_CONTEXT) + { + gss_delete_sec_context (NULL, &context->gss_ctx, GSS_C_NO_BUFFER); + } + if (context->gss_target != GSS_C_NO_NAME) + { + gss_release_name (NULL, &context->gss_target); + } + if (context->gss_mech != NULL) + { + gss_release_oid (NULL, &context->gss_mech); + } + if (context->gss_channel_bindings != GSS_C_NO_CHANNEL_BINDINGS) + { + gss_release_buffer ( + NULL, &context->gss_channel_bindings->initiator_address); + gss_release_buffer (NULL, + &context->gss_channel_bindings->acceptor_address); + gss_release_buffer (NULL, + &context->gss_channel_bindings->application_data); + free (context->gss_channel_bindings); + } + if (context->gss_actual_mech_type != NULL) + { + gss_release_oid (NULL, &context->gss_actual_mech_type); + } free (context); } } diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index 7c19a7e21..b68f868c8 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -276,11 +276,11 @@ bool gss_update_context_more = false; tree_cell * nasl_okrb5_gss_update_context (lex_ctxt *lexic) { - (void) lexic; OKrb5ErrorCode result = O_KRB5_SUCCESS; tree_cell *retc; struct OKrb5Slice from_application; + if (to_application != NULL) { free (to_application->data); @@ -288,7 +288,8 @@ nasl_okrb5_gss_update_context (lex_ctxt *lexic) to_application = NULL; } - okrb5_set_slice_from_str (from_application, get_str_var_by_num (lexic, 0)); + from_application.data = (void *)get_str_var_by_num (lexic, 0); + from_application.len = get_var_size_by_num (lexic, 0); if (cached_gss_context == NULL) { diff --git a/nasl/nasl_var.c b/nasl/nasl_var.c index 81e1061f8..1c499019b 100644 --- a/nasl/nasl_var.c +++ b/nasl/nasl_var.c @@ -1077,6 +1077,7 @@ get_variable_by_name (lex_ctxt *ctxt, const char *name) case VAR2_STRING: case VAR2_DATA: v->string_form = g_malloc0 (v->v.v_str.s_siz + 1); + fprintf(stderr, "%s:%d: %s (type: %i, s_size:%i): %s\n", __FILE__, __LINE__, __func__, v->var_type, v->v.v_str.s_siz, v->v.v_str.s_val); memcpy (v->string_form, (char *) v->v.v_str.s_val ? (char *) v->v.v_str.s_val : "", v->v.v_str.s_siz + 1); diff --git a/rust/examples/gss.nasl b/rust/examples/gss.nasl index a9479617c..1c84820ef 100644 --- a/rust/examples/gss.nasl +++ b/rust/examples/gss.nasl @@ -18,30 +18,30 @@ if (krb5_is_failure(result)) { display('oh nooo, unable to initially update context, did you check vpn? Yes, oh.'); exit(42); } -while (krb5_gss_update_context_needs_more()) { - display('continue to send data to update_context...2'); - out = krb5_gss_update_context_out(); - soc = open_sock_tcp( 445 ); - if( ! soc ) { - display('no socket, exiting'); - exit(42); - } - display('sending data to update context...'); - send(socket:soc, data:out); - rec = recv(socket: sock); - if (!rec) { - display('no data received, exiting'); - # trying out ... it seems wrong, but who knows? - exit(42); - } - display('received data: ' + hexstr(rec)); - result = krb5_gss_update_context(rec); - if (krb5_is_failure(result)) { - display('oh nooo, unable to update context, did you check vpn? Yes, oh.'); - exit(42); - } - display('context updated'); -} +# while (krb5_gss_update_context_needs_more()) { +# display('continue to send data to update_context...2'); +# out = krb5_gss_update_context_out(); +# soc = open_sock_tcp( 445 ); +# if( ! soc ) { +# display('no socket, exiting'); +# exit(42); +# } +# display('sending data to update context...'); +# send(socket:soc, data:out); +# rec = recv(socket: sock); +# if (!rec) { +# display('no data received, exiting'); +# # trying out ... it seems wrong, but who knows? +# exit(42); +# } +# display('received data: ' + hexstr(rec)); +# result = krb5_gss_update_context(rec); +# if (krb5_is_failure(result)) { +# display('oh nooo, unable to update context, did you check vpn? Yes, oh.'); +# exit(42); +# } +# display('context updated'); +# } if (out) { display(hexstr(out)); @@ -54,6 +54,13 @@ display("Forking"); sk = krb5_gss_session_key(); display("Error code: " + krb5_error_code_to_string()); display("Session key: " + hexstr(sk)); +display("checking cleaning"); +result = krb5_gss_init(); +if (krb5_is_failure(result)) { + display('oh nooo, unable to init gss context'); + exit(42); +} + # TODO: provide clean up function From aff0b7e0d0ae702300e44d4fd9545e82c88485cb Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Thu, 7 Nov 2024 13:07:37 +0000 Subject: [PATCH 17/18] Add: releasing of gss when scan_attack is finished When scan_attack is done every cached gss context should be released/cleared. --- misc/scanneraux.c | 1 + nasl/nasl_krb5.c | 19 ++++++++++++++--- nasl/nasl_krb5.h | 53 +++++++++++++++++++++++++++-------------------- nasl/nasl_var.c | 1 - src/openvas.c | 2 ++ 5 files changed, 50 insertions(+), 26 deletions(-) diff --git a/misc/scanneraux.c b/misc/scanneraux.c index 33a351b98..b6b0acfe7 100644 --- a/misc/scanneraux.c +++ b/misc/scanneraux.c @@ -9,6 +9,7 @@ */ #include "scanneraux.h" +#include "../nasl/nasl_krb5.h" void destroy_scan_globals (struct scan_globals *globals) diff --git a/nasl/nasl_krb5.c b/nasl/nasl_krb5.c index b68f868c8..4e3a7c66a 100644 --- a/nasl/nasl_krb5.c +++ b/nasl/nasl_krb5.c @@ -280,7 +280,6 @@ nasl_okrb5_gss_update_context (lex_ctxt *lexic) tree_cell *retc; struct OKrb5Slice from_application; - if (to_application != NULL) { free (to_application->data); @@ -288,7 +287,7 @@ nasl_okrb5_gss_update_context (lex_ctxt *lexic) to_application = NULL; } - from_application.data = (void *)get_str_var_by_num (lexic, 0); + from_application.data = (void *) get_str_var_by_num (lexic, 0); from_application.len = get_var_size_by_num (lexic, 0); if (cached_gss_context == NULL) @@ -306,6 +305,21 @@ nasl_okrb5_gss_update_context (lex_ctxt *lexic) return retc; } +void +nasl_okrb5_clean (void) +{ + if (to_application != NULL) + { + free (to_application->data); + free (to_application); + to_application = NULL; + } + if (cached_gss_context != NULL) + { + okrb5_gss_free_context (cached_gss_context); + } +} + tree_cell * nasl_okrb5_gss_update_context_needs_more (lex_ctxt *lexic) { @@ -359,7 +373,6 @@ nasl_okrb5_gss_session_key_context (lex_ctxt *lexic) } tree_cell * - nasl_okrb5_error_code_to_string (lex_ctxt *lexic) { (void) lexic; diff --git a/nasl/nasl_krb5.h b/nasl/nasl_krb5.h index ab5844a13..12b75b4d6 100644 --- a/nasl/nasl_krb5.h +++ b/nasl/nasl_krb5.h @@ -2,18 +2,21 @@ #include "nasl_tree.h" /** - * @brief Returns the defined KDC of a given Realm + * @brief Returns the defined KDC of a given Realm * - * This function returns the KDC of a given Realm. The Realm is defined in the krb5.conf file. - * If there is no KDC for the given Realm, the function returns NULL within the tree_cell to the script. + * This function returns the KDC of a given Realm. The Realm is defined in the + * krb5.conf file. If there is no KDC for the given Realm, the function returns + * NULL within the tree_cell to the script. * * The nasl function has two optional parameter: - * - realm: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_REALM` is used. - * - config_path: The path to the krb5.conf file. If the path is not defined, then the env parameter `KRB5_CONFIG` is used. + * - realm: The realm for which the KDC should be returned. If the realm is not + * defined, then the env parameter `KRB5_REALM` is used. + * - config_path: The path to the krb5.conf file. If the path is not defined, + * then the env parameter `KRB5_CONFIG` is used. * * This function should only be used for debug purposes. * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing the KDC as a string. */ @@ -23,16 +26,20 @@ nasl_okrb5_find_kdc (lex_ctxt *lexic); /** * @brief Adds the given KDC to the given Realm * - * This function returns 0 on success. To retrieve a human readable error message, the function `okrb5_result` can be used. + * This function returns 0 on success. To retrieve a human readable error + * message, the function `okrb5_result` can be used. * * The nasl function has three optional parameter: - * - realm: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_REALM` is used. - * - kdc: The realm for which the KDC should be returned. If the realm is not defined, then the env parameter `KRB5_KDC` is used. - * - config_path: The path to the krb5.conf file. If the path is not defined, then the env parameter `KRB5_CONFIG` is used. + * - realm: The realm for which the KDC should be returned. If the realm is not + * defined, then the env parameter `KRB5_REALM` is used. + * - kdc: The realm for which the KDC should be returned. If the realm is not + * defined, then the env parameter `KRB5_KDC` is used. + * - config_path: The path to the krb5.conf file. If the path is not defined, + * then the env parameter `KRB5_CONFIG` is used. * * This function should only be used for debug purposes. * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success or failure. */ @@ -43,10 +50,11 @@ nasl_okrb5_add_realm (lex_ctxt *lexic); * @brief Returns the last result of the krb5 functions as a string * * The nasl function has one optional parameter: - * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * - retval: the return value of the krb5 function. If the value is not defined, + * the return value of the last krb5 function is used. * * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success or failure. */ @@ -57,10 +65,11 @@ nasl_okrb5_result (lex_ctxt *lexic); * @brief Returns 1 if the krb5 function was successful 0 otherwise * * The nasl function has one optional parameter: - * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * - retval: the return value of the krb5 function. If the value is not defined, + * the return value of the last krb5 function is used. * * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success. */ @@ -71,10 +80,11 @@ nasl_okrb5_is_success (lex_ctxt *lexic); * @brief Returns 0 if the krb5 function was successful and 1 if it failed * * The nasl function has one optional parameter: - * - retval: the return value of the krb5 function. If the value is not defined, the return value of the last krb5 function is used. + * - retval: the return value of the krb5 function. If the value is not defined, + * the return value of the last krb5 function is used. * * - * @param[in] lexic NASL lexer. + * @param[in] lexic NASL lexer. * * @return lex cell containing a number indicating success. */ @@ -84,24 +94,23 @@ nasl_okrb5_is_failure (lex_ctxt *lexic); tree_cell * nasl_okrb5_gss_init (lex_ctxt *lexic); - tree_cell * nasl_okrb5_gss_prepare_context (lex_ctxt *lexic); - tree_cell * nasl_okrb5_gss_update_context (lex_ctxt *lexic); - tree_cell * -nasl_okrb5_gss_update_context_needs_more(lex_ctxt *lexic); +nasl_okrb5_gss_update_context_needs_more (lex_ctxt *lexic); tree_cell * nasl_okrb5_gss_update_context_out (lex_ctxt *lexic); - tree_cell * nasl_okrb5_gss_session_key_context (lex_ctxt *lexic); tree_cell * nasl_okrb5_error_code_to_string (lex_ctxt *lexic); + +void +nasl_okrb5_clean (void); diff --git a/nasl/nasl_var.c b/nasl/nasl_var.c index 1c499019b..81e1061f8 100644 --- a/nasl/nasl_var.c +++ b/nasl/nasl_var.c @@ -1077,7 +1077,6 @@ get_variable_by_name (lex_ctxt *ctxt, const char *name) case VAR2_STRING: case VAR2_DATA: v->string_form = g_malloc0 (v->v.v_str.s_siz + 1); - fprintf(stderr, "%s:%d: %s (type: %i, s_size:%i): %s\n", __FILE__, __LINE__, __func__, v->var_type, v->v.v_str.s_siz, v->v.v_str.s_val); memcpy (v->string_form, (char *) v->v.v_str.s_val ? (char *) v->v.v_str.s_val : "", v->v.v_str.s_siz + 1); diff --git a/src/openvas.c b/src/openvas.c index 76f951c5c..09679a7b2 100644 --- a/src/openvas.c +++ b/src/openvas.c @@ -57,6 +57,7 @@ #include #include /* for waitpid */ #include /* for close() */ +#include "../nasl/nasl_krb5.h" /* for nasl_okrb5_clean */ #ifdef GIT_REV_AVAILABLE #include "gitrevision.h" @@ -640,6 +641,7 @@ openvas (int argc, char *argv[], char *env[]) gvm_close_sentry (); destroy_scan_globals (globals); + nasl_okrb5_clean(); #ifdef LOG_REFERENCES_AVAILABLE free_log_reference (); #endif // LOG_REFERENCES_AVAILABLE From 2bed824a46de14144d8c3c963bb5d04aea299395 Mon Sep 17 00:00:00 2001 From: Philipp Eder Date: Mon, 11 Nov 2024 09:49:14 +0000 Subject: [PATCH 18/18] Removes obsolete kerberos dir --- kerberos/.gitignore | 3 -- kerberos/authenticate_test.c | 77 ------------------------------------ 2 files changed, 80 deletions(-) delete mode 100644 kerberos/.gitignore delete mode 100644 kerberos/authenticate_test.c diff --git a/kerberos/.gitignore b/kerberos/.gitignore deleted file mode 100644 index bb1351e23..000000000 --- a/kerberos/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -a.out -test.sh -krb5.conf diff --git a/kerberos/authenticate_test.c b/kerberos/authenticate_test.c deleted file mode 100644 index e35c008ad..000000000 --- a/kerberos/authenticate_test.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "../misc/openvas-krb5.h" - -#include -#include -#include - -#define GUARD_ENV_SET(var, env) \ - do \ - { \ - okrb5_set_slice_from_str (var, getenv (env)); \ - if (var.len == 0) \ - { \ - fprintf (stderr, env " is not set\n"); \ - return 1; \ - } \ - } \ - while (0) - -struct OKrb5GSSContext *cached_gss_context = NULL; -int -main () -{ - char *kdc = NULL; - OKrb5ErrorCode result = O_KRB5_SUCCESS; - OKrb5Credential credentials; - - memset (&credentials, 0, sizeof (OKrb5Credential)); - struct OKrb5Slice from_application = {.data = NULL, .len = 0}; - struct OKrb5Slice *to_application = NULL; - bool more = false; - GUARD_ENV_SET (credentials.config_path, "KRB5_CONFIG"); - GUARD_ENV_SET (credentials.realm, "KRB5_REALM"); - GUARD_ENV_SET (credentials.user.user, "KRB5_USER"); - GUARD_ENV_SET (credentials.user.password, "KRB5_PASSWORD"); - GUARD_ENV_SET (credentials.target.host_name, "KRB5_TARGET_HOST"); - GUARD_ENV_SET (credentials.kdc, "KRB5_KDC"); - credentials.target.service = okrb5_slice_from_str ("cifs"); - memset (&credentials.target.domain, 0, sizeof (struct OKrb5Slice)); - printf ("Using realm: %s\n", (char *) credentials.realm.data); - // TODO: move to overall function - // TODO: refactor signature to use slice - // if (o_krb5_find_kdc (&credentials, &kdc)) - // { - // if (o_krb5_add_realm (&credentials, credentials.kdc.data)) - // { - // fprintf (stderr, "Unable to add kdc\n"); - // return 1; - // } - // } - // else - // { - // printf ("Using kdc: %s\n", kdc); - // free (kdc); - // } - cached_gss_context = okrb5_gss_init_context (); - printf ("Using realm: %s\n", (char *) credentials.realm.data); - if ((result = o_krb5_gss_prepare_context (&credentials, cached_gss_context))) - { - fprintf (stderr, "Unable to prepare context: %d\n", result); - return 1; - } - printf ("Using realm: %s\n", (char *) credentials.realm.data); - // first call always empty - if ((result = o_krb5_gss_update_context ( - cached_gss_context, &from_application, &to_application, &more))) - { - fprintf (stderr, "Unable to update context: %d\n", result); - return 1; - } - printf ("success: %d: outdata_len: %zu\n", result, to_application->len); - - for (size_t i = 0; i < to_application->len; i++) - { - printf ("%02x", ((char *) to_application->data)[i]); - } - printf ("\n"); -}