From 2f3cd781879e7063fcd996389071458587623e1c Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Mon, 22 Aug 2022 11:37:07 +0200 Subject: [PATCH 23/23] oidc_child: add --client-secret-stdin option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since there is the use-case of confidential client which requires that the client secret must be sent to the IdP we should handle it confidentially by not putting it on the command line but sending it via stdin. Resolves: https://github.com/SSSD/sssd/issues/6146 Reviewed-by: Justin Stephenson Reviewed-by: Pavel Březina (cherry picked from commit 1a475e0c537c905c80406ceb88c7b34e6400bc40) Reviewed-by: Alexey Tikhonov --- src/oidc_child/oidc_child.c | 89 ++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 7 deletions(-) diff --git a/src/oidc_child/oidc_child.c b/src/oidc_child/oidc_child.c index c8d35d5d8..7758cdc25 100644 --- a/src/oidc_child/oidc_child.c +++ b/src/oidc_child/oidc_child.c @@ -34,7 +34,7 @@ #include "util/atomic_io.h" #define IN_BUF_SIZE 4096 -static errno_t read_device_code_from_stdin(struct devicecode_ctx *dc_ctx) +static errno_t read_from_stdin(TALLOC_CTX *mem_ctx, char **out) { uint8_t buf[IN_BUF_SIZE]; ssize_t len; @@ -56,7 +56,7 @@ static errno_t read_device_code_from_stdin(struct devicecode_ctx *dc_ctx) return EINVAL; } - str = talloc_strndup(dc_ctx, (char *) buf, len); + str = talloc_strndup(mem_ctx, (char *) buf, len); sss_erase_mem_securely(buf, IN_BUF_SIZE); if (str == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n"); @@ -65,21 +65,72 @@ static errno_t read_device_code_from_stdin(struct devicecode_ctx *dc_ctx) talloc_set_destructor((void *) str, sss_erase_talloc_mem_securely); if (strlen(str) != len) { - DEBUG(SSSDBG_CRIT_FAILURE, - "Input contains additional data, " - "only JSON encoded device code expected.\n"); + DEBUG(SSSDBG_CRIT_FAILURE, "Input contains additional data.\n"); talloc_free(str); return EINVAL; } + *out = str; + + return EOK; +} + +static errno_t read_device_code_from_stdin(struct devicecode_ctx *dc_ctx, + const char **out) +{ + char *str; + errno_t ret; + char *sep; + + ret = read_from_stdin(dc_ctx, &str); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "read_from_stdin failed.\n"); + return ret; + } + + if (out != NULL) { + /* expect the client secret in the first line */ + sep = strchr(str, '\n'); + if (sep == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Format error, expecting client secret and JSON data.\n"); + talloc_free(str); + return EINVAL; + } + *sep = '\0'; + *out = str; + sep++; + } else { + sep = str; + } + clean_http_data(dc_ctx); - dc_ctx->http_data = str; + dc_ctx->http_data = talloc_strdup(dc_ctx, sep); DEBUG(SSSDBG_TRACE_ALL, "JSON device code: [%s].\n", dc_ctx->http_data); return EOK; } +static errno_t read_client_secret_from_stdin(struct devicecode_ctx *dc_ctx, + const char **out) +{ + char *str; + errno_t ret; + + ret = read_from_stdin(dc_ctx, &str); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "read_from_stdin failed.\n"); + return ret; + } + + *out = str; + + DEBUG(SSSDBG_TRACE_ALL, "Client secret: [%s].\n", *out); + + return EOK; +} + static errno_t set_endpoints(struct devicecode_ctx *dc_ctx, const char *device_auth_endpoint, const char *token_endpoint, @@ -210,6 +261,7 @@ struct cli_opts { const char *jwks_uri; const char *scope; const char *client_secret; + bool client_secret_stdin; const char *ca_db; const char *user_identifier_attr; bool libcurl_debug; @@ -253,6 +305,8 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts) {"client-id", 0, POPT_ARG_STRING, &opts->client_id, 0, _("Client ID"), NULL}, {"client-secret", 0, POPT_ARG_STRING, &opts->client_secret, 0, _("Client secret (if needed)"), NULL}, + {"client-secret-stdin", 0, POPT_ARG_NONE, NULL, 's', + _("Read client secret from standard input"), NULL}, {"ca-db", 0, POPT_ARG_STRING, &opts->ca_db, 0, _("Path to PEM file with CA certificates"), NULL}, {"libcurl-debug", 0, POPT_ARG_NONE, NULL, 'c', @@ -280,6 +334,9 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts) case 'c': opts->libcurl_debug = true; break; + case 's': + opts->client_secret_stdin = true; + break; default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); @@ -324,6 +381,12 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts) goto done; } + if (opts->client_secret != NULL && opts->client_secret_stdin) { + fprintf(stderr, "\n--client-secret and --client-secret-stdin are " + "mutually exclusive.\n\n"); + goto done; + } + poptFreeContext(pc); print_usage = false; @@ -454,6 +517,15 @@ int main(int argc, const char *argv[]) } if (opts.get_device_code) { + if (opts.client_secret_stdin) { + ret = read_client_secret_from_stdin(dc_ctx, &opts.client_secret); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to read client secret from stdin.\n"); + goto done; + } + } + ret = get_devicecode(dc_ctx, opts.client_id, opts.client_secret); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Failed to get device code.\n"); @@ -463,7 +535,10 @@ int main(int argc, const char *argv[]) if (opts.get_access_token) { if (dc_ctx->device_code == NULL) { - ret = read_device_code_from_stdin(dc_ctx); + ret = read_device_code_from_stdin(dc_ctx, + opts.client_secret_stdin + ? &opts.client_secret + : NULL); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Failed to read device code from stdin.\n"); -- 2.37.3