free(g->group_name);
free(g->realm);
free(g->group_name_and_realm_auto);
+ strv_free(g->aliases);
free(g->description);
strv_free(g->members);
{ "matchMachineId", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{ "matchHostname", _SD_JSON_VARIANT_TYPE_INVALID, NULL, 0, 0 },
{ "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(GroupRecord, gid), 0 },
+ { "aliases", SD_JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, aliases), SD_JSON_RELAX },
{ "members", SD_JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, members), SD_JSON_RELAX },
{ "administrators", SD_JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, administrators), SD_JSON_RELAX },
{},
static const sd_json_dispatch_field group_dispatch_table[] = {
{ "groupName", SD_JSON_VARIANT_STRING, json_dispatch_user_group_name, offsetof(GroupRecord, group_name), SD_JSON_RELAX },
+ { "aliases", SD_JSON_VARIANT_ARRAY, json_dispatch_user_group_list, offsetof(GroupRecord, aliases), SD_JSON_RELAX },
{ "realm", SD_JSON_VARIANT_STRING, json_dispatch_realm, offsetof(GroupRecord, realm), 0 },
{ "uuid", SD_JSON_VARIANT_STRING, sd_json_dispatch_id128, offsetof(GroupRecord, uuid), 0 },
{ "description", SD_JSON_VARIANT_STRING, json_dispatch_gecos, offsetof(GroupRecord, description), 0 },
if (streq_ptr(g->group_name_and_realm_auto, group_name))
return true;
+ if (strv_contains(g->aliases, group_name))
+ return true;
+
+ if (record_name_matches_alias_realm(group_name, (char * const*) g->aliases, g->realm))
+ return true;
+
return false;
}
h->description,
};
- if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names))
+ if (!user_name_fuzzy_match(names, ELEMENTSOF(names), match->fuzzy_names) &&
+ !user_name_fuzzy_match((const char**) h->aliases, strv_length(h->aliases), match->fuzzy_names))
return false;
}
char *group_name;
char *realm;
char *group_name_and_realm_auto;
+ char **aliases;
sd_id128_t uuid;
char *description;
#include "errno-util.h"
#include "format-util.h"
#include "group-record.h"
+#include "json-util.h"
#include "libcrypt-util.h"
#include "log.h"
#include "string-util.h"
return strv_extend_strv(dst, t, filter_duplicates);
}
+static int nss_add_valid_alias(char ***aliases, const char *name, const char *alias_name) {
+ assert(aliases);
+ assert(name);
+
+ if (isempty(alias_name) || streq_ptr(alias_name, name))
+ return 0;
+
+ if (!valid_user_group_name(alias_name, VALID_USER_RELAX))
+ return 0;
+
+ return strv_extend(aliases, alias_name);
+}
+
int nss_passwd_to_user_record(
const struct passwd *pwd,
const struct spwd *spwd,
+ const char *alias_name,
UserRecord **ret) {
_cleanup_(user_record_unrefp) UserRecord *hr = NULL;
if (r < 0)
return r;
+ r = nss_add_valid_alias(&hr->aliases, hr->user_name, alias_name);
+ if (r < 0)
+ return r;
+
/* Some bad NSS modules synthesize GECOS fields with embedded ":" or "\n" characters, which are not
* something we can output in /etc/passwd compatible format, since these are record separators
* there. We normally refuse that, but we need to maintain compatibility with arbitrary NSS modules,
r = sd_json_buildo(
&hr->json,
SD_JSON_BUILD_PAIR_STRING("userName", hr->user_name),
+ JSON_BUILD_PAIR_STRV_NON_EMPTY("aliases", hr->aliases),
SD_JSON_BUILD_PAIR_UNSIGNED("uid", hr->uid),
SD_JSON_BUILD_PAIR_UNSIGNED("gid", user_record_gid(hr)),
SD_JSON_BUILD_PAIR_CONDITION(!!hr->real_name, "realName", SD_JSON_BUILD_STRING(hr->real_name)),
} else
incomplete = true;
- r = nss_passwd_to_user_record(result, sresult, ret);
+ r = nss_passwd_to_user_record(result, sresult, name, ret);
if (r < 0)
return r;
} else
incomplete = true;
- r = nss_passwd_to_user_record(result, sresult, ret);
+ r = nss_passwd_to_user_record(result, sresult, /* alias_name= */ NULL, ret);
if (r < 0)
return r;
int nss_group_to_group_record(
const struct group *grp,
const struct sgrp *sgrp,
+ const char *alias_name,
GroupRecord **ret) {
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
if (!g->group_name)
return -ENOMEM;
+ r = nss_add_valid_alias(&g->aliases, g->group_name, alias_name);
+ if (r < 0)
+ return r;
+
r = strv_extend_strv_utf8_only(&g->members, grp->gr_mem, false);
if (r < 0)
return r;
r = sd_json_buildo(
&g->json,
SD_JSON_BUILD_PAIR_STRING("groupName", g->group_name),
+ JSON_BUILD_PAIR_STRV_NON_EMPTY("aliases", g->aliases),
SD_JSON_BUILD_PAIR_UNSIGNED("gid", g->gid),
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->members), "members", SD_JSON_BUILD_STRV(g->members)),
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(g->hashed_password), "privileged", SD_JSON_BUILD_OBJECT(SD_JSON_BUILD_PAIR_STRV("hashedPassword", g->hashed_password))),
} else
incomplete = true;
- r = nss_group_to_group_record(result, sresult, ret);
+ r = nss_group_to_group_record(result, sresult, name, ret);
if (r < 0)
return r;
} else
incomplete = true;
- r = nss_group_to_group_record(result, sresult, ret);
+ r = nss_group_to_group_record(result, sresult, /* alias_name= */ NULL, ret);
if (r < 0)
return r;
/* Synthesize UserRecord and GroupRecord objects from NSS data */
-int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, UserRecord **ret);
+int nss_passwd_to_user_record(const struct passwd *pwd, const struct spwd *spwd, const char *alias_name, UserRecord **ret);
int nss_spwd_for_passwd(const struct passwd *pwd, struct spwd *ret_spwd, char **ret_buffer);
int nss_user_record_by_name(const char *name, bool with_shadow, UserRecord **ret);
int nss_user_record_by_uid(uid_t uid, bool with_shadow, UserRecord **ret);
-int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, GroupRecord **ret);
+int nss_group_to_group_record(const struct group *grp, const struct sgrp *sgrp, const char *alias_name, GroupRecord **ret);
int nss_sgrp_for_group(const struct group *grp, struct sgrp *ret_sgrp, char **ret_buffer);
int nss_group_record_by_name(const char *name, bool with_shadow, GroupRecord **ret);
if (strv_contains(u->aliases, user_name))
return true;
- const char *realm = strrchr(user_name, '@');
- if (realm && streq_ptr(realm+1, u->realm))
- STRV_FOREACH(a, u->aliases)
- if (startswith(user_name, *a) == realm)
- return true;
+ if (record_name_matches_alias_realm(user_name, (char * const*) u->aliases, u->realm))
+ return true;
+
+ return false;
+}
+
+bool record_name_matches_alias_realm(const char *name, char * const *aliases, const char *realm) {
+ const char *suffix;
+
+ assert(name);
+
+ suffix = strrchr(name, '@');
+ if (!suffix || !streq_ptr(suffix + 1, realm))
+ return false;
+
+ STRV_FOREACH(a, aliases)
+ if (startswith(name, *a) == suffix)
+ return true;
return false;
}
void userdb_match_done(UserDBMatch *match);
+bool record_name_matches_alias_realm(const char *name, char * const *aliases, const char *realm);
bool user_name_fuzzy_match(const char *names[], size_t n_names, char **matches);
bool user_record_match(UserRecord *u, const UserDBMatch *match);
incomplete = true;
}
- r = nss_passwd_to_user_record(pw, r >= 0 ? &spwd : NULL, ret);
+ r = nss_passwd_to_user_record(pw, r >= 0 ? &spwd : NULL, /* alias_name= */ NULL, ret);
if (r < 0)
return r;
incomplete = true;
}
- r = nss_group_to_group_record(gr, r >= 0 ? &sgrp : NULL, ret);
+ r = nss_group_to_group_record(gr, r >= 0 ? &sgrp : NULL, /* alias_name= */ NULL, ret);
if (r < 0)
return r;
if (r < 0)
return r;
- if (uid_is_valid(p.uid))
- r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
- else if (p.name)
+ /* Prefer lookup by name if both name and UID are specified, so NSS-backed records preserve
+ * the requested lookup name as an alias before we check the UID below. */
+ if (p.name)
r = userdb_by_name(p.name, &p.match, userdb_flags, &hr);
+ else if (uid_is_valid(p.uid))
+ r = userdb_by_uid(p.uid, &p.match, userdb_flags, &hr);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
if (r < 0)
return r;
- if (gid_is_valid(p.gid))
- r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
- else if (p.name)
+ /* Prefer lookup by name if both name and GID are specified, so NSS-backed records preserve
+ * the requested lookup name as an alias before we check the GID below. */
+ if (p.name)
r = groupdb_by_name(p.name, &p.match, userdb_flags, &g);
+ else if (gid_is_valid(p.gid))
+ r = groupdb_by_gid(p.gid, &p.match, userdb_flags, &g);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
return sd_varlink_error(link, "io.systemd.UserDatabase.ServiceNotAvailable", NULL);
}
- if ((uid_is_valid(p.gid) && g->gid != p.gid) ||
+ if ((gid_is_valid(p.gid) && g->gid != p.gid) ||
(p.name && !group_record_matches_group_name(g, p.name)))
return sd_varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);