summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2019-01-16 23:23:45 +0000
committerDamien Miller <djm@mindrot.org>2019-01-17 11:08:13 +1100
commit60d8c84e0887514c99c9ce071965fafaa1c3d34a (patch)
tree771daf5a0f48b41115daf2b9b552e4e6d10dba7d
parentdbbc7e0eab7262f34b8e0cd6efecd1c77b905ed0 (diff)
upstream: Add "-h" flag to sftp chown/chgrp/chmod commands to
request they do not follow symlinks. Requires recently-committed lsetstat@openssh.com extension on the server side. ok markus@ dtucker@ OpenBSD-Commit-ID: f93bb3f6f7eb2fb7ef1e59126e72714f1626d604
-rw-r--r--sftp-client.c42
-rw-r--r--sftp-client.h5
-rw-r--r--sftp.131
-rw-r--r--sftp.c43
4 files changed, 103 insertions, 18 deletions
diff --git a/sftp-client.c b/sftp-client.c
index 4986d6d8d..d3f80e5a0 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.130 2018/07/31 03:07:24 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.131 2019/01/16 23:23:45 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -86,6 +86,7 @@ struct sftp_conn {
86#define SFTP_EXT_FSTATVFS 0x00000004 86#define SFTP_EXT_FSTATVFS 0x00000004
87#define SFTP_EXT_HARDLINK 0x00000008 87#define SFTP_EXT_HARDLINK 0x00000008
88#define SFTP_EXT_FSYNC 0x00000010 88#define SFTP_EXT_FSYNC 0x00000010
89#define SFTP_EXT_LSETSTAT 0x00000020
89 u_int exts; 90 u_int exts;
90 u_int64_t limit_kbps; 91 u_int64_t limit_kbps;
91 struct bwlimit bwlimit_in, bwlimit_out; 92 struct bwlimit bwlimit_in, bwlimit_out;
@@ -463,6 +464,10 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests,
463 strcmp((char *)value, "1") == 0) { 464 strcmp((char *)value, "1") == 0) {
464 ret->exts |= SFTP_EXT_FSYNC; 465 ret->exts |= SFTP_EXT_FSYNC;
465 known = 1; 466 known = 1;
467 } else if (strcmp(name, "lsetstat@openssh.com") == 0 &&
468 strcmp((char *)value, "1") == 0) {
469 ret->exts |= SFTP_EXT_LSETSTAT;
470 known = 1;
466 } 471 }
467 if (known) { 472 if (known) {
468 debug2("Server supports extension \"%s\" revision %s", 473 debug2("Server supports extension \"%s\" revision %s",
@@ -1096,7 +1101,6 @@ do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
1096 1101
1097 if ((msg = sshbuf_new()) == NULL) 1102 if ((msg = sshbuf_new()) == NULL)
1098 fatal("%s: sshbuf_new failed", __func__); 1103 fatal("%s: sshbuf_new failed", __func__);
1099 sshbuf_reset(msg);
1100 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1104 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1101 (r = sshbuf_put_u32(msg, id)) != 0 || 1105 (r = sshbuf_put_u32(msg, id)) != 0 ||
1102 (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 1106 (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
@@ -1125,7 +1129,6 @@ do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
1125 1129
1126 if ((msg = sshbuf_new()) == NULL) 1130 if ((msg = sshbuf_new()) == NULL)
1127 fatal("%s: sshbuf_new failed", __func__); 1131 fatal("%s: sshbuf_new failed", __func__);
1128 sshbuf_reset(msg);
1129 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || 1132 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1130 (r = sshbuf_put_u32(msg, id)) != 0 || 1133 (r = sshbuf_put_u32(msg, id)) != 0 ||
1131 (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 1134 (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
@@ -1138,6 +1141,38 @@ do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len,
1138} 1141}
1139#endif 1142#endif
1140 1143
1144int
1145do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a)
1146{
1147 struct sshbuf *msg;
1148 u_int status, id;
1149 int r;
1150
1151 if ((conn->exts & SFTP_EXT_LSETSTAT) == 0) {
1152 error("Server does not support lsetstat@openssh.com extension");
1153 return -1;
1154 }
1155
1156 id = conn->msg_id++;
1157 if ((msg = sshbuf_new()) == NULL)
1158 fatal("%s: sshbuf_new failed", __func__);
1159 if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 ||
1160 (r = sshbuf_put_u32(msg, id)) != 0 ||
1161 (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 ||
1162 (r = sshbuf_put_cstring(msg, path)) != 0 ||
1163 (r = encode_attrib(msg, a)) != 0)
1164 fatal("%s: buffer error: %s", __func__, ssh_err(r));
1165 send_msg(conn, msg);
1166 sshbuf_free(msg);
1167
1168 status = get_status(conn, id);
1169 if (status != SSH2_FX_OK)
1170 error("Couldn't setstat on \"%s\": %s", path,
1171 fx2txt(status));
1172
1173 return status == SSH2_FX_OK ? 0 : -1;
1174}
1175
1141static void 1176static void
1142send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, 1177send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1143 u_int len, const u_char *handle, u_int handle_len) 1178 u_int len, const u_char *handle, u_int handle_len)
@@ -1147,7 +1182,6 @@ send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset,
1147 1182
1148 if ((msg = sshbuf_new()) == NULL) 1183 if ((msg = sshbuf_new()) == NULL)
1149 fatal("%s: sshbuf_new failed", __func__); 1184 fatal("%s: sshbuf_new failed", __func__);
1150 sshbuf_reset(msg);
1151 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || 1185 if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 ||
1152 (r = sshbuf_put_u32(msg, id)) != 0 || 1186 (r = sshbuf_put_u32(msg, id)) != 0 ||
1153 (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || 1187 (r = sshbuf_put_string(msg, handle, handle_len)) != 0 ||
diff --git a/sftp-client.h b/sftp-client.h
index 14a3b8182..63a9b8b13 100644
--- a/sftp-client.h
+++ b/sftp-client.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.h,v 1.27 2015/05/08 06:45:13 djm Exp $ */ 1/* $OpenBSD: sftp-client.h,v 1.28 2019/01/16 23:23:45 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 4 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
@@ -91,6 +91,9 @@ int do_setstat(struct sftp_conn *, const char *, Attrib *);
91/* Set file attributes of open file 'handle' */ 91/* Set file attributes of open file 'handle' */
92int do_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *); 92int do_fsetstat(struct sftp_conn *, const u_char *, u_int, Attrib *);
93 93
94/* Set file attributes of 'path', not following symlinks */
95int do_lsetstat(struct sftp_conn *conn, const char *path, Attrib *a);
96
94/* Canonicalise 'path' - caller must free result */ 97/* Canonicalise 'path' - caller must free result */
95char *do_realpath(struct sftp_conn *, const char *); 98char *do_realpath(struct sftp_conn *, const char *);
96 99
diff --git a/sftp.1 b/sftp.1
index 7140bc19b..722a34419 100644
--- a/sftp.1
+++ b/sftp.1
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp.1,v 1.122 2018/11/16 02:30:20 djm Exp $ 1.\" $OpenBSD: sftp.1,v 1.123 2019/01/16 23:23:45 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2001 Damien Miller. All rights reserved. 3.\" Copyright (c) 2001 Damien Miller. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: November 16 2018 $ 25.Dd $Mdocdate: January 16 2019 $
26.Dt SFTP 1 26.Dt SFTP 1
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -316,31 +316,52 @@ Change remote directory to
316If 316If
317.Ar path 317.Ar path
318is not specified, then change directory to the one the session started in. 318is not specified, then change directory to the one the session started in.
319.It Ic chgrp Ar grp Ar path 319.It Xo Ic chgrp
320.Op Fl h
321.Ar grp
322.Ar path
323.Xc
320Change group of file 324Change group of file
321.Ar path 325.Ar path
322to 326to
323.Ar grp . 327.Ar grp .
328If the
329.Fl h
330flag is specified, then symlinks will not be followed.
324.Ar path 331.Ar path
325may contain 332may contain
326.Xr glob 7 333.Xr glob 7
327characters and may match multiple files. 334characters and may match multiple files.
328.Ar grp 335.Ar grp
329must be a numeric GID. 336must be a numeric GID.
330.It Ic chmod Ar mode Ar path 337.It Xo Ic chmod
338.Op Fl h
339.Ar mode
340.Ar path
341.Xc
331Change permissions of file 342Change permissions of file
332.Ar path 343.Ar path
333to 344to
334.Ar mode . 345.Ar mode .
346If the
347.Fl h
348flag is specified, then symlinks will not be followed.
335.Ar path 349.Ar path
336may contain 350may contain
337.Xr glob 7 351.Xr glob 7
338characters and may match multiple files. 352characters and may match multiple files.
339.It Ic chown Ar own Ar path 353.It Xo Ic chown
354.Op Fl h
355.Ar own
356.Ar path
357.Xc
340Change owner of file 358Change owner of file
341.Ar path 359.Ar path
342to 360to
343.Ar own . 361.Ar own .
362If the
363.Fl h
364flag is specified, then symlinks will not be followed.
344.Ar path 365.Ar path
345may contain 366may contain
346.Xr glob 7 367.Xr glob 7
diff --git a/sftp.c b/sftp.c
index f886b330b..0f3f89d33 100644
--- a/sftp.c
+++ b/sftp.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp.c,v 1.188 2018/11/16 03:26:01 djm Exp $ */ 1/* $OpenBSD: sftp.c,v 1.189 2019/01/16 23:23:45 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -278,9 +278,9 @@ help(void)
278 printf("Available commands:\n" 278 printf("Available commands:\n"
279 "bye Quit sftp\n" 279 "bye Quit sftp\n"
280 "cd path Change remote directory to 'path'\n" 280 "cd path Change remote directory to 'path'\n"
281 "chgrp grp path Change group of file 'path' to 'grp'\n" 281 "chgrp [-h] grp path Change group of file 'path' to 'grp'\n"
282 "chmod mode path Change permissions of file 'path' to 'mode'\n" 282 "chmod [-h] mode path Change permissions of file 'path' to 'mode'\n"
283 "chown own path Change owner of file 'path' to 'own'\n" 283 "chown [-h] own path Change owner of file 'path' to 'own'\n"
284 "df [-hi] [path] Display statistics for current directory or\n" 284 "df [-hi] [path] Display statistics for current directory or\n"
285 " filesystem containing 'path'\n" 285 " filesystem containing 'path'\n"
286 "exit Quit sftp\n" 286 "exit Quit sftp\n"
@@ -562,6 +562,30 @@ parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag)
562} 562}
563 563
564static int 564static int
565parse_ch_flags(const char *cmd, char **argv, int argc, int *hflag)
566{
567 extern int opterr, optind, optopt, optreset;
568 int ch;
569
570 optind = optreset = 1;
571 opterr = 0;
572
573 *hflag = 0;
574 while ((ch = getopt(argc, argv, "h")) != -1) {
575 switch (ch) {
576 case 'h':
577 *hflag = 1;
578 break;
579 default:
580 error("%s: Invalid flag -%c", cmd, optopt);
581 return -1;
582 }
583 }
584
585 return optind;
586}
587
588static int
565parse_no_flags(const char *cmd, char **argv, int argc) 589parse_no_flags(const char *cmd, char **argv, int argc)
566{ 590{
567 extern int opterr, optind, optopt, optreset; 591 extern int opterr, optind, optopt, optreset;
@@ -1456,7 +1480,7 @@ parse_args(const char **cpp, int *ignore_errors, int *disable_echo, int *aflag,
1456 /* FALLTHROUGH */ 1480 /* FALLTHROUGH */
1457 case I_CHOWN: 1481 case I_CHOWN:
1458 case I_CHGRP: 1482 case I_CHGRP:
1459 if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) 1483 if ((optidx = parse_ch_flags(cmd, argv, argc, hflag)) == -1)
1460 return -1; 1484 return -1;
1461 /* Get numeric arg (mandatory) */ 1485 /* Get numeric arg (mandatory) */
1462 if (argc - optidx < 1) 1486 if (argc - optidx < 1)
@@ -1675,7 +1699,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1675 if (!quiet) 1699 if (!quiet)
1676 mprintf("Changing mode on %s\n", 1700 mprintf("Changing mode on %s\n",
1677 g.gl_pathv[i]); 1701 g.gl_pathv[i]);
1678 err = do_setstat(conn, g.gl_pathv[i], &a); 1702 err = (hflag ? do_lsetstat : do_setstat)(conn,
1703 g.gl_pathv[i], &a);
1679 if (err != 0 && err_abort) 1704 if (err != 0 && err_abort)
1680 break; 1705 break;
1681 } 1706 }
@@ -1685,7 +1710,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1685 path1 = make_absolute(path1, *pwd); 1710 path1 = make_absolute(path1, *pwd);
1686 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); 1711 remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
1687 for (i = 0; g.gl_pathv[i] && !interrupted; i++) { 1712 for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
1688 if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { 1713 if (!(aa = (hflag ? do_lstat : do_stat)(conn,
1714 g.gl_pathv[i], 0))) {
1689 if (err_abort) { 1715 if (err_abort) {
1690 err = -1; 1716 err = -1;
1691 break; 1717 break;
@@ -1713,7 +1739,8 @@ parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd,
1713 g.gl_pathv[i]); 1739 g.gl_pathv[i]);
1714 aa->gid = n_arg; 1740 aa->gid = n_arg;
1715 } 1741 }
1716 err = do_setstat(conn, g.gl_pathv[i], aa); 1742 err = (hflag ? do_lsetstat : do_setstat)(conn,
1743 g.gl_pathv[i], aa);
1717 if (err != 0 && err_abort) 1744 if (err != 0 && err_abort)
1718 break; 1745 break;
1719 } 1746 }