summaryrefslogtreecommitdiffstats
path: root/src/krb5_plugin/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/krb5_plugin/common')
-rw-r--r--src/krb5_plugin/common/radius_clpreauth.c46
-rw-r--r--src/krb5_plugin/common/radius_clpreauth.h37
-rw-r--r--src/krb5_plugin/common/radius_kdcpreauth.c611
-rw-r--r--src/krb5_plugin/common/radius_kdcpreauth.h185
-rw-r--r--src/krb5_plugin/common/utils.c254
-rw-r--r--src/krb5_plugin/common/utils.h68
6 files changed, 1201 insertions, 0 deletions
diff --git a/src/krb5_plugin/common/radius_clpreauth.c b/src/krb5_plugin/common/radius_clpreauth.c
new file mode 100644
index 0000000..b9779b2
--- /dev/null
+++ b/src/krb5_plugin/common/radius_clpreauth.c
@@ -0,0 +1,46 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <krad.h>
+#include <krb5/kdcpreauth_plugin.h>
+
+#include "radius_kdcpreauth.h"
+#include "util/util.h"
+
+void
+sss_radiuscl_init(krb5_context context,
+ krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq *modreq_out)
+{
+ return;
+}
+
+void
+sss_radiuscl_fini(krb5_context context,
+ krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq modreq)
+{
+ return;
+}
diff --git a/src/krb5_plugin/common/radius_clpreauth.h b/src/krb5_plugin/common/radius_clpreauth.h
new file mode 100644
index 0000000..1dfe3df
--- /dev/null
+++ b/src/krb5_plugin/common/radius_clpreauth.h
@@ -0,0 +1,37 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RADIUS_CLPREAUTH_H_
+#define _RADIUS_CLPREAUTH_H_
+
+#include <stdlib.h>
+#include <krb5/preauth_plugin.h>
+
+void
+sss_radiuscl_init(krb5_context context,
+ krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq *modreq_out);
+
+void
+sss_radiuscl_fini(krb5_context context,
+ krb5_clpreauth_moddata moddata,
+ krb5_clpreauth_modreq modreq);
+
+#endif /* _RADIUS_CLPREAUTH_H_ */
diff --git a/src/krb5_plugin/common/radius_kdcpreauth.c b/src/krb5_plugin/common/radius_kdcpreauth.c
new file mode 100644
index 0000000..77f3661
--- /dev/null
+++ b/src/krb5_plugin/common/radius_kdcpreauth.c
@@ -0,0 +1,611 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <krad.h>
+#include <krb5/kdcpreauth_plugin.h>
+
+#include "krb5_plugin/common/radius_kdcpreauth.h"
+#include "krb5_plugin/common/utils.h"
+#include "util/util.h"
+
+krb5_error_code
+sss_radiuskdc_init(const char *plugin_name,
+ krb5_context kctx,
+ krb5_kdcpreauth_moddata *_moddata,
+ const char **_realmnames)
+{
+ struct sss_radiuskdc_state *state;
+
+ state = malloc(sizeof(struct sss_radiuskdc_state));
+ if (state == NULL) {
+ return ENOMEM;
+ }
+
+ state->plugin_name = plugin_name;
+
+ /* IPA is the only consumer so far so it is fine to hardcode the values. */
+ state->server = KRB5_KDC_RUNDIR "/DEFAULT.socket";
+ state->secret = "";
+ state->timeout = 5 * 1000;
+ state->retries = 3;
+
+ *_moddata = (krb5_kdcpreauth_moddata)state;
+
+ return 0;
+}
+
+void
+sss_radiuskdc_fini(krb5_context kctx,
+ krb5_kdcpreauth_moddata moddata)
+{
+ struct sss_radiuskdc_state *state;
+
+ state = (struct sss_radiuskdc_state *)moddata;
+
+ if (state == NULL) {
+ return;
+ }
+
+ free(state);
+}
+
+int
+sss_radiuskdc_flags(krb5_context kctx,
+ krb5_preauthtype pa_type)
+{
+ return PA_REPLACES_KEY;
+}
+
+krb5_error_code
+sss_radiuskdc_return_padata(krb5_context kctx,
+ krb5_pa_data *padata,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa_out,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_moddata moddata,
+ krb5_kdcpreauth_modreq modreq)
+{
+ struct sss_radiuskdc_state *state;
+ krb5_keyblock *armor_key;
+ bool *result;
+
+ state = (struct sss_radiuskdc_state *)moddata;
+ result = (bool *)modreq;
+
+ /* This should not happen. */
+ if (state == NULL) {
+ return EINVAL;
+ }
+
+ /* Verification was not successful. Do not replace the key. */
+ if (result == NULL || *result == false) {
+ return 0;
+ }
+
+ /* Get the armor key. */
+ armor_key = cb->fast_armor(kctx, rock);
+ if (armor_key == NULL) {
+ com_err(state->plugin_name, ENOENT,
+ "No armor key found when returning padata");
+ return ENOENT;
+ }
+
+ /* Replace the reply key with the FAST armor key. */
+ krb5_free_keyblock_contents(kctx, encrypting_key);
+ return krb5_copy_keyblock_contents(kctx, armor_key, encrypting_key);
+}
+
+krb5_error_code
+sss_radiuskdc_enabled(const char *config_name,
+ krb5_context kctx,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ char **_config)
+{
+ krb5_error_code ret;
+ char *config;
+
+ ret = cb->get_string(kctx, rock, config_name, &config);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* Disabled. */
+ if (config == NULL) {
+ return ENOENT;
+ }
+
+ /* Enabled. Return the config string. */
+ *_config = config;
+
+ return 0;
+}
+
+void
+sss_radiuskdc_config_free(struct sss_radiuskdc_config *config)
+{
+ if (config == NULL) {
+ return;
+ }
+
+ free(config->username);
+ free(config->server);
+ free(config->secret);
+ free(config);
+}
+
+krb5_error_code
+sss_radiuskdc_config_init(struct sss_radiuskdc_state *state,
+ krb5_context kctx,
+ krb5_const_principal princ,
+ const char *configstr,
+ struct sss_radiuskdc_config **_config)
+{
+ struct sss_radiuskdc_config *config;
+ krb5_error_code ret;
+ char *username;
+
+ if (state == NULL) {
+ return EINVAL;
+ }
+
+ config = malloc(sizeof(struct sss_radiuskdc_config));
+ if (config == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ memset(config, 0, sizeof(struct sss_radiuskdc_config));
+
+ config->server = strdup(state->server);
+ config->secret = strdup(state->secret);
+ config->retries = state->retries;
+ config->timeout = state->timeout;
+
+ if (config->server == NULL || config->secret == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = krb5_unparse_name_flags(kctx, princ, 0, &username);
+ if (ret != 0) {
+ goto done;
+ }
+
+ config->username = strdup(username);
+ krb5_free_unparsed_name(kctx, username);
+ if (config->username == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_config = config;
+ ret = 0;
+
+done:
+ if (ret != 0) {
+ sss_radiuskdc_config_free(config);
+ }
+
+ return ret;
+}
+
+krb5_error_code
+sss_radiuskdc_set_cookie(krb5_context context,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_preauthtype pa_type,
+ const krb5_data *state)
+{
+ krb5_data cookie;
+ unsigned int len;
+ uint8_t *blob;
+ size_t pctr;
+
+ len = sizeof(uint16_t) + state->length;
+ blob = malloc(len);
+ if (blob == NULL) {
+ return ENOMEM;
+ }
+
+ pctr = 0;
+ SAFEALIGN_SET_UINT16(&blob[pctr], 1, &pctr);
+ SAFEALIGN_SET_STRING(&blob[pctr], state->data, state->length, &pctr);
+
+ cookie.magic = 0;
+ cookie.data = (char *)blob;
+ cookie.length = len;
+
+ return cb->set_cookie(context, rock, pa_type, &cookie);
+}
+
+krb5_error_code
+sss_radiuskdc_get_cookie(krb5_context context,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_preauthtype pa_type,
+ krb5_data *_state)
+{
+ uint16_t version;
+ krb5_data cookie;
+ krb5_data state;
+ size_t pctr;
+
+ if (!cb->get_cookie(context, rock, pa_type, &cookie)) {
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ if (cookie.length < sizeof(uint16_t)) {
+ return EINVAL;
+ }
+
+ pctr = 0;
+ SAFEALIGN_COPY_UINT16(&version, cookie.data, &pctr);
+ state.magic = 0;
+ state.data = &cookie.data[pctr];
+ state.length = cookie.length - sizeof(uint16_t);
+
+ *_state = state;
+
+ return 0;
+}
+
+
+/* Some attributes have limited length. In order to accept longer values,
+ * we will concatenate all attribute values to single krb5_data. */
+krb5_error_code
+sss_radiuskdc_get_complete_attr(const krad_packet *rres,
+ const char *attr_name,
+ krb5_data *_data)
+{
+ krad_attr attr = krad_attr_name2num(attr_name);
+ const krb5_data *rmsg;
+ krb5_data data = {0};
+ unsigned int memindex;
+ unsigned int i;
+
+ i = 0;
+ do {
+ rmsg = krad_packet_get_attr(rres, attr, i);
+ if (rmsg != NULL) {
+ data.length += rmsg->length;
+ }
+ i++;
+ } while (rmsg != NULL);
+
+ if (data.length == 0) {
+ return ENOENT;
+ }
+
+ data.data = malloc(data.length);
+ if (data.data == NULL) {
+ return ENOMEM;
+ }
+
+ i = 0;
+ memindex = 0;
+ do {
+ rmsg = krad_packet_get_attr(rres, attr, i);
+ if (rmsg != NULL) {
+ memcpy(&data.data[memindex], rmsg->data, rmsg->length);
+ memindex += rmsg->length;
+ }
+ i++;
+ } while (rmsg != NULL);
+
+ if (memindex != data.length) {
+ free(data.data);
+ return ERANGE;
+ }
+
+ *_data = data;
+
+ return 0;
+}
+
+/* From krad internals, RFC 2865 */
+#ifndef UCHAR_MAX
+#define UCHAR_MAX 255
+#endif
+#define MAX_ATTRSIZE (UCHAR_MAX - 2)
+
+krb5_error_code
+sss_radiuskdc_put_complete_attr(krad_attrset *attrset,
+ krad_attr attr,
+ const krb5_data *datap)
+{
+ krb5_data state = {0};
+ char *p = datap->data;
+ unsigned int len = datap->length;
+ krb5_error_code ret = 0;
+
+ do {
+ /* - 5 to make sure we fit into minimal value length */
+ state.data = p;
+ state.length = MIN(MAX_ATTRSIZE - 5, len);
+ p += state.length;
+
+ ret = krad_attrset_add(attrset, attr, &(state));
+ if (ret != 0) {
+ break;
+ }
+ len -= state.length;
+ } while (len > 0);
+
+ return ret;
+}
+
+char *
+sss_radiuskdc_get_attr_as_string(const krad_packet *packet, const char *attr)
+{
+ krb5_data data = {0};
+ krb5_error_code ret;
+ char *str;
+
+ ret = sss_radiuskdc_get_complete_attr(packet, attr, &data);
+ if (ret != 0) {
+ return NULL;
+ }
+
+ str = strndup(data.data, data.length);
+ free(data.data);
+
+ return str;
+}
+
+krb5_error_code
+sss_radiuskdc_set_attr_as_string(krad_attrset *attrset,
+ const char *attr,
+ const char *value)
+{
+ krb5_data data = {0};
+ krb5_error_code ret;
+
+ data.data = discard_const(value);
+ data.length = strlen(value) + 1;
+
+ ret = sss_radiuskdc_put_complete_attr(attrset,
+ krad_attr_name2num(attr),
+ &data);
+
+ return ret;
+}
+
+void
+sss_radiuskdc_client_free(struct sss_radiuskdc_client *client)
+{
+ if (client == NULL) {
+ return;
+ }
+
+ krad_client_free(client->client);
+ krad_attrset_free(client->attrs);
+ free(client);
+}
+
+struct sss_radiuskdc_client *
+sss_radiuskdc_client_init(krb5_context kctx,
+ verto_ctx *vctx,
+ struct sss_radiuskdc_config *config)
+{
+ struct sss_radiuskdc_client *client;
+ char hostname[HOST_NAME_MAX + 1];
+ krb5_data data = {0};
+ krb5_error_code ret;
+
+ client = malloc(sizeof(struct sss_radiuskdc_client));
+ if (client == NULL) {
+ return NULL;
+ }
+ memset(client, 0, sizeof(struct sss_radiuskdc_client));
+
+ ret = krad_client_new(kctx, vctx, &client->client);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ ret = krad_attrset_new(kctx, &client->attrs);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ ret = gethostname(hostname, sizeof(hostname) / sizeof(char));
+ if (ret != 0) {
+ goto fail;
+ }
+
+ data.data = hostname;
+ data.length = strlen(hostname);
+ ret = krad_attrset_add(client->attrs, krad_attr_name2num("NAS-Identifier"),
+ &data);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ ret = krad_attrset_add_number(client->attrs, krad_attr_name2num("Service-Type"),
+ KRAD_SERVICE_TYPE_AUTHENTICATE_ONLY);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ data.data = config->username;
+ data.length = strlen(config->username);
+ ret = krad_attrset_add(client->attrs, krad_attr_name2num("User-Name"),
+ &data);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ return client;
+
+fail:
+ sss_radiuskdc_client_free(client);
+ return NULL;
+}
+
+void
+sss_radiuskdc_challenge_free(struct sss_radiuskdc_challenge *state)
+{
+ if (state == NULL) {
+ return;
+ }
+
+ sss_radiuskdc_client_free(state->client);
+ free(state);
+}
+
+struct sss_radiuskdc_challenge *
+sss_radiuskdc_challenge_init(krb5_context kctx,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_edata_respond_fn respond,
+ void *arg,
+ struct sss_radiuskdc_config *config)
+{
+ struct sss_radiuskdc_challenge *state;
+
+ state = malloc(sizeof(struct sss_radiuskdc_challenge));
+ if (state == NULL) {
+ return NULL;
+ }
+ memset(state, 0, sizeof(struct sss_radiuskdc_challenge));
+
+ state->kctx = kctx;
+ state->cb = cb;
+ state->rock = rock;
+ state->respond = respond;
+ state->arg = arg;
+
+ state->client = sss_radiuskdc_client_init(kctx,
+ cb->event_context(kctx, rock),
+ config);
+ if (state->client == NULL) {
+ sss_radiuskdc_challenge_free(state);
+ return NULL;
+ }
+
+ return state;
+}
+
+void
+sss_radiuskdc_verify_free(struct sss_radiuskdc_verify *state)
+{
+ if (state == NULL) {
+ return;
+ }
+
+ sss_string_array_free(state->indicators);
+ sss_radiuskdc_client_free(state->client);
+ free(state);
+}
+
+struct sss_radiuskdc_verify *
+sss_radiuskdc_verify_init(krb5_context kctx,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_kdcpreauth_verify_respond_fn respond,
+ void *arg,
+ char **indicators,
+ struct sss_radiuskdc_config *config)
+{
+ struct sss_radiuskdc_verify *state;
+
+ state = malloc(sizeof(struct sss_radiuskdc_verify));
+ if (state == NULL) {
+ return NULL;
+ }
+ memset(state, 0, sizeof(struct sss_radiuskdc_verify));
+
+ state->kctx = kctx;
+ state->rock = rock;
+ state->cb = cb;
+ state->enc_tkt_reply = enc_tkt_reply;
+ state->respond = respond;
+ state->arg = arg;
+
+ state->indicators = sss_string_array_copy(indicators);
+ if (state->indicators == NULL) {
+ sss_radiuskdc_verify_free(state);
+ return NULL;
+ }
+
+ state->client = sss_radiuskdc_client_init(kctx,
+ cb->event_context(kctx, rock),
+ config);
+ if (state->client == NULL) {
+ sss_radiuskdc_verify_free(state);
+ return NULL;
+ }
+
+ return state;
+}
+
+void
+sss_radiuskdc_verify_done(krb5_error_code rret,
+ const krad_packet *rreq,
+ const krad_packet *rres,
+ void *data)
+{
+ static bool verify_success = true;
+ static bool verify_failure = false;
+ struct sss_radiuskdc_verify *state;
+ krb5_kdcpreauth_modreq modreq;
+ krb5_error_code ret;
+ int i;
+
+ state = (struct sss_radiuskdc_verify *)data;
+ modreq = (krb5_kdcpreauth_modreq)&verify_failure;
+
+ if (rret != 0) {
+ ret = rret;
+ goto done;
+ }
+
+ if (krad_packet_get_code(rres) != krad_code_name2num("Access-Accept")) {
+ ret = KRB5_PREAUTH_FAILED;
+ goto done;
+ }
+
+ state->enc_tkt_reply->flags |= TKT_FLG_PRE_AUTH;
+
+ for (i = 0; state->indicators[i] != NULL; i++) {
+ ret = state->cb->add_auth_indicator(state->kctx, state->rock,
+ state->indicators[i]);
+ if (ret != 0) {
+ goto done;
+ }
+ }
+
+ modreq = (krb5_kdcpreauth_modreq)&verify_success;
+ ret = 0;
+
+done:
+ state->respond(state->arg, ret, modreq, NULL, NULL);
+ sss_radiuskdc_verify_free(state);
+}
diff --git a/src/krb5_plugin/common/radius_kdcpreauth.h b/src/krb5_plugin/common/radius_kdcpreauth.h
new file mode 100644
index 0000000..7e032b3
--- /dev/null
+++ b/src/krb5_plugin/common/radius_kdcpreauth.h
@@ -0,0 +1,185 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _RADIUS_KDCPREAUTH_H_
+#define _RADIUS_KDCPREAUTH_H_
+
+#include <stdlib.h>
+#include <krb5/preauth_plugin.h>
+
+struct sss_radiuskdc_state {
+ const char *plugin_name;
+ const char *server;
+ const char *secret;
+ size_t retries;
+ int timeout;
+};
+
+struct sss_radiuskdc_config {
+ char *username;
+ char *server;
+ char *secret;
+ size_t retries;
+ int timeout;
+};
+
+struct sss_radiuskdc_client {
+ krad_client *client;
+ krad_attrset *attrs;
+};
+
+struct sss_radiuskdc_challenge {
+ struct sss_radiuskdc_client *client;
+
+ krb5_context kctx;
+ krb5_kdcpreauth_callbacks cb;
+ krb5_kdcpreauth_rock rock;
+ krb5_kdcpreauth_edata_respond_fn respond;
+ void *arg;
+};
+
+struct sss_radiuskdc_verify {
+ struct sss_radiuskdc_client *client;
+ char **indicators;
+
+ krb5_context kctx;
+ krb5_kdcpreauth_rock rock;
+ krb5_kdcpreauth_callbacks cb;
+ krb5_enc_tkt_part *enc_tkt_reply;
+ krb5_kdcpreauth_verify_respond_fn respond;
+ void *arg;
+};
+
+krb5_error_code
+sss_radiuskdc_init(const char *plugin_name,
+ krb5_context kctx,
+ krb5_kdcpreauth_moddata *_moddata,
+ const char **_realmnames);
+
+void
+sss_radiuskdc_fini(krb5_context kctx,
+ krb5_kdcpreauth_moddata moddata);
+
+int
+sss_radiuskdc_flags(krb5_context kctx,
+ krb5_preauthtype pa_type);
+
+krb5_error_code
+sss_radiuskdc_return_padata(krb5_context kctx,
+ krb5_pa_data *padata,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_kdc_rep *reply,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa_out,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_moddata moddata,
+ krb5_kdcpreauth_modreq modreq);
+
+krb5_error_code
+sss_radiuskdc_enabled(const char *config_name,
+ krb5_context kctx,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ char **_config);
+
+void
+sss_radiuskdc_config_free(struct sss_radiuskdc_config *config);
+
+krb5_error_code
+sss_radiuskdc_config_init(struct sss_radiuskdc_state *state,
+ krb5_context kctx,
+ krb5_const_principal princ,
+ const char *configstr,
+ struct sss_radiuskdc_config **_config);
+
+krb5_error_code
+sss_radiuskdc_set_cookie(krb5_context context,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_preauthtype pa_type,
+ const krb5_data *state);
+
+krb5_error_code
+sss_radiuskdc_get_cookie(krb5_context context,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_preauthtype pa_type,
+ krb5_data *_state);
+
+krb5_error_code
+sss_radiuskdc_get_complete_attr(const krad_packet *rres,
+ const char *attr_name,
+ krb5_data *_data);
+
+krb5_error_code
+sss_radiuskdc_put_complete_attr(krad_attrset *attrset,
+ krad_attr attr,
+ const krb5_data *datap);
+
+char *
+sss_radiuskdc_get_attr_as_string(const krad_packet *packet, const char *attr);
+
+
+krb5_error_code
+sss_radiuskdc_set_attr_as_string(krad_attrset *attrset,
+ const char *attr,
+ const char *value);
+
+void
+sss_radiuskdc_client_free(struct sss_radiuskdc_client *client);
+
+struct sss_radiuskdc_client *
+sss_radiuskdc_client_init(krb5_context kctx,
+ verto_ctx *vctx,
+ struct sss_radiuskdc_config *config);
+
+void
+sss_radiuskdc_challenge_free(struct sss_radiuskdc_challenge *state);
+
+struct sss_radiuskdc_challenge *
+sss_radiuskdc_challenge_init(krb5_context kctx,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_edata_respond_fn respond,
+ void *arg,
+ struct sss_radiuskdc_config *config);
+
+void
+sss_radiuskdc_verify_free(struct sss_radiuskdc_verify *state);
+
+struct sss_radiuskdc_verify *
+sss_radiuskdc_verify_init(krb5_context kctx,
+ krb5_kdcpreauth_rock rock,
+ krb5_kdcpreauth_callbacks cb,
+ krb5_enc_tkt_part *enc_tkt_reply,
+ krb5_kdcpreauth_verify_respond_fn respond,
+ void *arg,
+ char **indicators,
+ struct sss_radiuskdc_config *config);
+
+void
+sss_radiuskdc_verify_done(krb5_error_code rret,
+ const krad_packet *rreq,
+ const krad_packet *rres,
+ void *data);
+
+#endif /* _RADIUS_KDCPREAUTH_H_ */
diff --git a/src/krb5_plugin/common/utils.c b/src/krb5_plugin/common/utils.c
new file mode 100644
index 0000000..2a0ecc0
--- /dev/null
+++ b/src/krb5_plugin/common/utils.c
@@ -0,0 +1,254 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jansson.h>
+#include <krb5/preauth_plugin.h>
+
+#include "krb5_plugin/common/utils.h"
+
+void
+sss_string_array_free(char **array)
+{
+ size_t i;
+
+ if (array == NULL) {
+ return;
+ }
+
+ for (i = 0; array[i] != NULL; i++) {
+ free(array[i]);
+ }
+
+ free(array);
+}
+
+char **
+sss_string_array_copy(char **array)
+{
+ char **copy;
+ size_t i;
+
+ for (i = 0; array[i] != NULL; i++) {
+ /* Just count. */
+ }
+
+ copy = calloc(i + 1, sizeof(char *));
+ if (copy == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; array[i] != NULL; i++) {
+ copy[i] = strdup(array[i]);
+ if (copy[i] == NULL) {
+ sss_string_array_free(copy);
+ return NULL;
+ }
+ }
+
+ copy[i] = NULL;
+
+ return copy;
+}
+
+char **
+sss_json_array_to_strings(json_t *jarray)
+{
+ const char *strval;
+ char **array;
+ json_t *jval;
+ size_t i;
+
+ if (!json_is_array(jarray)) {
+ return NULL;
+ }
+
+ array = calloc(json_array_size(jarray) + 1, sizeof(char *));
+ if (array == NULL) {
+ return NULL;
+ }
+
+ json_array_foreach(jarray, i, jval) {
+ strval = json_string_value(jval);
+ if (strval == NULL) {
+ goto fail;
+ }
+
+ array[i] = strdup(strval);
+ if (array[i] == NULL) {
+ goto fail;
+ }
+ }
+
+ return array;
+
+fail:
+ sss_string_array_free(array);
+
+ return NULL;
+}
+
+json_t *
+sss_strings_to_json_array(char **array)
+{
+ json_t *jarray;
+ json_t *jstr;
+ size_t i;
+ int jret;
+
+ jarray = json_array();
+ if (jarray == NULL) {
+ return NULL;
+ }
+
+ if (array == NULL) {
+ return jarray;
+ }
+
+ for (i = 0; array[i] != NULL; i++) {
+ jstr = json_string(array[i]);
+ if (jstr == NULL) {
+ goto fail;
+ }
+
+ jret = json_array_append_new(jarray, jstr);
+ if (jret != 0) {
+ goto fail;
+ }
+ }
+
+ return jarray;
+
+fail:
+ json_decref(jarray);
+
+ return NULL;
+}
+
+void *
+sss_radius_message_decode(const char *prefix,
+ sss_radius_message_decode_fn fn,
+ const char *str)
+{
+ size_t prefix_len;
+
+ if (str == NULL) {
+ return NULL;
+ }
+
+ prefix_len = strlen(prefix);
+ if (strncmp(str, prefix, prefix_len) != 0) {
+ return NULL;
+ }
+
+ return fn(str + prefix_len);
+}
+
+char *
+sss_radius_message_encode(const char *prefix,
+ sss_radius_message_encode_fn fn,
+ const void *data)
+{
+ char *json_str;
+ char *str;
+ int aret;
+
+ json_str = fn(data);
+ if (json_str == NULL) {
+ return NULL;
+ }
+
+ aret = asprintf(&str, "%s%s", prefix, json_str);
+ free(json_str);
+ if (aret < 0) {
+ return NULL;
+ }
+
+ return str;
+}
+
+krb5_pa_data *
+sss_radius_encode_padata(krb5_preauthtype patype,
+ sss_radius_message_encode_fn fn,
+ const void *data)
+{
+ krb5_pa_data *padata;
+ char *str;
+
+ str = fn(data);
+ if (str == NULL) {
+ return NULL;
+ }
+
+ padata = malloc(sizeof(krb5_pa_data));
+ if (padata == NULL) {
+ free(str);
+ return NULL;
+ }
+
+ padata->pa_type = patype;
+ padata->contents = (krb5_octet*)str;
+ padata->length = strlen(str) + 1;
+
+ return padata;
+}
+
+void *
+sss_radius_decode_padata(sss_radius_message_decode_fn fn,
+ krb5_pa_data *padata)
+{
+ if (padata->length == 0 || padata->contents == NULL) {
+ return NULL;
+ }
+
+ /* contents is NULL terminated string */
+ if (padata->contents[padata->length - 1] != '\0') {
+ return NULL;
+ }
+
+ return fn((const char*)padata->contents);
+}
+
+krb5_pa_data **
+sss_radius_encode_padata_array(krb5_preauthtype patype,
+ sss_radius_message_encode_fn fn,
+ const void *data)
+{
+ krb5_pa_data **array;
+
+ array = calloc(2, sizeof(krb5_pa_data *));
+ if (array == NULL) {
+ return NULL;
+ }
+
+ array[0] = sss_radius_encode_padata(patype, fn, data);
+ array[1] = NULL;
+
+ if (array[0] == NULL) {
+ free(array);
+ return NULL;
+ }
+
+ return array;
+}
diff --git a/src/krb5_plugin/common/utils.h b/src/krb5_plugin/common/utils.h
new file mode 100644
index 0000000..b7042c6
--- /dev/null
+++ b/src/krb5_plugin/common/utils.h
@@ -0,0 +1,68 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2023 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _KRB5_PLUGIN_UTILS_H_
+#define _KRB5_PLUGIN_UTILS_H_
+
+#include <jansson.h>
+#include <krb5/preauth_plugin.h>
+
+#define is_empty(var) ((var) == NULL || (var)[0] == '\0')
+
+void
+sss_string_array_free(char **array);
+
+char **
+sss_string_array_copy(char **array);
+
+char **
+sss_json_array_to_strings(json_t *jarray);
+
+json_t *
+sss_strings_to_json_array(char **array);
+
+typedef void * (*sss_radius_message_decode_fn)(const char *);
+typedef char * (*sss_radius_message_encode_fn)(const void *);
+
+void *
+sss_radius_message_decode(const char *prefix,
+ sss_radius_message_decode_fn fn,
+ const char *str);
+
+char *
+sss_radius_message_encode(const char *prefix,
+ sss_radius_message_encode_fn fn,
+ const void *data);
+
+krb5_pa_data *
+sss_radius_encode_padata(krb5_preauthtype patype,
+ sss_radius_message_encode_fn fn,
+ const void *data);
+
+void *
+sss_radius_decode_padata(sss_radius_message_decode_fn fn,
+ krb5_pa_data *padata);
+
+krb5_pa_data **
+sss_radius_encode_padata_array(krb5_preauthtype patype,
+ sss_radius_message_encode_fn fn,
+ const void *data);
+
+#endif /* _KRB5_PLUGIN_UTILS_H_ */