diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h index a33e0561ef..ff6329b3fd 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -304,6 +304,9 @@ #define CONFDB_PC_PASSKEY_INTERACTIVE_PROMPT "interactive_prompt" #define CONFDB_PC_PASSKEY_TOUCH "touch" #define CONFDB_PC_PASSKEY_TOUCH_PROMPT "touch_prompt" +#define CONFDB_PC_TYPE_OAUTH2 "oauth2" +#define CONFDB_PC_OAUTH2_INTERACTIVE "interactive" +#define CONFDB_PC_OAUTH2_INTERACTIVE_PROMPT "interactive_prompt" struct confdb_ctx; diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini index fb9fc2a459..c355737386 100644 --- a/src/config/cfg_rules.ini +++ b/src/config/cfg_rules.ini @@ -16,6 +16,8 @@ section_re = ^prompting/2fa$ section_re = ^prompting/2fa/[^/\@]\{1,\}$ section_re = ^prompting/passkey$ section_re = ^prompting/passkey/[^/\@]\{1,\}$ +section_re = ^prompting/oauth2$ +section_re = ^prompting/oauth2/[^/\@]\{1,\}$ section_re = ^domain/[^/\@]\{1,\}$ section_re = ^domain/[^/\@]\{1,\}/[^/\@]\{1,\}$ section_re = ^application/[^/\@]\{1,\}$ diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml index b69d6bfc6e..0fe38b0477 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -4496,6 +4496,41 @@ ldap_user_extra_attrs = phone:telephoneNumber + + + [prompting/oauth2] + + to configure OAuth2 authentication prompting, + allowed options are: + + + interactive + + boolean value, if True prompt a message after + asking the user to authenticate, and wait + before requesting the access token. + + If False, make sure to set the + idp_request_timeout + sufficiently high, to give the user time to + authenticate. + + Default: true + + + + + interactive_prompt + + to change the message of the interactive prompt. + + + + + + + + It is possible to add a subsection for specific PAM services, diff --git a/src/responder/pam/pam_prompting_config.c b/src/responder/pam/pam_prompting_config.c index 27f794b929..19d7cec398 100644 --- a/src/responder/pam/pam_prompting_config.c +++ b/src/responder/pam/pam_prompting_config.c @@ -27,6 +27,7 @@ #define DEFAULT_PASSKEY_PROMPT_INTERACTIVE _("Insert your Passkey device, then press ENTER.") #define DEFAULT_PASSKEY_PROMPT_TOUCH _("Please touch the device.") +#define DEFAULT_OAUTH2_PROMPT_INTERACTIVE _("Press ENTER to continue.") typedef errno_t (pam_set_prompting_fn_t)(TALLOC_CTX *, struct confdb_ctx *, const char *, @@ -147,6 +148,39 @@ static errno_t pam_set_passkey_prompting_options(TALLOC_CTX *tmp_ctx, return ret; } + +static errno_t pam_set_oauth2_prompting_options(TALLOC_CTX *tmp_ctx, + struct confdb_ctx *cdb, + const char *section, + struct prompt_config ***pc_list) +{ + bool oauth2_interactive = false; + char *oauth2_interactive_prompt = NULL; + int ret; + + + ret = confdb_get_bool(cdb, section, CONFDB_PC_OAUTH2_INTERACTIVE, true, + &oauth2_interactive); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "confdb_get_bool failed, using defaults"); + } + + if (oauth2_interactive) { + ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_OAUTH2_INTERACTIVE_PROMPT, + DEFAULT_OAUTH2_PROMPT_INTERACTIVE, &oauth2_interactive_prompt); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed, using defaults"); + } + } + + ret = pc_list_add_oauth2(pc_list, oauth2_interactive_prompt); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_oauth2 failed.\n"); + } + + return ret; +} + static errno_t pam_set_prompting_options(struct confdb_ctx *cdb, const char *service_name, char **sections, @@ -232,6 +266,19 @@ errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd, goto done; } + if (types.oauth2_auth) { + ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service, + pctx->prompting_config_sections, + pctx->num_prompting_config_sections, + CONFDB_PC_TYPE_OAUTH2, + pam_set_oauth2_prompting_options, + &pc_list); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "pam_set_prompting_options failed.\n"); + goto done; + } + } + if (types.passkey_auth) { ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service, pctx->prompting_config_sections, diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h index cee29125bd..d843715c49 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -119,6 +119,7 @@ struct pam_resp_auth_type { bool cert_auth; bool passkey_auth; bool backend_returned_no_auth_type; + bool oauth2_auth; }; struct sss_cmd_table *get_pam_cmds(void); diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c index fd55ac0150..fe5dbec0fe 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -914,6 +914,9 @@ static void evaluate_pam_resp_list(struct pam_data *pd, case SSS_PAM_PASSKEY_KRB_INFO: types.passkey_auth = true; break; + case SSS_PAM_OAUTH2_INFO: + types.oauth2_auth = true; + break; case SSS_PASSWORD_PROMPTING: types.password_auth = true; break; @@ -954,7 +957,8 @@ errno_t pam_get_auth_types(struct pam_data *pd, evaluate_pam_resp_list(pd, &types, NULL); - if (!types.password_auth && !types.otp_auth && !types.cert_auth && !types.passkey_auth) { + if (!types.password_auth && !types.otp_auth && !types.cert_auth && + !types.oauth2_auth && !types.passkey_auth) { /* If the backend cannot determine which authentication types are * available the default would be to prompt for a password. */ types.password_auth = true; @@ -962,10 +966,11 @@ errno_t pam_get_auth_types(struct pam_data *pd, } DEBUG(SSSDBG_TRACE_ALL, "Authentication types for user [%s] and service " - "[%s]:%s%s%s%s\n", pd->user, pd->service, + "[%s]:%s%s%s%s%s\n", pd->user, pd->service, types.password_auth ? " password": "", types.otp_auth ? " two-factor" : "", types.passkey_auth ? " passkey" : "", + types.oauth2_auth ? " oauth2" : "", types.cert_auth ? " smartcard" : ""); ret = EOK; diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c index 21d2e7f284..af0c2b55a1 100644 --- a/src/sss_client/pam_sss.c +++ b/src/sss_client/pam_sss.c @@ -1849,37 +1849,47 @@ static int prompt_2fa_single(pam_handle_t *pamh, struct pam_items *pi, return PAM_SUCCESS; } -static int prompt_oauth2(pam_handle_t *pamh, struct pam_items *pi) +static int prompt_oauth2(pam_handle_t *pamh, struct pam_items *pi, + const char *prompt) { - char *answer = NULL; char *msg; int ret; if (pi->oauth2_url_complete != NULL) { - ret = asprintf(&msg, _("Authenticate at %1$s and press ENTER."), + ret = asprintf(&msg, _("Authenticate at \"%1$s\"."), pi->oauth2_url_complete); } else { - ret = asprintf(&msg, _("Authenticate with PIN %1$s at %2$s and press " - "ENTER."), pi->oauth2_pin, pi->oauth2_url); + ret = asprintf(&msg, _("Authenticate with PIN \"%1$s\" at \"%2$s\"."), + pi->oauth2_pin, pi->oauth2_url); } if (ret == -1) { return PAM_SYSTEM_ERR; } - ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, msg, NULL, &answer); + ret = do_pam_conversation(pamh, PAM_TEXT_INFO, msg, NULL, NULL); free(msg); if (ret != PAM_SUCCESS) { D(("do_pam_conversation failed.")); return ret; } - /* We don't care about answer here. We just need to notify that the - * authentication has finished. */ - free(answer); + if (prompt != NULL && prompt[0] != '\0') { + char *answer = NULL; + + ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer); + if (ret != PAM_SUCCESS) { + D(("do_pam_conversation failed.")); + return ret; + } + + /* We don't care about answer here. We just need to notify that the + * authentication has finished. */ + free(answer); + } pi->pam_authtok = strdup(pi->oauth2_pin); pi->pam_authtok_type = SSS_AUTHTOK_TYPE_OAUTH2; - pi->pam_authtok_size=strlen(pi->oauth2_pin); + pi->pam_authtok_size = strlen(pi->oauth2_pin); return PAM_SUCCESS; } @@ -2608,6 +2618,10 @@ static int prompt_by_config(pam_handle_t *pamh, struct pam_items *pi) pc_get_passkey_inter_prompt(pi->pc[c]), pc_get_passkey_touch_prompt(pi->pc[c])); break; + case PC_TYPE_OAUTH2: + ret = prompt_oauth2(pamh, pi, + pc_get_oauth2_inter_prompt(pi->pc[c])); + break; case PC_TYPE_SMARTCARD: ret = prompt_sc_pin(pamh, pi); /* Todo: add extra string option */ @@ -2647,13 +2661,13 @@ static int get_authtok_for_authentication(pam_handle_t *pamh, } pi->pam_authtok_size = strlen(pi->pam_authtok); } else { - if (pi->oauth2_url != NULL) { - /* Prompt config is not supported for OAuth2. */ - ret = prompt_oauth2(pamh, pi); - } else if (pi->pc != NULL) { + if (pi->pc != NULL) { ret = prompt_by_config(pamh, pi); } else { - if (pi->cert_list != NULL) { + if (pi->oauth2_url != NULL) { + /* Prompt config is not supported for OAuth2. */ + ret = prompt_oauth2(pamh, pi, _("Press ENTER to continue.")); + } else if (pi->cert_list != NULL) { if (pi->cert_list->next == NULL) { /* Only one certificate */ pi->selected_cert = pi->cert_list; diff --git a/src/sss_client/pam_sss_prompt_config.c b/src/sss_client/pam_sss_prompt_config.c index caf308c826..9e72c22fb6 100644 --- a/src/sss_client/pam_sss_prompt_config.c +++ b/src/sss_client/pam_sss_prompt_config.c @@ -55,6 +55,10 @@ struct prompt_config_eidp { char *prompt_link; }; +struct prompt_config_oauth2 { + char *prompt_inter; +}; + struct prompt_config { enum prompt_config_type type; union { @@ -64,6 +68,7 @@ struct prompt_config { struct prompt_config_passkey passkey; struct prompt_config_smartcard smartcard; struct prompt_config_eidp eidp; + struct prompt_config_oauth2 oauth2; } data; }; @@ -155,6 +160,14 @@ const char *pc_get_smartcard_pin_prompt(struct prompt_config *pc) return NULL; } +const char *pc_get_oauth2_inter_prompt(struct prompt_config *pc) +{ + if (pc != NULL && (pc_get_type(pc) == PC_TYPE_OAUTH2)) { + return pc->data.oauth2.prompt_inter; + } + return NULL; +} + static void pc_free_passkey(struct prompt_config *pc) { if (pc != NULL && pc_get_type(pc) == PC_TYPE_PASSKEY) { @@ -166,6 +179,15 @@ static void pc_free_passkey(struct prompt_config *pc) return; } +static void pc_free_oauth2(struct prompt_config *pc) +{ + if (pc != NULL && pc_get_type(pc) == PC_TYPE_OAUTH2) { + free(pc->data.oauth2.prompt_inter); + pc->data.oauth2.prompt_inter = NULL; + } + return; +} + static void pc_free_password(struct prompt_config *pc) { if (pc != NULL && pc_get_type(pc) == PC_TYPE_PASSWORD) { @@ -246,6 +268,9 @@ void pc_list_free(struct prompt_config **pc_list) case PC_TYPE_EIDP: pc_free_eidp(pc_list[c]); break; + case PC_TYPE_OAUTH2: + pc_free_oauth2(pc_list[c]); + break; default: return; } @@ -275,6 +300,31 @@ static errno_t pc_list_add_pc(struct prompt_config ***pc_list, return EOK; } +static errno_t pc_copy_string(int size, uint8_t *buf, size_t *off, char **out) { + char *str; + uint32_t l; + size_t rp = *off; + + if (rp > size - sizeof(uint32_t)) { + return EINVAL; + } + SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); + + if (l > size || rp > size - l) { + return EINVAL; + } + str = strndup((char *) buf + rp, l); + if (str == NULL) { + return ENOMEM; + } + rp += l; + + *out = str; + *off = rp; + + return EOK; +} + #define DEFAULT_PASSWORD_PROMPT _("Password: ") #define DEFAULT_2FA_SINGLE_PROMPT _("Password + Token value: ") #define DEFAULT_2FA_PROMPT_1ST _("First Factor: ") @@ -545,6 +595,46 @@ errno_t pc_list_add_smartcard(struct prompt_config ***pc_list, return ret; } +errno_t pc_list_add_oauth2(struct prompt_config ***pc_list, + const char *prompt_inter) +{ + struct prompt_config *pc; + int ret; + + if (pc_list == NULL) { + return EINVAL; + } + + pc = calloc(1, sizeof(struct prompt_config)); + if (pc == NULL) { + return ENOMEM; + } + + pc->type = PC_TYPE_OAUTH2; + + pc->data.oauth2.prompt_inter = strdup(prompt_inter != NULL ? prompt_inter + : ""); + if (pc->data.oauth2.prompt_inter == NULL) { + ret = ENOMEM; + goto done; + } + + ret = pc_list_add_pc(pc_list, pc); + if (ret != EOK) { + goto done; + } + + ret = EOK; + +done: + if (ret != EOK) { + free(pc->data.oauth2.prompt_inter); + free(pc); + } + + return ret; +} + errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len, uint8_t **data) { @@ -594,6 +684,10 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len, l += sizeof(uint32_t); l += strlen(pc_list[c]->data.eidp.prompt_link); break; + case PC_TYPE_OAUTH2: + l += sizeof(uint32_t); + l += strlen(pc_list[c]->data.oauth2.prompt_inter); + break; default: return EINVAL; } @@ -675,6 +769,13 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len, safealign_memcpy(&d[rp], pc_list[c]->data.eidp.prompt_link, strlen(pc_list[c]->data.eidp.prompt_link), &rp); break; + case PC_TYPE_OAUTH2: + SAFEALIGN_SET_UINT32(&d[rp], + strlen(pc_list[c]->data.oauth2.prompt_inter), + &rp); + safealign_memcpy(&d[rp], pc_list[c]->data.oauth2.prompt_inter, + strlen(pc_list[c]->data.oauth2.prompt_inter), &rp); + break; default: free(d); return EINVAL; @@ -723,22 +824,10 @@ errno_t pc_list_from_response(int size, uint8_t *buf, switch (type) { case PC_TYPE_PASSWORD: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - ret = EINVAL; - goto done; - } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - rp += l; ret = pc_list_add_password(&pl, str); free(str); @@ -747,42 +836,16 @@ errno_t pc_list_from_response(int size, uint8_t *buf, } break; case PC_TYPE_2FA: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - ret = EINVAL; + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; - goto done; - } - rp += l; - if (rp > size - sizeof(uint32_t)) { + ret = pc_copy_string(size, buf, &rp, &str2); + if (ret != 0) { free(str); - ret = EINVAL; goto done; } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - free(str); - ret = EINVAL; - goto done; - } - str2 = strndup((char *) buf + rp, l); - if (str2 == NULL) { - free(str); - ret = ENOMEM; - goto done; - } - rp += l; ret = pc_list_add_2fa(&pl, str, str2); free(str); @@ -792,42 +855,16 @@ errno_t pc_list_from_response(int size, uint8_t *buf, } break; case PC_TYPE_PASSKEY: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - if (l > size || rp > size - l) { - ret = EINVAL; - goto done; - } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; - goto done; - } - rp += l; - - if (rp > size - sizeof(uint32_t)) { - free(str); - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - free(str); - ret = EINVAL; - goto done; - } - str2 = strndup((char *) buf + rp, l); - if (str2 == NULL) { + ret = pc_copy_string(size, buf, &rp, &str2); + if (ret != 0) { free(str); - ret = ENOMEM; goto done; } - rp += l; ret = pc_list_add_passkey(&pl, str, str2); free(str); @@ -836,23 +873,23 @@ errno_t pc_list_from_response(int size, uint8_t *buf, goto done; } break; - case PC_TYPE_2FA_SINGLE: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; + case PC_TYPE_OAUTH2: + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - if (l > size || rp > size - l) { - ret = EINVAL; + ret = pc_list_add_oauth2(&pl, str); + free(str); + if (ret != EOK) { goto done; } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; + break; + case PC_TYPE_2FA_SINGLE: + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - rp += l; ret = pc_list_add_2fa_single(&pl, str); free(str); @@ -861,42 +898,16 @@ errno_t pc_list_from_response(int size, uint8_t *buf, } break; case PC_TYPE_SMARTCARD: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - ret = EINVAL; - goto done; - } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - rp += l; - if (rp > size - sizeof(uint32_t)) { + ret = pc_copy_string(size, buf, &rp, &str2); + if (ret != 0) { free(str); - ret = EINVAL; goto done; } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - free(str); - ret = EINVAL; - goto done; - } - str2 = strndup((char *) buf + rp, l); - if (str2 == NULL) { - free(str); - ret = ENOMEM; - goto done; - } - rp += l; ret = pc_list_add_smartcard(&pl, str, str2); free(str); @@ -906,42 +917,16 @@ errno_t pc_list_from_response(int size, uint8_t *buf, } break; case PC_TYPE_EIDP: - if (rp > size - sizeof(uint32_t)) { - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - ret = EINVAL; - goto done; - } - str = strndup((char *) buf + rp, l); - if (str == NULL) { - ret = ENOMEM; + ret = pc_copy_string(size, buf, &rp, &str); + if (ret != 0) { goto done; } - rp += l; - if (rp > size - sizeof(uint32_t)) { - free(str); - ret = EINVAL; - goto done; - } - SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp); - - if (l > size || rp > size - l) { - free(str); - ret = EINVAL; - goto done; - } - str2 = strndup((char *) buf + rp, l); - if (str2 == NULL) { + ret = pc_copy_string(size, buf, &rp, &str2); + if (ret != 0) { free(str); - ret = ENOMEM; goto done; } - rp += l; ret = pc_list_add_eidp(&pl, str, str2); free(str); diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h index 727d5ba8e3..57e1ead454 100644 --- a/src/sss_client/sss_cli.h +++ b/src/sss_client/sss_cli.h @@ -682,6 +682,7 @@ enum prompt_config_type { PC_TYPE_PASSKEY, PC_TYPE_SMARTCARD, PC_TYPE_EIDP, + PC_TYPE_OAUTH2, PC_TYPE_LAST }; @@ -698,9 +699,12 @@ const char *pc_get_eidp_init_prompt(struct prompt_config *pc); const char *pc_get_eidp_link_prompt(struct prompt_config *pc); const char *pc_get_smartcard_init_prompt(struct prompt_config *pc); const char *pc_get_smartcard_pin_prompt(struct prompt_config *pc); +const char *pc_get_oauth2_inter_prompt(struct prompt_config *pc); errno_t pc_list_add_passkey(struct prompt_config ***pc_list, const char *inter_prompt, const char *touch_prompt); +errno_t pc_list_add_oauth2(struct prompt_config ***pc_list, + const char *inter_prompt); void pc_list_free(struct prompt_config **pc_list); errno_t pc_list_add_password(struct prompt_config ***pc_list, const char *prompt);