From: Zbigniew Jędrzejewski-Szmek Date: Mon, 8 Dec 2025 15:40:35 +0000 (+0100) Subject: homectl: split out parser parse_ssh_authorized_keys X-Git-Tag: v260-rc1~391^2~23 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fbd7ca0ef093c027536f894601f95afe6e98cd03;p=thirdparty%2Fsystemd.git homectl: split out parser parse_ssh_authorized_keys The idea is to split out helpers to make parse_argv() itself more manageable. If possible, the helpers will be reused in many places. But even if not, as in this case, it seems good to split out the code for a specific option. Always, the sd_json_variant** pointer or pointers that are operated on are selected in the caller. This way it is easier to see which of the identity variables is used and if two different ones are used. --- diff --git a/src/home/homectl.c b/src/home/homectl.c index 8d8f2c0f121..08a07665726 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -3048,6 +3048,79 @@ static int drop_from_identity(const char *field) { return 0; } +static int parse_ssh_authorized_keys(sd_json_variant **identity, const char *field, const char *arg) { + _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; + _cleanup_strv_free_ char **l = NULL, **add = NULL; + int r; + + assert(identity); + assert(field); + + if (isempty(arg)) + return drop_from_identity(field); + + if (arg[0] == '@') { + /* If prefixed with '@', read from a file */ + + _cleanup_fclose_ FILE *f = fopen(arg + 1, "re"); + if (!f) + return log_error_errno(errno, "Failed to open '%s': %m", arg + 1); + + for (;;) { + _cleanup_free_ char *line = NULL; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read from '%s': %m", arg + 1); + if (r == 0) + break; + + if (isempty(line) || line[0] == '#') + continue; + + r = strv_consume(&add, TAKE_PTR(line)); + if (r < 0) + return log_oom(); + } + } else { + /* Otherwise, assume it's a literal key. Let's do some superficial checks + * before accepting it though. */ + + if (string_has_cc(arg, NULL)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Authorized key contains control characters, refusing."); + if (arg[0] == '#') + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?"); + + add = strv_new(arg); + if (!add) + return log_oom(); + } + + v = sd_json_variant_ref(sd_json_variant_by_key(*identity, field)); + if (v) { + r = sd_json_variant_strv(v, &l); + if (r < 0) + return log_error_errno(r, "Failed to parse %s list: %m", field); + } + + r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ true); + if (r < 0) + return log_oom(); + + v = sd_json_variant_unref(v); + + r = sd_json_variant_new_array_strv(&v, l); + if (r < 0) + return log_oom(); + + r = sd_json_variant_set_field(identity, field, v); + if (r < 0) + return log_error_errno(r, "Failed to set %s field: %m", field); + + return 0; +} + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL; int r; @@ -3484,7 +3557,8 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not hooked up everywhere let's make it an environment variable only. */ + /* Eventually we should probably turn this into a proper --dry-run option, but as long as it is not + * hooked up everywhere let's make it an environment variable only. */ r = getenv_bool("SYSTEMD_HOME_DRY_RUN"); if (r >= 0) arg_dry_run = r; @@ -4212,83 +4286,12 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_SSH_AUTHORIZED_KEYS: { - _cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL; - _cleanup_strv_free_ char **l = NULL, **add = NULL; - - if (isempty(optarg)) { - r = drop_from_identity("sshAuthorizedKeys"); - if (r < 0) - return r; - - break; - } - - if (optarg[0] == '@') { - _cleanup_fclose_ FILE *f = NULL; - - /* If prefixed with '@' read from a file */ - - f = fopen(optarg+1, "re"); - if (!f) - return log_error_errno(errno, "Failed to open '%s': %m", optarg+1); - - for (;;) { - _cleanup_free_ char *line = NULL; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return log_error_errno(r, "Failed to read from '%s': %m", optarg+1); - if (r == 0) - break; - - if (isempty(line)) - continue; - - if (line[0] == '#') - continue; - - r = strv_consume(&add, TAKE_PTR(line)); - if (r < 0) - return log_oom(); - } - } else { - /* Otherwise, assume it's a literal key. Let's do some superficial checks - * before accept it though. */ - - if (string_has_cc(optarg, NULL)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Authorized key contains control characters, refusing."); - if (optarg[0] == '#') - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key is a comment?"); - - add = strv_new(optarg); - if (!add) - return log_oom(); - } - - v = sd_json_variant_ref(sd_json_variant_by_key(arg_identity_extra_privileged, "sshAuthorizedKeys")); - if (v) { - r = sd_json_variant_strv(v, &l); - if (r < 0) - return log_error_errno(r, "Failed to parse SSH authorized keys list: %m"); - } - - r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ true); + case ARG_SSH_AUTHORIZED_KEYS: + r = parse_ssh_authorized_keys(&arg_identity_extra_privileged, "sshAuthorizedKeys", optarg); if (r < 0) - return log_oom(); - - v = sd_json_variant_unref(v); - - r = sd_json_variant_new_array_strv(&v, l); - if (r < 0) - return log_oom(); - - r = sd_json_variant_set_field(&arg_identity_extra_privileged, "sshAuthorizedKeys", v); - if (r < 0) - return log_error_errno(r, "Failed to set authorized keys: %m"); + return r; break; - } case ARG_NOT_BEFORE: case ARG_NOT_AFTER: