2 * Copyright (c) 2022 Damien Miller <djm@mindrot.org>
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.
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.
17 /* sftp client user/group lookup and caching */
21 #include <sys/types.h>
22 #include <openbsd-compat/sys-tree.h>
31 #include "sftp-common.h"
32 #include "sftp-client.h"
33 #include "sftp-usergroup.h"
35 /* Tree of id, name */
39 RB_ENTRY(idname
) entry
;
40 /* XXX implement bounded cache as TAILQ */
43 idname_cmp(struct idname
*a
, struct idname
*b
)
47 return a
->id
> b
->id
? 1 : -1;
49 RB_HEAD(idname_tree
, idname
);
50 RB_GENERATE_STATIC(idname_tree
, idname
, entry
, idname_cmp
)
52 static struct idname_tree user_idname
= RB_INITIALIZER(&user_idname
);
53 static struct idname_tree group_idname
= RB_INITIALIZER(&group_idname
);
56 idname_free(struct idname
*idname
)
65 idname_enter(struct idname_tree
*tree
, u_int id
, const char *name
)
67 struct idname
*idname
;
69 if ((idname
= xcalloc(1, sizeof(*idname
))) == NULL
)
72 idname
->name
= xstrdup(name
);
73 if (RB_INSERT(idname_tree
, tree
, idname
) != NULL
)
78 idname_lookup(struct idname_tree
*tree
, u_int id
)
80 struct idname idname
, *found
;
82 memset(&idname
, 0, sizeof(idname
));
84 if ((found
= RB_FIND(idname_tree
, tree
, &idname
)) != NULL
)
90 freenames(char **names
, u_int nnames
)
96 for (i
= 0; i
< nnames
; i
++)
102 lookup_and_record(struct sftp_conn
*conn
,
103 u_int
*uids
, u_int nuids
, u_int
*gids
, u_int ngids
)
107 char **usernames
= NULL
, **groupnames
= NULL
;
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");
114 for (i
= 0; i
< nuids
; i
++) {
115 if (usernames
[i
] == NULL
) {
116 debug3_f("uid %u not resolved", uids
[i
]);
119 debug3_f("record uid %u => \"%s\"", uids
[i
], usernames
[i
]);
120 idname_enter(&user_idname
, uids
[i
], usernames
[i
]);
122 for (i
= 0; i
< ngids
; i
++) {
123 if (groupnames
[i
] == NULL
) {
124 debug3_f("gid %u not resolved", gids
[i
]);
127 debug3_f("record gid %u => \"%s\"", gids
[i
], groupnames
[i
]);
128 idname_enter(&group_idname
, gids
[i
], groupnames
[i
]);
130 freenames(usernames
, nuids
);
131 freenames(groupnames
, ngids
);
135 has_id(u_int id
, u_int
*ids
, u_int nids
)
143 for (i
= 0; i
< nids
; i
++) {
151 collect_ids_from_glob(glob_t
*g
, int user
, u_int
**idsp
, u_int
*nidsp
)
153 u_int id
, i
, n
= 0, *ids
= NULL
;
155 for (i
= 0; g
->gl_pathv
[i
] != NULL
; i
++) {
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
;
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
;
165 if (has_id(id
, ids
, n
))
167 ids
= xrecallocarray(ids
, n
, n
+ 1, sizeof(*ids
));
175 get_remote_user_groups_from_glob(struct sftp_conn
*conn
, glob_t
*g
)
177 u_int
*uids
= NULL
, nuids
= 0, *gids
= NULL
, ngids
= 0;
179 if (!sftp_can_get_users_groups_by_id(conn
))
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
);
190 collect_ids_from_dirents(SFTP_DIRENT
**d
, int user
, u_int
**idsp
, u_int
*nidsp
)
192 u_int id
, i
, n
= 0, *ids
= NULL
;
194 for (i
= 0; d
[i
] != NULL
; i
++) {
196 if (ruser_name((uid_t
)(d
[i
]->a
.uid
)) != NULL
)
197 continue; /* Already seen */
200 if (rgroup_name((gid_t
)(d
[i
]->a
.gid
)) != NULL
)
201 continue; /* Already seen */
204 if (has_id(id
, ids
, n
))
206 ids
= xrecallocarray(ids
, n
, n
+ 1, sizeof(*ids
));
214 get_remote_user_groups_from_dirents(struct sftp_conn
*conn
, SFTP_DIRENT
**d
)
216 u_int
*uids
= NULL
, nuids
= 0, *gids
= NULL
, ngids
= 0;
218 if (!sftp_can_get_users_groups_by_id(conn
))
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
);
229 ruser_name(uid_t uid
)
231 return idname_lookup(&user_idname
, (u_int
)uid
);
235 rgroup_name(uid_t gid
)
237 return idname_lookup(&group_idname
, (u_int
)gid
);