Sftpfilecontrol Patch v1.3
A patch to provide control over umask, chmod, chown, and chgrp in the sftp-server that comes with openssh.
This patch is derived from the sftplogging patch.

Original patch by Michael Martinez <sftpfilecontrol@gmail.com>
Copyright (c) 2002 - 2010, Michael Martinez
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
following conditions are met:

- Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- Neither the name of Michael Martinez nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Patch source using: patch -p0 < /path/to/patch
================================================================================
Only in openssh-5.4p1-sftp_mods/: Makefile
Common subdirectories: openssh-5.4p1/autom4te-2.53.cache and openssh-5.4p1-sftp_mods/autom4te-2.53.cache
Common subdirectories: openssh-5.4p1/contrib and openssh-5.4p1-sftp_mods/contrib
Common subdirectories: openssh-5.4p1/openbsd-compat and openssh-5.4p1-sftp_mods/openbsd-compat
Common subdirectories: openssh-5.4p1/regress and openssh-5.4p1-sftp_mods/regress
Common subdirectories: openssh-5.4p1/scard and openssh-5.4p1-sftp_mods/scard
diff -u openssh-5.4p1/version.h openssh-5.4p1-sftp_mods/version.h
--- openssh-5.4p1/version.h  Mon Feb 23 17:24:15 2004
+++ openssh-5.4p1-sftp_mods/version.h	Tues Apr  5 09:43:35 2005
@@ -5,2 +5,2 @@
-#define SSH_PORTABLE	"p1"
+#define SSH_PORTABLE	"p1+sftpfilecontrol-v1.3"
 #define SSH_RELEASE	SSH_VERSION SSH_PORTABLE
diff -u openssh-5.4p1/servconf.c openssh-5.4p1-sftp_mods/servconf.c
--- openssh-5.4p1/servconf.c	Thu Sep  5 00:35:15 2002
+++ openssh-5.4p1-sftp_mods/servconf.c	Wed Jan 29 09:43:35 2003
@@ -119,4 +119,10 @@
 	options->authorized_keys_file = NULL;
 	options->authorized_keys_file2 = NULL;
+
+ 	memset(options->sftp_umask, 0, SFTP_UMASK_LENGTH);
+
+	options->sftp_permit_chmod = SFTP_PERMIT_NOT_SET;
+	options->sftp_permit_chown = SFTP_PERMIT_NOT_SET;
+
 	options->num_accept_env = 0;
 	options->permit_tun = -1;
