Bugzilla – Attachment 1126 Details for
Bug 474
sftp should provide logging
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
revised patch, adding chroot option
sftp-server-logging3.diff (text/plain), 32.16 KB, created by
Damien Miller
on 2006-04-25 18:37:31 AEST
(
hide
)
Description:
revised patch, adding chroot option
Filename:
MIME Type:
Creator:
Damien Miller
Created:
2006-04-25 18:37:31 AEST
Size:
32.16 KB
patch
obsolete
>Index: misc.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/misc.c,v >retrieving revision 1.52 >diff -u -p -r1.52 misc.c >--- misc.c 30 Mar 2006 09:58:15 -0000 1.52 >+++ misc.c 25 Apr 2006 08:34:47 -0000 >@@ -31,6 +31,7 @@ > #include <net/if.h> > #include <netinet/tcp.h> > >+#include <ctype.h> > #include <paths.h> > > #include "misc.h" >@@ -456,6 +457,80 @@ freeargs(arglist *args) > args->nalloc = args->num = 0; > args->list = NULL; > } >+} >+ >+/* >+ * Crack a string into an argument vector and append it to the supplied >+ * arglist. Copes with quoted strings (returned as one argument) and >+ * escaped spaces. Returns number of arguments added to the arglist or >+ * -1 on error (broken quoting, etc.) >+ */ >+int >+makeargv(arglist *args, const char *line) >+{ >+ const char *lastquote, *argstart; >+ int n, escaped, i, j; >+ char *arg; >+ >+ for (n = 0; *line != '\0';) { >+ /* Skip space */ >+ for(; *line != '\0' && isspace(*line); line++) >+ ; >+ if (*line == '\0') >+ break; >+ >+ /* Extract an argument */ >+ if (*line == '"' || *line == '\'') { >+ /* Separate quoted argument */ >+ lastquote = line; >+ line++; >+ argstart = line; >+ /* Find last, non-escaped quote */ >+ for (escaped = 0; *line != '\0'; line++) { >+ if (!escaped && *line == '\\') { >+ escaped = 1; >+ continue; >+ } >+ if (!escaped && *line == *lastquote) >+ break; >+ escaped = 0; >+ } >+ /* Check for unterminated quotes or "foo"blah */ >+ if (*line != *lastquote || >+ (line[1] != '\0' && !isspace(line[1]))) >+ return (-1); >+ } else { >+ /* Separate unquoted argument */ >+ argstart = line; >+ for (escaped = 0; *line != '\0'; line++) { >+ if (*line == '\\' && !escaped) { >+ escaped = 1; >+ continue; >+ } >+ if (!escaped && isspace(*line)) >+ break; >+ escaped = 0; >+ } >+ } >+ >+ /* Copy off argument and strip out escape chars */ >+ arg = xcalloc(1, 1 + line - argstart); >+ for (escaped = i = j = 0; i < line - argstart; i++) { >+ if (!escaped && argstart[i] == '\\') { >+ escaped = 1; >+ continue; >+ } >+ arg[j++] = argstart[i]; >+ escaped = 0; >+ } >+ addargs(args, "%s", arg); >+ xfree(arg); >+ n++; >+ >+ if (*line != '\0') >+ line++; >+ } >+ return (n); > } > > /* >Index: misc.h >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/misc.h,v >retrieving revision 1.31 >diff -u -p -r1.31 misc.h >--- misc.h 30 Mar 2006 09:58:15 -0000 1.31 >+++ misc.h 25 Apr 2006 08:34:47 -0000 >@@ -46,6 +46,7 @@ void addargs(arglist *, char *, ...) > void replacearg(arglist *, u_int, char *, ...) > __attribute__((format(printf, 3, 4))); > void freeargs(arglist *); >+int makeargv(arglist *, const char *); > > /* readpass.c */ > >Index: servconf.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/servconf.c,v >retrieving revision 1.150 >diff -u -p -r1.150 servconf.c >--- servconf.c 25 Mar 2006 13:17:02 -0000 1.150 >+++ servconf.c 25 Apr 2006 08:34:47 -0000 >@@ -854,12 +867,33 @@ parse_flag: > if (strcmp(arg, options->subsystem_name[i]) == 0) > fatal("%s line %d: Subsystem '%s' already defined.", > filename, linenum, arg); >+ > options->subsystem_name[options->num_subsystems] = xstrdup(arg); > arg = strdelim(&cp); > if (!arg || *arg == '\0') > fatal("%s line %d: Missing subsystem command.", > filename, linenum); > options->subsystem_command[options->num_subsystems] = xstrdup(arg); >+ >+ /* Generate an argv[0] for the subsystem */ >+ if ((p = strrchr(arg, '/')) != NULL) >+ p++; >+ else >+ p = arg; >+ >+ memset(&options->subsystem_args[options->num_subsystems], '\0', >+ sizeof(options->subsystem_args[options->num_subsystems])); >+ options->subsystem_args[options->num_subsystems].list = NULL; >+ addargs(&options->subsystem_args[options->num_subsystems], >+ "%s", p); >+ >+ /* Collect extra arguments, if any */ >+ if (makeargv(&options->subsystem_args[options->num_subsystems], >+ cp) < 0) >+ fatal("%s line %d: Bad subsystem arguments", >+ filename, linenum); >+ >+ *cp = '\0'; /* Avoid "junk at end of line" fatal() */ > options->num_subsystems++; > break; > >Index: servconf.h >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/servconf.h,v >retrieving revision 1.73 >diff -u -p -r1.73 servconf.h >--- servconf.h 25 Mar 2006 22:22:43 -0000 1.73 >+++ servconf.h 25 Apr 2006 08:34:47 -0000 >@@ -17,6 +17,7 @@ > #define SERVCONF_H > > #include "buffer.h" >+#include "misc.h" > > #define MAX_PORTS 256 /* Max # ports. */ > >@@ -111,6 +113,7 @@ typedef struct { > u_int num_subsystems; > char *subsystem_name[MAX_SUBSYSTEMS]; > char *subsystem_command[MAX_SUBSYSTEMS]; >+ arglist subsystem_args[MAX_SUBSYSTEMS]; > > u_int num_accept_env; > char *accept_env[MAX_ACCEPT_ENV]; >Index: session.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/session.c,v >retrieving revision 1.203 >diff -u -p -r1.203 session.c >--- session.c 20 Apr 2006 21:53:44 -0000 1.203 >+++ session.c 25 Apr 2006 08:34:48 -0000 >@@ -82,11 +82,11 @@ void session_set_fds(Session *, int, int > void session_pty_cleanup(Session *); > void session_proctitle(Session *); > int session_setup_x11fwd(Session *); >-void do_exec_pty(Session *, const char *); >-void do_exec_no_pty(Session *, const char *); >-void do_exec(Session *, const char *); >+void do_exec_pty(Session *, const char *, char **); >+void do_exec_no_pty(Session *, const char *, char **); >+void do_exec(Session *, const char *, char **); > void do_login(Session *, const char *); >-void do_child(Session *, const char *); >+void do_child(Session *, const char *, char **); > void do_motd(void); > int check_quietlogin(Session *, const char *); > >@@ -341,10 +341,10 @@ do_authenticated1(Authctxt *authctxt) > if (type == SSH_CMSG_EXEC_CMD) { > command = packet_get_string(&dlen); > debug("Exec command '%.500s'", command); >- do_exec(s, command); >+ do_exec(s, command, NULL); > xfree(command); > } else { >- do_exec(s, NULL); >+ do_exec(s, NULL, NULL); > } > packet_check_eom(); > session_close(s); >@@ -375,7 +375,7 @@ do_authenticated1(Authctxt *authctxt) > * setting up file descriptors and such. > */ > void >-do_exec_no_pty(Session *s, const char *command) >+do_exec_no_pty(Session *s, const char *command, char **subsys_args) > { > pid_t pid; > >@@ -419,7 +419,7 @@ do_exec_no_pty(Session *s, const char *c > perror("dup2 stderr"); > > /* Do processing for the child (exec command etc). */ >- do_child(s, command); >+ do_child(s, command, subsys_args); > /* NOTREACHED */ > } > if (pid < 0) >@@ -451,7 +451,7 @@ do_exec_no_pty(Session *s, const char *c > * lastlog, and other such operations. > */ > void >-do_exec_pty(Session *s, const char *command) >+do_exec_pty(Session *s, const char *command, char **subsys_args) > { > int fdout, ptyfd, ttyfd, ptymaster; > pid_t pid; >@@ -489,7 +489,7 @@ do_exec_pty(Session *s, const char *comm > do_login(s, command); > > /* Do common processing for the child, such as execing the command. */ >- do_child(s, command); >+ do_child(s, command, subsys_args); > /* NOTREACHED */ > } > if (pid < 0) >@@ -529,9 +529,16 @@ do_exec_pty(Session *s, const char *comm > * to be forced, execute that instead. > */ > void >-do_exec(Session *s, const char *command) >+do_exec(Session *s, const char *command, char **subsys_args) > { > if (forced_command) { >+ /* >+ * If a forced command overrides a subsystem, then >+ * do not apply the subsystem's arguments to it. >+ */ >+ if (strcmp(forced_command, command) != 0) >+ subsys_args = NULL; >+ > original_command = command; > command = forced_command; > debug("Forced command '%.900s'", command); >@@ -546,9 +553,9 @@ do_exec(Session *s, const char *command) > #endif > > if (s->ttyfd != -1) >- do_exec_pty(s, command); >+ do_exec_pty(s, command, subsys_args); > else >- do_exec_no_pty(s, command); >+ do_exec_no_pty(s, command, subsys_args); > > original_command = NULL; > >@@ -1047,7 +1054,7 @@ child_close_fds(void) > * ids, and executing the command or shell. > */ > void >-do_child(Session *s, const char *command) >+do_child(Session *s, const char *command, char **subsys_args) > { > extern char **environ; > char **env; >@@ -1194,6 +1201,12 @@ do_child(Session *s, const char *command > perror(shell); > exit(1); > } >+ /* If the subsystem supplies arguments, then execute it directly */ >+ if (s->is_subsystem && subsys_args != NULL) { >+ execve(command, subsys_args, env); >+ perror(command); >+ exit(1); >+ } > /* > * Execute the command using the user's shell. This uses the -c > * option to execute the command. >@@ -1418,23 +1431,25 @@ session_subsystem_req(Session *s) > struct stat st; > u_int len; > int success = 0; >- char *cmd, *subsys = packet_get_string(&len); >+ char *prog, *subsys = packet_get_string(&len); > u_int i; >+ arglist *args; > > packet_check_eom(); > logit("subsystem request for %.100s", subsys); > > for (i = 0; i < options.num_subsystems; i++) { > if (strcmp(subsys, options.subsystem_name[i]) == 0) { >- cmd = options.subsystem_command[i]; >- if (stat(cmd, &st) < 0) { >- error("subsystem: cannot stat %s: %s", cmd, >+ prog = options.subsystem_command[i]; >+ args = &options.subsystem_args[i]; >+ if (stat(prog, &st) < 0) { >+ error("subsystem: cannot stat %s: %s", prog, > strerror(errno)); > break; > } >- debug("subsystem: exec() %s", cmd); >+ debug("subsystem: exec() %s", prog); > s->is_subsystem = 1; >- do_exec(s, cmd); >+ do_exec(s, prog, options.subsystem_args[i].list); > success = 1; > break; > } >@@ -1478,7 +1493,7 @@ static int > session_shell_req(Session *s) > { > packet_check_eom(); >- do_exec(s, NULL); >+ do_exec(s, NULL, NULL); > return 1; > } > >@@ -1488,7 +1503,7 @@ session_exec_req(Session *s) > u_int len; > char *command = packet_get_string(&len); > packet_check_eom(); >- do_exec(s, command); >+ do_exec(s, command, NULL); > xfree(command); > return 1; > } >Index: sftp-server.8 >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/sftp-server.8,v >retrieving revision 1.10 >diff -u -p -r1.10 sftp-server.8 >--- sftp-server.8 8 Oct 2003 08:27:36 -0000 1.10 >+++ sftp-server.8 25 Apr 2006 08:34:48 -0000 >@@ -30,6 +30,9 @@ > .Nd SFTP server subsystem > .Sh SYNOPSIS > .Nm sftp-server >+.Op Fl C Ar chroot_path >+.Op Fl f Ar log_facility >+.Op Fl l Ar log_level > .Sh DESCRIPTION > .Nm > is a program that speaks the server side of SFTP protocol >@@ -40,9 +43,55 @@ is not intended to be called directly, b > using the > .Cm Subsystem > option. >+.Pp >+Command-line flags to >+.Nm >+should be specified in the >+.Cm Subsystem >+declaration. > See > .Xr sshd_config 5 > for more information. >+.Pp >+Valid options are: >+.Bl -tag -width Ds >+.It Fl C Ar chroot_path >+Requests that >+.Nm >+.Xr chroot 2 >+itself to the specified path prior to processing requests from the user. >+The >+.Ar chroot_path >+use the tilde syntax to refer to a user's home directory or one of the >+following >+escape characters: >+.Ql %d >+(local user's home directory) or >+.Ql %g >+(local user's primary group name). >+Note that >+.Xr chroot 2 >+support requires >+.Nm >+to be installed setuid root. >+.It Fl f Ar log_facility >+Specifies the facility code that is used when logging messages from >+.Nm . >+The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, >+LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. >+The default is AUTH. >+.It Fl l Ar log_level >+Specifies which messages will be logged by >+.Nm . >+The possible values are: >+QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. >+INFO and VERBOSE log transactions that >+.Nm >+performs on behalf of the client. >+DEBUG and DEBUG1 are equivalent. >+DEBUG2 and DEBUG3 each specify higher levels of debugging output. >+The default is ERROR. >+.El > .Sh SEE ALSO > .Xr sftp 1 , > .Xr ssh 1 , >Index: sftp-server.c >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/sftp-server.c,v >retrieving revision 1.57 >diff -u -p -r1.57 sftp-server.c >--- sftp-server.c 30 Mar 2006 09:58:16 -0000 1.57 >+++ sftp-server.c 25 Apr 2006 08:34:48 -0000 >@@ -26,6 +26,7 @@ > #include "log.h" > #include "xmalloc.h" > #include "misc.h" >+#include "uidswap.h" > > #include "sftp.h" > #include "sftp-common.h" >@@ -34,7 +35,13 @@ > #define get_int64() buffer_get_int64(&iqueue); > #define get_int() buffer_get_int(&iqueue); > #define get_string(lenp) buffer_get_string(&iqueue, lenp); >-#define TRACE debug >+ >+/* Our verbosity */ >+LogLevel log_level = SYSLOG_LEVEL_ERROR; >+ >+/* Our client */ >+struct passwd *pw; >+char *client_addr; > > /* input and output queue */ > Buffer iqueue; >@@ -106,6 +113,33 @@ flags_from_portable(int pflags) > return flags; > } > >+static const char * >+string_from_portable(int pflags) >+{ >+ static char ret[128]; >+ >+ *ret = '\0'; >+ >+#define PAPPEND(str) { \ >+ if (*ret != '\0') \ >+ strlcat(ret, ",", sizeof(ret)); \ >+ strlcat(ret, str, sizeof(ret)); \ >+ } >+ >+ if (pflags & SSH2_FXF_READ) >+ PAPPEND("READ") >+ if (pflags & SSH2_FXF_WRITE) >+ PAPPEND("WRITE") >+ if (pflags & SSH2_FXF_CREAT) >+ PAPPEND("CREATE") >+ if (pflags & SSH2_FXF_TRUNC) >+ PAPPEND("TRUNCATE") >+ if (pflags & SSH2_FXF_EXCL) >+ PAPPEND("EXCL") >+ >+ return ret; >+} >+ > static Attrib * > get_attrib(void) > { >@@ -120,6 +154,7 @@ struct Handle { > DIR *dirp; > int fd; > char *name; >+ u_int64_t bytes_read, bytes_write; > }; > > enum { >@@ -150,6 +185,7 @@ handle_new(int use, const char *name, in > handles[i].dirp = dirp; > handles[i].fd = fd; > handles[i].name = xstrdup(name); >+ handles[i].bytes_read = handles[i].bytes_write = 0; > return i; > } > } >@@ -213,6 +249,36 @@ handle_to_fd(int handle) > return -1; > } > >+static void >+handle_update_read(int handle, ssize_t bytes) >+{ >+ if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) >+ handles[handle].bytes_read += bytes; >+} >+ >+static void >+handle_update_write(int handle, ssize_t bytes) >+{ >+ if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) >+ handles[handle].bytes_write += bytes; >+} >+ >+static u_int64_t >+handle_bytes_read(int handle) >+{ >+ if (handle_is_ok(handle, HANDLE_FILE)) >+ return (handles[handle].bytes_read); >+ return 0; >+} >+ >+static u_int64_t >+handle_bytes_write(int handle) >+{ >+ if (handle_is_ok(handle, HANDLE_FILE)) >+ return (handles[handle].bytes_write); >+ return 0; >+} >+ > static int > handle_close(int handle) > { >@@ -232,6 +298,31 @@ handle_close(int handle) > return ret; > } > >+static void >+handle_log_close(int handle, char *emsg) >+{ >+ if (handle_is_ok(handle, HANDLE_FILE)) { >+ logit("%s%sclose \"%s\" bytes read %llu written %llu", >+ emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", >+ handle_to_name(handle), >+ handle_bytes_read(handle), handle_bytes_write(handle)); >+ } else { >+ logit("%s%sclosedir \"%s\"", >+ emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", >+ handle_to_name(handle)); >+ } >+} >+ >+static void >+handle_log_exit(void) >+{ >+ u_int i; >+ >+ for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) >+ if (handles[i].use != HANDLE_UNUSED) >+ handle_log_close(i, "forced"); >+} >+ > static int > get_handle(void) > { >@@ -258,10 +349,9 @@ send_msg(Buffer *m) > buffer_consume(m, mlen); > } > >-static void >-send_status(u_int32_t id, u_int32_t status) >+static const char * >+status_to_message(u_int32_t status) > { >- Buffer msg; > const char *status_messages[] = { > "Success", /* SSH_FX_OK */ > "End of file", /* SSH_FX_EOF */ >@@ -274,15 +364,24 @@ send_status(u_int32_t id, u_int32_t stat > "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ > "Unknown error" /* Others */ > }; >+ return (status_messages[MIN(status,SSH2_FX_MAX)]); >+} >+ >+static void >+send_status(u_int32_t id, u_int32_t status) >+{ >+ Buffer msg; > >- TRACE("sent status id %u error %u", id, status); >+ debug3("request %u: sent status %u", id, status); >+ if (log_level > SYSLOG_LEVEL_VERBOSE || >+ (status != SSH2_FX_OK && status != SSH2_FX_EOF)) >+ logit("sent status %s", status_to_message(status)); > buffer_init(&msg); > buffer_put_char(&msg, SSH2_FXP_STATUS); > buffer_put_int(&msg, id); > buffer_put_int(&msg, status); > if (version >= 3) { >- buffer_put_cstring(&msg, >- status_messages[MIN(status,SSH2_FX_MAX)]); >+ buffer_put_cstring(&msg, status_to_message(status)); > buffer_put_cstring(&msg, ""); > } > send_msg(&msg); >@@ -304,7 +403,7 @@ send_data_or_handle(char type, u_int32_t > static void > send_data(u_int32_t id, const char *data, int dlen) > { >- TRACE("sent data id %u len %d", id, dlen); >+ debug("request %u: sent data len %d", id, dlen); > send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); > } > >@@ -315,7 +414,7 @@ send_handle(u_int32_t id, int handle) > int hlen; > > handle_to_string(handle, &string, &hlen); >- TRACE("sent handle id %u handle %d", id, handle); >+ debug("request %u: sent handle handle %d", id, handle); > send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); > xfree(string); > } >@@ -330,7 +429,7 @@ send_names(u_int32_t id, int count, cons > buffer_put_char(&msg, SSH2_FXP_NAME); > buffer_put_int(&msg, id); > buffer_put_int(&msg, count); >- TRACE("sent names id %u count %d", id, count); >+ debug("request %u: sent names count %d", id, count); > for (i = 0; i < count; i++) { > buffer_put_cstring(&msg, stats[i].name); > buffer_put_cstring(&msg, stats[i].long_name); >@@ -345,7 +444,7 @@ send_attrib(u_int32_t id, const Attrib * > { > Buffer msg; > >- TRACE("sent attrib id %u have 0x%x", id, a->flags); >+ debug("request %u: sent attrib have 0x%x", id, a->flags); > buffer_init(&msg); > buffer_put_char(&msg, SSH2_FXP_ATTRS); > buffer_put_int(&msg, id); >@@ -362,7 +461,7 @@ process_init(void) > Buffer msg; > > version = get_int(); >- TRACE("client version %d", version); >+ verbose("received client version %d", version); > buffer_init(&msg); > buffer_put_char(&msg, SSH2_FXP_VERSION); > buffer_put_int(&msg, SSH2_FILEXFER_VERSION); >@@ -379,12 +478,14 @@ process_open(void) > int handle, fd, flags, mode, status = SSH2_FX_FAILURE; > > id = get_int(); >+ debug3("request %u: open flags %d", id, pflags); > name = get_string(NULL); > pflags = get_int(); /* portable flags */ > a = get_attrib(); > flags = flags_from_portable(pflags); > mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; >- TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); >+ logit("open \"%s\" flags %s mode 0%o", >+ name, string_from_portable(pflags), mode); > fd = open(name, flags, mode); > if (fd < 0) { > status = errno_to_portable(errno); >@@ -410,7 +511,8 @@ process_close(void) > > id = get_int(); > handle = get_handle(); >- TRACE("close id %u handle %d", id, handle); >+ debug3("request %u: close handle %u", id, handle); >+ handle_log_close(handle, NULL); > ret = handle_close(handle); > status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; > send_status(id, status); >@@ -429,11 +531,11 @@ process_read(void) > off = get_int64(); > len = get_int(); > >- TRACE("read id %u handle %d off %llu len %d", id, handle, >- (unsigned long long)off, len); >+ debug("request %u: read \"%s\" (handle %d) off %llu len %d", >+ id, handle_to_name(handle), handle, (unsigned long long)off, len); > if (len > sizeof buf) { > len = sizeof buf; >- logit("read change len %d", len); >+ debug2("read change len %d", len); > } > fd = handle_to_fd(handle); > if (fd >= 0) { >@@ -449,6 +551,7 @@ process_read(void) > } else { > send_data(id, buf, ret); > status = SSH2_FX_OK; >+ handle_update_read(handle, ret); > } > } > } >@@ -470,8 +573,8 @@ process_write(void) > off = get_int64(); > data = get_string(&len); > >- TRACE("write id %u handle %d off %llu len %d", id, handle, >- (unsigned long long)off, len); >+ debug("request %u: write \"%s\" (handle %d) off %llu len %d", >+ id, handle_to_name(handle), handle, (unsigned long long)off, len); > fd = handle_to_fd(handle); > if (fd >= 0) { > if (lseek(fd, off, SEEK_SET) < 0) { >@@ -485,8 +588,9 @@ process_write(void) > status = errno_to_portable(errno); > } else if ((size_t)ret == len) { > status = SSH2_FX_OK; >+ handle_update_write(handle, ret); > } else { >- logit("nothing at all written"); >+ debug2("nothing at all written"); > } > } > } >@@ -505,7 +609,8 @@ process_do_stat(int do_lstat) > > id = get_int(); > name = get_string(NULL); >- TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); >+ debug3("request %u: %sstat", id, do_lstat ? "l" : ""); >+ verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); > ret = do_lstat ? lstat(name, &st) : stat(name, &st); > if (ret < 0) { > status = errno_to_portable(errno); >@@ -541,7 +646,8 @@ process_fstat(void) > > id = get_int(); > handle = get_handle(); >- TRACE("fstat id %u handle %d", id, handle); >+ debug("request %u: fstat \"%s\" (handle %u)", >+ id, handle_to_name(handle), handle); > fd = handle_to_fd(handle); > if (fd >= 0) { > ret = fstat(fd, &st); >@@ -580,23 +686,33 @@ process_setstat(void) > id = get_int(); > name = get_string(NULL); > a = get_attrib(); >- TRACE("setstat id %u name %s", id, name); >+ debug("request %u: setstat name \"%s\"", id, name); > if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { >+ logit("set \"%s\" size %llu", name, a->size); > ret = truncate(name, a->size); > if (ret == -1) > 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 & 0777); > if (ret == -1) > status = errno_to_portable(errno); > } > if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { >+ char buf[64]; >+ time_t t = a->mtime; >+ >+ strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", >+ localtime(&t)); >+ logit("set \"%s\" modtime %s", name, buf); > ret = utimes(name, attrib_to_tv(a)); > if (ret == -1) > status = errno_to_portable(errno); > } > 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); >@@ -616,27 +732,39 @@ process_fsetstat(void) > id = get_int(); > handle = get_handle(); > a = get_attrib(); >- TRACE("fsetstat id %u handle %d", id, handle); >+ debug("request %u: fsetstat handle %d", id, handle); > fd = handle_to_fd(handle); > if (fd < 0) { > status = SSH2_FX_FAILURE; > } else { >+ char *name = handle_to_name(handle); >+ > if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { >+ logit("set \"%s\" size %llu", name, a->size); > ret = ftruncate(fd, a->size); > if (ret == -1) > status = errno_to_portable(errno); > } > if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { >+ logit("set \"%s\" mode %04o", name, a->perm); > ret = fchmod(fd, a->perm & 0777); > if (ret == -1) > status = errno_to_portable(errno); > } > if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { >+ char buf[64]; >+ time_t t = a->mtime; >+ >+ strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", >+ localtime(&t)); >+ logit("set \"%s\" modtime %s", name, buf); > ret = futimes(fd, attrib_to_tv(a)); > if (ret == -1) > status = errno_to_portable(errno); > } > if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { >+ logit("set \"%s\" owner %lu group %lu", name, >+ (u_long)a->uid, (u_long)a->gid); > ret = fchown(fd, a->uid, a->gid); > if (ret == -1) > status = errno_to_portable(errno); >@@ -655,7 +783,8 @@ process_opendir(void) > > id = get_int(); > path = get_string(NULL); >- TRACE("opendir id %u path %s", id, path); >+ debug3("request %u: opendir", id); >+ logit("opendir \"%s\"", path); > dirp = opendir(path); > if (dirp == NULL) { > status = errno_to_portable(errno); >@@ -685,14 +814,15 @@ process_readdir(void) > > id = get_int(); > handle = get_handle(); >- TRACE("readdir id %u handle %d", id, handle); >+ debug("request %u: readdir \"%s\" (handle %d)", id, >+ handle_to_name(handle), handle); > dirp = handle_to_dir(handle); > path = handle_to_name(handle); > if (dirp == NULL || path == NULL) { > send_status(id, SSH2_FX_FAILURE); > } else { > struct stat st; >- char pathname[1024]; >+ char pathname[MAXPATHLEN]; > Stat *stats; > int nstats = 10, count = 0, i; > >@@ -739,7 +869,8 @@ process_remove(void) > > id = get_int(); > name = get_string(NULL); >- TRACE("remove id %u name %s", id, name); >+ debug3("request %u: remove", id); >+ logit("remove name \"%s\"", name); > ret = unlink(name); > status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; > send_status(id, status); >@@ -759,7 +890,8 @@ process_mkdir(void) > a = get_attrib(); > mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? > a->perm & 0777 : 0777; >- TRACE("mkdir id %u name %s mode 0%o", id, name, mode); >+ debug3("request %u: mkdir", id); >+ logit("mkdir name \"%s\" mode 0%o", name, mode); > ret = mkdir(name, mode); > status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; > send_status(id, status); >@@ -775,7 +907,8 @@ process_rmdir(void) > > id = get_int(); > name = get_string(NULL); >- TRACE("rmdir id %u name %s", id, name); >+ debug3("request %u: rmdir", id); >+ logit("rmdir name \"%s\"", name); > ret = rmdir(name); > status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; > send_status(id, status); >@@ -795,7 +928,8 @@ process_realpath(void) > xfree(path); > path = xstrdup("."); > } >- TRACE("realpath id %u path %s", id, path); >+ debug3("request %u: realpath", id); >+ verbose("realpath \"%s\"", path); > if (realpath(path, resolvedname) == NULL) { > send_status(id, errno_to_portable(errno)); > } else { >@@ -818,7 +952,8 @@ process_rename(void) > id = get_int(); > oldpath = get_string(NULL); > newpath = get_string(NULL); >- TRACE("rename id %u old %s new %s", id, oldpath, newpath); >+ debug3("request %u: rename", id); >+ logit("rename old \"%s\" new \"%s\"", oldpath, newpath); > status = SSH2_FX_FAILURE; > if (lstat(oldpath, &sb) == -1) > status = errno_to_portable(errno); >@@ -869,7 +1004,8 @@ process_readlink(void) > > id = get_int(); > path = get_string(NULL); >- TRACE("readlink id %u path %s", id, path); >+ debug3("request %u: readlink", id); >+ verbose("readlink \"%s\"", path); > if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) > send_status(id, errno_to_portable(errno)); > else { >@@ -893,7 +1029,8 @@ process_symlink(void) > id = get_int(); > oldpath = get_string(NULL); > newpath = get_string(NULL); >- TRACE("symlink id %u old %s new %s", id, oldpath, newpath); >+ debug3("request %u: symlink", id); >+ logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); > /* this will fail if 'newpath' exists */ > ret = symlink(oldpath, newpath); > status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; >@@ -931,8 +1068,9 @@ process(void) > cp = buffer_ptr(&iqueue); > msg_len = get_u32(cp); > if (msg_len > SFTP_MAX_MSG_LENGTH) { >- error("bad message "); >- exit(11); >+ error("bad message from %s local user %s", >+ client_addr, pw->pw_name); >+ cleanup_exit(11); > } > if (buf_len < msg_len + 4) > return; >@@ -1006,31 +1144,136 @@ process(void) > } > /* discard the remaining bytes from the current packet */ > if (buf_len < buffer_len(&iqueue)) >- fatal("iqueue grows"); >+ fatal("iqueue grew unexpectedly (client %s local user %s)", >+ client_addr, pw->pw_name); > consumed = buf_len - buffer_len(&iqueue); > if (msg_len < consumed) >- fatal("msg_len %d < consumed %d", msg_len, consumed); >+ fatal("msg_len %d < consumed %d (client %s local user %s)", >+ msg_len, consumed, client_addr, pw->pw_name); > if (msg_len > consumed) > buffer_consume(&iqueue, msg_len - consumed); > } > >+static void >+do_chroot(const char *chroot_path_template) >+{ >+ char *cp, *chroot_path; >+ struct group *gr; >+ >+ if ((gr = getgrgid(pw->pw_gid)) == NULL) >+ fatal("No group found for gid %lu", (u_long)pw->pw_gid); >+ >+ cp = percent_expand(chroot_path_template, "d", pw->pw_dir, >+ "u", pw->pw_name, "g", gr->gr_name, (char *)NULL); >+ chroot_path = tilde_expand_filename(cp, getuid()); >+ xfree(cp); >+ >+ logit("chroot to %s", chroot_path); >+ >+ /* Ensure the user has rights to access the chroot path first */ >+ temporarily_use_uid(pw); >+ if (chdir(chroot_path) == -1) >+ fatal("chdir(\"%s\"): %s", chroot_path, strerror(errno)); >+ restore_uid(); >+ >+ if (chroot(chroot_path) == -1) >+ fatal("chroot(\"%s\"): %s", chroot_path, strerror(errno)); >+ if (chdir("/") == -1) >+ fatal("chdir(\"/\"): %s", strerror(errno)); >+ xfree(chroot_path); >+} >+ >+/* Cleanup handler that logs active handles upon normal exit */ >+void >+cleanup_exit(int i) >+{ >+ handle_log_exit(); >+ _exit(i); >+} >+ >+static void >+usage(void) >+{ >+ extern char *__progname; >+ >+ fprintf(stderr, >+ "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); >+ exit(1); >+} >+ > int >-main(int ac, char **av) >+main(int argc, char **argv) > { > fd_set *rset, *wset; >- int in, out, max; >+ int in, out, max, ch, skipargs = 0, log_stderr = 0; > ssize_t len, olen, set_size; >+ SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; >+ char *cp, *chroot_path = NULL; >+ >+ extern int optind; >+ extern char *optarg; >+ extern char *__progname; > > /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ > sanitise_stdfd(); > >- /* XXX should use getopt */ >+ log_init(__progname, log_level, log_facility, log_stderr); > >- handle_init(); >+ while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { >+ switch (ch) { >+ case 'C': >+ chroot_path = optarg; >+ break; >+ case 'c': >+ /* >+ * Ignore all arguments if we are invoked as a >+ * shell using "sftp-server -c command" >+ */ >+ skipargs = 1; >+ break; >+ case 'e': >+ log_stderr = 1; >+ break; >+ case 'l': >+ log_level = log_level_number(optarg); >+ if (log_level == SYSLOG_LEVEL_NOT_SET) >+ error("Invalid log level \"%s\"", optarg); >+ break; >+ case 'f': >+ log_facility = log_facility_number(optarg); >+ if (log_level == SYSLOG_FACILITY_NOT_SET) >+ error("Invalid log facility \"%s\"", optarg); >+ break; >+ case 'h': >+ default: >+ usage(); >+ } >+ } > >-#ifdef DEBUG_SFTP_SERVER >- log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); >-#endif >+ log_init(__progname, log_level, log_facility, log_stderr); >+ >+ if ((cp = getenv("SSH_CONNECTION")) != NULL) { >+ client_addr = xstrdup(cp); >+ if ((cp = strchr(client_addr, ' ')) == NULL) >+ fatal("Malformed SSH_CONNECTION variable: \"%s\"", >+ getenv("SSH_CONNECTION")); >+ *cp = '\0'; >+ } else >+ client_addr = xstrdup("UNKNOWN"); >+ >+ if ((pw = getpwuid(getuid())) == NULL) >+ fatal("No user found for uid %lu", (u_long)getuid()); >+ pw = pwcopy(pw); >+ >+ logit("session opened for client %s local user %s", >+ client_addr, pw->pw_name); >+ >+ if (chroot_path != NULL) >+ do_chroot(chroot_path); >+ if (getuid() != geteuid()) >+ permanently_set_uid(pw); >+ >+ handle_init(); > > in = dup(STDIN_FILENO); > out = dup(STDOUT_FILENO); >@@ -1060,7 +1303,9 @@ main(int ac, char **av) > if (select(max+1, rset, wset, NULL, NULL) < 0) { > if (errno == EINTR) > continue; >- exit(2); >+ error("select %s (client %s local user %s)", >+ strerror(errno), client_addr, pw->pw_name); >+ cleanup_exit(2); > } > > /* copy stdin to iqueue */ >@@ -1069,10 +1314,13 @@ main(int ac, char **av) > len = read(in, buf, sizeof buf); > if (len == 0) { > debug("read eof"); >- exit(0); >+ logit("session closed for client %s " >+ "local user %s", client_addr, pw->pw_name); >+ cleanup_exit(0); > } else if (len < 0) { >- error("read error"); >- exit(1); >+ error("read error %s (client %s local user %s)", >+ strerror(errno), client_addr, pw->pw_name); >+ cleanup_exit(1); > } else { > buffer_append(&iqueue, buf, len); > } >@@ -1081,8 +1329,9 @@ main(int ac, char **av) > if (FD_ISSET(out, wset)) { > len = write(out, buffer_ptr(&oqueue), olen); > if (len < 0) { >- error("write error"); >- exit(1); >+ error("write error %s (client %s local user %s)", >+ strerror(errno), client_addr, pw->pw_name); >+ cleanup_exit(1); > } else { > buffer_consume(&oqueue, len); > } >Index: sshd_config.5 >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/sshd_config.5,v >retrieving revision 1.57 >diff -u -p -r1.57 sshd_config.5 >--- sshd_config.5 14 Mar 2006 16:32:48 -0000 1.57 >+++ sshd_config.5 25 Apr 2006 08:34:48 -0000 >@@ -636,8 +644,8 @@ The default is > .Dq yes . > .It Cm Subsystem > Configures an external subsystem (e.g. file transfer daemon). >-Arguments should be a subsystem name and a command to execute upon subsystem >-request. >+Arguments should be a subsystem name and a command (with optional arguments) >+to execute upon subsystem request. > The command > .Xr sftp-server 8 > implements the >Index: sftp-server/Makefile >=================================================================== >RCS file: /cvs/src/usr.bin/ssh/sftp-server/Makefile,v >retrieving revision 1.6 >diff -u -p -r1.6 Makefile >--- sftp-server/Makefile 18 Apr 2006 10:44:28 -0000 1.6 >+++ sftp-server/Makefile 25 Apr 2006 08:36:12 -0000 >@@ -5,7 +5,7 @@ > PROG= sftp-server > BINOWN= root > >-BINMODE?=555 >+BINMODE?=4555 > > BINDIR= /usr/libexec > MAN= sftp-server.8
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 474
:
835
|
1010
|
1106
| 1126