diff options
Diffstat (limited to 'src/krb5_plugin/common')
-rw-r--r-- | src/krb5_plugin/common/radius_clpreauth.c | 46 | ||||
-rw-r--r-- | src/krb5_plugin/common/radius_clpreauth.h | 37 | ||||
-rw-r--r-- | src/krb5_plugin/common/radius_kdcpreauth.c | 611 | ||||
-rw-r--r-- | src/krb5_plugin/common/radius_kdcpreauth.h | 185 | ||||
-rw-r--r-- | src/krb5_plugin/common/utils.c | 254 | ||||
-rw-r--r-- | src/krb5_plugin/common/utils.h | 68 |
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_ */ |