@@ -108,6 +108,6 @@
 void
 fill_default_server_options(ServerOptions *options)
 {
-	/* Portable-specific options */
+/* Portable-specific options */
	if (options->use_pam == -1)
		options->use_pam = 1;
@@ -225,6 +225,16 @@
 	if (options->authorized_keys_file == NULL)
 		options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
 
+	/* Don't set sftp-server umask */
+	if (!options->sftp_umask)
+		memset(options->sftp_umask, 0, SFTP_UMASK_LENGTH);
+
+	/* allow sftp client to issue chmod, chown / chgrp commands */
+	if (options->sftp_permit_chmod == SFTP_PERMIT_NOT_SET)
+		options->sftp_permit_chmod = SFTP_PERMIT_YES;
+	if (options->sftp_permit_chown == SFTP_PERMIT_NOT_SET)
+		options->sftp_permit_chown = SFTP_PERMIT_YES;
+
 	/* Turn privilege separation on by default */
 	if (use_privsep == -1)
 		use_privsep = 1;
@@ -310,4 +310,6 @@
	sMatch, sPermitOpen, sForceCommand, sChrootDirectory,
	sUsePrivilegeSeparation, sAllowAgentForwarding,
+	sSftpUmask,
+	sSftpPermitChown, sSftpPermitChmod,
	sZeroKnowledgePasswordAuthentication, sHostCertificate,
	sRevokedKeys, sTrustedUserCAKeys,
@@ -431,3 +431,6 @@
 	{ "acceptenv", sAcceptEnv, SSHCFG_GLOBAL },
+	{ "sftpumask", sSftpUmask},
+	{ "sftppermitchmod", sSftpPermitChmod},
+	{ "sftppermitchown", sSftpPermitChown},
 	{ "permittunnel", sPermitTunnel, SSHCFG_GLOBAL },
 	{ "match", sMatch, SSHCFG_ALL },
@@ -640,8 +640,10 @@
 	char *cp, **charptr, *arg, *p;
 	int cmdline = 0, *intptr, value, n;
	SyslogFacility *log_facility_ptr;
	LogLevel *log_level_ptr;
+	unsigned int umaskvalue = 0;
+	char *umaskptr;
	ServerOpCodes opcode;
	int port;
	u_int i, flags = 0;
	size_t len;
@@ -1149,6 +1149,32 @@
 	case sBanner:
 		charptr = &options->banner;
 		goto parse_filename;

+
+        case sSftpUmask:
+                arg = strdelim(&cp);
+		umaskptr = arg;
+                while (*arg && *arg >= '0' && *arg <= '9')
+                    umaskvalue = umaskvalue * 8 + *arg++ - '0';
+                if (*arg || umaskvalue > 0777)
+                    fatal("%s line %d: bad value for umask",
+			    filename, linenum);
+		else {
+			while (*umaskptr && *umaskptr == '0')
+					*umaskptr++;
+			strncpy(options->sftp_umask, umaskptr,
+				SFTP_UMASK_LENGTH);
+		}
+
+                break;
+
+        case sSftpPermitChmod:
+                intptr = &options->sftp_permit_chmod;
+                goto parse_flag;
+
+        case sSftpPermitChown:
+                intptr = &options->sftp_permit_chown;
+                goto parse_flag;
+
 	/*
 	 * These options can contain %X options expanded at
@@ -1290,6 +1290,7 @@
 	if ((arg = strdelim(&cp)) != NULL && *arg != '\0')
 		fatal("%s line %d: garbage at end of line; \"%.200s\".",
 		    filename, linenum, arg);
+
 	return 0;
 }
 
diff -u openssh-5.4p1/servconf.h openssh-5.4p1-sftp_mods/servconf.h
--- openssh-5.4p1/servconf.h	Wed Jul 31 21:28:39 2002
+++ openssh-5.4p1-sftp_mods/servconf.h	Wed Jan 29 09:41:06 2003
@@ -35,4 +35,11 @@
 #define PERMIT_NO_PASSWD	2
 #define PERMIT_YES		3
 
+/* sftp-server umask control */
+#define SFTP_UMASK_LENGTH	5
+
+/* sftp-server client priviledge */
+#define SFTP_PERMIT_NOT_SET	-1
+#define SFTP_PERMIT_NO		0
+#define SFTP_PERMIT_YES		1
 #define DEFAULT_AUTH_FAIL_MAX	6	/* Default for MaxAuthTries */
@@ -145,2 +145,5 @@
	int	use_pam;		/* Enable auth via PAM */
+	char	sftp_umask[SFTP_UMASK_LENGTH];		/* Sftp Umask */
+	int	sftp_permit_chmod;
+	int	sftp_permit_chown;
	int	permit_tun;
diff -u openssh-5.4p1/session.c openssh-5.4p1-sftp_mods/session.c
--- openssh-5.4p1/session.c	Wed Sep 25 20:38:50 2002
+++ openssh-5.4p1-sftp_mods/session.c	Wed Jan 29 09:44:18 2003
@@ -111,6 +111,8 @@
 login_cap_t *lc;
 #endif
 
+static char *sftpumask;
+
 /* Name and directory of socket for authentication agent forwarding. */
 static char *auth_sock_name = NULL;
 static char *auth_sock_dir = NULL;
@@ -957,6 +966,7 @@
 	env = xmalloc(envsize * sizeof(char *));
 	env[0] = NULL;
 
+
 #ifdef HAVE_CYGWIN
 	/*
 	 * The Windows environment contains some setting which are
@@ -1083,6 +1093,43 @@
 	if (auth_sock_name != NULL)
 		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
 		    auth_sock_name);
+
+	/* SFTP_UMASK */
+
+	if (options.sftp_umask[0] == '\0')
+		child_set_env(&env, &envsize, "SFTP_UMASK", 
+			"" );
+	else {
+		if (!(sftpumask = calloc(SFTP_UMASK_LENGTH,1))) {
+
+logit("session.c: unabled to allocate memory for SftpUmask. SftpUmask control \
+will be turned off.");
+
+		child_set_env(&env, &envsize, "SFTP_UMASK", 
+			"" );
+		} else {
+			strncpy(sftpumask, options.sftp_umask,
+				SFTP_UMASK_LENGTH);
+			child_set_env(&env, &envsize, "SFTP_UMASK", 
+				sftpumask );
+		}
+	}
+
+        /* SFTP_PERMIT_CHMOD */
+        if (options.sftp_permit_chmod == -1 )
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHMOD", "-1");
+        else if (options.sftp_permit_chmod == 0)
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHMOD", "0");
+        else
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHMOD", "1");
+
+        /* SFTP_PERMIT_CHOWN */
+        if (options.sftp_permit_chown == -1 )
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHOWN", "-1");
+        else if (options.sftp_permit_chown == 0)
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHOWN", "0");
+        else
+                child_set_env(&env, &envsize, "SFTP_PERMIT_CHOWN", "1");
 
 	/* read $HOME/.ssh/environment. */
 	if (options.permit_user_env && !options.use_login) {
diff -u openssh-5.4p1/sftp-server.8 openssh-5.4p1-sftp_mods/sftp-server.8
--- openssh-5.4p1/sftp-server.8	Mon Jun 25 00:45:35 2001
+++ openssh-5.4p1-sftp_mods/sftp-server.8	Wed Jan 29 10:11:28 2003
@@ -51,3 +51,12 @@
 See
 .Xr sshd_config 5
+for more information. 
+The administrator may exert control over the file and directory
+permission and ownership, with
+.Cm SftpUmask ,
+.Cm SftpPermitChmod ,
+and
+.Cm SftpPermitChown
+. See
+.Xr sshd_config 5
 for more information.
@@ -75,8 +75,9 @@ 
 .Sh SEE ALSO
 .Xr sftp 1 ,
 .Xr ssh 1 ,
 .Xr sshd_config 5 ,
-.Xr sshd 8
+.Xr sshd 8,
+.Xr sshd_config 5
 .Rs
 .%A T. Ylonen
 .%A S. Lehtinen
diff -u openssh-5.4p1/sshd.c openssh-5.4p1-sftp_mods/sshd.c
--- openssh-5.4p1/sshd.c       Wed Sep 11 19:54:27 2002
+++ openssh-5.4p1-sftp_mods/sshd.c        Mon Nov 10 11:26:45 2003
@@ -379,4 +379,3 @@
	}
-	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s%s", major, minor,
-	    SSH_VERSION, newline);
+	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_RELEASE);
	server_version_string = xstrdup(buf);
