1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2014 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/types.h>
30 #include "specifier.h"
31 #include "path-util.h"
34 #include "conf-files.h"
38 typedef enum ItemType {
60 static char *arg_root = NULL;
62 static const char conf_file_dirs[] =
63 "/usr/local/lib/sysusers.d\0"
64 "/usr/lib/sysusers.d\0"
70 static Hashmap *users = NULL, *groups = NULL;
71 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
73 static Hashmap *database_uid = NULL, *database_user = NULL;
74 static Hashmap *database_gid = NULL, *database_group = NULL;
76 static uid_t search_uid = SYSTEM_UID_MAX;
77 static gid_t search_gid = SYSTEM_GID_MAX;
79 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
80 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
82 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
83 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
85 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
87 static int load_user_database(void) {
88 _cleanup_fclose_ FILE *f = NULL;
89 const char *passwd_path;
93 passwd_path = fix_root("/etc/passwd");
94 f = fopen(passwd_path, "re");
96 return errno == ENOENT ? 0 : -errno;
98 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
102 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
107 while ((pw = fgetpwent(f))) {
111 n = strdup(pw->pw_name);
115 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
116 if (k < 0 && k != -EEXIST) {
121 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
122 if (q < 0 && q != -EEXIST) {
133 if (!IN_SET(errno, 0, ENOENT))
139 static int load_group_database(void) {
140 _cleanup_fclose_ FILE *f = NULL;
141 const char *group_path;
145 group_path = fix_root("/etc/group");
146 f = fopen(group_path, "re");
148 return errno == ENOENT ? 0 : -errno;
150 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
154 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
159 while ((gr = fgetgrent(f))) {
163 n = strdup(gr->gr_name);
167 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
168 if (k < 0 && k != -EEXIST) {
173 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
174 if (q < 0 && q != -EEXIST) {
185 if (!IN_SET(errno, 0, ENOENT))
191 static int make_backup(const char *x) {
192 _cleanup_close_ int src = -1, dst = -1;
194 struct timespec ts[2];
198 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
200 if (errno == ENOENT) /* No backup necessary... */
206 if (fstat(src, &st) < 0)
209 temp = strappenda(x, ".XXXXXX");
210 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
214 r = copy_bytes(src, dst, (off_t) -1);
218 /* Copy over the access mask */
219 if (fchmod(dst, st.st_mode & 07777) < 0) {
224 /* Don't fail on chmod(). If it stays owned by us, then it
225 * isn't too bad... */
226 fchown(dst, st.st_uid, st.st_gid);
232 backup = strappenda(x, "-");
233 if (rename(temp, backup) < 0)
243 static int write_files(void) {
245 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
246 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
247 const char *passwd_path = NULL, *group_path = NULL;
252 /* We don't patch /etc/shadow or /etc/gshadow here, since we
253 * only create user accounts without passwords anyway. */
255 if (hashmap_size(todo_gids) > 0) {
256 _cleanup_fclose_ FILE *original = NULL;
258 group_path = fix_root("/etc/group");
259 r = fopen_temporary(group_path, &group, &group_tmp);
263 if (fchmod(fileno(group), 0644) < 0) {
268 original = fopen(group_path, "re");
273 while ((gr = fgetgrent(original))) {
274 /* Safety checks against name and GID
275 * collisions. Normally, this should
276 * be unnecessary, but given that we
277 * look at the entries anyway here,
278 * let's make an extra verification
279 * step that we don't generate
280 * duplicate entries. */
282 i = hashmap_get(groups, gr->gr_name);
283 if (i && i->todo_group) {
288 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
293 if (putgrent(gr, group) < 0) {
300 if (!IN_SET(errno, 0, ENOENT)) {
305 } else if (errno != ENOENT) {
310 HASHMAP_FOREACH(i, todo_gids, iterator) {
314 .gr_passwd = (char*) "x",
317 if (putgrent(&n, group) < 0) {
323 r = fflush_and_check(group);
328 if (hashmap_size(todo_uids) > 0) {
329 _cleanup_fclose_ FILE *original = NULL;
331 passwd_path = fix_root("/etc/passwd");
332 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
336 if (fchmod(fileno(passwd), 0644) < 0) {
341 original = fopen(passwd_path, "re");
346 while ((pw = fgetpwent(original))) {
348 i = hashmap_get(users, pw->pw_name);
349 if (i && i->todo_user) {
354 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
359 if (putpwent(pw, passwd) < 0) {
366 if (!IN_SET(errno, 0, ENOENT)) {
371 } else if (errno != ENOENT) {
376 HASHMAP_FOREACH(i, todo_uids, iterator) {
381 .pw_gecos = i->description,
382 .pw_passwd = (char*) "x",
385 /* Initialize the home directory and the shell
386 * to nologin, with one exception: for root we
387 * patch in something special */
389 n.pw_shell = (char*) "/bin/sh";
390 n.pw_dir = (char*) "/root";
392 n.pw_shell = (char*) "/sbin/nologin";
393 n.pw_dir = (char*) "/";
396 if (putpwent(&n, passwd) < 0) {
402 r = fflush_and_check(passwd);
407 /* Make a backup of the old files */
409 r = make_backup(group_path);
415 r = make_backup(passwd_path);
420 /* And make the new files count */
422 if (rename(group_tmp, group_path) < 0) {
432 if (rename(passwd_tmp, passwd_path) < 0) {
454 static int uid_is_ok(uid_t uid, const char *name) {
460 /* Let's see if we already have assigned the UID a second time */
461 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
464 /* Try to avoid using uids that are already used by a group
465 * that doesn't have the same name as our new user. */
466 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
467 if (i && !streq(i->name, name))
470 /* Let's check the files directly */
471 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
474 n = hashmap_get(database_gid, GID_TO_PTR(uid));
475 if (n && !streq(n, name))
478 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
484 if (!IN_SET(errno, 0, ENOENT))
488 g = getgrgid((gid_t) uid);
490 if (!streq(g->gr_name, name))
492 } else if (!IN_SET(errno, 0, ENOENT))
499 static int root_stat(const char *p, struct stat *st) {
503 if (stat(fix, st) < 0)
509 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
511 bool found_uid = false, found_gid = false;
517 /* First, try to get the gid directly */
518 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
523 /* Then, try to get the uid directly */
524 if ((_uid || (_gid && !found_gid))
526 && root_stat(i->uid_path, &st) >= 0) {
531 /* If we need the gid, but had no success yet, also derive it from the uid path */
532 if (_gid && !found_gid) {
538 /* If that didn't work yet, then let's reuse the gid as uid */
539 if (_uid && !found_uid && i->gid_path) {
544 } else if (root_stat(i->gid_path, &st) >= 0) {
545 uid = (uid_t) st.st_gid;
567 static int add_user(Item *i) {
573 /* Check the database directly */
574 z = hashmap_get(database_user, i->name);
576 log_debug("User %s already exists.", i->name);
577 i->uid = PTR_TO_UID(z);
588 p = getpwnam(i->name);
590 log_debug("User %s already exists.", i->name);
594 free(i->description);
595 i->description = strdup(p->pw_gecos);
598 if (!IN_SET(errno, 0, ENOENT)) {
599 log_error("Failed to check if user %s already exists: %m", i->name);
603 /* And shadow too, just to be sure */
605 sp = getspnam(i->name);
607 log_error("User %s already exists in shadow database, but not in user database.", i->name);
610 if (!IN_SET(errno, 0, ENOENT)) {
611 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
616 /* Try to use the suggested numeric uid */
618 r = uid_is_ok(i->uid, i->name);
620 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
624 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
629 /* If that didn't work, try to read it from the specified path */
633 if (read_id_from_file(i, &c, NULL) > 0) {
635 if (c <= 0 || c > SYSTEM_UID_MAX)
636 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
638 r = uid_is_ok(c, i->name);
640 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
646 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
651 /* Otherwise try to reuse the group ID */
652 if (!i->uid_set && i->gid_set) {
653 r = uid_is_ok((uid_t) i->gid, i->name);
655 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
659 i->uid = (uid_t) i->gid;
664 /* And if that didn't work either, let's try to find a free one */
666 for (; search_uid > 0; search_uid--) {
668 r = uid_is_ok(search_uid, i->name);
670 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
676 if (search_uid <= 0) {
677 log_error("No free user ID available for %s.", i->name);
687 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
691 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
696 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
701 static int gid_is_ok(gid_t gid) {
705 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
708 /* Avoid reusing gids that are already used by a different user */
709 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
712 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
715 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
723 if (!IN_SET(errno, 0, ENOENT))
727 p = getpwuid((uid_t) gid);
730 if (!IN_SET(errno, 0, ENOENT))
737 static int add_group(Item *i) {
743 /* Check the database directly */
744 z = hashmap_get(database_group, i->name);
746 log_debug("Group %s already exists.", i->name);
747 i->gid = PTR_TO_GID(z);
757 g = getgrnam(i->name);
759 log_debug("Group %s already exists.", i->name);
764 if (!IN_SET(errno, 0, ENOENT)) {
765 log_error("Failed to check if group %s already exists: %m", i->name);
770 /* Try to use the suggested numeric gid */
772 r = gid_is_ok(i->gid);
774 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
778 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
783 /* Try to reuse the numeric uid, if there's one */
784 if (!i->gid_set && i->uid_set) {
785 r = gid_is_ok((gid_t) i->uid);
787 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
791 i->gid = (gid_t) i->uid;
796 /* If that didn't work, try to read it from the specified path */
800 if (read_id_from_file(i, NULL, &c) > 0) {
802 if (c <= 0 || c > SYSTEM_GID_MAX)
803 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
807 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
813 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
818 /* And if that didn't work either, let's try to find a free one */
820 for (; search_gid > 0; search_gid--) {
821 r = gid_is_ok(search_gid);
823 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
829 if (search_gid <= 0) {
830 log_error("No free group ID available for %s.", i->name);
840 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
844 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
848 i->todo_group = true;
849 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
854 static int process_item(Item *i) {
871 j = hashmap_get(users, i->name);
873 /* There's already user to be created for this
874 * name, let's process that in one step */
883 j->gid_path = strdup(i->gid_path);
895 assert_not_reached("Unknown item type");
898 static void item_free(Item *i) {
906 free(i->description);
910 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
912 static bool item_equal(Item *a, Item *b) {
916 if (a->type != b->type)
919 if (!streq_ptr(a->name, b->name))
922 if (!streq_ptr(a->uid_path, b->uid_path))
925 if (!streq_ptr(a->gid_path, b->gid_path))
928 if (!streq_ptr(a->description, b->description))
931 if (a->uid_set != b->uid_set)
934 if (a->uid_set && a->uid != b->uid)
937 if (a->gid_set != b->gid_set)
940 if (a->gid_set && a->gid != b->gid)
946 static bool valid_user_group_name(const char *u) {
953 if (!(u[0] >= 'a' && u[0] <= 'z') &&
954 !(u[0] >= 'A' && u[0] <= 'Z') &&
958 for (i = u+1; *i; i++) {
959 if (!(*i >= 'a' && *i <= 'z') &&
960 !(*i >= 'A' && *i <= 'Z') &&
961 !(*i >= '0' && *i <= '9') &&
967 sz = sysconf(_SC_LOGIN_NAME_MAX);
970 if ((size_t) (i-u) > (size_t) sz)
976 static bool valid_gecos(const char *d) {
978 if (!utf8_is_valid(d))
981 if (strpbrk(d, ":\n"))
987 static int parse_line(const char *fname, unsigned line, const char *buffer) {
989 static const Specifier specifier_table[] = {
990 { 'm', specifier_machine_id, NULL },
991 { 'b', specifier_boot_id, NULL },
992 { 'H', specifier_host_name, NULL },
993 { 'v', specifier_kernel_release, NULL },
997 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL;
998 _cleanup_(item_freep) Item *i = NULL;
1014 log_error("[%s:%u] Syntax error.", fname, line);
1018 if (strlen(action) != 1) {
1019 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1023 if (!IN_SET(action[0], ADD_USER, ADD_GROUP)) {
1024 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1032 i->type = action[0];
1034 r = specifier_printf(name, specifier_table, NULL, &i->name);
1036 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1040 if (!valid_user_group_name(i->name)) {
1041 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, i->name);
1046 n += strspn(buffer+n, WHITESPACE);
1047 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1048 i->description = unquote(buffer+n, "\"");
1049 if (!i->description)
1052 if (!valid_gecos(i->description)) {
1053 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1059 if (id && !streq(id, "-")) {
1061 if (path_is_absolute(id)) {
1068 path_kill_slashes(p);
1070 if (i->type == ADD_USER)
1075 } else if (i->type == ADD_USER) {
1076 r = parse_uid(id, &i->uid);
1078 log_error("Failed to parse UID: %s", id);
1085 assert(i->type == ADD_GROUP);
1087 r = parse_gid(id, &i->gid);
1089 log_error("Failed to parse GID: %s", id);
1097 if (i->type == ADD_USER) {
1098 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1101 assert(i->type == ADD_GROUP);
1102 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1108 existing = hashmap_get(h, i->name);
1111 /* Two identical items are fine */
1112 if (!item_equal(existing, i))
1113 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1118 r = hashmap_put(h, i->name, i);
1120 log_error("Failed to insert item %s: %s", i->name, strerror(-r));
1128 static int read_config_file(const char *fn, bool ignore_enoent) {
1129 _cleanup_fclose_ FILE *f = NULL;
1130 char line[LINE_MAX];
1136 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1138 if (ignore_enoent && r == -ENOENT)
1141 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1145 FOREACH_LINE(line, f, break) {
1152 if (*l == '#' || *l == 0)
1155 k = parse_line(fn, v, l);
1156 if (k < 0 && r == 0)
1161 log_error("Failed to read from file %s: %m", fn);
1169 static int take_lock(void) {
1171 struct flock flock = {
1173 .l_whence = SEEK_SET,
1181 /* This is roughly the same as lckpwdf(), but not as awful. We
1182 * don't want to use alarm() and signals, hence we implement
1183 * our own trivial version of this.
1185 * Note that shadow-utils also takes per-database locks in
1186 * addition to lckpwdf(). However, we don't given that they
1187 * are redundant as they they invoke lckpwdf() first and keep
1188 * it during everything they do. The per-database locks are
1189 * awfully racy, and thus we just won't do them. */
1191 path = fix_root("/etc/.pwd.lock");
1192 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
1196 r = fcntl(fd, F_SETLKW, &flock);
1205 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1209 name = hashmap_first(by_id);
1213 hashmap_remove(by_name, name);
1215 hashmap_steal_first_key(by_id);
1219 while ((name = hashmap_steal_first_key(by_name)))
1222 hashmap_free(by_name);
1223 hashmap_free(by_id);
1226 static int help(void) {
1228 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1229 "Creates system user accounts.\n\n"
1230 " -h --help Show this help\n"
1231 " --version Show package version\n"
1232 " --root=PATH Operate on an alternate filesystem root\n",
1233 program_invocation_short_name);
1238 static int parse_argv(int argc, char *argv[]) {
1241 ARG_VERSION = 0x100,
1245 static const struct option options[] = {
1246 { "help", no_argument, NULL, 'h' },
1247 { "version", no_argument, NULL, ARG_VERSION },
1248 { "root", required_argument, NULL, ARG_ROOT },
1257 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1265 puts(PACKAGE_STRING);
1266 puts(SYSTEMD_FEATURES);
1271 arg_root = path_make_absolute_cwd(optarg);
1275 path_kill_slashes(arg_root);
1282 assert_not_reached("Unhandled option");
1289 int main(int argc, char *argv[]) {
1291 _cleanup_close_ int lock = -1;
1296 r = parse_argv(argc, argv);
1300 log_set_target(LOG_TARGET_AUTO);
1301 log_parse_environment();
1308 if (optind < argc) {
1311 for (j = optind; j < argc; j++) {
1312 k = read_config_file(argv[j], false);
1313 if (k < 0 && r == 0)
1317 _cleanup_strv_free_ char **files = NULL;
1320 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1322 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1326 STRV_FOREACH(f, files) {
1327 k = read_config_file(*f, true);
1328 if (k < 0 && r == 0)
1335 log_error("Failed to take lock: %s", strerror(-lock));
1339 r = load_user_database();
1341 log_error("Failed to load user database: %s", strerror(-r));
1345 r = load_group_database();
1347 log_error("Failed to read group database: %s", strerror(-r));
1351 HASHMAP_FOREACH(i, groups, iterator)
1354 HASHMAP_FOREACH(i, users, iterator)
1359 log_error("Failed to write files: %s", strerror(-r));
1362 while ((i = hashmap_steal_first(groups)))
1365 while ((i = hashmap_steal_first(users)))
1368 hashmap_free(groups);
1369 hashmap_free(users);
1370 hashmap_free(todo_uids);
1371 hashmap_free(todo_gids);
1373 free_database(database_user, database_uid);
1374 free_database(database_group, database_gid);
1378 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;