1 /* $NetBSD: sftp-usergroup.c,v 1.2 2022/10/05 22:39:36 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2022 Damien Miller <djm@mindrot.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* sftp client user/group lookup and caching */ 20 #include "includes.h" 21 __RCSID("$NetBSD: sftp-usergroup.c,v 1.2 2022/10/05 22:39:36 christos Exp $"); 22 23 #include <sys/types.h> 24 #include <sys/tree.h> 25 26 #include <glob.h> 27 #include <stdlib.h> 28 #include <stdarg.h> 29 #include <string.h> 30 31 #include "log.h" 32 #include "xmalloc.h" 33 34 #include "sftp-common.h" 35 #include "sftp-client.h" 36 #include "sftp-usergroup.h" 37 38 /* Tree of id, name */ 39 struct idname { 40 u_int id; 41 char *name; 42 RB_ENTRY(idname) entry; 43 /* XXX implement bounded cache as TAILQ */ 44 }; 45 static int 46 idname_cmp(struct idname *a, struct idname *b) 47 { 48 if (a->id == b->id) 49 return 0; 50 return a->id > b->id ? 1 : -1; 51 } 52 RB_HEAD(idname_tree, idname); 53 RB_GENERATE_STATIC(idname_tree, idname, entry, idname_cmp) 54 55 static struct idname_tree user_idname = RB_INITIALIZER(&user_idname); 56 static struct idname_tree group_idname = RB_INITIALIZER(&group_idname); 57 58 static void 59 idname_free(struct idname *idname) 60 { 61 if (idname == NULL) 62 return; 63 free(idname->name); 64 free(idname); 65 } 66 67 static void 68 idname_enter(struct idname_tree *tree, u_int id, const char *name) 69 { 70 struct idname *idname; 71 72 if ((idname = xcalloc(1, sizeof(*idname))) == NULL) 73 fatal_f("alloc"); 74 idname->id = id; 75 idname->name = xstrdup(name); 76 if (RB_INSERT(idname_tree, tree, idname) != NULL) 77 idname_free(idname); 78 } 79 80 static const char * 81 idname_lookup(struct idname_tree *tree, u_int id) 82 { 83 struct idname idname, *found; 84 85 memset(&idname, 0, sizeof(idname)); 86 idname.id = id; 87 if ((found = RB_FIND(idname_tree, tree, &idname)) != NULL) 88 return found->name; 89 return NULL; 90 } 91 92 static void 93 freenames(char **names, u_int nnames) 94 { 95 u_int i; 96 97 if (names == NULL) 98 return; 99 for (i = 0; i < nnames; i++) 100 free(names[i]); 101 free(names); 102 } 103 104 static void 105 lookup_and_record(struct sftp_conn *conn, 106 u_int *uids, u_int nuids, u_int *gids, u_int ngids) 107 { 108 int r; 109 u_int i; 110 char **usernames = NULL, **groupnames = NULL; 111 112 if ((r = do_get_users_groups_by_id(conn, uids, nuids, gids, ngids, 113 &usernames, &groupnames)) != 0) { 114 debug_fr(r, "do_get_users_groups_by_id"); 115 return; 116 } 117 for (i = 0; i < nuids; i++) { 118 if (usernames[i] == NULL) { 119 debug3_f("uid %u not resolved", uids[i]); 120 continue; 121 } 122 debug3_f("record uid %u => \"%s\"", uids[i], usernames[i]); 123 idname_enter(&user_idname, uids[i], usernames[i]); 124 } 125 for (i = 0; i < ngids; i++) { 126 if (groupnames[i] == NULL) { 127 debug3_f("gid %u not resolved", gids[i]); 128 continue; 129 } 130 debug3_f("record gid %u => \"%s\"", gids[i], groupnames[i]); 131 idname_enter(&group_idname, gids[i], groupnames[i]); 132 } 133 freenames(usernames, nuids); 134 freenames(groupnames, ngids); 135 } 136 137 static int 138 has_id(u_int id, u_int *ids, u_int nids) 139 { 140 u_int i; 141 142 if (nids == 0) 143 return 0; 144 145 /* XXX O(N^2) */ 146 for (i = 0; i < nids; i++) { 147 if (ids[i] == id) 148 break; 149 } 150 return i < nids; 151 } 152 153 static void 154 collect_ids_from_glob(glob_t *g, int user, u_int **idsp, u_int *nidsp) 155 { 156 u_int id, i, n = 0, *ids = NULL; 157 158 for (i = 0; g->gl_pathv[i] != NULL; i++) { 159 struct stat *stp; 160 #if GLOB_KEEPSTAT != 0 161 stp = g->gl_statv[i]; 162 #else 163 struct stat st; 164 if (lstat(g->gl_pathv[i], stp = &st) == -1) { 165 error("no stat information for %s", g->gl_pathv[i]); 166 continue; 167 } 168 #endif 169 if (user) { 170 if (ruser_name(stp->st_uid) != NULL) 171 continue; /* Already seen */ 172 id = (u_int)stp->st_uid; 173 } else { 174 if (rgroup_name(stp->st_gid) != NULL) 175 continue; /* Already seen */ 176 id = (u_int)stp->st_gid; 177 } 178 if (has_id(id, ids, n)) 179 continue; 180 ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); 181 ids[n++] = id; 182 } 183 *idsp = ids; 184 *nidsp = n; 185 } 186 187 void 188 get_remote_user_groups_from_glob(struct sftp_conn *conn, glob_t *g) 189 { 190 u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; 191 192 if (!can_get_users_groups_by_id(conn)) 193 return; 194 195 collect_ids_from_glob(g, 1, &uids, &nuids); 196 collect_ids_from_glob(g, 0, &gids, &ngids); 197 lookup_and_record(conn, uids, nuids, gids, ngids); 198 free(uids); 199 free(gids); 200 } 201 202 static void 203 collect_ids_from_dirents(SFTP_DIRENT **d, int user, u_int **idsp, u_int *nidsp) 204 { 205 u_int id, i, n = 0, *ids = NULL; 206 207 for (i = 0; d[i] != NULL; i++) { 208 if (user) { 209 if (ruser_name((uid_t)(d[i]->a.uid)) != NULL) 210 continue; /* Already seen */ 211 id = d[i]->a.uid; 212 } else { 213 if (rgroup_name((gid_t)(d[i]->a.gid)) != NULL) 214 continue; /* Already seen */ 215 id = d[i]->a.gid; 216 } 217 if (has_id(id, ids, n)) 218 continue; 219 ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); 220 ids[n++] = id; 221 } 222 *idsp = ids; 223 *nidsp = n; 224 } 225 226 void 227 get_remote_user_groups_from_dirents(struct sftp_conn *conn, SFTP_DIRENT **d) 228 { 229 u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; 230 231 if (!can_get_users_groups_by_id(conn)) 232 return; 233 234 collect_ids_from_dirents(d, 1, &uids, &nuids); 235 collect_ids_from_dirents(d, 0, &gids, &ngids); 236 lookup_and_record(conn, uids, nuids, gids, ngids); 237 free(uids); 238 free(gids); 239 } 240 241 const char * 242 ruser_name(uid_t uid) 243 { 244 return idname_lookup(&user_idname, (u_int)uid); 245 } 246 247 const char * 248 rgroup_name(uid_t gid) 249 { 250 return idname_lookup(&group_idname, (u_int)gid); 251 } 252 253