summaryrefslogtreecommitdiff
path: root/sftp-client.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
committerColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
commit137d76ba65883aa8143af1fcad83b57e7badef0c (patch)
treef426e804bb5248ceafedfab7bb78ae6e6752942c /sftp-client.c
parentdac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff)
parentef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff)
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1: - 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9): + Added chroot(2) support for sshd(8), controlled by a new option "ChrootDirectory" (closes: #139047, LP: #24777). + Linked sftp-server(8) into sshd(8). The internal sftp server is used when the command "internal-sftp" is specified in a Subsystem or ForceCommand declaration. When used with ChrootDirectory, the internal sftp server requires no special configuration of files inside the chroot environment. + Added a protocol extension method "posix-rename@openssh.com" for sftp-server(8) to perform POSIX atomic rename() operations; sftp(1) prefers this if available (closes: #308561). + Removed the fixed limit of 100 file handles in sftp-server(8). + ssh(8) will now skip generation of SSH protocol 1 ephemeral server keys when in inetd mode and protocol 2 connections are negotiated. This speeds up protocol 2 connections to inetd-mode servers that also allow Protocol 1. + Accept the PermitRootLogin directive in a sshd_config(5) Match block. Allows for, e.g. permitting root only from the local network. + Reworked sftp(1) argument splitting and escaping to be more internally consistent (i.e. between sftp commands) and more consistent with sh(1). Please note that this will change the interpretation of some quoted strings, especially those with embedded backslash escape sequences. + Support "Banner=none" in sshd_config(5) to disable sending of a pre-login banner (e.g. in a Match block). + ssh(1) ProxyCommands are now executed with $SHELL rather than /bin/sh. + ssh(1)'s ConnectTimeout option is now applied to both the TCP connection and the SSH banner exchange (previously it just covered the TCP connection). This allows callers of ssh(1) to better detect and deal with stuck servers that accept a TCP connection but don't progress the protocol, and also makes ConnectTimeout useful for connections via a ProxyCommand. + scp(1) incorrectly reported "stalled" on slow copies (closes: #140828). + scp(1) date underflow for timestamps before epoch. + ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS, instead of the current standard RRSIG. + Correctly drain ACKs when a sftp(1) upload write fails midway, avoids a fatal() exit from what should be a recoverable condition. + Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF hostname") to not include any IP address in the data to be hashed. + Make ssh(1) skip listening on the IPv6 wildcard address when a binding address of 0.0.0.0 is used against an old SSH server that does not support the RFC4254 syntax for wildcard bind addresses. + Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is already done for X11/TCP forwarding sockets (closes: #439661). + Fix FD leak that could hang a ssh(1) connection multiplexing master. + Make ssh(1) -q option documentation consistent with reality. + Fixed sshd(8) PAM support not calling pam_session_close(), or failing to call it with root privileges (closes: #372680). + Fix activation of OpenSSL engine support when requested in configure (LP: #119295). - 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1): + Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1) and ssh-keygen(1). Visual fingerprint display is controlled by a new ssh_config(5) option "VisualHostKey". The intent is to render SSH host keys in a visual form that is amenable to easy recall and rejection of changed host keys. + sshd_config(5) now supports CIDR address/masklen matching in "Match address" blocks, with a fallback to classic wildcard matching. + sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys from="..." restrictions, also with a fallback to classic wildcard matching. + Added an extended test mode (-T) to sshd(8) to request that it write its effective configuration to stdout and exit. Extended test mode also supports the specification of connection parameters (username, source address and hostname) to test the application of sshd_config(5) Match rules. + ssh(1) now prints the number of bytes transferred and the overall connection throughput for SSH protocol 2 sessions when in verbose mode (previously these statistics were displayed for protocol 1 connections only). + sftp-server(8) now supports extension methods statvfs@openssh.com and fstatvfs@openssh.com that implement statvfs(2)-like operations. + sftp(1) now has a "df" command to the sftp client that uses the statvfs@openssh.com to produce a df(1)-like display of filesystem space and inode utilisation (requires statvfs@openssh.com support on the server). + Added a MaxSessions option to sshd_config(5) to allow control of the number of multiplexed sessions supported over a single TCP connection. This allows increasing the number of allowed sessions above the previous default of 10, disabling connection multiplexing (MaxSessions=1) or disallowing login/shell/subsystem sessions entirely (MaxSessions=0). + Added a no-more-sessions@openssh.com global request extension that is sent from ssh(1) to sshd(8) when the client knows that it will never request another session (i.e. when session multiplexing is disabled). This allows a server to disallow further session requests and terminate the session in cases where the client has been hijacked. + ssh-keygen(1) now supports the use of the -l option in combination with -F to search for a host in ~/.ssh/known_hosts and display its fingerprint. + ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of "rsa1". + Added an AllowAgentForwarding option to sshd_config(8) to control whether authentication agent forwarding is permitted. Note that this is a loose control, as a client may install their own unofficial forwarder. + ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when receiving network data, resulting in a ~10% speedup. + ssh(1) and sshd(8) will now try additional addresses when connecting to a port forward destination whose DNS name resolves to more than one address. The previous behaviour was to try the only first address and give up if that failed. + ssh(1) and sshd(8) now support signalling that channels are half-closed for writing, through a channel protocol extension notification "eow@openssh.com". This allows propagation of closed file descriptors, so that commands such as "ssh -2 localhost od /bin/ls | true" do not send unnecessary data over the wire. + sshd(8): increased the default size of ssh protocol 1 ephemeral keys from 768 to 1024 bits. + When ssh(1) has been requested to fork after authentication ("ssh -f") with ExitOnForwardFailure enabled, delay the fork until after replies for any -R forwards have been seen. Allows for robust detection of -R forward failure when using -f. + "Match group" blocks in sshd_config(5) now support negation of groups. E.g. "Match group staff,!guests". + sftp(1) and sftp-server(8) now allow chmod-like operations to set set[ug]id/sticky bits. + The MaxAuthTries option is now permitted in sshd_config(5) match blocks. + Multiplexed ssh(1) sessions now support a subset of the ~ escapes that are available to a primary connection. + ssh(1) connection multiplexing will now fall back to creating a new connection in most error cases (closes: #352830). + Make ssh(1) deal more gracefully with channel requests that fail. Previously it would optimistically assume that requests would always succeed, which could cause hangs if they did not (e.g. when the server runs out of file descriptors). + ssh(1) now reports multiplexing errors via the multiplex slave's stderr where possible (subject to LogLevel in the mux master). + Prevent sshd(8) from erroneously applying public key restrictions leaned from ~/.ssh/authorized_keys to other authentication methods when public key authentication subsequently fails (LP: #161047). + Fixed an UMAC alignment problem that manifested on Itanium platforms.
Diffstat (limited to 'sftp-client.c')
-rw-r--r--sftp-client.c214
1 files changed, 177 insertions, 37 deletions
diff --git a/sftp-client.c b/sftp-client.c
index 2746f3245..5e39aa7d2 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.76 2007/01/22 11:32:50 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.86 2008/06/26 06:10:09 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 *
@@ -24,6 +24,9 @@
24 24
25#include <sys/types.h> 25#include <sys/types.h>
26#include <sys/param.h> 26#include <sys/param.h>
27#ifdef HAVE_SYS_STATVFS_H
28#include <sys/statvfs.h>
29#endif
27#include "openbsd-compat/sys-queue.h" 30#include "openbsd-compat/sys-queue.h"
28#ifdef HAVE_SYS_STAT_H 31#ifdef HAVE_SYS_STAT_H
29# include <sys/stat.h> 32# include <sys/stat.h>
@@ -65,6 +68,10 @@ struct sftp_conn {
65 u_int num_requests; 68 u_int num_requests;
66 u_int version; 69 u_int version;
67 u_int msg_id; 70 u_int msg_id;
71#define SFTP_EXT_POSIX_RENAME 0x00000001
72#define SFTP_EXT_STATVFS 0x00000002
73#define SFTP_EXT_FSTATVFS 0x00000004
74 u_int exts;
68}; 75};
69 76
70static void 77static void
@@ -236,10 +243,61 @@ get_decode_stat(int fd, u_int expected_id, int quiet)
236 return(a); 243 return(a);
237} 244}
238 245
246static int
247get_decode_statvfs(int fd, struct sftp_statvfs *st, u_int expected_id,
248 int quiet)
249{
250 Buffer msg;
251 u_int type, id, flag;
252
253 buffer_init(&msg);
254 get_msg(fd, &msg);
255
256 type = buffer_get_char(&msg);
257 id = buffer_get_int(&msg);
258
259 debug3("Received statvfs reply T:%u I:%u", type, id);
260 if (id != expected_id)
261 fatal("ID mismatch (%u != %u)", id, expected_id);
262 if (type == SSH2_FXP_STATUS) {
263 int status = buffer_get_int(&msg);
264
265 if (quiet)
266 debug("Couldn't statvfs: %s", fx2txt(status));
267 else
268 error("Couldn't statvfs: %s", fx2txt(status));
269 buffer_free(&msg);
270 return -1;
271 } else if (type != SSH2_FXP_EXTENDED_REPLY) {
272 fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u",
273 SSH2_FXP_EXTENDED_REPLY, type);
274 }
275
276 bzero(st, sizeof(*st));
277 st->f_bsize = buffer_get_int64(&msg);
278 st->f_frsize = buffer_get_int64(&msg);
279 st->f_blocks = buffer_get_int64(&msg);
280 st->f_bfree = buffer_get_int64(&msg);
281 st->f_bavail = buffer_get_int64(&msg);
282 st->f_files = buffer_get_int64(&msg);
283 st->f_ffree = buffer_get_int64(&msg);
284 st->f_favail = buffer_get_int64(&msg);
285 st->f_fsid = buffer_get_int64(&msg);
286 flag = buffer_get_int64(&msg);
287 st->f_namemax = buffer_get_int64(&msg);
288
289 st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0;
290 st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0;
291
292 buffer_free(&msg);
293
294 return 0;
295}
296
239struct sftp_conn * 297struct sftp_conn *
240do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests) 298do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
241{ 299{
242 u_int type; 300 u_int type, exts = 0;
243 int version; 301 int version;
244 Buffer msg; 302 Buffer msg;
245 struct sftp_conn *ret; 303 struct sftp_conn *ret;
@@ -268,8 +326,27 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
268 while (buffer_len(&msg) > 0) { 326 while (buffer_len(&msg) > 0) {
269 char *name = buffer_get_string(&msg, NULL); 327 char *name = buffer_get_string(&msg, NULL);
270 char *value = buffer_get_string(&msg, NULL); 328 char *value = buffer_get_string(&msg, NULL);
271 329 int known = 0;
272 debug2("Init extension: \"%s\"", name); 330
331 if (strcmp(name, "posix-rename@openssh.com") == 0 &&
332 strcmp(value, "1") == 0) {
333 exts |= SFTP_EXT_POSIX_RENAME;
334 known = 1;
335 } else if (strcmp(name, "statvfs@openssh.com") == 0 &&
336 strcmp(value, "2") == 0) {
337 exts |= SFTP_EXT_STATVFS;
338 known = 1;
339 } if (strcmp(name, "fstatvfs@openssh.com") == 0 &&
340 strcmp(value, "2") == 0) {
341 exts |= SFTP_EXT_FSTATVFS;
342 known = 1;
343 }
344 if (known) {
345 debug2("Server supports extension \"%s\" revision %s",
346 name, value);
347 } else {
348 debug2("Unrecognised server extension \"%s\"", name);
349 }
273 xfree(name); 350 xfree(name);
274 xfree(value); 351 xfree(value);
275 } 352 }
@@ -283,6 +360,7 @@ do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests)
283 ret->num_requests = num_requests; 360 ret->num_requests = num_requests;
284 ret->version = version; 361 ret->version = version;
285 ret->msg_id = 1; 362 ret->msg_id = 1;
363 ret->exts = exts;
286 364
287 /* Some filexfer v.0 servers don't support large packets */ 365 /* Some filexfer v.0 servers don't support large packets */
288 if (version == 0) 366 if (version == 0)
@@ -534,6 +612,7 @@ do_lstat(struct sftp_conn *conn, char *path, int quiet)
534 return(get_decode_stat(conn->fd_in, id, quiet)); 612 return(get_decode_stat(conn->fd_in, id, quiet));
535} 613}
536 614
615#ifdef notyet
537Attrib * 616Attrib *
538do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet) 617do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
539{ 618{
@@ -545,6 +624,7 @@ do_fstat(struct sftp_conn *conn, char *handle, u_int handle_len, int quiet)
545 624
546 return(get_decode_stat(conn->fd_in, id, quiet)); 625 return(get_decode_stat(conn->fd_in, id, quiet));
547} 626}
627#endif
548 628
549int 629int
550do_setstat(struct sftp_conn *conn, char *path, Attrib *a) 630do_setstat(struct sftp_conn *conn, char *path, Attrib *a)
@@ -637,13 +717,20 @@ do_rename(struct sftp_conn *conn, char *oldpath, char *newpath)
637 717
638 /* Send rename request */ 718 /* Send rename request */
639 id = conn->msg_id++; 719 id = conn->msg_id++;
640 buffer_put_char(&msg, SSH2_FXP_RENAME); 720 if ((conn->exts & SFTP_EXT_POSIX_RENAME)) {
641 buffer_put_int(&msg, id); 721 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
722 buffer_put_int(&msg, id);
723 buffer_put_cstring(&msg, "posix-rename@openssh.com");
724 } else {
725 buffer_put_char(&msg, SSH2_FXP_RENAME);
726 buffer_put_int(&msg, id);
727 }
642 buffer_put_cstring(&msg, oldpath); 728 buffer_put_cstring(&msg, oldpath);
643 buffer_put_cstring(&msg, newpath); 729 buffer_put_cstring(&msg, newpath);
644 send_msg(conn->fd_out, &msg); 730 send_msg(conn->fd_out, &msg);
645 debug3("Sent message SSH2_FXP_RENAME \"%s\" -> \"%s\"", oldpath, 731 debug3("Sent message %s \"%s\" -> \"%s\"",
646 newpath); 732 (conn->exts & SFTP_EXT_POSIX_RENAME) ? "posix-rename@openssh.com" :
733 "SSH2_FXP_RENAME", oldpath, newpath);
647 buffer_free(&msg); 734 buffer_free(&msg);
648 735
649 status = get_status(conn->fd_in, id); 736 status = get_status(conn->fd_in, id);
@@ -686,6 +773,7 @@ do_symlink(struct sftp_conn *conn, char *oldpath, char *newpath)
686 return(status); 773 return(status);
687} 774}
688 775
776#ifdef notyet
689char * 777char *
690do_readlink(struct sftp_conn *conn, char *path) 778do_readlink(struct sftp_conn *conn, char *path)
691{ 779{
@@ -732,6 +820,61 @@ do_readlink(struct sftp_conn *conn, char *path)
732 820
733 return(filename); 821 return(filename);
734} 822}
823#endif
824
825int
826do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st,
827 int quiet)
828{
829 Buffer msg;
830 u_int id;
831
832 if ((conn->exts & SFTP_EXT_STATVFS) == 0) {
833 error("Server does not support statvfs@openssh.com extension");
834 return -1;
835 }
836
837 id = conn->msg_id++;
838
839 buffer_init(&msg);
840 buffer_clear(&msg);
841 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
842 buffer_put_int(&msg, id);
843 buffer_put_cstring(&msg, "statvfs@openssh.com");
844 buffer_put_cstring(&msg, path);
845 send_msg(conn->fd_out, &msg);
846 buffer_free(&msg);
847
848 return get_decode_statvfs(conn->fd_in, st, id, quiet);
849}
850
851#ifdef notyet
852int
853do_fstatvfs(struct sftp_conn *conn, const char *handle, u_int handle_len,
854 struct sftp_statvfs *st, int quiet)
855{
856 Buffer msg;
857 u_int id;
858
859 if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) {
860 error("Server does not support fstatvfs@openssh.com extension");
861 return -1;
862 }
863
864 id = conn->msg_id++;
865
866 buffer_init(&msg);
867 buffer_clear(&msg);
868 buffer_put_char(&msg, SSH2_FXP_EXTENDED);
869 buffer_put_int(&msg, id);
870 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
871 buffer_put_string(&msg, handle, handle_len);
872 send_msg(conn->fd_out, &msg);
873 buffer_free(&msg);
874
875 return get_decode_statvfs(conn->fd_in, st, id, quiet);
876}
877#endif
735 878
736static void 879static void
737send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len, 880send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
@@ -777,7 +920,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
777 if (a == NULL) 920 if (a == NULL)
778 return(-1); 921 return(-1);
779 922
780 /* XXX: should we preserve set[ug]id? */ 923 /* Do not preserve set[ug]id here, as we do not preserve ownership */
781 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 924 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
782 mode = a->perm & 0777; 925 mode = a->perm & 0777;
783 else 926 else
@@ -819,6 +962,7 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
819 if (local_fd == -1) { 962 if (local_fd == -1) {
820 error("Couldn't open local file \"%s\" for writing: %s", 963 error("Couldn't open local file \"%s\" for writing: %s",
821 local_path, strerror(errno)); 964 local_path, strerror(errno));
965 do_close(conn, handle, handle_len);
822 buffer_free(&msg); 966 buffer_free(&msg);
823 xfree(handle); 967 xfree(handle);
824 return(-1); 968 return(-1);
@@ -992,9 +1136,10 @@ int
992do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1136do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
993 int pflag) 1137 int pflag)
994{ 1138{
995 int local_fd, status; 1139 int local_fd;
1140 int status = SSH2_FX_OK;
996 u_int handle_len, id, type; 1141 u_int handle_len, id, type;
997 u_int64_t offset; 1142 off_t offset;
998 char *handle, *data; 1143 char *handle, *data;
999 Buffer msg; 1144 Buffer msg;
1000 struct stat sb; 1145 struct stat sb;
@@ -1004,7 +1149,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1004 struct outstanding_ack { 1149 struct outstanding_ack {
1005 u_int id; 1150 u_int id;
1006 u_int len; 1151 u_int len;
1007 u_int64_t offset; 1152 off_t offset;
1008 TAILQ_ENTRY(outstanding_ack) tq; 1153 TAILQ_ENTRY(outstanding_ack) tq;
1009 }; 1154 };
1010 TAILQ_HEAD(ackhead, outstanding_ack) acks; 1155 TAILQ_HEAD(ackhead, outstanding_ack) acks;
@@ -1054,7 +1199,7 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1054 if (handle == NULL) { 1199 if (handle == NULL) {
1055 close(local_fd); 1200 close(local_fd);
1056 buffer_free(&msg); 1201 buffer_free(&msg);
1057 return(-1); 1202 return -1;
1058 } 1203 }
1059 1204
1060 startid = ackid = id + 1; 1205 startid = ackid = id + 1;
@@ -1074,11 +1219,12 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1074 * Simulate an EOF on interrupt, allowing ACKs from the 1219 * Simulate an EOF on interrupt, allowing ACKs from the
1075 * server to drain. 1220 * server to drain.
1076 */ 1221 */
1077 if (interrupted) 1222 if (interrupted || status != SSH2_FX_OK)
1078 len = 0; 1223 len = 0;
1079 else do 1224 else do
1080 len = read(local_fd, data, conn->transfer_buflen); 1225 len = read(local_fd, data, conn->transfer_buflen);
1081 while ((len == -1) && (errno == EINTR || errno == EAGAIN)); 1226 while ((len == -1) &&
1227 (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK));
1082 1228
1083 if (len == -1) 1229 if (len == -1)
1084 fatal("Couldn't read from \"%s\": %s", local_path, 1230 fatal("Couldn't read from \"%s\": %s", local_path,
@@ -1130,46 +1276,40 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1130 if (ack == NULL) 1276 if (ack == NULL)
1131 fatal("Can't find request for ID %u", r_id); 1277 fatal("Can't find request for ID %u", r_id);
1132 TAILQ_REMOVE(&acks, ack, tq); 1278 TAILQ_REMOVE(&acks, ack, tq);
1133 1279 debug3("In write loop, ack for %u %u bytes at %lld",
1134 if (status != SSH2_FX_OK) { 1280 ack->id, ack->len, (long long)ack->offset);
1135 error("Couldn't write to remote file \"%s\": %s",
1136 remote_path, fx2txt(status));
1137 if (showprogress)
1138 stop_progress_meter();
1139 do_close(conn, handle, handle_len);
1140 close(local_fd);
1141 xfree(data);
1142 xfree(ack);
1143 status = -1;
1144 goto done;
1145 }
1146 debug3("In write loop, ack for %u %u bytes at %llu",
1147 ack->id, ack->len, (unsigned long long)ack->offset);
1148 ++ackid; 1281 ++ackid;
1149 xfree(ack); 1282 xfree(ack);
1150 } 1283 }
1151 offset += len; 1284 offset += len;
1285 if (offset < 0)
1286 fatal("%s: offset < 0", __func__);
1152 } 1287 }
1288 buffer_free(&msg);
1289
1153 if (showprogress) 1290 if (showprogress)
1154 stop_progress_meter(); 1291 stop_progress_meter();
1155 xfree(data); 1292 xfree(data);
1156 1293
1294 if (status != SSH2_FX_OK) {
1295 error("Couldn't write to remote file \"%s\": %s",
1296 remote_path, fx2txt(status));
1297 status = -1;
1298 }
1299
1157 if (close(local_fd) == -1) { 1300 if (close(local_fd) == -1) {
1158 error("Couldn't close local file \"%s\": %s", local_path, 1301 error("Couldn't close local file \"%s\": %s", local_path,
1159 strerror(errno)); 1302 strerror(errno));
1160 do_close(conn, handle, handle_len);
1161 status = -1; 1303 status = -1;
1162 goto done;
1163 } 1304 }
1164 1305
1165 /* Override umask and utimes if asked */ 1306 /* Override umask and utimes if asked */
1166 if (pflag) 1307 if (pflag)
1167 do_fsetstat(conn, handle, handle_len, &a); 1308 do_fsetstat(conn, handle, handle_len, &a);
1168 1309
1169 status = do_close(conn, handle, handle_len); 1310 if (do_close(conn, handle, handle_len) != SSH2_FX_OK)
1170 1311 status = -1;
1171done:
1172 xfree(handle); 1312 xfree(handle);
1173 buffer_free(&msg); 1313
1174 return(status); 1314 return status;
1175} 1315}