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 {
59 static char *arg_root = NULL;
61 static const char conf_file_dirs[] =
62 "/usr/local/lib/sysusers.d\0"
63 "/usr/lib/sysusers.d\0"
69 static Hashmap *users = NULL, *groups = NULL;
70 static Hashmap *todo_uids = NULL, *todo_gids = NULL;
72 static Hashmap *database_uid = NULL, *database_user = NULL;
73 static Hashmap *database_gid = NULL, *database_group = NULL;
75 static uid_t search_uid = SYSTEM_UID_MAX;
76 static gid_t search_gid = SYSTEM_GID_MAX;
78 #define UID_TO_PTR(u) (ULONG_TO_PTR(u+1))
79 #define PTR_TO_UID(u) ((uid_t) (PTR_TO_ULONG(u)-1))
81 #define GID_TO_PTR(g) (ULONG_TO_PTR(g+1))
82 #define PTR_TO_GID(g) ((gid_t) (PTR_TO_ULONG(g)-1))
84 #define fix_root(x) (arg_root ? strappenda(arg_root, x) : x)
86 static int load_user_database(void) {
87 _cleanup_fclose_ FILE *f = NULL;
88 const char *passwd_path;
92 passwd_path = fix_root("/etc/passwd");
93 f = fopen(passwd_path, "re");
95 return errno == ENOENT ? 0 : -errno;
97 r = hashmap_ensure_allocated(&database_user, string_hash_func, string_compare_func);
101 r = hashmap_ensure_allocated(&database_uid, trivial_hash_func, trivial_compare_func);
106 while ((pw = fgetpwent(f))) {
110 n = strdup(pw->pw_name);
114 k = hashmap_put(database_user, n, UID_TO_PTR(pw->pw_uid));
115 if (k < 0 && k != -EEXIST) {
120 q = hashmap_put(database_uid, UID_TO_PTR(pw->pw_uid), n);
121 if (q < 0 && q != -EEXIST) {
132 if (!IN_SET(errno, 0, ENOENT))
138 static int load_group_database(void) {
139 _cleanup_fclose_ FILE *f = NULL;
140 const char *group_path;
144 group_path = fix_root("/etc/group");
145 f = fopen(group_path, "re");
147 return errno == ENOENT ? 0 : -errno;
149 r = hashmap_ensure_allocated(&database_group, string_hash_func, string_compare_func);
153 r = hashmap_ensure_allocated(&database_gid, trivial_hash_func, trivial_compare_func);
158 while ((gr = fgetgrent(f))) {
162 n = strdup(gr->gr_name);
166 k = hashmap_put(database_group, n, GID_TO_PTR(gr->gr_gid));
167 if (k < 0 && k != -EEXIST) {
172 q = hashmap_put(database_gid, GID_TO_PTR(gr->gr_gid), n);
173 if (q < 0 && q != -EEXIST) {
184 if (!IN_SET(errno, 0, ENOENT))
190 static int make_backup(const char *x) {
191 _cleanup_close_ int src = -1, dst = -1;
193 struct timespec ts[2];
197 src = open(x, O_RDONLY|O_CLOEXEC|O_NOCTTY);
199 if (errno == ENOENT) /* No backup necessary... */
205 if (fstat(src, &st) < 0)
208 temp = strappenda(x, ".XXXXXX");
209 dst = mkostemp_safe(temp, O_WRONLY|O_CLOEXEC|O_NOCTTY);
213 r = copy_bytes(src, dst);
217 /* Copy over the access mask */
218 if (fchmod(dst, st.st_mode & 07777) < 0) {
223 /* Don't fail on chmod(). If it stays owned by us, then it
224 * isn't too bad... */
225 fchown(dst, st.st_uid, st.st_gid);
231 backup = strappenda(x, "-");
232 if (rename(temp, backup) < 0)
242 static int write_files(void) {
244 _cleanup_fclose_ FILE *passwd = NULL, *group = NULL;
245 _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL;
246 const char *passwd_path = NULL, *group_path = NULL;
251 /* We don't patch /etc/shadow or /etc/gshadow here, since we
252 * only create user accounts without passwords anyway. */
254 if (hashmap_size(todo_gids) > 0) {
255 _cleanup_fclose_ FILE *original = NULL;
257 group_path = fix_root("/etc/group");
258 r = fopen_temporary(group_path, &group, &group_tmp);
262 if (fchmod(fileno(group), 0644) < 0) {
267 original = fopen(group_path, "re");
272 while ((gr = fgetgrent(original))) {
273 /* Safety checks against name and GID
274 * collisions. Normally, this should
275 * be unnecessary, but given that we
276 * look at the entries anyway here,
277 * let's make an extra verification
278 * step that we don't generate
279 * duplicate entries. */
281 i = hashmap_get(groups, gr->gr_name);
287 if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
292 if (putgrent(gr, group) < 0) {
299 if (!IN_SET(errno, 0, ENOENT)) {
304 } else if (errno != ENOENT) {
309 HASHMAP_FOREACH(i, todo_gids, iterator) {
313 .gr_passwd = (char*) "x",
316 if (putgrent(&n, group) < 0) {
322 r = fflush_and_check(group);
327 if (hashmap_size(todo_uids) > 0) {
328 _cleanup_fclose_ FILE *original = NULL;
330 passwd_path = fix_root("/etc/passwd");
331 r = fopen_temporary(passwd_path, &passwd, &passwd_tmp);
335 if (fchmod(fileno(passwd), 0644) < 0) {
340 original = fopen(passwd_path, "re");
345 while ((pw = fgetpwent(original))) {
347 i = hashmap_get(users, pw->pw_name);
353 if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
358 if (putpwent(pw, passwd) < 0) {
365 if (!IN_SET(errno, 0, ENOENT)) {
370 } else if (errno != ENOENT) {
375 HASHMAP_FOREACH(i, todo_uids, iterator) {
380 .pw_gecos = i->description,
381 .pw_passwd = (char*) "x",
384 /* Initialize the home directory and the shell
385 * to nologin, with one exception: for root we
386 * patch in something special */
388 n.pw_shell = (char*) "/bin/sh";
389 n.pw_dir = (char*) "/root";
391 n.pw_shell = (char*) "/sbin/nologin";
392 n.pw_dir = (char*) "/";
395 if (putpwent(&n, passwd) < 0) {
401 r = fflush_and_check(passwd);
406 /* Make a backup of the old files */
408 r = make_backup(group_path);
414 r = make_backup(passwd_path);
419 /* And make the new files count */
421 if (rename(group_tmp, group_path) < 0) {
431 if (rename(passwd_tmp, passwd_path) < 0) {
453 static int uid_is_ok(uid_t uid, const char *name) {
459 /* Let's see if we already have assigned the UID a second time */
460 if (hashmap_get(todo_uids, UID_TO_PTR(uid)))
463 /* Try to avoid using uids that are already used by a group
464 * that doesn't have the same name as our new user. */
465 i = hashmap_get(todo_gids, GID_TO_PTR(uid));
466 if (i && !streq(i->name, name))
469 /* Let's check the files directly */
470 if (hashmap_contains(database_uid, UID_TO_PTR(uid)))
473 n = hashmap_get(database_gid, GID_TO_PTR(uid));
474 if (n && !streq(n, name))
477 /* Let's also check via NSS, to avoid UID clashes over LDAP and such, just in case */
487 g = getgrgid((gid_t) uid);
489 if (!streq(g->gr_name, name))
491 } else if (errno != 0)
498 static int root_stat(const char *p, struct stat *st) {
502 if (stat(fix, st) < 0)
508 static int read_id_from_file(Item *i, uid_t *_uid, gid_t *_gid) {
510 bool found_uid = false, found_gid = false;
516 /* First, try to get the gid directly */
517 if (_gid && i->gid_path && root_stat(i->gid_path, &st) >= 0) {
522 /* Then, try to get the uid directly */
523 if ((_uid || (_gid && !found_gid))
525 && root_stat(i->uid_path, &st) >= 0) {
530 /* If we need the gid, but had no success yet, also derive it from the uid path */
531 if (_gid && !found_gid) {
537 /* If that didn't work yet, then let's reuse the gid as uid */
538 if (_uid && !found_uid && i->gid_path) {
543 } else if (root_stat(i->gid_path, &st) >= 0) {
544 uid = (uid_t) st.st_gid;
566 static int add_user(Item *i) {
572 /* Check the database directly */
573 z = hashmap_get(database_user, i->name);
575 log_debug("User %s already exists.", i->name);
576 i->uid = PTR_TO_GID(z);
587 p = getpwnam(i->name);
589 log_debug("User %s already exists.", i->name);
593 free(i->description);
594 i->description = strdup(p->pw_gecos);
598 log_error("Failed to check if user %s already exists: %m", i->name);
602 /* And shadow too, just to be sure */
604 sp = getspnam(i->name);
606 log_error("User %s already exists in shadow database, but not in user database.", i->name);
610 log_error("Failed to check if user %s already exists in shadow database: %m", i->name);
615 /* Try to use the suggested numeric uid */
617 r = uid_is_ok(i->uid, i->name);
619 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
623 log_debug("Suggested user ID " UID_FMT " for %s already used.", i->uid, i->name);
628 /* If that didn't work, try to read it from the specified path */
632 if (read_id_from_file(i, &c, NULL) > 0) {
634 if (c <= 0 || c > SYSTEM_UID_MAX)
635 log_debug("User ID " UID_FMT " of file not suitable for %s.", c, i->name);
637 r = uid_is_ok(c, i->name);
639 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
645 log_debug("User ID " UID_FMT " of file for %s is already used.", c, i->name);
650 /* Otherwise try to reuse the group ID */
651 if (!i->uid_set && i->gid_set) {
652 r = uid_is_ok((uid_t) i->gid, i->name);
654 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
658 i->uid = (uid_t) i->gid;
663 /* And if that didn't work either, let's try to find a free one */
665 for (; search_uid > 0; search_uid--) {
667 r = uid_is_ok(search_uid, i->name);
669 log_error("Failed to verify uid " UID_FMT ": %s", i->uid, strerror(-r));
675 if (search_uid <= 0) {
676 log_error("No free user ID available for %s.", i->name);
686 r = hashmap_ensure_allocated(&todo_uids, trivial_hash_func, trivial_compare_func);
690 r = hashmap_put(todo_uids, UID_TO_PTR(i->uid), i);
695 log_info("Creating user %s (%s) with uid " UID_FMT " and gid " GID_FMT ".", i->name, strna(i->description), i->uid, i->gid);
700 static int gid_is_ok(gid_t gid) {
704 if (hashmap_get(todo_gids, GID_TO_PTR(gid)))
707 /* Avoid reusing gids that are already used by a different user */
708 if (hashmap_get(todo_uids, UID_TO_PTR(gid)))
711 if (hashmap_contains(database_gid, GID_TO_PTR(gid)))
714 if (hashmap_contains(database_uid, UID_TO_PTR(gid)))
726 p = getpwuid((uid_t) gid);
736 static int add_group(Item *i) {
742 /* Check the database directly */
743 z = hashmap_get(database_group, i->name);
745 log_debug("Group %s already exists.", i->name);
746 i->gid = PTR_TO_GID(z);
756 g = getgrnam(i->name);
758 log_debug("Group %s already exists.", i->name);
764 log_error("Failed to check if group %s already exists: %m", i->name);
769 /* Try to use the suggested numeric gid */
771 r = gid_is_ok(i->gid);
773 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
777 log_debug("Suggested group ID " GID_FMT " for %s already used.", i->gid, i->name);
782 /* Try to reuse the numeric uid, if there's one */
783 if (!i->gid_set && i->uid_set) {
784 r = gid_is_ok((gid_t) i->uid);
786 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
790 i->gid = (gid_t) i->uid;
795 /* If that didn't work, try to read it from the specified path */
799 if (read_id_from_file(i, NULL, &c) > 0) {
801 if (c <= 0 || c > SYSTEM_GID_MAX)
802 log_debug("Group ID " GID_FMT " of file not suitable for %s.", c, i->name);
806 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
812 log_debug("Group ID " GID_FMT " of file for %s already used.", c, i->name);
817 /* And if that didn't work either, let's try to find a free one */
819 for (; search_gid > 0; search_gid--) {
820 r = gid_is_ok(search_gid);
822 log_error("Failed to verify gid " GID_FMT ": %s", i->gid, strerror(-r));
828 if (search_gid <= 0) {
829 log_error("No free group ID available for %s.", i->name);
839 r = hashmap_ensure_allocated(&todo_gids, trivial_hash_func, trivial_compare_func);
843 r = hashmap_put(todo_gids, GID_TO_PTR(i->gid), i);
848 log_info("Creating group %s with gid " GID_FMT ".", i->name, i->gid);
853 static int process_item(Item *i) {
870 j = hashmap_get(users, i->name);
872 /* There's already user to be created for this
873 * name, let's process that in one step */
882 j->gid_path = strdup(i->gid_path);
894 assert_not_reached("Unknown item type");
897 static void item_free(Item *i) {
905 free(i->description);
909 DEFINE_TRIVIAL_CLEANUP_FUNC(Item*, item_free);
911 static bool item_equal(Item *a, Item *b) {
915 if (a->type != b->type)
918 if (!streq_ptr(a->name, b->name))
921 if (!streq_ptr(a->uid_path, b->uid_path))
924 if (!streq_ptr(a->gid_path, b->gid_path))
927 if (!streq_ptr(a->description, b->description))
930 if (a->uid_set != b->uid_set)
933 if (a->uid_set && a->uid != b->uid)
936 if (a->gid_set != b->gid_set)
939 if (a->gid_set && a->gid != b->gid)
945 static bool valid_user_group_name(const char *u) {
952 if (!(u[0] >= 'a' && u[0] <= 'z') &&
953 !(u[0] >= 'A' && u[0] <= 'Z') &&
957 for (i = u+1; *i; i++) {
958 if (!(*i >= 'a' && *i <= 'z') &&
959 !(*i >= 'A' && *i <= 'Z') &&
960 !(*i >= '0' && *i <= '9') &&
966 sz = sysconf(_SC_LOGIN_NAME_MAX);
969 if ((size_t) (i-u) > (size_t) sz)
975 static bool valid_gecos(const char *d) {
977 if (!utf8_is_valid(d))
980 if (strpbrk(d, ":\n"))
986 static int parse_line(const char *fname, unsigned line, const char *buffer) {
988 static const Specifier specifier_table[] = {
989 { 'm', specifier_machine_id, NULL },
990 { 'b', specifier_boot_id, NULL },
991 { 'H', specifier_host_name, NULL },
992 { 'v', specifier_kernel_release, NULL },
996 _cleanup_free_ char *action = NULL, *name = NULL, *id = NULL;
997 _cleanup_(item_freep) Item *i = NULL;
1013 log_error("[%s:%u] Syntax error.", fname, line);
1017 if (strlen(action) != 1) {
1018 log_error("[%s:%u] Unknown modifier '%s'", fname, line, action);
1022 if (!IN_SET(action[0], ADD_USER, ADD_GROUP)) {
1023 log_error("[%s:%u] Unknown command command type '%c'.", fname, line, action[0]);
1031 i->type = action[0];
1033 r = specifier_printf(name, specifier_table, NULL, &i->name);
1035 log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name);
1039 if (!valid_user_group_name(i->name)) {
1040 log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, i->name);
1045 n += strspn(buffer+n, WHITESPACE);
1046 if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
1047 i->description = unquote(buffer+n, "\"");
1048 if (!i->description)
1051 if (!valid_gecos(i->description)) {
1052 log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, i->description);
1058 if (id && !streq(id, "-")) {
1060 if (path_is_absolute(id)) {
1067 path_kill_slashes(p);
1069 if (i->type == ADD_USER)
1074 } else if (i->type == ADD_USER) {
1075 r = parse_uid(id, &i->uid);
1077 log_error("Failed to parse UID: %s", id);
1084 assert(i->type == ADD_GROUP);
1086 r = parse_gid(id, &i->gid);
1088 log_error("Failed to parse GID: %s", id);
1096 if (i->type == ADD_USER) {
1097 r = hashmap_ensure_allocated(&users, string_hash_func, string_compare_func);
1100 assert(i->type == ADD_GROUP);
1101 r = hashmap_ensure_allocated(&groups, string_hash_func, string_compare_func);
1107 existing = hashmap_get(h, i->name);
1110 /* Two identical items are fine */
1111 if (!item_equal(existing, i))
1112 log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name);
1117 r = hashmap_put(h, i->name, i);
1119 log_error("Failed to insert item %s: %s", i->name, strerror(-r));
1127 static int read_config_file(const char *fn, bool ignore_enoent) {
1128 _cleanup_fclose_ FILE *f = NULL;
1129 char line[LINE_MAX];
1135 r = search_and_fopen_nulstr(fn, "re", arg_root, conf_file_dirs, &f);
1137 if (ignore_enoent && r == -ENOENT)
1140 log_error("Failed to open '%s', ignoring: %s", fn, strerror(-r));
1144 FOREACH_LINE(line, f, break) {
1151 if (*l == '#' || *l == 0)
1154 k = parse_line(fn, v, l);
1155 if (k < 0 && r == 0)
1160 log_error("Failed to read from file %s: %m", fn);
1168 static int take_lock(void) {
1170 struct flock flock = {
1172 .l_whence = SEEK_SET,
1180 /* This is roughly the same as lckpwdf(), but not as awful. We
1181 * don't want to use alarm() and signals, hence we implement
1182 * our own trivial version of this.
1184 * Note that shadow-utils also takes per-database locks in
1185 * addition to lckpwdf(). However, we don't given that they
1186 * are redundant as they they invoke lckpwdf() first and keep
1187 * it during everything they do. The per-database locks are
1188 * awfully racy, and thus we just won't do them. */
1190 path = fix_root("/etc/.pwd.lock");
1191 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
1195 r = fcntl(fd, F_SETLKW, &flock);
1204 static void free_database(Hashmap *by_name, Hashmap *by_id) {
1208 name = hashmap_first(by_id);
1212 hashmap_remove(by_name, name);
1214 hashmap_steal_first_key(by_id);
1218 while ((name = hashmap_steal_first_key(by_name)))
1221 hashmap_free(by_name);
1222 hashmap_free(by_id);
1225 static int help(void) {
1227 printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
1228 "Creates system user accounts.\n\n"
1229 " -h --help Show this help\n"
1230 " --version Show package version\n"
1231 " --root=PATH Operate on an alternate filesystem root\n",
1232 program_invocation_short_name);
1237 static int parse_argv(int argc, char *argv[]) {
1240 ARG_VERSION = 0x100,
1244 static const struct option options[] = {
1245 { "help", no_argument, NULL, 'h' },
1246 { "version", no_argument, NULL, ARG_VERSION },
1247 { "root", required_argument, NULL, ARG_ROOT },
1256 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1264 puts(PACKAGE_STRING);
1265 puts(SYSTEMD_FEATURES);
1270 arg_root = path_make_absolute_cwd(optarg);
1274 path_kill_slashes(arg_root);
1281 assert_not_reached("Unhandled option");
1288 int main(int argc, char *argv[]) {
1290 _cleanup_close_ int lock = -1;
1295 r = parse_argv(argc, argv);
1299 log_set_target(LOG_TARGET_AUTO);
1300 log_parse_environment();
1307 if (optind < argc) {
1310 for (j = optind; j < argc; j++) {
1311 k = read_config_file(argv[j], false);
1312 if (k < 0 && r == 0)
1316 _cleanup_strv_free_ char **files = NULL;
1319 r = conf_files_list_nulstr(&files, ".conf", arg_root, conf_file_dirs);
1321 log_error("Failed to enumerate sysusers.d files: %s", strerror(-r));
1325 STRV_FOREACH(f, files) {
1326 k = read_config_file(*f, true);
1327 if (k < 0 && r == 0)
1334 log_error("Failed to take lock: %s", strerror(-lock));
1338 r = load_user_database();
1340 log_error("Failed to load user database: %s", strerror(-r));
1344 r = load_group_database();
1346 log_error("Failed to read group database: %s", strerror(-r));
1350 HASHMAP_FOREACH(i, groups, iterator)
1353 HASHMAP_FOREACH(i, users, iterator)
1358 log_error("Failed to write files: %s", strerror(-r));
1361 while ((i = hashmap_steal_first(groups)))
1364 while ((i = hashmap_steal_first(users)))
1367 hashmap_free(groups);
1368 hashmap_free(users);
1369 hashmap_free(todo_uids);
1370 hashmap_free(todo_gids);
1372 free_database(database_user, database_uid);
1373 free_database(database_group, database_gid);
1377 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;