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