Filter files received on scp server

Jon Earle earlej at hotmail.com
Sat Aug 5 03:52:39 AEST 2017


That was the perfect guidance, thank you Darren!  We have a Darren here at work, he's ever so helpful too, must be a trait common to "Darrens"!  Haha!


Thanks as well to Malcolm and Philip for their contributions to the discussion!


Anyway, between the "scp protocol for dummies" link and the hint you gave, I worked up our solution (to reject hidden files).  I have included the patch below.  It's a bit more lengthy, owing to my adding a few comments within that function.


Concern about alternate means to create the files is mitigated by not having alternate means to create the files.  In reality, there is no harm in creating them, but, our application won't see them, so there is no point sending them in.


Cheers!

Jon


My reject_hidden_files.patch:

------------------------ 8< ----------------------------
*** openssh-7.5p1/scp.c    2017-03-19 22:39:27.000000000 -0400
--- openssh-7.5p1.new/scp.c    2017-08-04 13:34:49.292281741 -0400
***************
*** 980,993 ****
              ++errs;
              continue;
          }
          if (buf[0] == 'E') {
              (void) atomicio(vwrite, remout, "", 1);
              return;
          }
          if (ch == '\n')
              *--cp = 0;
-
          cp = buf;
          if (*cp == 'T') {
              setimes++;
              cp++;
--- 984,1003 ----
              ++errs;
              continue;
          }
+
+         /* Process protocol messages. */
+
+         /* End of current Directory. */
          if (buf[0] == 'E') {
              (void) atomicio(vwrite, remout, "", 1);
              return;
          }
+
          if (ch == '\n')
              *--cp = 0;
          cp = buf;
+
+         /* Modification and Access times, used with -p option. */
          if (*cp == 'T') {
              setimes++;
              cp++;
***************
*** 1020,1025 ****
--- 1030,1038 ----
              (void) atomicio(vwrite, remout, "", 1);
              continue;
          }
+
+         /* Only other control codes allowed are file (C) and directory (D).
+            Reject any odd code specifiers. */
          if (*cp != 'C' && *cp != 'D') {
              /*
               * Check for the case "rcp remote:foo\* local:bar".
***************
*** 1034,1039 ****
--- 1047,1056 ----
              }
              SCREWUP("expected control record");
          }
+
+         /* Files and directories are specified identically. */
+
+         /* File / Directory mode. */
          mode = 0;
          for (++cp; cp < buf + 5; cp++) {
              if (*cp < '0' || *cp > '7')
***************
*** 1043,1056 ****
          if (*cp++ != ' ')
              SCREWUP("mode not delimited");

          for (size = 0; isdigit((unsigned char)*cp);)
              size = size * 10 + (*cp++ - '0');
          if (*cp++ != ' ')
              SCREWUP("size not delimited");
!         if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) {
              run_err("error: unexpected filename: %s", cp);
              exit(1);
          }
          if (targisdir) {
              static char *namebuf;
              static size_t cursize;
--- 1060,1080 ----
          if (*cp++ != ' ')
              SCREWUP("mode not delimited");

+         /* File / Directory size. Size is ignored for directory. */
          for (size = 0; isdigit((unsigned char)*cp);)
              size = size * 10 + (*cp++ - '0');
          if (*cp++ != ' ')
              SCREWUP("size not delimited");
!
!         /* File / directory name. */
!         if ((strchr(cp, '/') != NULL)   /* Reject files containing path separators. */
!          || (strcmp(cp, "..") == 0)     /* Reject files containing the parent dir. */
!          || (cp[0] == '.'))             /* Reject hidden files. */
!         {
              run_err("error: unexpected filename: %s", cp);
              exit(1);
          }
+
          if (targisdir) {
              static char *namebuf;
              static size_t cursize;
***************
*** 1067,1074 ****
--- 1091,1100 ----
              np = namebuf;
          } else
              np = targ;
+
          curfile = cp;
          exists = stat(np, &stb) == 0;
+
          if (buf[0] == 'D') {
              int mod_flag = pflag;
              if (!iamrecursive)
***************
*** 1114,1119 ****
--- 1145,1151 ----
          cp = bp->buf;
          wrerr = NO;

+         /* Start reading the file data, up to a max of 'size' bytes. */
          statbytes = 0;
          if (showprogress)
              start_progress_meter(curfile, size, &statbytes);
------------------------ 8< ----------------------------



________________________________
From: dtucker at dtucker.net <dtucker at dtucker.net> on behalf of Darren Tucker <dtucker at zip.com.au>
Sent: Friday, August 4, 2017 12:13 AM
To: Jon Earle
Cc: Morham Anthelleron; openssh-unix-dev at mindrot.org
Subject: Re: Filter files received on scp server

On Fri, Aug 4, 2017 at 1:37 PM, Jon Earle <earlej at hotmail.com<mailto:earlej at hotmail.com>> wrote:
Hey,

So, I would be looking at type A. Forgive me if my understanding of how OpenSSH operates is not reflective of reality. I am assuming that, the file transfer is happening somewhat logically, with a name being known, content written, blah blah.

>From reading scp.c, it appears that, the client end at least knows the file name so I must assume the server end must be given it.

scp merely uses ssh as an 8-bit-clean transport, your copy is two cooperating scp processes (the remote one having the '-t' flag as you noted).  For your purposes you can ignore ssh/sshd and just focus on scp.

In scp, the data and control messages are sent over stdin/stdout, anything on stderr on the remote end will get passed back to the client and shown on the client's terminal.  A good description of the protocol, such as it is, is here: https://web.archive.org/web/20170215184048/https://blogs.oracle.com/janp/entry/how_the_scp_protocol_works

There is no mechanism in the existing scp program to do filtering.  You could modify your scp to do this; I'd suggest looking at the sink() function (look for "namebuf") but be aware that the entire thing dates back to 4.2BSD and it's not the prettiest code ever.  Note the also "sink" will be used when copying onto a machine when scp is used as the client.

Note that you need to ensure that your users cant create files any other way (sftp, tar, shell redirection...) otherwise the exercise will be pointless.

--
Darren Tucker (dtucker at zip.com.au<http://zip.com.au>)
GPG key 11EAA6FA / A86E 3E07 5B19 5880 E860  37F4 9357 ECEF 11EA A6FA (new)
    Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.


More information about the openssh-unix-dev mailing list