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 <sys/types.h> 20 #include <sys/tree.h> 21 22 #include <glob.h> 23 #include <stdlib.h> 24 #include <stdarg.h> 25 #include <string.h> 26 27 #include "log.h" 28 #include "xmalloc.h" 29 30 #include "sftp-common.h" 31 #include "sftp-client.h" 32 #include "sftp-usergroup.h" 33 34 /* Tree of id, name */ 35 struct idname { 36 u_int id; 37 char *name; 38 RB_ENTRY(idname) entry; 39 /* XXX implement bounded cache as TAILQ */ 40 }; 41 static int 42 idname_cmp(struct idname *a, struct idname *b) 43 { 44 if (a->id == b->id) 45 return 0; 46 return a->id > b->id ? 1 : -1; 47 } 48 RB_HEAD(idname_tree, idname); 49 RB_GENERATE_STATIC(idname_tree, idname, entry, idname_cmp) 50 51 static struct idname_tree user_idname = RB_INITIALIZER(&user_idname); 52 static struct idname_tree group_idname = RB_INITIALIZER(&group_idname); 53 54 static void 55 idname_free(struct idname *idname) 56 { 57 if (idname == NULL) 58 return; 59 free(idname->name); 60 free(idname); 61 } 62 63 static void 64 idname_enter(struct idname_tree *tree, u_int id, const char *name) 65 { 66 struct idname *idname; 67 68 if ((idname = xcalloc(1, sizeof(*idname))) == NULL) 69 fatal_f("alloc"); 70 idname->id = id; 71 idname->name = xstrdup(name); 72 if (RB_INSERT(idname_tree, tree, idname) != NULL) 73 idname_free(idname); 74 } 75 76 static const char * 77 idname_lookup(struct idname_tree *tree, u_int id) 78 { 79 struct idname idname, *found; 80 81 memset(&idname, 0, sizeof(idname)); 82 idname.id = id; 83 if ((found = RB_FIND(idname_tree, tree, &idname)) != NULL) 84 return found->name; 85 return NULL; 86 } 87 88 static void 89 freenames(char **names, u_int nnames) 90 { 91 u_int i; 92 93 if (names == NULL) 94 return; 95 for (i = 0; i < nnames; i++) 96 free(names[i]); 97 free(names); 98 } 99 100 static void 101 lookup_and_record(struct sftp_conn *conn, 102 u_int *uids, u_int nuids, u_int *gids, u_int ngids) 103 { 104 int r; 105 u_int i; 106 char **usernames = NULL, **groupnames = NULL; 107 108 if ((r = sftp_get_users_groups_by_id(conn, uids, nuids, gids, ngids, 109 &usernames, &groupnames)) != 0) { 110 debug_fr(r, "sftp_get_users_groups_by_id"); 111 return; 112 } 113 for (i = 0; i < nuids; i++) { 114 if (usernames[i] == NULL) { 115 debug3_f("uid %u not resolved", uids[i]); 116 continue; 117 } 118 debug3_f("record uid %u => \"%s\"", uids[i], usernames[i]); 119 idname_enter(&user_idname, uids[i], usernames[i]); 120 } 121 for (i = 0; i < ngids; i++) { 122 if (groupnames[i] == NULL) { 123 debug3_f("gid %u not resolved", gids[i]); 124 continue; 125 } 126 debug3_f("record gid %u => \"%s\"", gids[i], groupnames[i]); 127 idname_enter(&group_idname, gids[i], groupnames[i]); 128 } 129 freenames(usernames, nuids); 130 freenames(groupnames, ngids); 131 } 132 133 static int 134 has_id(u_int id, u_int *ids, u_int nids) 135 { 136 u_int i; 137 138 if (nids == 0) 139 return 0; 140 141 /* XXX O(N^2) */ 142 for (i = 0; i < nids; i++) { 143 if (ids[i] == id) 144 break; 145 } 146 return i < nids; 147 } 148 149 static void 150 collect_ids_from_glob(glob_t *g, int user, u_int **idsp, u_int *nidsp) 151 { 152 u_int id, i, n = 0, *ids = NULL; 153 154 for (i = 0; g->gl_pathv[i] != NULL; i++) { 155 if (user) { 156 if (ruser_name(g->gl_statv[i]->st_uid) != NULL) 157 continue; /* Already seen */ 158 id = (u_int)g->gl_statv[i]->st_uid; 159 } else { 160 if (rgroup_name(g->gl_statv[i]->st_gid) != NULL) 161 continue; /* Already seen */ 162 id = (u_int)g->gl_statv[i]->st_gid; 163 } 164 if (has_id(id, ids, n)) 165 continue; 166 ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); 167 ids[n++] = id; 168 } 169 *idsp = ids; 170 *nidsp = n; 171 } 172 173 void 174 get_remote_user_groups_from_glob(struct sftp_conn *conn, glob_t *g) 175 { 176 u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; 177 178 if (!sftp_can_get_users_groups_by_id(conn)) 179 return; 180 181 collect_ids_from_glob(g, 1, &uids, &nuids); 182 collect_ids_from_glob(g, 0, &gids, &ngids); 183 lookup_and_record(conn, uids, nuids, gids, ngids); 184 free(uids); 185 free(gids); 186 } 187 188 static void 189 collect_ids_from_dirents(SFTP_DIRENT **d, int user, u_int **idsp, u_int *nidsp) 190 { 191 u_int id, i, n = 0, *ids = NULL; 192 193 for (i = 0; d[i] != NULL; i++) { 194 if (user) { 195 if (ruser_name((uid_t)(d[i]->a.uid)) != NULL) 196 continue; /* Already seen */ 197 id = d[i]->a.uid; 198 } else { 199 if (rgroup_name((gid_t)(d[i]->a.gid)) != NULL) 200 continue; /* Already seen */ 201 id = d[i]->a.gid; 202 } 203 if (has_id(id, ids, n)) 204 continue; 205 ids = xrecallocarray(ids, n, n + 1, sizeof(*ids)); 206 ids[n++] = id; 207 } 208 *idsp = ids; 209 *nidsp = n; 210 } 211 212 void 213 get_remote_user_groups_from_dirents(struct sftp_conn *conn, SFTP_DIRENT **d) 214 { 215 u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0; 216 217 if (!sftp_can_get_users_groups_by_id(conn)) 218 return; 219 220 collect_ids_from_dirents(d, 1, &uids, &nuids); 221 collect_ids_from_dirents(d, 0, &gids, &ngids); 222 lookup_and_record(conn, uids, nuids, gids, ngids); 223 free(uids); 224 free(gids); 225 } 226 227 const char * 228 ruser_name(uid_t uid) 229 { 230 return idname_lookup(&user_idname, (u_int)uid); 231 } 232 233 const char * 234 rgroup_name(uid_t gid) 235 { 236 return idname_lookup(&group_idname, (u_int)gid); 237 } 238 239