Fix CVE-2018-20482
[tar.git] / src / map.c
blob770e4f563fbf8c58ef0a852c2000eb55ca58f1b3
1 /* Owner/group mapping for tar
3 Copyright 2015-2017 Free Software Foundation, Inc.
5 This file is part of GNU tar.
7 GNU tar 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 3 of the License, or
10 (at your option) any later version.
12 GNU tar 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.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include <system.h>
21 #include "common.h"
22 #include "wordsplit.h"
23 #include <hash.h>
24 #include <pwd.h>
26 struct mapentry
28 uintmax_t orig_id;
29 uintmax_t new_id;
30 char *new_name;
33 static size_t
34 map_hash (void const *entry, size_t nbuckets)
36 struct mapentry const *map = entry;
37 return map->orig_id % nbuckets;
40 static bool
41 map_compare (void const *entry1, void const *entry2)
43 struct mapentry const *map1 = entry1;
44 struct mapentry const *map2 = entry2;
45 return map1->orig_id == map2->orig_id;
48 static int
49 parse_id (uintmax_t *retval,
50 char const *arg, char const *what, uintmax_t maxval,
51 char const *file, unsigned line)
53 uintmax_t v;
54 char *p;
56 errno = 0;
57 v = strtoumax (arg, &p, 10);
58 if (*p || errno)
60 error (0, 0, _("%s:%u: invalid %s: %s"), file, line, what, arg);
61 return -1;
63 if (v > maxval)
65 error (0, 0, _("%s:%u: %s out of range: %s"), file, line, what, arg);
66 return -1;
68 *retval = v;
69 return 0;
72 static void
73 map_read (Hash_table **ptab, char const *file,
74 uintmax_t (*name_to_id) (char const *), char const *what,
75 uintmax_t maxval)
77 FILE *fp;
78 char *buf = NULL;
79 size_t bufsize = 0;
80 ssize_t n;
81 struct wordsplit ws;
82 int wsopt;
83 unsigned line;
84 int err = 0;
86 fp = fopen (file, "r");
87 if (!fp)
88 open_fatal (file);
90 ws.ws_comment = "#";
91 wsopt = WRDSF_COMMENT | WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_SQUEEZE_DELIMS
92 | WRDSF_QUOTE;
93 line = 0;
94 while ((n = getline (&buf, &bufsize, fp)) > 0)
96 struct mapentry *ent;
97 uintmax_t orig_id, new_id;
98 char *name = NULL;
99 char *colon;
101 ++line;
102 if (wordsplit (buf, &ws, wsopt))
103 FATAL_ERROR ((0, 0, _("%s:%u: cannot split line: %s"),
104 file, line, wordsplit_strerror (&ws)));
105 wsopt |= WRDSF_REUSE;
106 if (ws.ws_wordc == 0)
107 continue;
108 if (ws.ws_wordc != 2)
110 error (0, 0, _("%s:%u: malformed line"), file, line);
111 err = 1;
112 continue;
115 if (ws.ws_wordv[0][0] == '+')
117 if (parse_id (&orig_id, ws.ws_wordv[0]+1, what, maxval, file, line))
119 err = 1;
120 continue;
123 else if (name_to_id)
125 orig_id = name_to_id (ws.ws_wordv[0]);
126 if (orig_id == UINTMAX_MAX)
128 error (0, 0, _("%s:%u: can't obtain %s of %s"),
129 file, line, what, ws.ws_wordv[0]);
130 err = 1;
131 continue;
135 colon = strchr (ws.ws_wordv[1], ':');
136 if (colon)
138 if (colon > ws.ws_wordv[1])
139 name = ws.ws_wordv[1];
140 *colon++ = 0;
141 if (parse_id (&new_id, colon, what, maxval, file, line))
143 err = 1;
144 continue;
147 else if (ws.ws_wordv[1][0] == '+')
149 if (parse_id (&new_id, ws.ws_wordv[1], what, maxval, file, line))
151 err = 1;
152 continue;
155 else
157 name = ws.ws_wordv[1];
158 new_id = name_to_id (ws.ws_wordv[1]);
159 if (new_id == UINTMAX_MAX)
161 error (0, 0, _("%s:%u: can't obtain %s of %s"),
162 file, line, what, ws.ws_wordv[1]);
163 err = 1;
164 continue;
168 ent = xmalloc (sizeof (*ent));
169 ent->orig_id = orig_id;
170 ent->new_id = new_id;
171 ent->new_name = name ? xstrdup (name) : NULL;
173 if (!((*ptab
174 || (*ptab = hash_initialize (0, 0, map_hash, map_compare, 0)))
175 && hash_insert (*ptab, ent)))
176 xalloc_die ();
178 if (wsopt & WRDSF_REUSE)
179 wordsplit_free (&ws);
180 fclose (fp);
181 if (err)
182 FATAL_ERROR ((0, 0, _("errors reading map file")));
185 /* UID translation */
187 static Hash_table *owner_map;
189 static uintmax_t
190 name_to_uid (char const *name)
192 struct passwd *pw = getpwnam (name);
193 return pw ? pw->pw_uid : UINTMAX_MAX;
196 void
197 owner_map_read (char const *file)
199 map_read (&owner_map, file, name_to_uid, "UID", TYPE_MAXIMUM (uid_t));
203 owner_map_translate (uid_t uid, uid_t *new_uid, char const **new_name)
205 int rc = 1;
207 if (owner_map)
209 struct mapentry ent, *res;
211 ent.orig_id = uid;
212 res = hash_lookup (owner_map, &ent);
213 if (res)
215 *new_uid = res->new_id;
216 *new_name = res->new_name;
217 return 0;
221 if (owner_option != (uid_t) -1)
223 *new_uid = owner_option;
224 rc = 0;
226 if (owner_name_option)
228 *new_name = owner_name_option;
229 rc = 0;
232 return rc;
235 /* GID translation */
237 static Hash_table *group_map;
239 static uintmax_t
240 name_to_gid (char const *name)
242 struct group *gr = getgrnam (name);
243 return gr ? gr->gr_gid : UINTMAX_MAX;
246 void
247 group_map_read (char const *file)
249 map_read (&group_map, file, name_to_gid, "GID", TYPE_MAXIMUM (gid_t));
253 group_map_translate (gid_t gid, gid_t *new_gid, char const **new_name)
255 int rc = 1;
257 if (group_map)
259 struct mapentry ent, *res;
261 ent.orig_id = gid;
262 res = hash_lookup (group_map, &ent);
263 if (res)
265 *new_gid = res->new_id;
266 *new_name = res->new_name;
267 return 0;
271 if (group_option != (uid_t) -1)
273 *new_gid = group_option;
274 rc = 0;
276 if (group_name_option)
278 *new_name = group_name_option;
279 rc = 0;
282 return rc;