diff -u openssh-5.4p1/sftp-server.c openssh-5.4p1-sftp_mods/sftp-server.c
--- openssh-5.4p1/sftp-server.c       Wed Sep 11 19:54:27 2002
+++ openssh-5.4p1-sftp_mods/sftp-server.c     Mon Nov 10 11:26:45 2003
@@ -51,3 +51,9 @@
 #define get_string(lenp) 		buffer_get_string(&iqueue, lenp);
 
+/* SFTP_UMASK */
+static mode_t setumask = 0;
+
+static int permit_chmod = 1;
+static int permit_chown = 1;
+
 /* Our verbosity */
@@ -500,5 +500,12 @@
 	flags = flags_from_portable(pflags);
 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
+
+	if (setumask != 0) {
+		logit("setting file creation mode to 0666 and umask to %o", setumask);
+		mode = 0666;
+		umask(setumask);
+	}
+
 	logit("open \"%s\" flags %s mode 0%o",
 	    name, string_from_portable(pflags), mode);
 	fd = open(name, flags, mode);
@@ -512,6 +512,7 @@
 			status = SSH2_FX_OK;
 		}
 	}
+	logit("open %s", name);
 	if (status != SSH2_FX_OK)
 		send_status(id, status);
 	xfree(name);
@@ -703,6 +703,8 @@
		status = SSH2_FX_PERMISSION_DENIED;
		a->flags = 0;
	}
