]>
Commit | Line | Data |
---|---|---|
70a5db58 LP |
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 | ||
3e93027b | 15 | /* Checks whether the specified name is suitable for management via homed. Note that client-side |
18143cd7 | 16 | * we usually validate with the simple valid_user_group_name(), while server-side we are a bit more |
2a4be3c5 ZJS |
17 | * restrictive, so that we can change the rules server-side without having to update things |
18 | * client-side too. */ | |
70a5db58 | 19 | |
7a8867ab | 20 | if (!valid_user_group_name(name, 0)) |
70a5db58 LP |
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 | ||
c07bf7a4 LP |
67 | bool supported_fstype(const char *fstype) { |
68 | /* Limit the set of supported file systems a bit, as protection against little tested kernel file | |
69 | * systems. Also, we only support the resize ioctls for these file systems. */ | |
70 | return STR_IN_SET(fstype, "ext4", "btrfs", "xfs"); | |
71 | } | |
72 | ||
70a5db58 LP |
73 | int split_user_name_realm(const char *t, char **ret_user_name, char **ret_realm) { |
74 | _cleanup_free_ char *user_name = NULL, *realm = NULL; | |
75 | const char *c; | |
76 | int r; | |
77 | ||
78 | assert(t); | |
79 | assert(ret_user_name); | |
80 | assert(ret_realm); | |
81 | ||
82 | c = strchr(t, '@'); | |
83 | if (!c) { | |
84 | user_name = strdup(t); | |
85 | if (!user_name) | |
86 | return -ENOMEM; | |
87 | } else { | |
88 | user_name = strndup(t, c - t); | |
89 | if (!user_name) | |
90 | return -ENOMEM; | |
91 | ||
92 | realm = strdup(c + 1); | |
93 | if (!realm) | |
94 | return -ENOMEM; | |
95 | } | |
96 | ||
97 | if (!suitable_user_name(user_name)) | |
98 | return -EINVAL; | |
99 | ||
100 | if (realm) { | |
101 | r = suitable_realm(realm); | |
102 | if (r < 0) | |
103 | return r; | |
104 | if (r == 0) | |
105 | return -EINVAL; | |
106 | } | |
107 | ||
108 | *ret_user_name = TAKE_PTR(user_name); | |
109 | *ret_realm = TAKE_PTR(realm); | |
110 | ||
111 | return 0; | |
112 | } | |
113 | ||
114 | int bus_message_append_secret(sd_bus_message *m, UserRecord *secret) { | |
115 | _cleanup_(erase_and_freep) char *formatted = NULL; | |
116 | JsonVariant *v; | |
117 | int r; | |
118 | ||
119 | assert(m); | |
120 | assert(secret); | |
121 | ||
122 | if (!FLAGS_SET(secret->mask, USER_RECORD_SECRET)) | |
123 | return sd_bus_message_append(m, "s", "{}"); | |
124 | ||
125 | v = json_variant_by_key(secret->json, "secret"); | |
126 | if (!v) | |
127 | return -EINVAL; | |
128 | ||
129 | r = json_variant_format(v, 0, &formatted); | |
130 | if (r < 0) | |
131 | return r; | |
132 | ||
2ffee2c9 LP |
133 | (void) sd_bus_message_sensitive(m); |
134 | ||
70a5db58 LP |
135 | return sd_bus_message_append(m, "s", formatted); |
136 | } | |
137 | ||
138 | int test_password_one(const char *hashed_password, const char *password) { | |
139 | struct crypt_data cc = {}; | |
140 | const char *k; | |
141 | bool b; | |
142 | ||
143 | errno = 0; | |
144 | k = crypt_r(password, hashed_password, &cc); | |
145 | if (!k) { | |
146 | explicit_bzero_safe(&cc, sizeof(cc)); | |
147 | return errno_or_else(EINVAL); | |
148 | } | |
149 | ||
150 | b = streq(k, hashed_password); | |
151 | explicit_bzero_safe(&cc, sizeof(cc)); | |
152 | return b; | |
153 | } | |
154 | ||
155 | int test_password_many(char **hashed_password, const char *password) { | |
156 | char **hpw; | |
157 | int r; | |
158 | ||
159 | STRV_FOREACH(hpw, hashed_password) { | |
160 | r = test_password_one(*hpw, password); | |
161 | if (r < 0) | |
162 | return r; | |
163 | if (r > 0) | |
164 | return true; | |
165 | } | |
166 | ||
167 | return false; | |
168 | } |