Unfortunately it appears that the ``command="somecommand"'' feature that
can be used as an option in the ~/.ssh/authorized_keys file is useless
in some valid circumstances at preventing shell access to the system
because it still requires, for no good reason, that the user have a
normal shell.

The problem is that even when this feature is used the user must still
have a "normal" shell -- i.e. a shell that supports '-c command'.  If
there's any other way that the user can access the remote system (such
as via telnet or especially rsh) then it's impossible to prevent them
from getting full shell access if they have a "normal" shell.  (Note
that I can't lock the account in the password file because I need to use
valid passwords for local POP, IMAP, and FTP authentication.)

I propose the following "fix" to eliminate this problem (delta against
1.2.27, with a minor fix to the locked account checking also included):

Index: sshd.c
===================================================================
RCS file: /cvs/misc/ssh/sshd.c,v
retrieving revision 1.1.1.4
diff -c -r1.1.1.4 sshd.c
*** sshd.c      1999/10/22 18:32:05     1.1.1.4
--- sshd.c      1999/10/23 03:27:00
***************
*** 641,653 ****
  void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
                   const char *ttyname, struct passwd *pw, const char *term,
                   const char *display, const char *auth_proto,
!                  const char *auth_data);
  void do_exec_no_pty(const char *command, struct passwd *pw,
                      const char *display, const char *auth_proto,
!                     const char *auth_data);
  void do_child(const char *command, struct passwd *pw, const char *term,
                const char *display, const char *auth_proto,
!               const char *auth_data, const char *ttyname);
  
  
  /* Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
--- 641,653 ----
  void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
                   const char *ttyname, struct passwd *pw, const char *term,
                   const char *display, const char *auth_proto,
!                  const char *auth_data, int forced);
  void do_exec_no_pty(const char *command, struct passwd *pw,
                      const char *display, const char *auth_proto,
!                     const char *auth_data, int forced);
  void do_child(const char *command, struct passwd *pw, const char *term,
                const char *display, const char *auth_proto,
!               const char *auth_data, const char *ttyname, int forced);
  
  
  /* Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
***************
*** 1593,1604 ****
  
  int login_permitted(char *user, struct passwd *pwd)
  {
!   char passwd[6];               /* Only for account lock check */
    struct group *grp;
    char *group;
   
-   strncpy(passwd, pwd->pw_passwd, sizeof(passwd));
-   passwd[sizeof(passwd) - 1] = '\0';
  #ifdef HAVE_USERSEC_H
    {
      char *expiration, current_time[100], normalized[100];
--- 1593,1602 ----
  
  int login_permitted(char *user, struct passwd *pwd)
  {
!   char *passwd = pwd->pw_passwd;
    struct group *grp;
    char *group;
   
  #ifdef HAVE_USERSEC_H
    {
      char *expiration, current_time[100], normalized[100];
***************
*** 1777,1784 ****
                    sp->sp_lstchg + sp->sp_max - today; 
                }
            }
!         strncpy(passwd, sp->sp_pwdp, sizeof(passwd));
!         passwd[sizeof(passwd) - 1] = '\0';
        }
      endspent();
    }
--- 1775,1781 ----
                    sp->sp_lstchg + sp->sp_max - today; 
                }
            }
!       passwd = sp->sp_pwdp;
        }
      endspent();
    }
***************
*** 1970,1980 ****
  #endif /* HAVE_HPUX_TCB_AUTH */  
  
    /*
!    * Check if account is locked. Check if encrypted password starts
!    * with "*LK*".
     */
    {
!     if ((strncmp(passwd,"*LK*", 4) == 0)
  #if defined(KERBEROS) && defined(KRB5)
          && (options.kerberos_or_local_passwd != 0)
  #endif /* defined(KERBEROS) && defined(KRB5) */
--- 1967,1986 ----
  #endif /* HAVE_HPUX_TCB_AUTH */  
  
    /*
!    * Check if account is locked.
     */
    {
!     /* The magic string for strspn() from crypt(3) is the range of characters
!      * used for a legal encoding of an encrypted password.  The initial check
!      * for an asterisk is a quick and dirty hack used because many Unix manuals
!      * recommend inserting a '*' in the passwd field to lock an account.
!      */
!     if ((strchr(passwd, "*") ||
!         (strspn(passwd, 
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz") != strlen(passwd))
! #if !defined(FreeBSD) /* FreeBSD can use a longer MD5 hash */
!        || (strlen(passwd) != 13)      /* XXX magic number from crypt(3) */
! #endif
!        )
  #if defined(KERBEROS) && defined(KRB5)
          && (options.kerberos_or_local_passwd != 0)
  #endif /* defined(KERBEROS) && defined(KRB5) */
***************
*** 3022,3030 ****
            debug("Forking shell.");
            if (have_pty)
              do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto,
!                         data);
            else
!             do_exec_no_pty(NULL, pw, display, proto, data);
            return;
  
          case SSH_CMSG_EXEC_CMD:
--- 3028,3036 ----
            debug("Forking shell.");
            if (have_pty)
              do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto,
!                         data, 0);
            else
!             do_exec_no_pty(NULL, pw, display, proto, data, 0);
            return;
  
          case SSH_CMSG_EXEC_CMD:
***************
*** 3043,3051 ****
            debug("Executing command '%.500s'", command);
            if (have_pty)
              do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display,
!                         proto, data);
            else
!             do_exec_no_pty(command, pw, display, proto, data);
            xfree(command);
            return;
  
--- 3049,3057 ----
            debug("Executing command '%.500s'", command);
            if (have_pty)
              do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display,
!                         proto, data, 0);
            else
!             do_exec_no_pty(command, pw, display, proto, data, 0);
            xfree(command);
            return;
  
***************
*** 3084,3092 ****
        debug("Executing forced command: %.900s", forced_command);
        if (have_pty)
          do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display,
!                     proto, data);
        else
!         do_exec_no_pty(forced_command, pw, display, proto, data);
        return;
      }
  }
