2019-07-04 18:35:39 +02:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
|
|
|
|
#include "dns-domain.h"
|
|
|
|
#include "errno-util.h"
|
|
|
|
#include "home-util.h"
|
|
|
|
#include "libcrypt-util.h"
|
|
|
|
#include "memory-util.h"
|
|
|
|
#include "path-util.h"
|
|
|
|
#include "string-util.h"
|
|
|
|
#include "strv.h"
|
|
|
|
#include "user-util.h"
|
|
|
|
|
|
|
|
bool suitable_user_name(const char *name) {
|
|
|
|
|
2020-03-01 12:24:25 +01:00
|
|
|
/* Checks whether the specified name is suitable for management via homed. Note that client-side
|
2020-02-26 11:11:57 +01:00
|
|
|
* we usually validate with the simple valid_user_group_name(), while server-side we are a bit more
|
2020-01-30 13:46:19 +01:00
|
|
|
* restrictive, so that we can change the rules server-side without having to update things
|
|
|
|
* client-side too. */
|
2019-07-04 18:35:39 +02:00
|
|
|
|
2020-04-04 12:23:02 +02:00
|
|
|
if (!valid_user_group_name(name, 0))
|
2019-07-04 18:35:39 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* We generally rely on NSS to tell us which users not to care for, but let's filter out some
|
|
|
|
* particularly well-known users. */
|
|
|
|
if (STR_IN_SET(name,
|
|
|
|
"root",
|
|
|
|
"nobody",
|
|
|
|
NOBODY_USER_NAME, NOBODY_GROUP_NAME))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Let's also defend our own namespace, as well as Debian's (unwritten?) logic of prefixing system
|
|
|
|
* users with underscores. */
|
|
|
|
if (STARTSWITH_SET(name, "systemd-", "_"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int suitable_realm(const char *realm) {
|
|
|
|
_cleanup_free_ char *normalized = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
/* Similar to the above: let's validate the realm a bit stricter server-side than client side */
|
|
|
|
|
|
|
|
r = dns_name_normalize(realm, 0, &normalized); /* this also checks general validity */
|
|
|
|
if (r == -EINVAL)
|
|
|
|
return 0;
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (!streq(realm, normalized)) /* is this normalized? */
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (dns_name_is_root(realm)) /* Don't allow top level domain */
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int suitable_image_path(const char *path) {
|
|
|
|
|
|
|
|
return !empty_or_root(path) &&
|
|
|
|
path_is_valid(path) &&
|
|
|
|
path_is_absolute(path);
|
|
|
|
}
|
|
|
|
|
2020-05-05 09:55:53 +02:00
|
|
|
bool supported_fstype(const char *fstype) {
|
|
|
|
/* Limit the set of supported file systems a bit, as protection against little tested kernel file
|
|
|
|
* systems. Also, we only support the resize ioctls for these file systems. */
|
|
|
|
return STR_IN_SET(fstype, "ext4", "btrfs", "xfs");
|
|
|
|
}
|
|
|
|
|
2019-07-04 18:35:39 +02:00
|
|
|
int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) {
|
|
|
|
_cleanup_free_ char *user_name = NULL, *realm = NULL;
|
|
|
|
const char *c;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(t);
|
|
|
|
assert(ret_user_name);
|
|
|
|
assert(ret_realm);
|
|
|
|
|
|
|
|
c = strchr(t, '@');
|
|
|
|
if (!c) {
|
|
|
|
user_name = strdup(t);
|
|
|
|
if (!user_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
} else {
|
|
|
|
user_name = strndup(t, c - t);
|
|
|
|
if (!user_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
realm = strdup(c + 1);
|
|
|
|
if (!realm)
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!suitable_user_name(user_name))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (realm) {
|
|
|
|
r = suitable_realm(realm);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r == 0)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ret_user_name = TAKE_PTR(user_name);
|
|
|
|
*ret_realm = TAKE_PTR(realm);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
|
|
|
|
_cleanup_(erase_and_freep) char *formatted = NULL;
|
|
|
|
JsonVariant *v;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(secret);
|
|
|
|
|
|
|
|
if (!FLAGS_SET(secret->mask, USER_RECORD_SECRET))
|
|
|
|
return sd_bus_message_append(m, "s", "{}");
|
|
|
|
|
|
|
|
v = json_variant_by_key(secret->json, "secret");
|
|
|
|
if (!v)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
r = json_variant_format(v, 0, &formatted);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2020-04-29 16:24:41 +02:00
|
|
|
(void) sd_bus_message_sensitive(m);
|
|
|
|
|
2019-07-04 18:35:39 +02:00
|
|
|
return sd_bus_message_append(m, "s", formatted);
|
|
|
|
}
|
|
|
|
|
|
|
|
int test_password_one(const char *hashed_password, const char *password) {
|
|
|
|
struct crypt_data cc = {};
|
|
|
|
const char *k;
|
|
|
|
bool b;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
k = crypt_r(password, hashed_password, &cc);
|
|
|
|
if (!k) {
|
|
|
|
explicit_bzero_safe(&cc, sizeof(cc));
|
|
|
|
return errno_or_else(EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
b = streq(k, hashed_password);
|
|
|
|
explicit_bzero_safe(&cc, sizeof(cc));
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
int test_password_many(char **hashed_password, const char *password) {
|
|
|
|
char **hpw;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
STRV_FOREACH(hpw, hashed_password) {
|
|
|
|
r = test_password_one(*hpw, password);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r > 0)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|