diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/krb5_plugin/idp | |
parent | Initial commit. (diff) | |
download | sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip |
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/krb5_plugin/idp')
-rw-r--r-- | src/krb5_plugin/idp/idp.h | 70 | ||||
-rw-r--r-- | src/krb5_plugin/idp/idp_clpreauth.c | 228 | ||||
-rw-r--r-- | src/krb5_plugin/idp/idp_kdcpreauth.c | 406 | ||||
-rw-r--r-- | src/krb5_plugin/idp/idp_utils.c | 273 | ||||
-rw-r--r-- | src/krb5_plugin/idp/sssd_enable_idp | 14 |
5 files changed, 991 insertions, 0 deletions
diff --git a/src/krb5_plugin/idp/idp.h b/src/krb5_plugin/idp/idp.h new file mode 100644 index 0000000..c964580 --- /dev/null +++ b/src/krb5_plugin/idp/idp.h @@ -0,0 +1,70 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2021 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 _IDP_H_ +#define _IDP_H_ + +#include <stdlib.h> +#include <krb5/preauth_plugin.h> + +#ifndef discard_const +#define discard_const(ptr) ((void *)((uintptr_t)(ptr))) +#endif + +#define SSSD_IDP_PLUGIN "idp" +#define SSSD_IDP_CONFIG "idp" +#define SSSD_IDP_OAUTH2_PADATA 152 // PA-REDHAT-IDP-OAUTH2 +#define SSSD_IDP_OAUTH2_QUESTION "idp-oauth2" +#define SSSD_IDP_OAUTH2_PREFIX "oauth2 " + +struct sss_idp_config { + char *type; + char **indicators; +}; + +void +sss_idp_config_free(struct sss_idp_config *idpcfg); + +krb5_error_code +sss_idp_config_init(const char *config, + struct sss_idp_config **_idpcfg); + +struct sss_idp_oauth2 { + char *verification_uri; + char *verification_uri_complete; + char *user_code; +}; + +void +sss_idp_oauth2_free(struct sss_idp_oauth2 *data); + +krb5_pa_data * +sss_idp_oauth2_encode_padata(struct sss_idp_oauth2 *data); + +struct sss_idp_oauth2 * +sss_idp_oauth2_decode_padata(krb5_pa_data *padata); + +char * +sss_idp_oauth2_encode_challenge(struct sss_idp_oauth2 *data); + +struct sss_idp_oauth2 * +sss_idp_oauth2_decode_challenge(const char *str); + +#endif /* _IDP_H_ */ diff --git a/src/krb5_plugin/idp/idp_clpreauth.c b/src/krb5_plugin/idp/idp_clpreauth.c new file mode 100644 index 0000000..91fe346 --- /dev/null +++ b/src/krb5_plugin/idp/idp_clpreauth.c @@ -0,0 +1,228 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2021 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <krb5/clpreauth_plugin.h> + +#include "krb5_plugin/common/radius_clpreauth.h" +#include "idp.h" + +static krb5_pa_data ** +sss_idpcl_encode_padata(void) +{ + krb5_pa_data **padata; + + padata = calloc(2, sizeof(krb5_pa_data *)); + if (padata == NULL) { + return NULL; + } + + padata[0] = malloc(sizeof(krb5_pa_data)); + if (padata[0] == NULL) { + free(padata); + return NULL; + } + + padata[0]->pa_type = SSSD_IDP_OAUTH2_PADATA; + padata[0]->contents = NULL; + padata[0]->length = 0; + + padata[1] = NULL; + + return padata; +} + +static krb5_error_code +sss_idpcl_prompt(krb5_context context, + krb5_prompter_fct prompter, + void *prompter_data, + struct sss_idp_oauth2 *data, + krb5_data *_reply) +{ + krb5_error_code ret; + krb5_prompt prompt; + char *prompt_str; + int aret; + + if (data->verification_uri_complete != NULL) { + aret = asprintf(&prompt_str, + "Authenticate at %1$s and press ENTER.", + data->verification_uri_complete); + } else { + aret = asprintf(&prompt_str, + "Authenticate with PIN %1$s at %2$s and press ENTER.", + data->user_code, data->verification_uri); + } + + if (aret < 0) { + return ENOMEM; + } + + prompt.reply = _reply; + prompt.prompt = prompt_str; + prompt.hidden = 1; + + ret = (*prompter)(context, prompter_data, NULL, NULL, 1, &prompt); + free(prompt_str); + + return ret; +} + +static krb5_error_code +sss_idpcl_prep_questions(krb5_context context, + krb5_clpreauth_moddata moddata, + krb5_clpreauth_modreq modreq, + krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, + krb5_clpreauth_rock rock, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data) +{ + struct sss_idp_oauth2 *data; + char *challenge = NULL; + krb5_error_code ret; + + data = sss_idp_oauth2_decode_padata(pa_data); + if (data == NULL) { + ret = ENOMEM; + goto done; + } + + challenge = sss_idp_oauth2_encode_challenge(data); + if (challenge == NULL) { + ret = ENOMEM; + goto done; + } + + ret = cb->ask_responder_question(context, rock, SSSD_IDP_OAUTH2_QUESTION, + challenge); + +done: + sss_idp_oauth2_free(data); + free(challenge); + + return ret; +} + +static krb5_error_code +sss_idpcl_process(krb5_context context, + krb5_clpreauth_moddata moddata, + krb5_clpreauth_modreq modreq, + krb5_get_init_creds_opt *opt, + krb5_clpreauth_callbacks cb, + krb5_clpreauth_rock rock, + krb5_kdc_req *request, + krb5_data *encoded_request_body, + krb5_data *encoded_previous_request, + krb5_pa_data *pa_data, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_pa_data ***_pa_data_out) +{ + krb5_keyblock *as_key; + krb5_pa_data **padata; + krb5_error_code ret; + krb5_data reply; + struct sss_idp_oauth2 *data = NULL; + char prompt_answer[255] = {0}; + const char *answer; + + data = sss_idp_oauth2_decode_padata(pa_data); + if (data == NULL) { + ret = ENOMEM; + goto done; + } + + /* Get FAST armor key. */ + as_key = cb->fast_armor(context, rock); + if (as_key == NULL) { + ret = ENOENT; + goto done; + } + + answer = cb->get_responder_answer(context, rock, SSSD_IDP_OAUTH2_QUESTION); + /* Call prompter if we have no answer. We don't really require any answer, + * but we need to present a prompt to the user and wait until the user has + * finished authentication via an idp provider. */ + if (answer == NULL) { + reply.magic = 0; + reply.length = sizeof(prompt_answer) / sizeof(char); + reply.data = prompt_answer; + + ret = sss_idpcl_prompt(context, prompter, prompter_data, data, &reply); + if (ret != 0) { + goto done; + } + } + + /* Use FAST armor key as response key. */ + ret = cb->set_as_key(context, rock, as_key); + if (ret != 0) { + goto done; + } + + /* Encode the request into the pa_data output. */ + padata = sss_idpcl_encode_padata(); + if (padata == NULL) { + ret = ENOMEM; + goto done; + } + + cb->disable_fallback(context, rock); + *_pa_data_out = padata; + + ret = 0; + +done: + sss_idp_oauth2_free(data); + return ret; +} + +krb5_error_code +clpreauth_idp_initvt(krb5_context context, + int maj_ver, + int min_ver, + krb5_plugin_vtable vtable) +{ + static krb5_preauthtype pa_type_list[] = { SSSD_IDP_OAUTH2_PADATA, 0 }; + krb5_clpreauth_vtable vt; + + if (maj_ver != 1) { + return KRB5_PLUGIN_VER_NOTSUPP; + } + + vt = (krb5_clpreauth_vtable)vtable; + vt->name = discard_const(SSSD_IDP_PLUGIN); + vt->pa_type_list = pa_type_list; + vt->request_init = sss_radiuscl_init; + vt->prep_questions = sss_idpcl_prep_questions; + vt->process = sss_idpcl_process; + vt->request_fini = sss_radiuscl_fini; + vt->gic_opts = NULL; + + return 0; +} diff --git a/src/krb5_plugin/idp/idp_kdcpreauth.c b/src/krb5_plugin/idp/idp_kdcpreauth.c new file mode 100644 index 0000000..7d6d032 --- /dev/null +++ b/src/krb5_plugin/idp/idp_kdcpreauth.c @@ -0,0 +1,406 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2021 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 <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <krad.h> +#include <krb5/kdcpreauth_plugin.h> + +#include "shared/safealign.h" +#include "krb5_plugin/common/radius_kdcpreauth.h" +#include "krb5_plugin/common/utils.h" +#include "idp.h" +#include "util/util.h" + +struct sss_idpkdc_config { + struct sss_radiuskdc_config *radius; + struct sss_idp_config *idpcfg; +}; + +static void +sss_idpkdc_config_free(struct sss_idpkdc_config *config) +{ + if (config == NULL) { + return; + } + + sss_radiuskdc_config_free(config->radius); + sss_idp_config_free(config->idpcfg); + free(config); +} + +static krb5_error_code +sss_idpkdc_config_init(struct sss_radiuskdc_state *state, + krb5_context kctx, + krb5_const_principal princ, + const char *configstr, + struct sss_idpkdc_config **_config) +{ + struct sss_idpkdc_config *config; + krb5_error_code ret; + + if (state == NULL) { + return EINVAL; + } + + config = malloc(sizeof(struct sss_idpkdc_config)); + if (config == NULL) { + ret = ENOMEM; + goto done; + } + memset(config, 0, sizeof(struct sss_idpkdc_config)); + + ret = sss_radiuskdc_config_init(state, kctx, princ, configstr, &config->radius); + if (ret != 0) { + goto done; + } + + ret = sss_idp_config_init(configstr, &config->idpcfg); + if (ret != 0) { + goto done; + } + + *_config = config; + ret = 0; + +done: + if (ret != 0) { + sss_idpkdc_config_free(config); + } + + return ret; +} + +static void +sss_idpkdc_challenge_done(krb5_error_code rret, + const krad_packet *rreq, + const krad_packet *rres, + void *data) +{ + struct sss_radiuskdc_challenge *state; + struct sss_idp_oauth2 *idp_oauth2 = NULL; + krb5_pa_data *padata = NULL; + krb5_data rstate = {0}; + char *rmsg = NULL; + krb5_error_code ret; + + state = (struct sss_radiuskdc_challenge *)data; + + if (rret != 0) { + ret = rret; + goto done; + } + + if (krad_packet_get_code(rres) != krad_code_name2num("Access-Challenge")) { + ret = ENOENT; + goto done; + } + + ret = sss_radiuskdc_get_complete_attr(rres, "Proxy-State", &rstate); + if (ret != 0) { + goto done; + } + + rmsg = sss_radiuskdc_get_attr_as_string(rres, "Reply-Message"); + if (rmsg == NULL) { + ret = EINVAL; + goto done; + } + + /* Remember the RADIUS state so it can be set in the Access-Request message + * sent in sss_idpkdc_verify(), thus allowing the RADIUS server to + * associate the message with its internal state. */ + ret = sss_radiuskdc_set_cookie(state->kctx, state->cb, state->rock, + SSSD_IDP_OAUTH2_PADATA, &rstate); + if (ret != 0) { + goto done; + } + + idp_oauth2 = sss_idp_oauth2_decode_challenge(rmsg); + if (idp_oauth2 == NULL) { + ret = ENOMEM; + goto done; + } + + padata = sss_idp_oauth2_encode_padata(idp_oauth2); + if (padata == NULL) { + ret = ENOMEM; + goto done; + } + + ret = 0; + +done: + state->respond(state->arg, ret, padata); + sss_radiuskdc_challenge_free(state); + sss_idp_oauth2_free(idp_oauth2); + free(rstate.data); + free(rmsg); + + /* padata should not be freed */ + + return; +} + +/* Send a password-less Access-Request and expect Access-Challenge response. */ +static krb5_error_code +sss_idpkdc_challenge_send(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; + krb5_error_code ret; + + state = sss_radiuskdc_challenge_init(kctx, cb, rock, respond, arg, config); + if (state == NULL) { + ret = ENOMEM; + goto done; + } + + ret = krad_client_send(state->client->client, + krad_code_name2num("Access-Request"), + state->client->attrs, config->server, + config->secret, config->timeout, config->retries, + sss_idpkdc_challenge_done, state); + +done: + if (ret != 0) { + sss_radiuskdc_challenge_free(state); + } + + return ret; +} + +/* Send Access-Request with password and state set to indicate that the user has + * finished authentication against idp provider. We expect Access-Accept. */ +static krb5_error_code +sss_idpkdc_verify_send(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, + const krb5_data *rstate, + char **indicators, + struct sss_radiuskdc_config *config) +{ + struct sss_radiuskdc_verify *state; + krb5_error_code ret; + + state = sss_radiuskdc_verify_init(kctx, rock, cb, enc_tkt_reply, respond, + arg, indicators, config); + if (state == NULL) { + return ENOMEM; + } + + ret = sss_radiuskdc_put_complete_attr(state->client->attrs, + krad_attr_name2num("Proxy-State"), + rstate); + if (ret != 0) { + goto done; + } + + ret = krad_client_send(state->client->client, + krad_code_name2num("Access-Request"), + state->client->attrs, config->server, + config->secret, config->timeout, config->retries, + sss_radiuskdc_verify_done, state); + +done: + if (ret != 0) { + sss_radiuskdc_verify_free(state); + } + + return ret; +} + +static krb5_error_code +sss_idpkdc_init(krb5_context kctx, + krb5_kdcpreauth_moddata *_moddata, + const char **_realmnames) +{ + return sss_radiuskdc_init(SSSD_IDP_PLUGIN, kctx, _moddata, _realmnames); +} + +static void +sss_idpkdc_edata(krb5_context kctx, + krb5_kdc_req *request, + krb5_kdcpreauth_callbacks cb, + krb5_kdcpreauth_rock rock, + krb5_kdcpreauth_moddata moddata, + krb5_preauthtype pa_type, + krb5_kdcpreauth_edata_respond_fn respond, + void *arg) +{ + struct sss_idpkdc_config *config = NULL; + struct sss_radiuskdc_state *state; + krb5_keyblock *armor_key; + char *configstr = NULL; + krb5_error_code ret; + + state = (struct sss_radiuskdc_state *)moddata; + + ret = sss_radiuskdc_enabled(SSSD_IDP_CONFIG, kctx, cb, rock, &configstr); + if (ret != 0) { + goto done; + } + + armor_key = cb->fast_armor(kctx, rock); + if (armor_key == NULL) { + ret = ENOENT; + goto done; + } + + ret = sss_idpkdc_config_init(state, kctx, cb->client_name(kctx, rock), + configstr, &config); + if (ret != 0) { + goto done; + } + + ret = sss_idpkdc_challenge_send(kctx, cb, rock, respond, arg, + config->radius); + +done: + if (ret != 0) { + respond(arg, ret, NULL); + } + + cb->free_string(kctx, rock, configstr); + sss_idpkdc_config_free(config); +} + +static void +sss_idpkdc_verify(krb5_context kctx, + krb5_data *req_pkt, + krb5_kdc_req *request, + krb5_enc_tkt_part *enc_tkt_reply, + krb5_pa_data *pa, + krb5_kdcpreauth_callbacks cb, + krb5_kdcpreauth_rock rock, + krb5_kdcpreauth_moddata moddata, + krb5_kdcpreauth_verify_respond_fn respond, + void *arg) +{ + struct sss_radiuskdc_state *state; + struct sss_idpkdc_config *config = NULL; + char *configstr = NULL; + krb5_error_code ret; + krb5_data rstate; + + state = (struct sss_radiuskdc_state *)moddata; + + ret = sss_radiuskdc_enabled(SSSD_IDP_CONFIG, kctx, cb, rock, &configstr); + if (ret != 0) { + goto done; + } + + ret = sss_idpkdc_config_init(state, kctx, cb->client_name(kctx, rock), + configstr, &config); + if (ret != 0) { + goto done; + } + + ret = sss_radiuskdc_get_cookie(kctx, cb, rock, SSSD_IDP_OAUTH2_PADATA, + &rstate); + if (ret != 0) { + goto done; + } + + if (pa->pa_type != SSSD_IDP_OAUTH2_PADATA || pa->length != 0) { + ret = KRB5_PREAUTH_BAD_TYPE; + goto done; + } + + /* config is freed by verify_done if ret == 0 */ + ret = sss_idpkdc_verify_send(kctx, rock, cb, enc_tkt_reply, respond, arg, + &rstate, config->idpcfg->indicators, config->radius); + if (ret != 0) { + goto done; + } + + ret = 0; + +done: + if (ret != 0) { + respond(arg, ret, NULL, NULL, NULL); + } + + cb->free_string(kctx, rock, configstr); + sss_idpkdc_config_free(config); +} + +krb5_error_code +sss_idpkdc_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) +{ + /* Unexpected padata. Return error. */ + if (padata->length != 0) { + return EINVAL; + } + + return sss_radiuskdc_return_padata(kctx, padata, req_pkt, request, reply, + encrypting_key, send_pa_out, cb, rock, + moddata, modreq); +} + +krb5_error_code +kdcpreauth_idp_initvt(krb5_context kctx, + int maj_ver, + int min_ver, + krb5_plugin_vtable vtable) +{ + static krb5_preauthtype pa_type_list[] = { SSSD_IDP_OAUTH2_PADATA, 0 }; + krb5_kdcpreauth_vtable vt; + + if (maj_ver != 1) { + return KRB5_PLUGIN_VER_NOTSUPP; + } + + vt = (krb5_kdcpreauth_vtable)vtable; + vt->name = discard_const(SSSD_IDP_PLUGIN); + vt->pa_type_list = pa_type_list; + vt->init = sss_idpkdc_init; + vt->fini = sss_radiuskdc_fini; + vt->flags = sss_radiuskdc_flags; + vt->edata = sss_idpkdc_edata; + vt->verify = sss_idpkdc_verify; + vt->return_padata = sss_idpkdc_return_padata; + + com_err(SSSD_IDP_PLUGIN, 0, "SSSD IdP plugin loaded"); + + return 0; +} diff --git a/src/krb5_plugin/idp/idp_utils.c b/src/krb5_plugin/idp/idp_utils.c new file mode 100644 index 0000000..90424d0 --- /dev/null +++ b/src/krb5_plugin/idp/idp_utils.c @@ -0,0 +1,273 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + + Copyright (C) 2021 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 <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <jansson.h> +#include <arpa/inet.h> +#include <krb5/preauth_plugin.h> + +#include "krb5_plugin/common/utils.h" +#include "krb5_plugin/idp/idp.h" + +void +sss_idp_config_free(struct sss_idp_config *idpcfg) +{ + if (idpcfg == NULL) { + return; + } + + if (idpcfg->type != NULL) { + free(idpcfg->type); + } + + sss_string_array_free(idpcfg->indicators); + free(idpcfg); +} + +/** + * [{ + * "type": "oauth2", + * "indicators": ["..."] (optional) + * }] + * + * Note: array and type is used for future extensibility. + */ +krb5_error_code +sss_idp_config_init(const char *config, + struct sss_idp_config **_idpcfg) +{ + struct sss_idp_config *idpcfg; + json_t *jindicators = NULL; + json_error_t jret; + json_t *jroot; + krb5_error_code ret; + + idpcfg = malloc(sizeof(struct sss_idp_config)); + if (idpcfg == NULL) { + return ENOMEM; + } + memset(idpcfg, 0, sizeof(struct sss_idp_config)); + + jroot = json_loads(config, 0, &jret); + if (jroot == NULL) { + ret = EINVAL; + goto done; + } + + /* Only one item is supported at the moment. The rest is ignored. */ + ret = json_unpack(jroot, "[{s:s, s?:o}]", + "type", &idpcfg->type, + "indicators", &jindicators); + if (ret != 0) { + ret = EINVAL; + goto done; + } + + /* Only oauth2 type is supported at the moment. */ + if (strcmp(idpcfg->type, "oauth2") != 0) { + ret = EINVAL; + goto done; + } + + idpcfg->type = strdup(idpcfg->type); + if (idpcfg->type == NULL) { + ret = ENOMEM; + goto done; + } + + /* Are indicators set? */ + if (jindicators != NULL) { + idpcfg->indicators = sss_json_array_to_strings(jindicators); + if (idpcfg->indicators == NULL) { + ret = EINVAL; + goto done; + } + } + + *_idpcfg = idpcfg; + + ret = 0; + +done: + if (ret != 0) { + sss_idp_config_free(idpcfg); + } + + if (jroot != NULL) { + json_decref(jroot); + } + + return ret; +} + +void +sss_idp_oauth2_free(struct sss_idp_oauth2 *data) +{ + if (data == NULL) { + return; + } + + free(data->verification_uri); + free(data->verification_uri_complete); + free(data->user_code); + free(data); +} + +static struct sss_idp_oauth2 * +sss_idp_oauth2_init(const char *verification_uri, + const char *verification_uri_complete, + const char *user_code) +{ + struct sss_idp_oauth2 *data; + + /* These are required fields. */ + if (is_empty(verification_uri) || is_empty(user_code)) { + return NULL; + } + + data = malloc(sizeof(struct sss_idp_oauth2)); + if (data == NULL) { + return NULL; + } + memset(data, 0, sizeof(struct sss_idp_oauth2)); + + data->verification_uri = strdup(verification_uri); + data->user_code = strdup(user_code); + if (data->verification_uri == NULL || data->user_code == NULL) { + sss_idp_oauth2_free(data); + return NULL; + } + + if (!is_empty(verification_uri_complete)) { + data->verification_uri_complete = strdup(verification_uri_complete); + if (data->verification_uri_complete == NULL) { + sss_idp_oauth2_free(data); + return NULL; + } + } + + return data; +} + +static struct sss_idp_oauth2 * +sss_idp_oauth2_from_json(const char *json_str) +{ + struct sss_idp_oauth2 jdata = {0}; + struct sss_idp_oauth2 *data; + json_error_t jret; + json_t *jroot; + int ret; + + jroot = json_loads(json_str, 0, &jret); + if (jroot == NULL) { + return NULL; + } + + ret = json_unpack(jroot, "{s:s, s?:s, s:s}", + "verification_uri", &jdata.verification_uri, + "verification_uri_complete", &jdata.verification_uri_complete, + "user_code", &jdata.user_code); + if (ret != 0) { + json_decref(jroot); + return NULL; + } + + data = sss_idp_oauth2_init(jdata.verification_uri, + jdata.verification_uri_complete, + jdata.user_code); + + json_decref(jroot); + return data; +} + +static char * +sss_idp_oauth2_to_json(const struct sss_idp_oauth2 *data) +{ + json_t *jroot; + char *str; + + if (data == NULL) { + return NULL; + } + + /* These are required fields. */ + if (data->verification_uri == NULL || data->user_code == NULL) { + return NULL; + } + + jroot = json_pack("{s:s?, s:s*, s:s?}", + "verification_uri", data->verification_uri, + "verification_uri_complete", data->verification_uri_complete, + "user_code", data->user_code); + if (jroot == NULL) { + return NULL; + } + + str = json_dumps(jroot, JSON_COMPACT); + json_decref(jroot); + + return str; +} + +static struct sss_idp_oauth2 * +sss_idp_oauth2_decode(const char *str) +{ + return sss_radius_message_decode(SSSD_IDP_OAUTH2_PREFIX, + (sss_radius_message_decode_fn)sss_idp_oauth2_from_json, str); +} + +static char * +sss_idp_oauth2_encode(struct sss_idp_oauth2 *data) +{ + return sss_radius_message_encode(SSSD_IDP_OAUTH2_PREFIX, + (sss_radius_message_encode_fn)sss_idp_oauth2_to_json, data); +} + +krb5_pa_data * +sss_idp_oauth2_encode_padata(struct sss_idp_oauth2 *data) +{ + return sss_radius_encode_padata(SSSD_IDP_OAUTH2_PADATA, + (sss_radius_message_encode_fn)sss_idp_oauth2_encode, data); +} + +struct sss_idp_oauth2 * +sss_idp_oauth2_decode_padata(krb5_pa_data *padata) +{ + return sss_radius_decode_padata( + (sss_radius_message_decode_fn)sss_idp_oauth2_decode, padata); +} + +char * +sss_idp_oauth2_encode_challenge(struct sss_idp_oauth2 *data) +{ + return sss_idp_oauth2_encode(data); +} + +struct sss_idp_oauth2 * +sss_idp_oauth2_decode_challenge(const char *str) +{ + return sss_idp_oauth2_decode(str); +} diff --git a/src/krb5_plugin/idp/sssd_enable_idp b/src/krb5_plugin/idp/sssd_enable_idp new file mode 100644 index 0000000..8f98174 --- /dev/null +++ b/src/krb5_plugin/idp/sssd_enable_idp @@ -0,0 +1,14 @@ +# Enable SSSD OAuth2 Kerberos preauthentication plugins. +# +# This will allow you to obtain Kerberos TGT through OAuth2 authentication. +# +# To disable the OAuth2 plugin, comment out the following lines. + +[plugins] + clpreauth = { + module = idp:/usr/lib64/sssd/modules/sssd_krb5_idp_plugin.so + } + + kdcpreauth = { + module = idp:/usr/lib64/sssd/modules/sssd_krb5_idp_plugin.so + } |