--- 3090,3098 ----
        debug("Executing forced command: %.900s", forced_command);
        if (have_pty)
          do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display,
!                     proto, data, 1);
        else
!         do_exec_no_pty(forced_command, pw, display, proto, data, 1);
        return;
      }
  }
***************
*** 3097,3103 ****
  
  void do_exec_no_pty(const char *command, struct passwd *pw,
                      const char *display, const char *auth_proto,
!                     const char *auth_data)
  {  
    int pid;
  #ifdef USE_PIPES
--- 3103,3109 ----
  
  void do_exec_no_pty(const char *command, struct passwd *pw,
                      const char *display, const char *auth_proto,
!                     const char *auth_data, int forced)
  {  
    int pid;
  #ifdef USE_PIPES
***************
*** 3204,3210 ****
  #endif /* HAVE_SIA */
  
        /* Do processing for the child (exec command etc). */
!       do_child(command, pw, NULL, display, auth_proto, auth_data, NULL);
        /*NOTREACHED*/
      }
    if (pid < 0)
--- 3210,3216 ----
  #endif /* HAVE_SIA */
  
        /* Do processing for the child (exec command etc). */
!       do_child(command, pw, NULL, display, auth_proto, auth_data, NULL, forced);
        /*NOTREACHED*/
      }
    if (pid < 0)
***************
*** 3271,3277 ****
  void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
                   const char *ttyname, struct passwd *pw, const char *term,
                   const char *display, const char *auth_proto, 
!                  const char *auth_data)
  {
    int pid, fdout;
    const char *hostname;
--- 3277,3283 ----
  void do_exec_pty(const char *command, int ptyfd, int ttyfd, 
                   const char *ttyname, struct passwd *pw, const char *term,
                   const char *display, const char *auth_proto, 
!                  const char *auth_data, int forced)
  {
    int pid, fdout;
    const char *hostname;
***************
*** 3500,3506 ****
  #endif
  
        /* Do common processing for the child, such as execing the command. */
!       do_child(command, pw, term, display, auth_proto, auth_data, ttyname);
        /*NOTREACHED*/
      }
    if (pid < 0)
--- 3506,3512 ----
  #endif
  
        /* Do common processing for the child, such as execing the command. */
!       do_child(command, pw, term, display, auth_proto, auth_data, ttyname, forced);
        /*NOTREACHED*/
      }
    if (pid < 0)
***************
*** 3859,3865 ****
  
  void do_child(const char *command, struct passwd *pw, const char *term,
                const char *display, const char *auth_proto, 
!               const char *auth_data, const char *ttyname)
  {
    const char *shell, *cp;
    char buf[256];
--- 3865,3871 ----
  
  void do_child(const char *command, struct passwd *pw, const char *term,
                const char *display, const char *auth_proto, 
!               const char *auth_data, const char *ttyname, int forced)
  {
    const char *shell, *cp;
    char buf[256];
***************
*** 4579,4590 ****
    argv[1] = "-c";
    argv[2] = (char *)command;
    argv[3] = NULL;
  #if defined (__FreeBSD__) && defined(HAVE_LOGIN_CAP_H)
!   execve(real_shell, argv, env);
  #else
!   execve(shell, argv, env);
  #endif /* HAVE_LOGIN_CAP_H */
!   perror(shell);
    exit(1);
  }
  
--- 4585,4604 ----
    argv[1] = "-c";
    argv[2] = (char *)command;
    argv[3] = NULL;
+   if (forced)
+    {
+       execve(DEFAULT_SHELL, argv, env);
+       perror(DEFAULT_SHELL);
+    }
+   else
+    {
  #if defined (__FreeBSD__) && defined(HAVE_LOGIN_CAP_H)
!       execve(real_shell, argv, env);
  #else
!       execve(shell, argv, env);
  #endif /* HAVE_LOGIN_CAP_H */
!       perror(shell);
!    }
    exit(1);
  }
  


-- 
                                                        Greg A. Woods

+1 416 218-0098      VE3TCP      <[EMAIL PROTECTED]>      <robohack!woods>
Planix, Inc. <[EMAIL PROTECTED]>; Secrets of the Weird <[EMAIL PROTECTED]>

Reply via email to