1/* $NetBSD: sftp-usergroup.c,v 1.3 2023/10/25 20:19:57 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.3 2023/10/25 20:19:57 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 */ 39struct idname { 40 u_int id; 41 char *name; 42 RB_ENTRY(idname) entry; 43 /* XXX implement bounded cache as TAILQ */ 44}; 45static int 46idname_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} 52RB_HEAD(idname_tree, idname); 53RB_GENERATE_STATIC(idname_tree, idname, entry, idname_cmp) 54 55static struct idname_tree user_idname = RB_INITIALIZER(&user_idname); 56static struct idname_tree group_idname = RB_INITIALIZER(&group_idname); 57 58static void 59idname_free(struct idname *idname) 60{ 61 if (idname == NULL) 62 return; 63 free(idname->name); 64 free(idname); 65} 66 67static void 68idname_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 80static const char * 81idname_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 92static void 93freenames(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 104static void 105lookup_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 = sftp_get_users_groups_by_id(conn, uids, nuids, gids, ngids, 113 &usernames, &groupnames)) != 0) { 114 debug_fr(r, "sftp_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 137static int 138has_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 153static void 154collect_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 187void 188get_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 (!sftp_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 202static void 203collect_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 226void 227get_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 (!sftp_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 241const char * 242ruser_name(uid_t uid) 243{ 244 return idname_lookup(&user_idname, (u_int)uid); 245} 246 247const char * 248rgroup_name(uid_t gid) 249{ 250 return idname_lookup(&group_idname, (u_int)gid); 251} 252 253