+ 
	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
+logit("process_setstat: truncate");
		logit("set \"%s\" size %llu",
		    name, (unsigned long long)a->size);
@@ -708,9 +708,15 @@
			status = errno_to_portable(errno);
	}
	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
-		logit("set \"%s\" mode %04o", name, a->perm);
-		ret = chmod(name, a->perm & 07777);
-		if (ret == -1)
-			status = errno_to_portable(errno);
+		if (permit_chmod == 1) {
+			ret = chmod(name, a->perm & 0777);
+			if (ret == -1)
+				status = errno_to_portable(errno);
+			else
+				logit("chmod'ed %s", name);
+		} else {
+			status = SSH2_FX_PERMISSION_DENIED;
+			logit("chmod %s: operation prohibited by sftp-server configuration.", name);
+		}
	}
	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
@@ -727,7 +727,12 @@
	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
-		logit("set \"%s\" owner %lu group %lu", name,
-		    (u_long)a->uid, (u_long)a->gid);
-		ret = chown(name, a->uid, a->gid);
-		if (ret == -1)
-			status = errno_to_portable(errno);
+		if (permit_chown == 1) {
+			ret = chown(name, a->uid, a->gid);
+			if (ret == -1)
+				status = errno_to_portable(errno);
+			else
+				logit("chown'ed %s.", name);
+		} else {
+			status = SSH2_FX_PERMISSION_DENIED;
+			logit("chown %s: operation prohibited by sftp-server configuration.", name);
+		}
	}
@@ -752,5 +752,6 @@
		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
			logit("set \"%s\" mode %04o", name, a->perm);
+			if (permit_chmod == 1) {
 #ifdef HAVE_FCHMOD
			ret = fchmod(fd, a->perm & 0777);
 #else
@@ -757,8 +757,14 @@
			ret = chmod(name, a->perm & 0777);
 #endif
			if (ret == -1)
				status = errno_to_portable(errno);
+			else
+				logit("chmod: succeeded.");
+		   } else { /* permit_chmod */
+                        status = SSH2_FX_PERMISSION_DENIED;
+			logit("chmod: operation prohibited by sftp-server configuration.");
+		   } /* permit_chmod */
		}
		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
			char buf[64];
			time_t t = a->mtime;
@@ -777,14 +777,21 @@
		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
			logit("set \"%s\" owner %lu group %lu", name,
			    (u_long)a->uid, (u_long)a->gid);
+			if (permit_chown == 1) {
 #ifdef HAVE_FCHOWN
			ret = fchown(fd, a->uid, a->gid);
 #else
			ret = chown(name, a->uid, a->gid);
 #endif
			if (ret == -1)
				status = errno_to_portable(errno);
+			else
+				logit("chown: succeeded");
+		   } else { /* permit_chown */
+			status = SSH2_FX_PERMISSION_DENIED;
+			logit("chown: operation prohibited by sftp-server configuration.");
+		   } /* permit_chown */
		}
	}
	send_status(id, status);
 }
@@ -916,6 +916,13 @@
	a = get_attrib();
	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
	    a->perm & 07777 : 0777;
+
+        if (setumask != 0) {
+                logit("setting directory creation mode to 0777 and umask to %o.", setumask);
+                mode = 0777;
+                umask(setumask);
+        }
+
	debug3("request %u: mkdir", id);
	logit("mkdir name \"%s\" mode 0%o", name, mode);
	ret = mkdir(name, mode);
