138a52bd3SEd Maste /*
238a52bd3SEd Maste * Copyright (c) 2022 Damien Miller <djm@mindrot.org>
338a52bd3SEd Maste *
438a52bd3SEd Maste * Permission to use, copy, modify, and distribute this software for any
538a52bd3SEd Maste * purpose with or without fee is hereby granted, provided that the above
638a52bd3SEd Maste * copyright notice and this permission notice appear in all copies.
738a52bd3SEd Maste *
838a52bd3SEd Maste * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
938a52bd3SEd Maste * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1038a52bd3SEd Maste * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1138a52bd3SEd Maste * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1238a52bd3SEd Maste * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1338a52bd3SEd Maste * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1438a52bd3SEd Maste * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1538a52bd3SEd Maste */
1638a52bd3SEd Maste
1738a52bd3SEd Maste /* sftp client user/group lookup and caching */
1838a52bd3SEd Maste
1938a52bd3SEd Maste #include "includes.h"
2038a52bd3SEd Maste
2138a52bd3SEd Maste #include <sys/types.h>
2238a52bd3SEd Maste #include <openbsd-compat/sys-tree.h>
2338a52bd3SEd Maste
2438a52bd3SEd Maste #include <stdlib.h>
2538a52bd3SEd Maste #include <stdarg.h>
2638a52bd3SEd Maste #include <string.h>
2738a52bd3SEd Maste
2838a52bd3SEd Maste #include "log.h"
2938a52bd3SEd Maste #include "xmalloc.h"
3038a52bd3SEd Maste
3138a52bd3SEd Maste #include "sftp-common.h"
3238a52bd3SEd Maste #include "sftp-client.h"
3338a52bd3SEd Maste #include "sftp-usergroup.h"
3438a52bd3SEd Maste
3538a52bd3SEd Maste /* Tree of id, name */
3638a52bd3SEd Maste struct idname {
3738a52bd3SEd Maste u_int id;
3838a52bd3SEd Maste char *name;
3938a52bd3SEd Maste RB_ENTRY(idname) entry;
4038a52bd3SEd Maste /* XXX implement bounded cache as TAILQ */
4138a52bd3SEd Maste };
4238a52bd3SEd Maste static int
idname_cmp(struct idname * a,struct idname * b)4338a52bd3SEd Maste idname_cmp(struct idname *a, struct idname *b)
4438a52bd3SEd Maste {
4538a52bd3SEd Maste if (a->id == b->id)
4638a52bd3SEd Maste return 0;
4738a52bd3SEd Maste return a->id > b->id ? 1 : -1;
4838a52bd3SEd Maste }
4938a52bd3SEd Maste RB_HEAD(idname_tree, idname);
5038a52bd3SEd Maste RB_GENERATE_STATIC(idname_tree, idname, entry, idname_cmp)
5138a52bd3SEd Maste
5238a52bd3SEd Maste static struct idname_tree user_idname = RB_INITIALIZER(&user_idname);
5338a52bd3SEd Maste static struct idname_tree group_idname = RB_INITIALIZER(&group_idname);
5438a52bd3SEd Maste
5538a52bd3SEd Maste static void
idname_free(struct idname * idname)5638a52bd3SEd Maste idname_free(struct idname *idname)
5738a52bd3SEd Maste {
5838a52bd3SEd Maste if (idname == NULL)
5938a52bd3SEd Maste return;
6038a52bd3SEd Maste free(idname->name);
6138a52bd3SEd Maste free(idname);
6238a52bd3SEd Maste }
6338a52bd3SEd Maste
6438a52bd3SEd Maste static void
idname_enter(struct idname_tree * tree,u_int id,const char * name)6538a52bd3SEd Maste idname_enter(struct idname_tree *tree, u_int id, const char *name)
6638a52bd3SEd Maste {
6738a52bd3SEd Maste struct idname *idname;
6838a52bd3SEd Maste
6938a52bd3SEd Maste if ((idname = xcalloc(1, sizeof(*idname))) == NULL)
7038a52bd3SEd Maste fatal_f("alloc");
7138a52bd3SEd Maste idname->id = id;
7238a52bd3SEd Maste idname->name = xstrdup(name);
7338a52bd3SEd Maste if (RB_INSERT(idname_tree, tree, idname) != NULL)
7438a52bd3SEd Maste idname_free(idname);
7538a52bd3SEd Maste }
7638a52bd3SEd Maste
7738a52bd3SEd Maste static const char *
idname_lookup(struct idname_tree * tree,u_int id)7838a52bd3SEd Maste idname_lookup(struct idname_tree *tree, u_int id)
7938a52bd3SEd Maste {
8038a52bd3SEd Maste struct idname idname, *found;
8138a52bd3SEd Maste
8238a52bd3SEd Maste memset(&idname, 0, sizeof(idname));
8338a52bd3SEd Maste idname.id = id;
8438a52bd3SEd Maste if ((found = RB_FIND(idname_tree, tree, &idname)) != NULL)
8538a52bd3SEd Maste return found->name;
8638a52bd3SEd Maste return NULL;
8738a52bd3SEd Maste }
8838a52bd3SEd Maste
8938a52bd3SEd Maste static void
freenames(char ** names,u_int nnames)9038a52bd3SEd Maste freenames(char **names, u_int nnames)
9138a52bd3SEd Maste {
9238a52bd3SEd Maste u_int i;
9338a52bd3SEd Maste
9438a52bd3SEd Maste if (names == NULL)
9538a52bd3SEd Maste return;
9638a52bd3SEd Maste for (i = 0; i < nnames; i++)
9738a52bd3SEd Maste free(names[i]);
9838a52bd3SEd Maste free(names);
9938a52bd3SEd Maste }
10038a52bd3SEd Maste
10138a52bd3SEd Maste static void
lookup_and_record(struct sftp_conn * conn,u_int * uids,u_int nuids,u_int * gids,u_int ngids)10238a52bd3SEd Maste lookup_and_record(struct sftp_conn *conn,
10338a52bd3SEd Maste u_int *uids, u_int nuids, u_int *gids, u_int ngids)
10438a52bd3SEd Maste {
10538a52bd3SEd Maste int r;
10638a52bd3SEd Maste u_int i;
10738a52bd3SEd Maste char **usernames = NULL, **groupnames = NULL;
10838a52bd3SEd Maste
109*edf85781SEd Maste if ((r = sftp_get_users_groups_by_id(conn, uids, nuids, gids, ngids,
11038a52bd3SEd Maste &usernames, &groupnames)) != 0) {
111*edf85781SEd Maste debug_fr(r, "sftp_get_users_groups_by_id");
11238a52bd3SEd Maste return;
11338a52bd3SEd Maste }
11438a52bd3SEd Maste for (i = 0; i < nuids; i++) {
11538a52bd3SEd Maste if (usernames[i] == NULL) {
11638a52bd3SEd Maste debug3_f("uid %u not resolved", uids[i]);
11738a52bd3SEd Maste continue;
11838a52bd3SEd Maste }
11938a52bd3SEd Maste debug3_f("record uid %u => \"%s\"", uids[i], usernames[i]);
12038a52bd3SEd Maste idname_enter(&user_idname, uids[i], usernames[i]);
12138a52bd3SEd Maste }
12238a52bd3SEd Maste for (i = 0; i < ngids; i++) {
12338a52bd3SEd Maste if (groupnames[i] == NULL) {
12438a52bd3SEd Maste debug3_f("gid %u not resolved", gids[i]);
12538a52bd3SEd Maste continue;
12638a52bd3SEd Maste }
12738a52bd3SEd Maste debug3_f("record gid %u => \"%s\"", gids[i], groupnames[i]);
12838a52bd3SEd Maste idname_enter(&group_idname, gids[i], groupnames[i]);
12938a52bd3SEd Maste }
13038a52bd3SEd Maste freenames(usernames, nuids);
13138a52bd3SEd Maste freenames(groupnames, ngids);
13238a52bd3SEd Maste }
13338a52bd3SEd Maste
13438a52bd3SEd Maste static int
has_id(u_int id,u_int * ids,u_int nids)13538a52bd3SEd Maste has_id(u_int id, u_int *ids, u_int nids)
13638a52bd3SEd Maste {
13738a52bd3SEd Maste u_int i;
13838a52bd3SEd Maste
13938a52bd3SEd Maste if (nids == 0)
14038a52bd3SEd Maste return 0;
14138a52bd3SEd Maste
14238a52bd3SEd Maste /* XXX O(N^2) */
14338a52bd3SEd Maste for (i = 0; i < nids; i++) {
14438a52bd3SEd Maste if (ids[i] == id)
14538a52bd3SEd Maste break;
14638a52bd3SEd Maste }
14738a52bd3SEd Maste return i < nids;
14838a52bd3SEd Maste }
14938a52bd3SEd Maste
15038a52bd3SEd Maste static void
collect_ids_from_glob(glob_t * g,int user,u_int ** idsp,u_int * nidsp)15138a52bd3SEd Maste collect_ids_from_glob(glob_t *g, int user, u_int **idsp, u_int *nidsp)
15238a52bd3SEd Maste {
15338a52bd3SEd Maste u_int id, i, n = 0, *ids = NULL;
15438a52bd3SEd Maste
15538a52bd3SEd Maste for (i = 0; g->gl_pathv[i] != NULL; i++) {
15638a52bd3SEd Maste if (user) {
15738a52bd3SEd Maste if (ruser_name(g->gl_statv[i]->st_uid) != NULL)
15838a52bd3SEd Maste continue; /* Already seen */
15938a52bd3SEd Maste id = (u_int)g->gl_statv[i]->st_uid;
16038a52bd3SEd Maste } else {
16138a52bd3SEd Maste if (rgroup_name(g->gl_statv[i]->st_gid) != NULL)
16238a52bd3SEd Maste continue; /* Already seen */
16338a52bd3SEd Maste id = (u_int)g->gl_statv[i]->st_gid;
16438a52bd3SEd Maste }
16538a52bd3SEd Maste if (has_id(id, ids, n))
16638a52bd3SEd Maste continue;
16738a52bd3SEd Maste ids = xrecallocarray(ids, n, n + 1, sizeof(*ids));
16838a52bd3SEd Maste ids[n++] = id;
16938a52bd3SEd Maste }
17038a52bd3SEd Maste *idsp = ids;
17138a52bd3SEd Maste *nidsp = n;
17238a52bd3SEd Maste }
17338a52bd3SEd Maste
17438a52bd3SEd Maste void
get_remote_user_groups_from_glob(struct sftp_conn * conn,glob_t * g)17538a52bd3SEd Maste get_remote_user_groups_from_glob(struct sftp_conn *conn, glob_t *g)
17638a52bd3SEd Maste {
17738a52bd3SEd Maste u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0;
17838a52bd3SEd Maste
179*edf85781SEd Maste if (!sftp_can_get_users_groups_by_id(conn))
18038a52bd3SEd Maste return;
18138a52bd3SEd Maste
18238a52bd3SEd Maste collect_ids_from_glob(g, 1, &uids, &nuids);
18338a52bd3SEd Maste collect_ids_from_glob(g, 0, &gids, &ngids);
18438a52bd3SEd Maste lookup_and_record(conn, uids, nuids, gids, ngids);
18538a52bd3SEd Maste free(uids);
18638a52bd3SEd Maste free(gids);
18738a52bd3SEd Maste }
18838a52bd3SEd Maste
18938a52bd3SEd Maste static void
collect_ids_from_dirents(SFTP_DIRENT ** d,int user,u_int ** idsp,u_int * nidsp)19038a52bd3SEd Maste collect_ids_from_dirents(SFTP_DIRENT **d, int user, u_int **idsp, u_int *nidsp)
19138a52bd3SEd Maste {
19238a52bd3SEd Maste u_int id, i, n = 0, *ids = NULL;
19338a52bd3SEd Maste
19438a52bd3SEd Maste for (i = 0; d[i] != NULL; i++) {
19538a52bd3SEd Maste if (user) {
19638a52bd3SEd Maste if (ruser_name((uid_t)(d[i]->a.uid)) != NULL)
19738a52bd3SEd Maste continue; /* Already seen */
19838a52bd3SEd Maste id = d[i]->a.uid;
19938a52bd3SEd Maste } else {
20038a52bd3SEd Maste if (rgroup_name((gid_t)(d[i]->a.gid)) != NULL)
20138a52bd3SEd Maste continue; /* Already seen */
20238a52bd3SEd Maste id = d[i]->a.gid;
20338a52bd3SEd Maste }
20438a52bd3SEd Maste if (has_id(id, ids, n))
20538a52bd3SEd Maste continue;
20638a52bd3SEd Maste ids = xrecallocarray(ids, n, n + 1, sizeof(*ids));
20738a52bd3SEd Maste ids[n++] = id;
20838a52bd3SEd Maste }
20938a52bd3SEd Maste *idsp = ids;
21038a52bd3SEd Maste *nidsp = n;
21138a52bd3SEd Maste }
21238a52bd3SEd Maste
21338a52bd3SEd Maste void
get_remote_user_groups_from_dirents(struct sftp_conn * conn,SFTP_DIRENT ** d)21438a52bd3SEd Maste get_remote_user_groups_from_dirents(struct sftp_conn *conn, SFTP_DIRENT **d)
21538a52bd3SEd Maste {
21638a52bd3SEd Maste u_int *uids = NULL, nuids = 0, *gids = NULL, ngids = 0;
21738a52bd3SEd Maste
218*edf85781SEd Maste if (!sftp_can_get_users_groups_by_id(conn))
21938a52bd3SEd Maste return;
22038a52bd3SEd Maste
22138a52bd3SEd Maste collect_ids_from_dirents(d, 1, &uids, &nuids);
22238a52bd3SEd Maste collect_ids_from_dirents(d, 0, &gids, &ngids);
22338a52bd3SEd Maste lookup_and_record(conn, uids, nuids, gids, ngids);
22438a52bd3SEd Maste free(uids);
22538a52bd3SEd Maste free(gids);
22638a52bd3SEd Maste }
22738a52bd3SEd Maste
22838a52bd3SEd Maste const char *
ruser_name(uid_t uid)22938a52bd3SEd Maste ruser_name(uid_t uid)
23038a52bd3SEd Maste {
23138a52bd3SEd Maste return idname_lookup(&user_idname, (u_int)uid);
23238a52bd3SEd Maste }
23338a52bd3SEd Maste
23438a52bd3SEd Maste const char *
rgroup_name(uid_t gid)23538a52bd3SEd Maste rgroup_name(uid_t gid)
23638a52bd3SEd Maste {
23738a52bd3SEd Maste return idname_lookup(&group_idname, (u_int)gid);
23838a52bd3SEd Maste }
23938a52bd3SEd Maste
240