SFTP client can't write to own home directory when chrooted there

Solution 1:

I found several working solutions to this problem, most of which were only usable because of my willingness to throw away the server after I got this one upload. But there's one that I think is pretty reasonable.

Ugliest solution first:

Bad idea #1 - Forget the chroot and chmod 777 /. Openssh only checks for sane permissions when you're chrooting.

Bad idea #2 - Allowing the client to write to the real root directory (like in bad idea #1) can be done more subtly, with group permissions or a user ACL.

Bad idea #3 - As I wrote in the question, it is possible to hack out the permission check from sshd. Just search for the error message bad ownership or modes for chroot in the source, change the nearby 022 to 002 and it won't notice loose group permissions and ACLs on the chroot any more.

Good idea(?) - That backslash is the source of the trouble. If the client would just upload to /SLA/SLA_Data_blah_blah.zip everything would be fine. So let's just fix the backslash at the point where it hits the filesystem, by wrapping the open syscall:

/* wrapopen.c */
#define _GNU_SOURCE
#include <string.h>
#include <sys/types.h>
#include <dlfcn.h>

static int (*libc_open)(const char *, int, mode_t);

/* Behave like normal open() but translate backslashes to slashes. */
int open(const char *fn, int flags, mode_t mode)
{
  char fn_fixed[strlen(fn)+1], *p;

  strcpy(fn_fixed, fn);
  for(p=fn_fixed;*p;++p)
    if(*p == '\\')
      *p = '/';

  if(!libc_open)
    libc_open = dlsym(RTLD_NEXT, "open");

  return libc_open(fn_fixed, flags, mode);
}

Usage:

$ gcc -fPIC -shared wrapopen.c -o wrapopen.so -ldl
$ /etc/init.d/ssh stop
$ LD_PRELOAD=$PWD/wrapopen.so /usr/sbin/sshd

Now when sshd tries to create SLA\SLA_Data_blah_blah it actually creates SLA/SLA_Data_blah_blah. The user can be chrooted, the chroot is owned by root mode 755, the SLA directory under the chroot is owned by the user, and the upload works.