From 78d13d3adeb9015dee6f3f0bca5f3575b0007e63 Mon Sep 17 00:00:00 2001 From: djm Date: Mon, 19 Sep 2022 10:40:52 +0000 Subject: [PATCH] sftp-server(8): add a "users-groups-by-id@openssh.com" extension request that allows the client to obtain user/group names that correspond to a set of uids/gids. Will be used to make directory listings more useful and consistent in sftp(1). ok markus@ --- usr.bin/ssh/PROTOCOL | 43 ++++++++++++++++++++++++++- usr.bin/ssh/sftp-server.c | 62 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/usr.bin/ssh/PROTOCOL b/usr.bin/ssh/PROTOCOL index f27073d24dc..aa089e9a86a 100644 --- a/usr.bin/ssh/PROTOCOL +++ b/usr.bin/ssh/PROTOCOL @@ -635,6 +635,47 @@ This request is identical to the "home-directory" request documented in: https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-extensions-00#section-5 +4.12. sftp: Extension request "users-groups-by-id@openssh.com" + +This request asks the server to returns user and/or group names that +correspond to one or more IDs (e.g. as returned from a SSH_FXP_STAT +request). This may be used by the client to provide usernames in +directory listings. + + byte SSH_FXP_EXTENDED + uint32 id + string "users-groups-by-id@openssh.com" + string uids + string gids + +Where "uids" and "gids" consists of one or more integer user or group +identifiers: + + uint32 id-0 + ... + +The server will reply with a SSH_FXP_EXTENDED_REPLY: + + byte SSH_FXP_EXTENDED_REPLY + string usernames + string groupnames + +Where "username" and "groupnames" consists of names in identical request +order to "uids" and "gids" respectively: + + string name-0 + ... + +If a name cannot be identified for a given user or group ID, an empty +string will be returned in its place. + +It is acceptable for either "uids" or "gids" to be an empty set, in +which case the respective "usernames" or "groupnames" list will also +be empty. + +This extension is advertised in the SSH_FXP_VERSION hello with version +"1". + 5. Miscellaneous changes 5.1 Public key format @@ -671,4 +712,4 @@ master instance and later clients. OpenSSH extends the usual agent protocol. These changes are documented in the PROTOCOL.agent file. -$OpenBSD: PROTOCOL,v 1.46 2022/08/12 05:20:28 djm Exp $ +$OpenBSD: PROTOCOL,v 1.47 2022/09/19 10:40:52 djm Exp $ diff --git a/usr.bin/ssh/sftp-server.c b/usr.bin/ssh/sftp-server.c index 53a1fe3c03a..566a95ebddc 100644 --- a/usr.bin/ssh/sftp-server.c +++ b/usr.bin/ssh/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.142 2022/09/16 06:55:37 djm Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.143 2022/09/19 10:40:52 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,7 @@ static void process_extended_limits(u_int32_t id); static void process_extended_expand(u_int32_t id); static void process_extended_copy_data(u_int32_t id); static void process_extended_home_directory(u_int32_t id); +static void process_extended_get_users_groups_by_id(u_int32_t id); static void process_extended(u_int32_t id); struct sftp_handler { @@ -160,6 +162,8 @@ static const struct sftp_handler extended_handlers[] = { { "copy-data", "copy-data", 0, process_extended_copy_data, 1 }, { "home-directory", "home-directory", 0, process_extended_home_directory, 0 }, + { "users-groups-by-id", "users-groups-by-id@openssh.com", 0, + process_extended_get_users_groups_by_id, 0 }, { NULL, NULL, 0, NULL, 0 } }; @@ -718,6 +722,7 @@ process_init(void) compose_extension(msg, "expand-path@openssh.com", "1"); compose_extension(msg, "copy-data", "1"); compose_extension(msg, "home-directory", "1"); + compose_extension(msg, "users-groups-by-id@openssh.com", "1"); send_msg(msg); sshbuf_free(msg); @@ -1680,6 +1685,61 @@ process_extended_home_directory(u_int32_t id) free(username); } +static void +process_extended_get_users_groups_by_id(u_int32_t id) +{ + struct passwd *user_pw; + struct group *gr; + struct sshbuf *uids, *gids, *usernames, *groupnames, *msg; + int r; + u_int n, nusers = 0, ngroups = 0; + const char *name; + + if ((usernames = sshbuf_new()) == NULL || + (groupnames = sshbuf_new()) == NULL || + (msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_froms(iqueue, &uids)) != 0 || + (r = sshbuf_froms(iqueue, &gids)) != 0) + fatal_fr(r, "parse"); + debug_f("uids len = %zu, gids len = %zu", + sshbuf_len(uids), sshbuf_len(gids)); + while (sshbuf_len(uids) != 0) { + if ((r = sshbuf_get_u32(uids, &n)) != 0) + fatal_fr(r, "parse inner uid"); + user_pw = getpwuid((uid_t)n); + name = user_pw == NULL ? "" : user_pw->pw_name; + debug3_f("uid %u => \"%s\"", n, name); + if ((r = sshbuf_put_cstring(usernames, name)) != 0) + fatal_fr(r, "assemble gid reply"); + nusers++; + } + while (sshbuf_len(gids) != 0) { + if ((r = sshbuf_get_u32(gids, &n)) != 0) + fatal_fr(r, "parse inner gid"); + gr = getgrgid((gid_t)n); + name = gr == NULL ? "" : gr->gr_name; + debug3_f("gid %u => \"%s\"", n, name); + if ((r = sshbuf_put_cstring(groupnames, name)) != 0) + fatal_fr(r, "assemble gid reply"); + nusers++; + } + verbose("users-groups-by-id: %u users, %u groups", nusers, ngroups); + + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + (r = sshbuf_put_stringb(msg, usernames)) != 0 || + (r = sshbuf_put_stringb(msg, groupnames)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + + sshbuf_free(uids); + sshbuf_free(gids); + sshbuf_free(usernames); + sshbuf_free(groupnames); + sshbuf_free(msg); +} + static void process_extended(u_int32_t id) { -- 2.20.1