Branch data Line data Source code
1 : : /*
2 : : * dpkg-statoverride - override ownership and mode of files
3 : : *
4 : : * Copyright © 2000, 2001 Wichert Akkerman <wakkerma@debian.org>
5 : : * Copyright © 2006-2015 Guillem Jover <guillem@debian.org>
6 : : *
7 : : * This is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU General Public License as published by
9 : : * the Free Software Foundation; either version 2 of the License, or
10 : : * (at your option) any later version.
11 : : *
12 : : * This is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : : * GNU General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU General Public License
18 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 : : *
20 : : */
21 : :
22 : : #include <config.h>
23 : : #include <compat.h>
24 : :
25 : : #include <sys/types.h>
26 : : #include <sys/stat.h>
27 : :
28 : : #include <errno.h>
29 : : #ifdef HAVE_LOCALE_H
30 : : #include <locale.h>
31 : : #endif
32 : : #include <string.h>
33 : : #include <grp.h>
34 : : #include <pwd.h>
35 : : #include <fnmatch.h>
36 : : #include <unistd.h>
37 : : #include <stdlib.h>
38 : : #include <stdio.h>
39 : :
40 : : #include <dpkg/i18n.h>
41 : : #include <dpkg/dpkg.h>
42 : : #include <dpkg/dpkg-db.h>
43 : : #include <dpkg/debug.h>
44 : : #include <dpkg/string.h>
45 : : #include <dpkg/path.h>
46 : : #include <dpkg/dir.h>
47 : : #include <dpkg/glob.h>
48 : : #include <dpkg/db-fsys.h>
49 : : #include <dpkg/options.h>
50 : :
51 : : #include "force.h"
52 : : #include "actions.h"
53 : : #include "security-mac.h"
54 : :
55 : : static const char printforhelp[] = N_(
56 : : "Use --help for help about overriding file stat information.");
57 : :
58 : : static int
59 : 47 : printversion(const char *const *argv)
60 : : {
61 : 47 : printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
62 : : PACKAGE_RELEASE);
63 : :
64 : 47 : printf(_(
65 : : "This is free software; see the GNU General Public License version 2 or\n"
66 : : "later for copying conditions. There is NO warranty.\n"));
67 : :
68 : 47 : m_output(stdout, _("<standard output>"));
69 : :
70 : 47 : return 0;
71 : : }
72 : :
73 : : static int
74 : 0 : usage(const char *const *argv)
75 : : {
76 : 0 : printf(_(
77 : : "Usage: %s [<option> ...] <command>\n"
78 : : "\n"), dpkg_get_progname());
79 : :
80 : 0 : printf(_(
81 : : "Commands:\n"
82 : : " --add <owner> <group> <mode> <path>\n"
83 : : " add a new <path> entry into the database.\n"
84 : : " --remove <path> remove <path> from the database.\n"
85 : : " --list [<glob-pattern>] list current overrides in the database.\n"
86 : : "\n"));
87 : :
88 : 0 : printf(_(
89 : : "Options:\n"
90 : : " --admindir <directory> set the directory with the statoverride file.\n"
91 : : " --instdir <directory> set the root directory, but not the admin dir.\n"
92 : : " --root <directory> set the directory of the root filesystem.\n"
93 : : " --update immediately update <path> permissions.\n"
94 : : " --force deprecated alias for --force-all.\n"
95 : : " --force-<thing>[,...] override problems (see --force-help).\n"
96 : : " --no-force-<thing>[,...] stop when problems encountered.\n"
97 : : " --refuse-<thing>[,...] ditto.\n"
98 : : " --quiet quiet operation, minimal output.\n"
99 : : " --help show this help message.\n"
100 : : " --version show the version.\n"
101 : : "\n"));
102 : :
103 : 0 : m_output(stdout, _("<standard output>"));
104 : :
105 : 0 : return 0;
106 : : }
107 : :
108 : : #define FORCE_STATCMD_MASK \
109 : : FORCE_NON_ROOT | \
110 : : FORCE_SECURITY_MAC | FORCE_STATOVERRIDE_ADD | FORCE_STATOVERRIDE_DEL
111 : :
112 : : static int opt_verbose = 1;
113 : : static int opt_update = 0;
114 : :
115 : : static char *
116 : 0 : path_cleanup(const char *path)
117 : : {
118 : 0 : char *new_path = m_strdup(path);
119 : :
120 : 0 : path_trim_slash_slashdot(new_path);
121 [ # # # # ]: 0 : if (opt_verbose && strcmp(path, new_path) != 0)
122 : 0 : warning(_("stripping trailing /"));
123 : :
124 : 0 : return new_path;
125 : : }
126 : :
127 : : static struct file_stat *
128 : 0 : statdb_node_new(const char *user, const char *group, const char *mode)
129 : : {
130 : : struct file_stat *filestat;
131 : :
132 : 0 : filestat = nfmalloc(sizeof(*filestat));
133 : :
134 : 0 : filestat->uid = statdb_parse_uid(user);
135 [ # # ]: 0 : if (filestat->uid == (uid_t)-1)
136 : 0 : ohshit(_("user '%s' does not exist"), user);
137 : 0 : filestat->uname = NULL;
138 : 0 : filestat->gid = statdb_parse_gid(group);
139 [ # # ]: 0 : if (filestat->gid == (gid_t)-1)
140 : 0 : ohshit(_("group '%s' does not exist"), group);
141 : 0 : filestat->gname = NULL;
142 : 0 : filestat->mode = statdb_parse_mode(mode);
143 : :
144 : 0 : return filestat;
145 : : }
146 : :
147 : : static struct file_stat **
148 : 0 : statdb_node_find(const char *filename)
149 : : {
150 : : struct fsys_namenode *file;
151 : :
152 : 0 : file = fsys_hash_find_node(filename, FHFF_NONE);
153 : :
154 : 0 : return &file->statoverride;
155 : : }
156 : :
157 : : static int
158 : 0 : statdb_node_remove(const char *filename)
159 : : {
160 : : struct fsys_namenode *file;
161 : :
162 : 0 : file = fsys_hash_find_node(filename, FHFF_NO_NEW);
163 [ # # # # ]: 0 : if (!file || !file->statoverride)
164 : 0 : return 0;
165 : :
166 : 0 : file->statoverride = NULL;
167 : :
168 : 0 : return 1;
169 : : }
170 : :
171 : : static void
172 : 0 : statdb_node_apply(const char *filename, struct file_stat *filestat)
173 : : {
174 : : int rc;
175 : :
176 : 0 : rc = chown(filename, filestat->uid, filestat->gid);
177 [ # # ]: 0 : if (forcible_nonroot_error(rc) < 0)
178 : 0 : ohshite(_("error setting ownership of '%.255s'"), filename);
179 : 0 : rc = chmod(filename, filestat->mode & ~S_IFMT);
180 [ # # ]: 0 : if (forcible_nonroot_error(rc) < 0)
181 : 0 : ohshite(_("error setting permissions of '%.255s'"), filename);
182 : :
183 : 0 : dpkg_selabel_load();
184 : 0 : dpkg_selabel_set_context(filename, filename, filestat->mode);
185 : 0 : dpkg_selabel_close();
186 : 0 : }
187 : :
188 : : static void
189 : 0 : statdb_node_print(FILE *out, struct fsys_namenode *file)
190 : : {
191 : 0 : struct file_stat *filestat = file->statoverride;
192 : : struct passwd *pw;
193 : : struct group *gr;
194 : :
195 [ # # ]: 0 : if (!filestat)
196 : 0 : return;
197 : :
198 : 0 : pw = getpwuid(filestat->uid);
199 [ # # ]: 0 : if (pw)
200 : 0 : fprintf(out, "%s ", pw->pw_name);
201 [ # # ]: 0 : else if (filestat->uname)
202 : 0 : fprintf(out, "%s ", filestat->uname);
203 : : else
204 : 0 : fprintf(out, "#%d ", filestat->uid);
205 : :
206 : 0 : gr = getgrgid(filestat->gid);
207 [ # # ]: 0 : if (gr)
208 : 0 : fprintf(out, "%s ", gr->gr_name);
209 [ # # ]: 0 : else if (filestat->gname)
210 : 0 : fprintf(out, "%s ", filestat->gname);
211 : : else
212 : 0 : fprintf(out, "#%d ", filestat->gid);
213 : :
214 : 0 : fprintf(out, "%o %s\n", filestat->mode & ~S_IFMT, file->name);
215 : : }
216 : :
217 : : static void
218 : 0 : statdb_write(void)
219 : : {
220 : : char *dbname;
221 : : struct atomic_file *dbfile;
222 : : struct fsys_hash_iter *iter;
223 : : struct fsys_namenode *file;
224 : :
225 : 0 : dbname = dpkg_db_get_path(STATOVERRIDEFILE);
226 : 0 : dbfile = atomic_file_new(dbname, ATOMIC_FILE_BACKUP);
227 : 0 : atomic_file_open(dbfile);
228 : :
229 : 0 : iter = fsys_hash_iter_new();
230 [ # # ]: 0 : while ((file = fsys_hash_iter_next(iter)))
231 : 0 : statdb_node_print(dbfile->fp, file);
232 : 0 : fsys_hash_iter_free(iter);
233 : :
234 : 0 : atomic_file_sync(dbfile);
235 : 0 : atomic_file_close(dbfile);
236 : 0 : atomic_file_commit(dbfile);
237 : 0 : atomic_file_free(dbfile);
238 : :
239 : 0 : dir_sync_path(dpkg_db_get_dir());
240 : :
241 : 0 : free(dbname);
242 : 0 : }
243 : :
244 : : static int
245 : 0 : statoverride_add(const char *const *argv)
246 : : {
247 : 0 : const char *user = argv[0];
248 : 0 : const char *group = argv[1];
249 : 0 : const char *mode = argv[2];
250 : 0 : const char *path = argv[3];
251 : : char *filename;
252 : : struct file_stat **filestat;
253 : :
254 [ # # # # : 0 : if (!user || !group || !mode || !path || argv[4])
# # # # #
# ]
255 : 0 : badusage(_("--%s needs four arguments"), cipaction->olong);
256 : :
257 [ # # ]: 0 : if (strchr(path, '\n'))
258 : 0 : badusage(_("path may not contain newlines"));
259 : :
260 : 0 : ensure_statoverrides(STATDB_PARSE_LAX);
261 : :
262 : 0 : filename = path_cleanup(path);
263 : :
264 : 0 : filestat = statdb_node_find(filename);
265 [ # # ]: 0 : if (*filestat != NULL) {
266 [ # # ]: 0 : if (in_force(FORCE_STATOVERRIDE_ADD))
267 : 0 : warning(_("an override for '%s' already exists, "
268 : : "but --force specified so will be ignored"),
269 : : filename);
270 : : else
271 : 0 : ohshit(_("an override for '%s' already exists; "
272 : : "aborting"), filename);
273 : : }
274 : :
275 : 0 : *filestat = statdb_node_new(user, group, mode);
276 : :
277 [ # # ]: 0 : if (opt_update) {
278 : : struct stat st;
279 : 0 : struct varbuf realfilename = VARBUF_INIT;
280 : :
281 : 0 : varbuf_add_str(&realfilename, dpkg_fsys_get_dir());
282 : 0 : varbuf_add_str(&realfilename, filename);
283 : :
284 [ # # ]: 0 : if (stat(varbuf_str(&realfilename), &st) == 0) {
285 : 0 : (*filestat)->mode |= st.st_mode & S_IFMT;
286 : 0 : statdb_node_apply(varbuf_str(&realfilename), *filestat);
287 [ # # ]: 0 : } else if (opt_verbose) {
288 : 0 : warning(_("--update given but %s does not exist"),
289 : : varbuf_str(&realfilename));
290 : : }
291 : :
292 : 0 : varbuf_destroy(&realfilename);
293 : : }
294 : :
295 : 0 : statdb_write();
296 : :
297 : 0 : free(filename);
298 : :
299 : 0 : return 0;
300 : : }
301 : :
302 : : static int
303 : 0 : statoverride_remove(const char *const *argv)
304 : : {
305 : 0 : const char *path = argv[0];
306 : : char *filename;
307 : :
308 [ # # # # ]: 0 : if (!path || argv[1])
309 : 0 : badusage(_("--%s needs a single argument"), "remove");
310 : :
311 : 0 : ensure_statoverrides(STATDB_PARSE_LAX);
312 : :
313 : 0 : filename = path_cleanup(path);
314 : :
315 [ # # ]: 0 : if (!statdb_node_remove(filename)) {
316 [ # # ]: 0 : if (opt_verbose)
317 : 0 : warning(_("no override present"));
318 [ # # ]: 0 : if (in_force(FORCE_STATOVERRIDE_DEL))
319 : 0 : return 0;
320 : : else
321 : 0 : return 2;
322 : : }
323 : :
324 [ # # # # ]: 0 : if (opt_update && opt_verbose)
325 : 0 : warning(_("--update is useless for --remove"));
326 : :
327 : 0 : statdb_write();
328 : :
329 : 0 : free(filename);
330 : :
331 : 0 : return 0;
332 : : }
333 : :
334 : : static int
335 : 0 : statoverride_list(const char *const *argv)
336 : : {
337 : : struct fsys_hash_iter *iter;
338 : : struct fsys_namenode *file;
339 : : const char *thisarg;
340 : 0 : struct glob_node *glob_list = NULL;
341 : 0 : int ret = 1;
342 : :
343 : 0 : ensure_statoverrides(STATDB_PARSE_LAX);
344 : :
345 [ # # ]: 0 : while ((thisarg = *argv++)) {
346 : 0 : char *pattern = path_cleanup(thisarg);
347 : :
348 : 0 : glob_list_prepend(&glob_list, pattern);
349 : : }
350 [ # # ]: 0 : if (glob_list == NULL)
351 : 0 : glob_list_prepend(&glob_list, m_strdup("*"));
352 : :
353 : 0 : iter = fsys_hash_iter_new();
354 [ # # ]: 0 : while ((file = fsys_hash_iter_next(iter))) {
355 : : struct glob_node *g;
356 : :
357 [ # # ]: 0 : for (g = glob_list; g; g = g->next) {
358 [ # # ]: 0 : if (fnmatch(g->pattern, file->name, 0) == 0) {
359 : 0 : statdb_node_print(stdout, file);
360 : 0 : ret = 0;
361 : 0 : break;
362 : : }
363 : : }
364 : : }
365 : 0 : fsys_hash_iter_free(iter);
366 : :
367 : 0 : glob_list_free(glob_list);
368 : :
369 : 0 : return ret;
370 : : }
371 : :
372 : : static void
373 : 0 : set_force_obsolete(const struct cmdinfo *cip, const char *value)
374 : : {
375 : 0 : warning(_("deprecated --%s option; use --%s instead"),
376 : 0 : cip->olong, "force-all");
377 : 0 : set_force(FORCE_ALL);
378 : 0 : }
379 : :
380 : : static const struct cmdinfo cmdinfos[] = {
381 : : ACTION("add", 0, act_install, statoverride_add),
382 : : ACTION("remove", 0, act_remove, statoverride_remove),
383 : : ACTION("list", 0, act_listfiles, statoverride_list),
384 : : ACTION("help", '?', act_help, usage),
385 : : ACTION("version", 0, act_version, printversion),
386 : :
387 : : { "admindir", 0, 1, NULL, NULL, set_admindir, 0 },
388 : : { "instdir", 0, 1, NULL, NULL, set_instdir, 0 },
389 : : { "root", 0, 1, NULL, NULL, set_root, 0 },
390 : : { "quiet", 0, 0, &opt_verbose, NULL, NULL, 0 },
391 : : { "force", 0, 0, NULL, NULL, set_force_obsolete },
392 : : { "force", 0, 2, NULL, NULL, set_force_option, 1 },
393 : : { "no-force", 0, 2, NULL, NULL, set_force_option, 0 },
394 : : { "refuse", 0, 2, NULL, NULL, set_force_option, 0 },
395 : : { "update", 0, 0, &opt_update, NULL, NULL, 1 },
396 : : { NULL, 0 }
397 : : };
398 : :
399 : : int
400 : 47 : main(int argc, const char *const *argv)
401 : : {
402 : : int ret;
403 : :
404 : 47 : dpkg_locales_init(PACKAGE);
405 : 47 : dpkg_program_init("dpkg-statoverride");
406 : 47 : set_force_default(FORCE_STATCMD_MASK);
407 : 47 : dpkg_options_parse(&argv, cmdinfos, printforhelp);
408 : :
409 : 47 : debug(dbg_general, "root=%s admindir=%s", dpkg_fsys_get_dir(), dpkg_db_get_dir());
410 : :
411 [ - + ]: 47 : if (!cipaction)
412 : 0 : badusage(_("need an action option"));
413 : :
414 : 47 : ret = cipaction->action(argv);
415 : :
416 : 47 : dpkg_program_done();
417 : 47 : dpkg_locales_done();
418 : :
419 : 47 : return ret;
420 : : }
|