1/* 2 * Handle the mapping of uid/gid and user/group names between systems. 3 * 4 * Copyright (C) 1996 Andrew Tridgell 5 * Copyright (C) 1996 Paul Mackerras 6 * Copyright (C) 2004, 2005, 2006 Wayne Davison 7 * 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 2 of the License, or 11 * (at your option) any later version. 12 * 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. 17 * 18 * You should have received a copy of the GNU General Public License along 19 * with this program; if not, write to the Free Software Foundation, Inc., 20 * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. 21 */ 22 23/* If the source username/group does not exist on the target then use 24 * the numeric IDs. Never do any mapping for uid=0 or gid=0 as these 25 * are special. */ 26 27#include "rsync.h" 28 29#ifdef HAVE_GETGROUPS 30# ifndef GETGROUPS_T 31# define GETGROUPS_T gid_t 32# endif 33#endif 34 35extern int verbose; 36extern int preserve_uid; 37extern int preserve_gid; 38extern int numeric_ids; 39extern int am_root; 40 41struct idlist { 42 struct idlist *next; 43 int id, id2; 44 char *name; 45}; 46 47static struct idlist *uidlist; 48static struct idlist *gidlist; 49 50static struct idlist *add_to_list(struct idlist **root, int id, char *name, 51 int id2) 52{ 53 struct idlist *node = new(struct idlist); 54 if (!node) 55 out_of_memory("add_to_list"); 56 node->next = *root; 57 node->name = name; 58 node->id = id; 59 node->id2 = id2; 60 *root = node; 61 return node; 62} 63 64/* turn a uid into a user name */ 65static char *uid_to_name(uid_t uid) 66{ 67 struct passwd *pass = getpwuid(uid); 68 if (pass) 69 return strdup(pass->pw_name); 70 return NULL; 71} 72 73/* turn a gid into a group name */ 74static char *gid_to_name(gid_t gid) 75{ 76 struct group *grp = getgrgid(gid); 77 if (grp) 78 return strdup(grp->gr_name); 79 return NULL; 80} 81 82static int map_uid(int id, char *name) 83{ 84 uid_t uid; 85 if (id != 0 && name_to_uid(name, &uid)) 86 return uid; 87 return id; 88} 89 90static int map_gid(int id, char *name) 91{ 92 gid_t gid; 93 if (id != 0 && name_to_gid(name, &gid)) 94 return gid; 95 return id; 96} 97 98static int is_in_group(gid_t gid) 99{ 100#ifdef HAVE_GETGROUPS 101 static gid_t last_in = GID_NONE, last_out; 102 static int ngroups = -2; 103 static GETGROUPS_T *gidset; 104 int n; 105 106 if (gid == last_in) 107 return last_out; 108 if (ngroups < -1) { 109 gid_t mygid = MY_GID(); 110 if ((ngroups = getgroups(0, NULL)) < 0) 111 ngroups = 0; 112 gidset = new_array(GETGROUPS_T, ngroups+1); 113 if (!gidset) 114 out_of_memory("is_in_group"); 115 if (ngroups > 0) 116 ngroups = getgroups(ngroups, gidset); 117 /* The default gid might not be in the list on some systems. */ 118 for (n = 0; n < ngroups; n++) { 119 if (gidset[n] == mygid) 120 break; 121 } 122 if (n == ngroups) 123 gidset[ngroups++] = mygid; 124 if (verbose > 3) { 125 int pos; 126 char *gidbuf = new_array(char, ngroups*21+32); 127 if (!gidbuf) 128 out_of_memory("is_in_group"); 129 pos = snprintf(gidbuf, 32, "process has %d gid%s: ", 130 ngroups, ngroups == 1? "" : "s"); 131 for (n = 0; n < ngroups; n++) { 132 pos += snprintf(gidbuf+pos, 21, " %d", (int)gidset[n]); 133 } 134 rprintf(FINFO, "%s\n", gidbuf); 135 free(gidbuf); 136 } 137 } 138 139 last_in = gid; 140 for (n = 0; n < ngroups; n++) { 141 if (gidset[n] == gid) 142 return last_out = 1; 143 } 144 return last_out = 0; 145 146#else 147 static gid_t mygid = GID_NONE; 148 if (mygid == GID_NONE) { 149 mygid = MY_GID(); 150 if (verbose > 3) 151 rprintf(FINFO, "process has gid %d\n", (int)mygid); 152 } 153 return gid == mygid; 154#endif 155} 156 157/* Add a uid to the list of uids. Only called on receiving side. */ 158static struct idlist *recv_add_uid(int id, char *name) 159{ 160 int id2 = name ? map_uid(id, name) : id; 161 struct idlist *node; 162 163 node = add_to_list(&uidlist, id, name, id2); 164 165 if (verbose > 3) { 166 rprintf(FINFO, "uid %d(%s) maps to %d\n", 167 id, name ? name : "", id2); 168 } 169 170 return node; 171} 172 173/* Add a gid to the list of gids. Only called on receiving side. */ 174static struct idlist *recv_add_gid(int id, char *name) 175{ 176 int id2 = name ? map_gid(id, name) : id; 177 struct idlist *node; 178 179 if (!am_root && !is_in_group(id2)) 180 id2 = GID_NONE; 181 node = add_to_list(&gidlist, id, name, id2); 182 183 if (verbose > 3) { 184 rprintf(FINFO, "gid %d(%s) maps to %d\n", 185 id, name ? name : "", id2); 186 } 187 188 return node; 189} 190 191/* this function is a definate candidate for a faster algorithm */ 192static uid_t match_uid(uid_t uid) 193{ 194 static uid_t last_in, last_out; 195 struct idlist *list; 196 197 if (uid == 0) 198 return 0; 199 200 if (uid == last_in) 201 return last_out; 202 203 last_in = uid; 204 205 for (list = uidlist; list; list = list->next) { 206 if (list->id == (int)uid) 207 return last_out = (uid_t)list->id2; 208 } 209 210 return last_out = uid; 211} 212 213static gid_t match_gid(gid_t gid) 214{ 215 static gid_t last_in = GID_NONE, last_out = GID_NONE; 216 struct idlist *list; 217 218 if (gid == GID_NONE) 219 return GID_NONE; 220 221 if (gid == last_in) 222 return last_out; 223 224 last_in = gid; 225 226 for (list = gidlist; list; list = list->next) { 227 if (list->id == (int)gid) 228 return last_out = (gid_t)list->id2; 229 } 230 231 list = recv_add_gid(gid, NULL); 232 return last_out = list->id2; 233} 234 235/* Add a uid to the list of uids. Only called on sending side. */ 236void add_uid(uid_t uid) 237{ 238 struct idlist *list; 239 240 if (uid == 0) /* don't map root */ 241 return; 242 243 for (list = uidlist; list; list = list->next) { 244 if (list->id == (int)uid) 245 return; 246 } 247 248 add_to_list(&uidlist, (int)uid, uid_to_name(uid), 0); 249} 250 251/* Add a gid to the list of gids. Only called on sending side. */ 252void add_gid(gid_t gid) 253{ 254 struct idlist *list; 255 256 if (gid == 0) /* don't map root */ 257 return; 258 259 for (list = gidlist; list; list = list->next) { 260 if (list->id == (int)gid) 261 return; 262 } 263 264 add_to_list(&gidlist, (int)gid, gid_to_name(gid), 0); 265} 266 267 268/* send a complete uid/gid mapping to the peer */ 269void send_uid_list(int f) 270{ 271 struct idlist *list; 272 273 if (numeric_ids) 274 return; 275 276 if (preserve_uid) { 277 int len; 278 /* we send sequences of uid/byte-length/name */ 279 for (list = uidlist; list; list = list->next) { 280 if (!list->name) 281 continue; 282 len = strlen(list->name); 283 write_int(f, list->id); 284 write_byte(f, len); 285 write_buf(f, list->name, len); 286 } 287 288 /* terminate the uid list with a 0 uid. We explicitly exclude 289 * 0 from the list */ 290 write_int(f, 0); 291 } 292 293 if (preserve_gid) { 294 int len; 295 for (list = gidlist; list; list = list->next) { 296 if (!list->name) 297 continue; 298 len = strlen(list->name); 299 write_int(f, list->id); 300 write_byte(f, len); 301 write_buf(f, list->name, len); 302 } 303 write_int(f, 0); 304 } 305} 306 307/* recv a complete uid/gid mapping from the peer and map the uid/gid 308 * in the file list to local names */ 309void recv_uid_list(int f, struct file_list *flist) 310{ 311 int id, i; 312 char *name; 313 314 if (preserve_uid && !numeric_ids) { 315 /* read the uid list */ 316 while ((id = read_int(f)) != 0) { 317 int len = read_byte(f); 318 name = new_array(char, len+1); 319 if (!name) 320 out_of_memory("recv_uid_list"); 321 read_sbuf(f, name, len); 322 recv_add_uid(id, name); /* node keeps name's memory */ 323 } 324 } 325 326 if (preserve_gid && !numeric_ids) { 327 /* read the gid list */ 328 while ((id = read_int(f)) != 0) { 329 int len = read_byte(f); 330 name = new_array(char, len+1); 331 if (!name) 332 out_of_memory("recv_uid_list"); 333 read_sbuf(f, name, len); 334 recv_add_gid(id, name); /* node keeps name's memory */ 335 } 336 } 337 338 /* Now convert all the uids/gids from sender values to our values. */ 339 if (am_root && preserve_uid && !numeric_ids) { 340 for (i = 0; i < flist->count; i++) 341 flist->files[i]->uid = match_uid(flist->files[i]->uid); 342 } 343 if (preserve_gid && (!am_root || !numeric_ids)) { 344 for (i = 0; i < flist->count; i++) 345 flist->files[i]->gid = match_gid(flist->files[i]->gid); 346 } 347} 348