diff options
author | Louis Rannou <lrannou@baylibre.com> | 2023-06-15 13:43:53 +0200 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2023-06-17 11:50:57 +0100 |
commit | f5b3ded656a491b59f8c9d5ce12cee665bb5d5f5 (patch) | |
tree | c8a5cd0ebc6f2c80d3ef4c3c2361156ce3a6d166 /meta/classes-recipe | |
parent | 88abdec715ed0c1f613c9b5132cd45db741d5c65 (diff) | |
download | poky-f5b3ded656a491b59f8c9d5ce12cee665bb5d5f5.tar.gz |
rootfs-postcommands: change sysusers.d command
The configuration in sysusers.d used to be parsed to create users/groups at
build time instead at runtime. This was leading to several conflicts with
users/groups defined in base-passwd recipe and specific definitions in recipes
inheriting the useradd class. Some of those conflicts raised unseen errors in
the do_rootfs command's logs.
As an example, the root home directory is set by default to `/home/root` but
systemd expects it as `/root`.
The new command `systemd_sysusers_check` checks each configuration for
users/groups and compare their properties to what is actually defined in the
`/etc/passwd` and `/etc/group` of the target rootfs.
(From OE-Core rev: 0c7e76df68acfeca059a6b906d2a891d56f01e77)
Signed-off-by: Louis Rannou <lrannou@baylibre.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/classes-recipe')
-rw-r--r-- | meta/classes-recipe/rootfs-postcommands.bbclass | 133 |
1 files changed, 109 insertions, 24 deletions
diff --git a/meta/classes-recipe/rootfs-postcommands.bbclass b/meta/classes-recipe/rootfs-postcommands.bbclass index 690fa976aa..652601b95f 100644 --- a/meta/classes-recipe/rootfs-postcommands.bbclass +++ b/meta/classes-recipe/rootfs-postcommands.bbclass | |||
@@ -43,7 +43,7 @@ ROOTFS_POSTUNINSTALL_COMMAND =+ "write_image_manifest ; " | |||
43 | POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log" | 43 | POSTINST_LOGFILE ?= "${localstatedir}/log/postinstall.log" |
44 | # Set default target for systemd images | 44 | # Set default target for systemd images |
45 | SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains_any("IMAGE_FEATURES", [ "x11-base", "weston" ], "graphical.target", "multi-user.target", d)}' | 45 | SYSTEMD_DEFAULT_TARGET ?= '${@bb.utils.contains_any("IMAGE_FEATURES", [ "x11-base", "weston" ], "graphical.target", "multi-user.target", d)}' |
46 | ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; systemd_create_users;", "", d)}' | 46 | ROOTFS_POSTPROCESS_COMMAND += '${@bb.utils.contains("DISTRO_FEATURES", "systemd", "set_systemd_default_target; systemd_sysusers_check;", "", d)}' |
47 | 47 | ||
48 | ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;' | 48 | ROOTFS_POSTPROCESS_COMMAND += 'empty_var_volatile;' |
49 | 49 | ||
@@ -69,29 +69,114 @@ python () { | |||
69 | d.appendVar('ROOTFS_POSTPROCESS_COMMAND', 'rootfs_reproducible;') | 69 | d.appendVar('ROOTFS_POSTPROCESS_COMMAND', 'rootfs_reproducible;') |
70 | } | 70 | } |
71 | 71 | ||
72 | systemd_create_users () { | 72 | # Resolve the ID as described in the sysusers.d(5) manual: ID can be a numeric |
73 | for conffile in ${IMAGE_ROOTFS}/usr/lib/sysusers.d/*.conf; do | 73 | # uid, a couple uid:gid or uid:groupname or it is '-' meaning leaving it |
74 | [ -e $conffile ] || continue | 74 | # automatic or it can be a path. In the latter, the uid/gid matches the |
75 | grep -v "^#" $conffile | sed -e '/^$/d' | while read type name id comment; do | 75 | # user/group owner of that file. |
76 | if [ "$type" = "u" ]; then | 76 | def resolve_sysusers_id(d, sid): |
77 | useradd_params="--shell /sbin/nologin" | 77 | # If the id is a path, the uid/gid matchs to the target's uid/gid in the |
78 | [ "$id" != "-" ] && useradd_params="$useradd_params --uid $id" | 78 | # rootfs. |
79 | [ "$comment" != "-" ] && useradd_params="$useradd_params --comment $comment" | 79 | if '/' in sid: |
80 | useradd_params="$useradd_params --system $name" | 80 | try: |
81 | eval useradd --root ${IMAGE_ROOTFS} $useradd_params || true | 81 | osstat = os.stat(os.path.join(d.getVar('IMAGE_ROOTFS'), sid)) |
82 | elif [ "$type" = "g" ]; then | 82 | except FileNotFoundError: |
83 | groupadd_params="" | 83 | bb.error('sysusers.d: file %s is required but it does not exist in the rootfs', sid) |
84 | [ "$id" != "-" ] && groupadd_params="$groupadd_params --gid $id" | 84 | return ('-', '-') |
85 | groupadd_params="$groupadd_params --system $name" | 85 | return (osstat.st_uid, osstat.st_gid) |
86 | eval groupadd --root ${IMAGE_ROOTFS} $groupadd_params || true | 86 | # Else it is a uid:gid or uid:groupname syntax |
87 | elif [ "$type" = "m" ]; then | 87 | if ':' in sid: |
88 | group=$id | 88 | return sid.split(':') |
89 | eval groupadd --root ${IMAGE_ROOTFS} --system $group || true | 89 | else: |
90 | eval useradd --root ${IMAGE_ROOTFS} --shell /sbin/nologin --system $name --no-user-group || true | 90 | return (sid, '-') |
91 | eval usermod --root ${IMAGE_ROOTFS} -a -G $group $name | 91 | |
92 | fi | 92 | # Check a user exists in the rootfs password file and return its properties |
93 | done | 93 | def check_user_exists(d, uname=None, uid=None): |
94 | done | 94 | with open(os.path.join(d.getVar('IMAGE_ROOTFS'), 'etc/passwd'), 'r') as pwfile: |
95 | for line in pwfile: | ||
96 | (name, _, u_id, gid, comment, homedir, ushell) = line.strip().split(':') | ||
97 | if uname == name or uid == u_id: | ||
98 | return (name, u_id, gid, comment or '-', homedir or '/', ushell or '-') | ||
99 | return None | ||
100 | |||
101 | # Check a group exists in the rootfs group file and return its properties | ||
102 | def check_group_exists(d, gname=None, gid=None): | ||
103 | with open(os.path.join(d.getVar('IMAGE_ROOTFS'), 'etc/group'), 'r') as gfile: | ||
104 | for line in gfile: | ||
105 | (name, _, g_id, _) = line.strip().split(':') | ||
106 | if name == gname or g_id == gid: | ||
107 | return (name, g_id) | ||
108 | return None | ||
109 | |||
110 | def compare_users(user, e_user): | ||
111 | # user and e_user must not have None values. Unset values must be '-'. | ||
112 | (name, uid, gid, comment, homedir, ushell) = user | ||
113 | (e_name, e_uid, e_gid, e_comment, e_homedir, e_ushell) = e_user | ||
114 | # Ignore 'uid', 'gid' or 'comment' if they are not set | ||
115 | # Ignore 'shell' and 'ushell' if one is not set | ||
116 | return name == e_name \ | ||
117 | and (uid == '-' or uid == e_uid) \ | ||
118 | and (gid == '-' or gid == e_gid) \ | ||
119 | and (comment == '-' or e_comment == '-' or comment.lower() == e_comment.lower()) \ | ||
120 | and (homedir == '-' or e_homedir == '-' or homedir == e_homedir) \ | ||
121 | and (ushell == '-' or e_ushell == '-' or ushell == e_ushell) | ||
122 | |||
123 | # Open sysusers.d configuration files and parse each line to check the users and | ||
124 | # groups are already defined in /etc/passwd and /etc/groups with similar | ||
125 | # properties. Refer to the sysusers.d(5) manual for its syntax. | ||
126 | python systemd_sysusers_check() { | ||
127 | import glob | ||
128 | import re | ||
129 | |||
130 | pattern_comment = r'(-|\"[^:\"]+\")' | ||
131 | pattern_word = r'[^\s]+' | ||
132 | pattern_line = r'(' + pattern_word + r')\s+(' + pattern_word + r')\s+(' + pattern_word + r')(\s+' \ | ||
133 | + pattern_comment + r')?' + r'(\s+(' + pattern_word + r'))?' + r'(\s+(' + pattern_word + r'))?' | ||
134 | |||
135 | for conffile in glob.glob(os.path.join(d.getVar('IMAGE_ROOTFS'), 'usr/lib/sysusers.d/*.conf')): | ||
136 | with open(conffile, 'r') as f: | ||
137 | for line in f: | ||
138 | line = line.strip() | ||
139 | if not len(line) or line[0] == '#': continue | ||
140 | ret = re.fullmatch(pattern_line, line.strip()) | ||
141 | if not ret: continue | ||
142 | (stype, sname, sid, _, scomment, _, shomedir, _, sshell) = ret.groups() | ||
143 | if stype == 'u': | ||
144 | if sid: | ||
145 | (suid, sgid) = resolve_sysusers_id(d, sid) | ||
146 | if sgid.isalpha(): | ||
147 | sgid = check_group_exists(d, gname=sgid) | ||
148 | elif sgid.isdigit(): | ||
149 | check_group_exists(d, gid=sgid) | ||
150 | else: | ||
151 | sgid = '-' | ||
152 | else: | ||
153 | suid = '-' | ||
154 | sgid = '-' | ||
155 | scomment = scomment.replace('"', '') if scomment else '-' | ||
156 | shomedir = shomedir or '-' | ||
157 | sshell = sshell or '-' | ||
158 | e_user = check_user_exists(d, uname=sname) | ||
159 | if not e_user: | ||
160 | bb.warn('User %s has never been defined' % sname) | ||
161 | elif not compare_users((sname, suid, sgid, scomment, shomedir, sshell), e_user): | ||
162 | bb.warn('User %s has been defined as (%s) but sysusers.d expects it as (%s)' | ||
163 | % (sname, ', '.join(e_user), | ||
164 | ', '.join((sname, suid, sgid, scomment, shomedir, sshell)))) | ||
165 | elif stype == 'g': | ||
166 | gid = sid or '-' | ||
167 | if '/' in gid: | ||
168 | (_, gid) = resolve_sysusers_id(d, sid) | ||
169 | e_group = check_group_exists(d, gname=sname) | ||
170 | if not e_group: | ||
171 | bb.warn('Group %s has never been defined' % sname) | ||
172 | elif gid != '-': | ||
173 | (_, e_gid) = e_group | ||
174 | if gid != e_gid: | ||
175 | bb.warn('Group %s has been defined with id (%s) but sysusers.d expects gid (%s)' | ||
176 | % (sname, e_gid, gid)) | ||
177 | elif stype == 'm': | ||
178 | check_user_exists(d, sname) | ||
179 | check_group_exists(d, sid) | ||
95 | } | 180 | } |
96 | 181 | ||
97 | # | 182 | # |