]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/home/home-util.c
Merge pull request #15651 from poettering/newlocale-check
[thirdparty/systemd.git] / src / home / home-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "dns-domain.h"
4 #include "errno-util.h"
5 #include "home-util.h"
6 #include "libcrypt-util.h"
7 #include "memory-util.h"
8 #include "path-util.h"
9 #include "string-util.h"
10 #include "strv.h"
11 #include "user-util.h"
12
13 bool suitable_user_name(const char *name) {
14
15 /* Checks whether the specified name is suitable for management via homed. Note that client-side
16 * we usually validate with the simple valid_user_group_name(), while server-side we are a bit more
17 * restrictive, so that we can change the rules server-side without having to update things
18 * client-side too. */
19
20 if (!valid_user_group_name(name, 0))
21 return false;
22
23 /* We generally rely on NSS to tell us which users not to care for, but let's filter out some
24 * particularly well-known users. */
25 if (STR_IN_SET(name,
26 "root",
27 "nobody",
28 NOBODY_USER_NAME, NOBODY_GROUP_NAME))
29 return false;
30
31 /* Let's also defend our own namespace, as well as Debian's (unwritten?) logic of prefixing system
32 * users with underscores. */
33 if (STARTSWITH_SET(name, "systemd-", "_"))
34 return false;
35
36 return true;
37 }
38
39 int suitable_realm(const char *realm) {
40 _cleanup_free_ char *normalized = NULL;
41 int r;
42
43 /* Similar to the above: let's validate the realm a bit stricter server-side than client side */
44
45 r = dns_name_normalize(realm, 0, &normalized); /* this also checks general validity */
46 if (r == -EINVAL)
47 return 0;
48 if (r < 0)
49 return r;
50
51 if (!streq(realm, normalized)) /* is this normalized? */
52 return false;
53
54 if (dns_name_is_root(realm)) /* Don't allow top level domain */
55 return false;
56
57 return true;
58 }
59
60 int suitable_image_path(const char *path) {
61
62 return !empty_or_root(path) &&
63 path_is_valid(path) &&
64 path_is_absolute(path);
65 }
66
67 int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) {
68 _cleanup_free_ char *user_name = NULL, *realm = NULL;
69 const char *c;
70 int r;
71
72 assert(t);
73 assert(ret_user_name);
74 assert(ret_realm);
75
76 c = strchr(t, '@');
77 if (!c) {
78 user_name = strdup(t);
79 if (!user_name)
80 return -ENOMEM;
81 } else {
82 user_name = strndup(t, c - t);
83 if (!user_name)
84 return -ENOMEM;
85
86 realm = strdup(c + 1);
87 if (!realm)
88 return -ENOMEM;
89 }
90
91 if (!suitable_user_name(user_name))
92 return -EINVAL;
93
94 if (realm) {
95 r = suitable_realm(realm);
96 if (r < 0)
97 return r;
98 if (r == 0)
99 return -EINVAL;
100 }
101
102 *ret_user_name = TAKE_PTR(user_name);
103 *ret_realm = TAKE_PTR(realm);
104
105 return 0;
106 }
107
108 int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) {
109 _cleanup_(erase_and_freep) char *formatted = NULL;
110 JsonVariant *v;
111 int r;
112
113 assert(m);
114 assert(secret);
115
116 if (!FLAGS_SET(secret->mask, USER_RECORD_SECRET))
117 return sd_bus_message_append(m, "s", "{}");
118
119 v = json_variant_by_key(secret->json, "secret");
120 if (!v)
121 return -EINVAL;
122
123 r = json_variant_format(v, 0, &formatted);
124 if (r < 0)
125 return r;
126
127 (void) sd_bus_message_sensitive(m);
128
129 return sd_bus_message_append(m, "s", formatted);
130 }
131
132 int test_password_one(const char *hashed_password, const char *password) {
133 struct crypt_data cc = {};
134 const char *k;
135 bool b;
136
137 errno = 0;
138 k = crypt_r(password, hashed_password, &cc);
139 if (!k) {
140 explicit_bzero_safe(&cc, sizeof(cc));
141 return errno_or_else(EINVAL);
142 }
143
144 b = streq(k, hashed_password);
145 explicit_bzero_safe(&cc, sizeof(cc));
146 return b;
147 }
148
149 int test_password_many(char **hashed_password, const char *password) {
150 char **hpw;
151 int r;
152
153 STRV_FOREACH(hpw, hashed_password) {
154 r = test_password_one(*hpw, password);
155 if (r < 0)
156 return r;
157 if (r > 0)
158 return true;
159 }
160
161 return false;
162 }