SSH, limiting to SCP or Rsync only

From FreeBSDwiki
Revision as of 18:20, 31 July 2005 by Jimbo (Talk | contribs)
Jump to: navigation, search

Fairly commonly, you may want to set something up so that a user can scp or rsync files to (or from) a server of yours, but you don't want to allow them a shell account. That's not ENTIRELY possible, but you can manage something along those lines by either creating a jail for them, or, more simply, by forcing them to use a very very neutered custom shell when they log in that will only allow them access to the commands that you want them to be able to use.

You can use the small C program at the bottom of this article to create a custom shell for user accounts that you want to be able to use scp, sftp, or rsync with SSH transport, but not to have an actual shell available. Remember that this limits "snoopiness" to some degree but is NOT any kind of hardcore lockout, as any files or directories that the user has read permissions on can be scp'ed or rsync'ed over to their machine for local perusal, and any which they have write permissions on can be OVERWRITTEN with versions scp'ed or rsync'ed over FROM their machine!

Once you've saved the code at the bottom of the article to a work directory as scpsftprsynconly.c, you can compile it and assign it as a user's shell with pw or chsh:

ph34r# gcc scpsftprsynconly.c -o /usr/local/bin/scpsftprsynconly
ph34r# pw usermod dave -s /usr/local/bin/scpsftprsynconly

Now user dave can use scp or sftp (if sftp is set up) or rsync (if rsync is available) commands with ssh, but cannot actually log into the box - remotely OR locally I might add! Here's a test showing ssh and local logins failing, but an scp succeeding:

ph34r# ssh dave@localhost
This account is currently not available.
ph34r# su dave
This account is currently not available.
ph34r# scp dave@localhost:/usr/local/bin/scpsftprsynconly .
scpsftprsynconly                               100%  130KB 129.7KB/s   00:00

And there we have it - it works. Note that you might want to actually LOOK at the code; it's assuming that rsync and scp will be findable via the system's PATH environment variable. If that's not the case, or if you're feeling a little extra-paranoid and want to hard-code in assurance that ONLY the particular executable you want - say, /usr/local/bin/rsync - can be run, you might want to hack that in the way you want it; just keep in mind that if your custom shell is looking to allow just rsync, it WON'T allow /usr/local/bin/rsync and vice versa. So you might wind up having to mess about with flags for your scp and rsync commands from remote machines to specify paths, if you screw around with that stuff.


 #include <unistd.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 **  Original by Patric Draper <>
 **  Changes on 13-Mar-2004 by Msquared <>
 **    * fixed bug in args to realloc()
 **    * fixed bug in parameter checks (validates entire command name)
 **    * Modified to work with OpenSSH SFTP
 **    * Added rsync support
 **  This code is in the public domain.  No warranty.  If it breaks,
 **  you can dispose of it as you see fit.
 **  Build with DEBUG to save calling arguments to /tmp/scpshell.log
 **  This is useful to add new protocols, debug existing calls, etc.
 char * restrictmsg = "This account is currently not available.\n";
 int main (int argc, char *argv []) {
         char **newargs = NULL;
         char *newbuff = NULL;
         int i;
         char *s;
 #ifdef DEBUG
         FILE * log = fopen("/tmp/scpshell.log","a+");
         if ( log ) {
                 char **par = argv;
                 while ( *par )
                         fprintf ( log, "[%s] ", *par++ );
                 fprintf ( log, "\n" );
         if (argc < 3) {
                 printf (restrictmsg);
                 return 1;
         if ((strncmp (argv [2], "scp ", 4) != 0) &&
             (strncmp (argv [2], "/usr/libexec/openssh/sftp-server", 33) != 0) &&
             (strncmp (argv [2], "rsync ", 6) != 0)) {
                 printf (restrictmsg);
                 return 2;
         i = 0;
         newbuff = strdup(argv[2]);
         s = strtok (newbuff, " ");
         do {
                 newargs = (char **) realloc (newargs, ++i*sizeof(*newargs));
                 newargs [i - 1] = strdup (s);
         } while ((s = strtok (NULL, " ")) != NULL);
         newargs = (char **) realloc (newargs, ++i*sizeof(*newargs));
         newargs [i - 1] = NULL;
         execvp (newargs [0], newargs);
         return 0;
Personal tools