sftp client library support for users-groups-by-id@openssh.com;
authordjm <djm@openbsd.org>
Mon, 19 Sep 2022 10:43:12 +0000 (10:43 +0000)
committerdjm <djm@openbsd.org>
Mon, 19 Sep 2022 10:43:12 +0000 (10:43 +0000)
ok markus@

usr.bin/ssh/sftp-client.c
usr.bin/ssh/sftp-client.h

index 2e792f1..a3e7a5d 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.c,v 1.164 2022/05/15 23:47:21 djm Exp $ */
+/* $OpenBSD: sftp-client.c,v 1.165 2022/09/19 10:43:12 djm Exp $ */
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
  *
@@ -75,15 +75,16 @@ struct sftp_conn {
        u_int num_requests;
        u_int version;
        u_int msg_id;
-#define SFTP_EXT_POSIX_RENAME  0x00000001
-#define SFTP_EXT_STATVFS       0x00000002
-#define SFTP_EXT_FSTATVFS      0x00000004
-#define SFTP_EXT_HARDLINK      0x00000008
-#define SFTP_EXT_FSYNC         0x00000010
-#define SFTP_EXT_LSETSTAT      0x00000020
-#define SFTP_EXT_LIMITS                0x00000040
-#define SFTP_EXT_PATH_EXPAND   0x00000080
-#define SFTP_EXT_COPY_DATA     0x00000100
+#define SFTP_EXT_POSIX_RENAME          0x00000001
+#define SFTP_EXT_STATVFS               0x00000002
+#define SFTP_EXT_FSTATVFS              0x00000004
+#define SFTP_EXT_HARDLINK              0x00000008
+#define SFTP_EXT_FSYNC                 0x00000010
+#define SFTP_EXT_LSETSTAT              0x00000020
+#define SFTP_EXT_LIMITS                        0x00000040
+#define SFTP_EXT_PATH_EXPAND           0x00000080
+#define SFTP_EXT_COPY_DATA             0x00000100
+#define SFTP_EXT_GETUSERSGROUPS_BY_ID  0x00000200
        u_int exts;
        u_int64_t limit_kbps;
        struct bwlimit bwlimit_in, bwlimit_out;
@@ -519,6 +520,11 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
                    strcmp((char *)value, "1") == 0) {
                        ret->exts |= SFTP_EXT_COPY_DATA;
                        known = 1;
+               } else if (strcmp(name,
+                   "users-groups-by-id@openssh.com") == 0 &&
+                   strcmp((char *)value, "1") == 0) {
+                       ret->exts |= SFTP_EXT_GETUSERSGROUPS_BY_ID;
+                       known = 1;
                }
                if (known) {
                        debug2("Server supports extension \"%s\" revision %s",
@@ -2727,6 +2733,120 @@ crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
        return ret;
 }
 
+int
+can_get_users_groups_by_id(struct sftp_conn *conn)
+{
+       return (conn->exts & SFTP_EXT_GETUSERSGROUPS_BY_ID) != 0;
+}
+
+int
+do_get_users_groups_by_id(struct sftp_conn *conn,
+    const u_int *uids, u_int nuids,
+    const u_int *gids, u_int ngids,
+    char ***usernamesp, char ***groupnamesp)
+{
+       struct sshbuf *msg, *uidbuf, *gidbuf;
+       u_int i, expected_id, id;
+       char *name, **usernames = NULL, **groupnames = NULL;
+       u_char type;
+       int r;
+
+       *usernamesp = *groupnamesp = NULL;
+       if (!can_get_users_groups_by_id(conn))
+               return SSH_ERR_FEATURE_UNSUPPORTED;
+
+       if ((msg = sshbuf_new()) == NULL ||
+           (uidbuf = sshbuf_new()) == NULL ||
+           (gidbuf = sshbuf_new()) == NULL)
+               fatal_f("sshbuf_new failed");
+       expected_id = id = conn->msg_id++;
+       debug2("Sending SSH2_FXP_EXTENDED(users-groups-by-id@openssh.com)");
+       for (i = 0; i < nuids; i++) {
+               if ((r = sshbuf_put_u32(uidbuf, uids[i])) != 0)
+                       fatal_fr(r, "compose uids");
+       }
+       for (i = 0; i < ngids; i++) {
+               if ((r = sshbuf_put_u32(gidbuf, gids[i])) != 0)
+                       fatal_fr(r, "compose gids");
+       }
+       if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
+           (r = sshbuf_put_u32(msg, id)) != 0 ||
+           (r = sshbuf_put_cstring(msg,
+           "users-groups-by-id@openssh.com")) != 0 ||
+           (r = sshbuf_put_stringb(msg, uidbuf)) != 0 ||
+           (r = sshbuf_put_stringb(msg, gidbuf)) != 0)
+               fatal_fr(r, "compose");
+       send_msg(conn, msg);
+       get_msg(conn, msg);
+       if ((r = sshbuf_get_u8(msg, &type)) != 0 ||
+           (r = sshbuf_get_u32(msg, &id)) != 0)
+               fatal_fr(r, "parse");
+       if (id != expected_id)
+               fatal("ID mismatch (%u != %u)", id, expected_id);
+       if (type == SSH2_FXP_STATUS) {
+               u_int status;
+               char *errmsg;
+
+               if ((r = sshbuf_get_u32(msg, &status)) != 0 ||
+                   (r = sshbuf_get_cstring(msg, &errmsg, NULL)) != 0)
+                       fatal_fr(r, "parse status");
+               error("users-groups-by-id %s",
+                   *errmsg == '\0' ? fx2txt(status) : errmsg);
+               free(errmsg);
+               sshbuf_free(msg);
+               sshbuf_free(uidbuf);
+               sshbuf_free(gidbuf);
+               return -1;
+       } else if (type != SSH2_FXP_EXTENDED_REPLY)
+               fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
+                   SSH2_FXP_EXTENDED_REPLY, type);
+
+       /* reuse */
+       sshbuf_free(uidbuf);
+       sshbuf_free(gidbuf);
+       uidbuf = gidbuf = NULL;
+       if ((r = sshbuf_froms(msg, &uidbuf)) != 0 ||
+           (r = sshbuf_froms(msg, &gidbuf)) != 0)
+               fatal_fr(r, "parse response");
+       if (nuids > 0) {
+               usernames = xcalloc(nuids, sizeof(*usernames));
+               for (i = 0; i < nuids; i++) {
+                       if ((r = sshbuf_get_cstring(uidbuf, &name, NULL)) != 0)
+                               fatal_fr(r, "parse user name");
+                       /* Handle unresolved names */
+                       if (*name == '\0') {
+                               free(name);
+                               name = NULL;
+                       }
+                       usernames[i] = name;
+               }
+       }
+       if (ngids > 0) {
+               groupnames = xcalloc(ngids, sizeof(*groupnames));
+               for (i = 0; i < ngids; i++) {
+                       if ((r = sshbuf_get_cstring(gidbuf, &name, NULL)) != 0)
+                               fatal_fr(r, "parse user name");
+                       /* Handle unresolved names */
+                       if (*name == '\0') {
+                               free(name);
+                               name = NULL;
+                       }
+                       groupnames[i] = name;
+               }
+       }
+       if (sshbuf_len(uidbuf) != 0)
+               fatal_f("unexpected extra username data");
+       if (sshbuf_len(gidbuf) != 0)
+               fatal_f("unexpected extra groupname data");
+       sshbuf_free(uidbuf);
+       sshbuf_free(gidbuf);
+       sshbuf_free(msg);
+       /* success */
+       *usernamesp = usernames;
+       *groupnamesp = groupnames;
+       return 0;
+}
+
 char *
 path_append(const char *p1, const char *p2)
 {
index d26cb71..2403b11 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-client.h,v 1.37 2022/05/13 06:31:50 djm Exp $ */
+/* $OpenBSD: sftp-client.h,v 1.38 2022/09/19 10:43:12 djm Exp $ */
 
 /*
  * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -177,6 +177,15 @@ int crossload_dir(struct sftp_conn *from, struct sftp_conn *to,
     Attrib *dirattrib, int preserve_flag, int print_flag,
     int follow_link_flag);
 
+/*
+ * User/group ID to name translation.
+ */
+int can_get_users_groups_by_id(struct sftp_conn *conn);
+int do_get_users_groups_by_id(struct sftp_conn *conn,
+    const u_int *uids, u_int nuids,
+    const u_int *gids, u_int ngids,
+    char ***usernamesp, char ***groupnamesp);
+
 /* Concatenate paths, taking care of slashes. Caller must free result. */
 char *path_append(const char *, const char *);