sysusers: split make_files()

This patch extracts the code which is in charge to write the new users or
groups into temporary files and move it into 4 dedicated functions.

This part was previously inlined in makes_files() making this function quite
big and hard to read and maintain.

There should be no functional change.
This commit is contained in:
Franck Bui 2017-05-09 09:37:37 +02:00
parent 9bfc0df113
commit b20b0b6606

View file

@ -370,341 +370,416 @@ static int rename_and_apply_smack(const char *temp_path, const char *dest_path)
return r;
}
static int write_files(void) {
static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
_cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
_cleanup_free_ char *passwd_tmp = NULL;
Iterator iterator;
Item *i;
int r;
_cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
_cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
if (hashmap_size(todo_uids) == 0)
return 0;
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
if (r < 0)
return r;
original = fopen(passwd_path, "re");
if (original) {
struct passwd *pw;
r = sync_rights(original, passwd);
if (r < 0)
goto fail;
errno = 0;
while ((pw = fgetpwent(original))) {
i = hashmap_get(users, pw->pw_name);
if (i && i->todo_user) {
log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
r = -EEXIST;
goto fail;
}
if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
r = -EEXIST;
goto fail;
}
errno = 0;
if (putpwent(pw, passwd) < 0) {
r = errno ? -errno : -EIO;
goto fail;
}
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto fail;
}
} else if (errno != ENOENT) {
r = -errno;
goto fail;
} else if (fchmod(fileno(passwd), 0644) < 0) {
r = -errno;
goto fail;
}
HASHMAP_FOREACH(i, todo_uids, iterator) {
struct passwd n = {
.pw_name = i->name,
.pw_uid = i->uid,
.pw_gid = i->gid,
.pw_gecos = i->description,
/* "x" means the password is stored in the shadow file */
.pw_passwd = (char*) "x",
/* We default to the root directory as home */
.pw_dir = i->home ? i->home : (char*) "/",
/* Initialize the shell to nologin, with one exception:
* for root we patch in something special */
.pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
};
errno = 0;
if (putpwent(&n, passwd) != 0) {
r = errno ? -errno : -EIO;
goto fail;
}
}
r = fflush_and_check(passwd);
if (r < 0)
goto fail;
*tmpfile = passwd;
*tmpfile_path = passwd_tmp;
passwd = NULL;
passwd_tmp = NULL;
return 0;
fail:
unlink(passwd_tmp);
return r;
}
static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
_cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
_cleanup_free_ char *shadow_tmp = NULL;
Iterator iterator;
long lstchg;
Item *i;
int r;
if (hashmap_size(todo_uids) == 0)
return 0;
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
if (r < 0)
return r;
lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
original = fopen(shadow_path, "re");
if (original) {
struct spwd *sp;
r = sync_rights(original, shadow);
if (r < 0)
goto fail;
errno = 0;
while ((sp = fgetspent(original))) {
i = hashmap_get(users, sp->sp_namp);
if (i && i->todo_user) {
/* we will update the existing entry */
sp->sp_lstchg = lstchg;
/* only the /etc/shadow stage is left, so we can
* safely remove the item from the todo set */
i->todo_user = false;
hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
}
errno = 0;
if (putspent(sp, shadow) < 0) {
r = errno ? -errno : -EIO;
goto fail;
}
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto fail;
}
} else if (errno != ENOENT) {
r = -errno;
goto fail;
} else if (fchmod(fileno(shadow), 0000) < 0) {
r = -errno;
goto fail;
}
HASHMAP_FOREACH(i, todo_uids, iterator) {
struct spwd n = {
.sp_namp = i->name,
.sp_pwdp = (char*) "!!",
.sp_lstchg = lstchg,
.sp_min = -1,
.sp_max = -1,
.sp_warn = -1,
.sp_inact = -1,
.sp_expire = -1,
.sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
};
errno = 0;
if (putspent(&n, shadow) != 0) {
r = errno ? -errno : -EIO;
goto fail;
}
}
r = fflush_and_check(shadow);
if (r < 0)
goto fail;
*tmpfile = shadow;
*tmpfile_path = shadow_tmp;
shadow = NULL;
shadow_tmp = NULL;
return 0;
fail:
unlink(shadow_tmp);
return r;
}
static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
_cleanup_fclose_ FILE *original = NULL, *group = NULL;
_cleanup_free_ char *group_tmp = NULL;
bool group_changed = false;
Iterator iterator;
Item *i;
int r;
if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
_cleanup_fclose_ FILE *original = NULL;
if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
return 0;
/* First we update the actual group list file */
group_path = prefix_roota(arg_root, "/etc/group");
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
if (r < 0)
return r;
original = fopen(group_path, "re");
if (original) {
struct group *gr;
r = sync_rights(original, group);
if (r < 0)
goto finish;
goto fail;
original = fopen(group_path, "re");
if (original) {
struct group *gr;
errno = 0;
while ((gr = fgetgrent(original))) {
/* Safety checks against name and GID collisions. Normally,
* this should be unnecessary, but given that we look at the
* entries anyway here, let's make an extra verification
* step that we don't generate duplicate entries. */
r = sync_rights(original, group);
i = hashmap_get(groups, gr->gr_name);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
r = -EEXIST;
goto fail;
}
if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
r = -EEXIST;
goto fail;
}
r = putgrent_with_members(gr, group);
if (r < 0)
goto finish;
goto fail;
if (r > 0)
group_changed = true;
errno = 0;
while ((gr = fgetgrent(original))) {
/* Safety checks against name and GID
* collisions. Normally, this should
* be unnecessary, but given that we
* look at the entries anyway here,
* let's make an extra verification
* step that we don't generate
* duplicate entries. */
i = hashmap_get(groups, gr->gr_name);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
r = -EEXIST;
goto finish;
}
if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
r = -EEXIST;
goto finish;
}
r = putgrent_with_members(gr, group);
if (r < 0)
goto finish;
if (r > 0)
group_changed = true;
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto finish;
}
} else if (errno != ENOENT) {
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto finish;
} else if (fchmod(fileno(group), 0644) < 0) {
r = -errno;
goto finish;
goto fail;
}
HASHMAP_FOREACH(i, todo_gids, iterator) {
struct group n = {
.gr_name = i->name,
.gr_gid = i->gid,
.gr_passwd = (char*) "x",
};
r = putgrent_with_members(&n, group);
if (r < 0)
goto finish;
group_changed = true;
}
r = fflush_and_check(group);
if (r < 0)
goto finish;
if (original) {
fclose(original);
original = NULL;
}
/* OK, now also update the shadow file for the group list */
gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
goto finish;
original = fopen(gshadow_path, "re");
if (original) {
struct sgrp *sg;
r = sync_rights(original, gshadow);
if (r < 0)
goto finish;
errno = 0;
while ((sg = fgetsgent(original))) {
i = hashmap_get(groups, sg->sg_namp);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
r = -EEXIST;
goto finish;
}
r = putsgent_with_members(sg, gshadow);
if (r < 0)
goto finish;
if (r > 0)
group_changed = true;
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto finish;
}
} else if (errno != ENOENT) {
r = -errno;
goto finish;
} else if (fchmod(fileno(gshadow), 0000) < 0) {
r = -errno;
goto finish;
}
HASHMAP_FOREACH(i, todo_gids, iterator) {
struct sgrp n = {
.sg_namp = i->name,
.sg_passwd = (char*) "!!",
};
r = putsgent_with_members(&n, gshadow);
if (r < 0)
goto finish;
group_changed = true;
}
r = fflush_and_check(gshadow);
if (r < 0)
goto finish;
} else if (errno != ENOENT) {
r = -errno;
goto fail;
} else if (fchmod(fileno(group), 0644) < 0) {
r = -errno;
goto fail;
}
if (hashmap_size(todo_uids) > 0) {
_cleanup_fclose_ FILE *original = NULL;
long lstchg;
HASHMAP_FOREACH(i, todo_gids, iterator) {
struct group n = {
.gr_name = i->name,
.gr_gid = i->gid,
.gr_passwd = (char*) "x",
};
/* First we update the user database itself */
passwd_path = prefix_roota(arg_root, "/etc/passwd");
r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
r = putgrent_with_members(&n, group);
if (r < 0)
goto finish;
goto fail;
original = fopen(passwd_path, "re");
if (original) {
struct passwd *pw;
r = sync_rights(original, passwd);
if (r < 0)
goto finish;
errno = 0;
while ((pw = fgetpwent(original))) {
i = hashmap_get(users, pw->pw_name);
if (i && i->todo_user) {
log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
r = -EEXIST;
goto finish;
}
if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
r = -EEXIST;
goto finish;
}
errno = 0;
if (putpwent(pw, passwd) < 0) {
r = errno ? -errno : -EIO;
goto finish;
}
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto finish;
}
} else if (errno != ENOENT) {
r = -errno;
goto finish;
} else if (fchmod(fileno(passwd), 0644) < 0) {
r = -errno;
goto finish;
}
HASHMAP_FOREACH(i, todo_uids, iterator) {
struct passwd n = {
.pw_name = i->name,
.pw_uid = i->uid,
.pw_gid = i->gid,
.pw_gecos = i->description,
/* "x" means the password is stored in
* the shadow file */
.pw_passwd = (char*) "x",
/* We default to the root directory as home */
.pw_dir = i->home ? i->home : (char*) "/",
/* Initialize the shell to nologin,
* with one exception: for root we
* patch in something special */
.pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
};
errno = 0;
if (putpwent(&n, passwd) != 0) {
r = errno ? -errno : -EIO;
goto finish;
}
}
r = fflush_and_check(passwd);
if (r < 0)
goto finish;
if (original) {
fclose(original);
original = NULL;
}
/* The we update the shadow database */
shadow_path = prefix_roota(arg_root, "/etc/shadow");
r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
if (r < 0)
goto finish;
lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
original = fopen(shadow_path, "re");
if (original) {
struct spwd *sp;
r = sync_rights(original, shadow);
if (r < 0)
goto finish;
errno = 0;
while ((sp = fgetspent(original))) {
i = hashmap_get(users, sp->sp_namp);
if (i && i->todo_user) {
/* we will update the existing entry */
sp->sp_lstchg = lstchg;
/* only the /etc/shadow stage is left, so we can
* safely remove the item from the todo set */
i->todo_user = false;
hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
}
errno = 0;
if (putspent(sp, shadow) < 0) {
r = errno ? -errno : -EIO;
goto finish;
}
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto finish;
}
} else if (errno != ENOENT) {
r = -errno;
goto finish;
} else if (fchmod(fileno(shadow), 0000) < 0) {
r = -errno;
goto finish;
}
HASHMAP_FOREACH(i, todo_uids, iterator) {
struct spwd n = {
.sp_namp = i->name,
.sp_pwdp = (char*) "!!",
.sp_lstchg = lstchg,
.sp_min = -1,
.sp_max = -1,
.sp_warn = -1,
.sp_inact = -1,
.sp_expire = -1,
.sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
};
errno = 0;
if (putspent(&n, shadow) != 0) {
r = errno ? -errno : -EIO;
goto finish;
}
}
r = fflush_and_check(shadow);
if (r < 0)
goto finish;
group_changed = true;
}
r = fflush_and_check(group);
if (r < 0)
goto fail;
if (group_changed) {
*tmpfile = group;
*tmpfile_path = group_tmp;
group = NULL;
group_tmp = NULL;
}
return 0;
fail:
unlink(group_tmp);
return r;
}
static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
_cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
_cleanup_free_ char *gshadow_tmp = NULL;
bool group_changed = false;
Iterator iterator;
Item *i;
int r;
if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
return 0;
r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
return r;
original = fopen(gshadow_path, "re");
if (original) {
struct sgrp *sg;
r = sync_rights(original, gshadow);
if (r < 0)
goto fail;
errno = 0;
while ((sg = fgetsgent(original))) {
i = hashmap_get(groups, sg->sg_namp);
if (i && i->todo_group) {
log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
r = -EEXIST;
goto fail;
}
r = putsgent_with_members(sg, gshadow);
if (r < 0)
goto fail;
if (r > 0)
group_changed = true;
errno = 0;
}
if (!IN_SET(errno, 0, ENOENT)) {
r = -errno;
goto fail;
}
} else if (errno != ENOENT) {
r = -errno;
goto fail;
} else if (fchmod(fileno(gshadow), 0000) < 0) {
r = -errno;
goto fail;
}
HASHMAP_FOREACH(i, todo_gids, iterator) {
struct sgrp n = {
.sg_namp = i->name,
.sg_passwd = (char*) "!!",
};
r = putsgent_with_members(&n, gshadow);
if (r < 0)
goto fail;
group_changed = true;
}
r = fflush_and_check(gshadow);
if (r < 0)
goto fail;
if (group_changed) {
*tmpfile = gshadow;
*tmpfile_path = gshadow_tmp;
gshadow = NULL;
gshadow_tmp = NULL;
}
return 0;
fail:
unlink(gshadow_tmp);
return r;
}
static int write_files(void) {
_cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
_cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
int r;
passwd_path = prefix_roota(arg_root, "/etc/passwd");
shadow_path = prefix_roota(arg_root, "/etc/shadow");
group_path = prefix_roota(arg_root, "/etc/group");
gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
r = write_temporary_group(group_path, &group, &group_tmp);
if (r < 0)
goto finish;
r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
if (r < 0)
goto finish;
r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
if (r < 0)
goto finish;
r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
if (r < 0)
goto finish;
/* Make a backup of the old files */
if (group_changed) {
if (group) {
r = make_backup("/etc/group", group_path);
if (r < 0)
goto finish;
}
if (gshadow) {
r = make_backup("/etc/gshadow", gshadow_path);
if (r < 0)
goto finish;
}
if (group) {
r = make_backup("/etc/group", group_path);
if (r < 0)
goto finish;
}
if (gshadow) {
r = make_backup("/etc/gshadow", gshadow_path);
if (r < 0)
goto finish;
}
if (passwd) {
@ -719,21 +794,19 @@ static int write_files(void) {
}
/* And make the new files count */
if (group_changed) {
if (group) {
r = rename_and_apply_smack(group_tmp, group_path);
if (r < 0)
goto finish;
if (group) {
r = rename_and_apply_smack(group_tmp, group_path);
if (r < 0)
goto finish;
group_tmp = mfree(group_tmp);
}
if (gshadow) {
r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
if (r < 0)
goto finish;
group_tmp = mfree(group_tmp);
}
if (gshadow) {
r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
if (r < 0)
goto finish;
gshadow_tmp = mfree(gshadow_tmp);
}
gshadow_tmp = mfree(gshadow_tmp);
}
if (passwd) {