@@ -1210,4 +1210,6 @@
	fd_set *rset, *wset;
	int in, out, max, ch, skipargs = 0, log_stderr = 0;
+	unsigned int val = 0;
+	char *umask_env;
	ssize_t len, olen, set_size;
	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
@@ -1271,4 +1271,33 @@
	handle_init();

+	/* Umask control */
+
+	umask_env = getenv("SFTP_UMASK");
+	if ( umask_env && *umask_env != NULL )
+	{
+		while (*umask_env && *umask_env >= '0' && *umask_env <= '9')
+			val = val * 8 + *umask_env++ - '0';
+
+		if (*umask_env || val > 0777 || val == 0) {
+			logit("bad value %o for SFTP_UMASK, turning umask control off.", val);
+			setumask = 0;
+		} else {
+			logit("umask control is on.");
+			setumask = val;
+		};
+	} else setumask = 0;
+
+
+	/* Sensitive client commands */
+	
+        if ( (getenv("SFTP_PERMIT_CHMOD") != NULL) && (atoi(getenv("SFTP_PERMIT_CHMOD")) != 1) ) {
+		permit_chmod = 0;
+                logit("client is not permitted to chmod.");
+	};
+        if ( (getenv("SFTP_PERMIT_CHOWN") != NULL) && (atoi(getenv("SFTP_PERMIT_CHOWN")) != 1) ) {
+		permit_chown = 0;
+                logit("client is not permitted to chown.");
+	};
+	
	in = dup(STDIN_FILENO);
	out = dup(STDOUT_FILENO);
Only in openssh-5.4p1-sftp_mods/: ssh_prng_cmds
diff -u openssh-5.4p1/sshd_config openssh-5.4p1-sftp_mods/sshd_config
--- openssh-5.4p1/sshd_config	Thu Sep 26 23:21:58 2002
+++ openssh-5.4p1-sftp_mods/sshd_config	Wed Jan 29 10:08:39 2003
@@ -91,5 +91,11 @@
 # override default of no subsystems
 Subsystem	sftp	/usr/libexec/sftp-server

+# sftp-server umask control
+#SftpUmask
+
+#SftpPermitChmod yes
+#SftpPermitChown yes
+
 # Example of overriding settings on a per-user basis
 #Match User anoncvs
diff -u openssh-5.4p1/sshd_config.5 openssh-5.4p1-sftp_mods/sshd_config.5
--- openssh-5.4p1/sshd_config.5	Wed Sep 18 21:51:22 2002
+++ openssh-5.4p1-sftp_mods/sshd_config.5	Wed Jan 29 10:10:03 2003
@@ -558,5 +562,21 @@
 .It Cm ServerKeyBits
 Defines the number of bits in the ephemeral protocol version 1 server key.
 The minimum value is 512, and the default is 1024.
+.It Cm SftpPermitChmod
+Specifies whether the sftp-server allows the sftp client to execute chmod 
+commands on the server. The default is yes.
+.It Cm SftpPermitChown
+Specifies whether the sftp-server allows the sftp client to execute chown
+or chgrp commands on the server. Turning this value on means that the client
+is allowed to execute both chown and chgrp commands. Turning it off means that
+the client is prohibited from executing either chown or chgrp.
+ The default is yes.
+.It Cm SftpUmask
+Specifies an optional umask for 
+.Nm sftp-server
+subsystem transactions. If a umask is given, this umask will override all system, 
+environment or sftp client permission modes. If
+no umask or an invalid umask is given, file creation mode defaults to the permission
+mode specified by the sftp client. The default is for no umask.
 .It Cm StrictModes
 Specifies whether
/* $OpenBSD: version.h,v 1.37 2003/04/01 10:56:46 markus Exp $ */

#define SSH_VERSION    "OpenSSH_5.4p1"
