The problem is that sftp runs as the user's id -- first, the sftp client ssh's into the target host as the given user, then runs sftp-server. Since sftp-server is running as a regular user, it has no way to "give away" a file (change owner of a file).
However, if you are able to use scp, and assign a key pair to each user, you can get around this. This involves adding a user's key to root's ~/.ssh/authorized_keys file, with a "command=" parameter to force it to run a script that sanitizes and alters the arguments of the server-side scp program. I've used this technique before to set up an anonymous scp dropbox that allowed anyone to submit a file, and ensure that no one could retrieve submitted files and also prevent overwrites.
If you are open to this technique, let me know and I'll update this post with a quick recipe.
Edit: Based on comments, here's my recipe that I originally used to set up an anonymous dropbox. But it should work for this purpose also.
If you want John to write to upload files to
/var/www/webstuff, and have any files written to it owned by the user id
1) Generate an ssh key pair:
ssh-keygen -t rsa -f john-scp-key
2) Edit the public key file "john-scp-key.pub", and insert a "command=" statement at the beginning as follows:
command="/usr/local/bin/strictscp ^/var/www/webstuff" ssh-rsa AAAA..... (reset
of key follows)
3) Add the contents of
~apache/.ssh/authorized_keys file (create the file if it doesn't exist, making sure that both the .ssh directory and authorized_keys are owned by
apache, and not writable by either group or world)
4) Create the script
/usr/local/bin/strictscp as follows:
read cmd arg1 arg2 <<EOT
if [ "$cmd" = scp -a "$arg1" = -t ] && echo "$arg2" |grep "$1" >/dev/null 2>&1
eval $cmd $arg1 $arg2
5) Now give the private key file
john-scp-key to John, and have him copy files to your web server like this:
scp -i john-scp-key some_file.html apache@yourserver:/var/www/webstuff/
The way this works is when you use scp to copy a file to a server, the scp client uses ssh to execute "scp -t /path/name" on the server. When using a key pair, you can override the command that gets executed and force a specific command to run when that key is used. The original command that was requested, along with its arguments, is in the environment variable "$SSH_ORIGINAL_COMMAND". So we point
COMMAND= to a script that checks for allowed arguments (in this case, it verifies that the third argument is the specific directory we want to write stuff to), so that this key is only good for its intended purpose and can't be used to run arbitrary commands on the server.
You can lock down the
strictscp script a bit further by checking if the target file exists or not, and you can also add a
chgrp command to the end to set the file's group ownership (assuming that
apache is a member of that group).
Hope this helps -- if you have any problems with it, let me know and I'll see if I can improve this answer further.