2 * Handle the mapping of uid/gid and user/group names between systems.
4 * Copyright (C) 1996 Andrew Tridgell
5 * Copyright (C) 1996 Paul Mackerras
6 * Copyright (C) 2004-2008 Wayne Davison
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, visit the http://fsf.org website.
22 /* If the source username/group does not exist on the target then use
23 * the numeric IDs. Never do any mapping for uid=0 or gid=0 as these
31 extern int preserve_uid
;
32 extern int preserve_gid
;
33 extern int preserve_acls
;
34 extern int numeric_ids
;
38 # define GETGROUPS_T gid_t
42 #define GID_NONE ((gid_t)-1)
51 static struct idlist
*uidlist
;
52 static struct idlist
*gidlist
;
54 static struct idlist
*add_to_list(struct idlist
**root
, id_t id
, const char *name
,
55 id_t id2
, uint16 flags
)
57 struct idlist
*node
= new(struct idlist
);
59 out_of_memory("add_to_list");
69 /* turn a uid into a user name */
70 static const char *uid_to_name(uid_t uid
)
72 struct passwd
*pass
= getpwuid(uid
);
74 return strdup(pass
->pw_name
);
78 /* turn a gid into a group name */
79 static const char *gid_to_name(gid_t gid
)
81 struct group
*grp
= getgrgid(gid
);
83 return strdup(grp
->gr_name
);
87 static uid_t
map_uid(uid_t id
, const char *name
)
90 if (id
!= 0 && name_to_uid(name
, &uid
))
95 static gid_t
map_gid(gid_t id
, const char *name
)
98 if (id
!= 0 && name_to_gid(name
, &gid
))
103 static int is_in_group(gid_t gid
)
105 #ifdef HAVE_GETGROUPS
106 static gid_t last_in
= GID_NONE
, last_out
;
107 static int ngroups
= -2;
108 static GETGROUPS_T
*gidset
;
114 gid_t mygid
= MY_GID();
115 if ((ngroups
= getgroups(0, NULL
)) < 0)
117 gidset
= new_array(GETGROUPS_T
, ngroups
+1);
119 out_of_memory("is_in_group");
121 ngroups
= getgroups(ngroups
, gidset
);
122 /* The default gid might not be in the list on some systems. */
123 for (n
= 0; n
< ngroups
; n
++) {
124 if (gidset
[n
] == mygid
)
128 gidset
[ngroups
++] = mygid
;
131 char *gidbuf
= new_array(char, ngroups
*21+32);
133 out_of_memory("is_in_group");
134 pos
= snprintf(gidbuf
, 32, "process has %d gid%s: ",
135 ngroups
, ngroups
== 1? "" : "s");
136 for (n
= 0; n
< ngroups
; n
++) {
137 pos
+= snprintf(gidbuf
+pos
, 21, " %d", (int)gidset
[n
]);
139 rprintf(FINFO
, "%s\n", gidbuf
);
145 for (n
= 0; n
< ngroups
; n
++) {
146 if (gidset
[n
] == gid
)
152 static gid_t mygid
= GID_NONE
;
153 if (mygid
== GID_NONE
) {
156 rprintf(FINFO
, "process has gid %u\n", (unsigned)mygid
);
162 /* Add a uid to the list of uids. Only called on receiving side. */
163 static struct idlist
*recv_add_uid(uid_t id
, const char *name
)
165 uid_t id2
= name
? map_uid(id
, name
) : id
;
168 node
= add_to_list(&uidlist
, id
, name
, id2
, 0);
171 rprintf(FINFO
, "uid %u(%s) maps to %u\n",
172 (unsigned)id
, name
? name
: "", (unsigned)id2
);
178 /* Add a gid to the list of gids. Only called on receiving side. */
179 static struct idlist
*recv_add_gid(gid_t id
, const char *name
)
181 gid_t id2
= name
? map_gid(id
, name
) : id
;
184 node
= add_to_list(&gidlist
, id
, name
, id2
,
185 !am_root
&& !is_in_group(id2
) ? FLAG_SKIP_GROUP
: 0);
188 rprintf(FINFO
, "gid %u(%s) maps to %u\n",
189 (unsigned)id
, name
? name
: "", (unsigned)id2
);
195 /* this function is a definate candidate for a faster algorithm */
196 uid_t
match_uid(uid_t uid
)
198 static uid_t last_in
, last_out
;
209 for (list
= uidlist
; list
; list
= list
->next
) {
211 return last_out
= list
->id2
;
214 return last_out
= uid
;
217 gid_t
match_gid(gid_t gid
, uint16
*flags_ptr
)
219 static struct idlist
*last
= NULL
;
222 if (last
&& gid
== last
->id
)
225 for (list
= gidlist
; list
; list
= list
->next
) {
230 list
= recv_add_gid(gid
, NULL
);
234 if (flags_ptr
&& list
->flags
& FLAG_SKIP_GROUP
)
235 *flags_ptr
|= FLAG_SKIP_GROUP
;
239 /* Add a uid to the list of uids. Only called on sending side. */
240 const char *add_uid(uid_t uid
)
245 if (uid
== 0) /* don't map root */
248 for (list
= uidlist
; list
; list
= list
->next
) {
253 node
= add_to_list(&uidlist
, uid
, uid_to_name(uid
), 0, 0);
257 /* Add a gid to the list of gids. Only called on sending side. */
258 const char *add_gid(gid_t gid
)
263 if (gid
== 0) /* don't map root */
266 for (list
= gidlist
; list
; list
= list
->next
) {
271 node
= add_to_list(&gidlist
, gid
, gid_to_name(gid
), 0, 0);
275 /* send a complete uid/gid mapping to the peer */
276 void send_id_list(int f
)
280 if (preserve_uid
|| preserve_acls
) {
282 /* we send sequences of uid/byte-length/name */
283 for (list
= uidlist
; list
; list
= list
->next
) {
286 len
= strlen(list
->name
);
287 write_varint30(f
, list
->id
);
289 write_buf(f
, list
->name
, len
);
292 /* terminate the uid list with a 0 uid. We explicitly exclude
294 write_varint30(f
, 0);
297 if (preserve_gid
|| preserve_acls
) {
299 for (list
= gidlist
; list
; list
= list
->next
) {
302 len
= strlen(list
->name
);
303 write_varint30(f
, list
->id
);
305 write_buf(f
, list
->name
, len
);
307 write_varint30(f
, 0);
311 uid_t
recv_user_name(int f
, uid_t uid
)
314 int len
= read_byte(f
);
315 char *name
= new_array(char, len
+1);
317 out_of_memory("recv_user_name");
318 read_sbuf(f
, name
, len
);
319 if (numeric_ids
< 0) {
323 node
= recv_add_uid(uid
, name
); /* node keeps name's memory */
327 gid_t
recv_group_name(int f
, gid_t gid
, uint16
*flags_ptr
)
330 int len
= read_byte(f
);
331 char *name
= new_array(char, len
+1);
333 out_of_memory("recv_group_name");
334 read_sbuf(f
, name
, len
);
335 if (numeric_ids
< 0) {
339 node
= recv_add_gid(gid
, name
); /* node keeps name's memory */
340 if (flags_ptr
&& node
->flags
& FLAG_SKIP_GROUP
)
341 *flags_ptr
|= FLAG_SKIP_GROUP
;
345 /* recv a complete uid/gid mapping from the peer and map the uid/gid
346 * in the file list to local names */
347 void recv_id_list(int f
, struct file_list
*flist
)
352 if ((preserve_uid
|| preserve_acls
) && numeric_ids
<= 0) {
353 /* read the uid list */
354 while ((id
= read_varint30(f
)) != 0)
355 recv_user_name(f
, id
);
358 if ((preserve_gid
|| preserve_acls
) && numeric_ids
<= 0) {
359 /* read the gid list */
360 while ((id
= read_varint30(f
)) != 0)
361 recv_group_name(f
, id
, NULL
);
364 /* Now convert all the uids/gids from sender values to our values. */
366 if (preserve_acls
&& !numeric_ids
)
369 if (am_root
&& preserve_uid
&& !numeric_ids
) {
370 for (i
= 0; i
< flist
->used
; i
++)
371 F_OWNER(flist
->files
[i
]) = match_uid(F_OWNER(flist
->files
[i
]));
373 if (preserve_gid
&& (!am_root
|| !numeric_ids
)) {
374 for (i
= 0; i
< flist
->used
; i
++) {
375 F_GROUP(flist
->files
[i
]) = match_gid(F_GROUP(flist
->files
[i
]),
376 &flist
->files
[i
]->flags
);