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-2009 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
49 static struct idlist
*uidlist
;
50 static struct idlist
*gidlist
;
52 static struct idlist
*add_to_list(struct idlist
**root
, id_t id
, const char *name
,
53 id_t id2
, uint16 flags
)
55 struct idlist
*node
= new(struct idlist
);
57 out_of_memory("add_to_list");
67 /* turn a uid into a user name */
68 static const char *uid_to_name(uid_t uid
)
70 struct passwd
*pass
= getpwuid(uid
);
72 return strdup(pass
->pw_name
);
76 /* turn a gid into a group name */
77 static const char *gid_to_name(gid_t gid
)
79 struct group
*grp
= getgrgid(gid
);
81 return strdup(grp
->gr_name
);
85 static uid_t
map_uid(uid_t id
, const char *name
)
88 if (id
!= 0 && name_to_uid(name
, &uid
))
93 static gid_t
map_gid(gid_t id
, const char *name
)
96 if (id
!= 0 && name_to_gid(name
, &gid
))
101 static int is_in_group(gid_t gid
)
103 #ifdef HAVE_GETGROUPS
104 static gid_t last_in
;
105 static int ngroups
= -2, last_out
= -1;
106 static GETGROUPS_T
*gidset
;
109 if (gid
== last_in
&& last_out
>= 0)
112 gid_t mygid
= MY_GID();
113 if ((ngroups
= getgroups(0, NULL
)) < 0)
115 gidset
= new_array(GETGROUPS_T
, ngroups
+1);
117 out_of_memory("is_in_group");
119 ngroups
= getgroups(ngroups
, gidset
);
120 /* The default gid might not be in the list on some systems. */
121 for (n
= 0; n
< ngroups
; n
++) {
122 if (gidset
[n
] == mygid
)
126 gidset
[ngroups
++] = mygid
;
129 char *gidbuf
= new_array(char, ngroups
*21+32);
131 out_of_memory("is_in_group");
132 pos
= snprintf(gidbuf
, 32, "process has %d gid%s: ",
133 ngroups
, ngroups
== 1? "" : "s");
134 for (n
= 0; n
< ngroups
; n
++) {
135 pos
+= snprintf(gidbuf
+pos
, 21, " %d", (int)gidset
[n
]);
137 rprintf(FINFO
, "%s\n", gidbuf
);
143 for (n
= 0; n
< ngroups
; n
++) {
144 if (gidset
[n
] == gid
)
150 static gid_t mygid
= GID_NONE
;
151 if (mygid
== GID_NONE
) {
154 rprintf(FINFO
, "process has gid %u\n", (unsigned)mygid
);
160 /* Add a uid to the list of uids. Only called on receiving side. */
161 static struct idlist
*recv_add_uid(uid_t id
, const char *name
)
163 uid_t id2
= name
? map_uid(id
, name
) : id
;
166 node
= add_to_list(&uidlist
, id
, name
, id2
, 0);
169 rprintf(FINFO
, "uid %u(%s) maps to %u\n",
170 (unsigned)id
, name
? name
: "", (unsigned)id2
);
176 /* Add a gid to the list of gids. Only called on receiving side. */
177 static struct idlist
*recv_add_gid(gid_t id
, const char *name
)
179 gid_t id2
= name
? map_gid(id
, name
) : id
;
182 node
= add_to_list(&gidlist
, id
, name
, id2
,
183 !am_root
&& !is_in_group(id2
) ? FLAG_SKIP_GROUP
: 0);
186 rprintf(FINFO
, "gid %u(%s) maps to %u\n",
187 (unsigned)id
, name
? name
: "", (unsigned)id2
);
193 /* this function is a definate candidate for a faster algorithm */
194 uid_t
match_uid(uid_t uid
)
196 static uid_t last_in
, last_out
;
207 for (list
= uidlist
; list
; list
= list
->next
) {
209 return last_out
= list
->id2
;
212 return last_out
= uid
;
215 gid_t
match_gid(gid_t gid
, uint16
*flags_ptr
)
217 static struct idlist
*last
= NULL
;
220 if (last
&& gid
== last
->id
)
223 for (list
= gidlist
; list
; list
= list
->next
) {
228 list
= recv_add_gid(gid
, NULL
);
232 if (flags_ptr
&& list
->flags
& FLAG_SKIP_GROUP
)
233 *flags_ptr
|= FLAG_SKIP_GROUP
;
237 /* Add a uid to the list of uids. Only called on sending side. */
238 const char *add_uid(uid_t uid
)
243 if (uid
== 0) /* don't map root */
246 for (list
= uidlist
; list
; list
= list
->next
) {
251 node
= add_to_list(&uidlist
, uid
, uid_to_name(uid
), 0, 0);
255 /* Add a gid to the list of gids. Only called on sending side. */
256 const char *add_gid(gid_t gid
)
261 if (gid
== 0) /* don't map root */
264 for (list
= gidlist
; list
; list
= list
->next
) {
269 node
= add_to_list(&gidlist
, gid
, gid_to_name(gid
), 0, 0);
273 /* send a complete uid/gid mapping to the peer */
274 void send_id_list(int f
)
278 if (preserve_uid
|| preserve_acls
) {
280 /* we send sequences of uid/byte-length/name */
281 for (list
= uidlist
; list
; list
= list
->next
) {
284 len
= strlen(list
->name
);
285 write_varint30(f
, list
->id
);
287 write_buf(f
, list
->name
, len
);
290 /* terminate the uid list with a 0 uid. We explicitly exclude
292 write_varint30(f
, 0);
295 if (preserve_gid
|| preserve_acls
) {
297 for (list
= gidlist
; list
; list
= list
->next
) {
300 len
= strlen(list
->name
);
301 write_varint30(f
, list
->id
);
303 write_buf(f
, list
->name
, len
);
305 write_varint30(f
, 0);
309 uid_t
recv_user_name(int f
, uid_t uid
)
312 int len
= read_byte(f
);
313 char *name
= new_array(char, len
+1);
315 out_of_memory("recv_user_name");
316 read_sbuf(f
, name
, len
);
317 if (numeric_ids
< 0) {
321 node
= recv_add_uid(uid
, name
); /* node keeps name's memory */
325 gid_t
recv_group_name(int f
, gid_t gid
, uint16
*flags_ptr
)
328 int len
= read_byte(f
);
329 char *name
= new_array(char, len
+1);
331 out_of_memory("recv_group_name");
332 read_sbuf(f
, name
, len
);
333 if (numeric_ids
< 0) {
337 node
= recv_add_gid(gid
, name
); /* node keeps name's memory */
338 if (flags_ptr
&& node
->flags
& FLAG_SKIP_GROUP
)
339 *flags_ptr
|= FLAG_SKIP_GROUP
;
343 /* recv a complete uid/gid mapping from the peer and map the uid/gid
344 * in the file list to local names */
345 void recv_id_list(int f
, struct file_list
*flist
)
350 if ((preserve_uid
|| preserve_acls
) && numeric_ids
<= 0) {
351 /* read the uid list */
352 while ((id
= read_varint30(f
)) != 0)
353 recv_user_name(f
, id
);
356 if ((preserve_gid
|| preserve_acls
) && numeric_ids
<= 0) {
357 /* read the gid list */
358 while ((id
= read_varint30(f
)) != 0)
359 recv_group_name(f
, id
, NULL
);
362 /* Now convert all the uids/gids from sender values to our values. */
364 if (preserve_acls
&& !numeric_ids
)
367 if (am_root
&& preserve_uid
&& !numeric_ids
) {
368 for (i
= 0; i
< flist
->used
; i
++)
369 F_OWNER(flist
->files
[i
]) = match_uid(F_OWNER(flist
->files
[i
]));
371 if (preserve_gid
&& (!am_root
|| !numeric_ids
)) {
372 for (i
= 0; i
< flist
->used
; i
++) {
373 F_GROUP(flist
->files
[i
]) = match_gid(F_GROUP(flist
->files
[i
]),
374 &flist
->files
[i
]->flags
);