[BACK]Return to sftp-client.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Diff for /src/usr.bin/ssh/sftp-client.c between version 1.88 and 1.89

version 1.88, 2009/08/14 18:17:49 version 1.89, 2009/08/18 18:36:20
Line 28 
Line 28 
 #include <sys/statvfs.h>  #include <sys/statvfs.h>
 #include <sys/uio.h>  #include <sys/uio.h>
   
   #include <dirent.h>
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <signal.h>  #include <signal.h>
Line 53 
Line 54 
 /* Minimum amount of data to read at a time */  /* Minimum amount of data to read at a time */
 #define MIN_READ_SIZE   512  #define MIN_READ_SIZE   512
   
   /* Maximum depth to descend in directory trees */
   #define MAX_DIR_DEPTH 64
   
 struct sftp_conn {  struct sftp_conn {
         int fd_in;          int fd_in;
         int fd_out;          int fd_out;
Line 489 
Line 493 
                         if (printflag)                          if (printflag)
                                 printf("%s\n", longname);                                  printf("%s\n", longname);
   
                           /*
                            * Directory entries should never contain '/'
                            * These can be used to attack recursive ops
                            * (e.g. send '../../../../etc/passwd')
                            */
                           if (strchr(filename, '/') != NULL) {
                                   error("Server sent suspect path \"%s\" "
                                       "during readdir of \"%s\"", filename, path);
                                   goto next;
                           }
   
                         if (dir) {                          if (dir) {
                                 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));                                  *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
                                 (*dir)[ents] = xmalloc(sizeof(***dir));                                  (*dir)[ents] = xmalloc(sizeof(***dir));
Line 497 
Line 512 
                                 memcpy(&(*dir)[ents]->a, a, sizeof(*a));                                  memcpy(&(*dir)[ents]->a, a, sizeof(*a));
                                 (*dir)[++ents] = NULL;                                  (*dir)[++ents] = NULL;
                         }                          }
    next:
                         xfree(filename);                          xfree(filename);
                         xfree(longname);                          xfree(longname);
                 }                  }
Line 552 
Line 567 
 }  }
   
 int  int
 do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)  do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
 {  {
         u_int status, id;          u_int status, id;
   
Line 561 
Line 576 
             strlen(path), a);              strlen(path), a);
   
         status = get_status(conn->fd_in, id);          status = get_status(conn->fd_in, id);
         if (status != SSH2_FX_OK)          if (status != SSH2_FX_OK && printflag)
                 error("Couldn't create directory: %s", fx2txt(status));                  error("Couldn't create directory: %s", fx2txt(status));
   
         return(status);          return(status);
Line 900 
Line 915 
   
 int  int
 do_download(struct sftp_conn *conn, char *remote_path, char *local_path,  do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
     int pflag)      Attrib *a, int pflag)
 {  {
         Attrib junk, *a;          Attrib junk;
         Buffer msg;          Buffer msg;
         char *handle;          char *handle;
         int local_fd, status = 0, write_error;          int local_fd, status = 0, write_error;
Line 921 
Line 936 
   
         TAILQ_INIT(&requests);          TAILQ_INIT(&requests);
   
         a = do_stat(conn, remote_path, 0);          if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
         if (a == NULL)                  return -1;
                 return(-1);  
   
         /* Do not preserve set[ug]id here, as we do not preserve ownership */          /* Do not preserve set[ug]id here, as we do not preserve ownership */
         if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)          if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
Line 1134 
Line 1148 
         return(status);          return(status);
 }  }
   
   static int
   download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
       Attrib *dirattrib, int pflag, int printflag, int depth)
   {
           int i, ret = 0;
           SFTP_DIRENT **dir_entries;
           char *filename, *new_src, *new_dst;
           mode_t mode = 0777;
   
           if (depth >= MAX_DIR_DEPTH) {
                   error("Maximum directory depth exceeded: %d levels", depth);
                   return -1;
           }
   
           if (dirattrib == NULL &&
               (dirattrib = do_stat(conn, src, 1)) == NULL) {
                   error("Unable to stat remote directory \"%s\"", src);
                   return -1;
           }
           if (!S_ISDIR(dirattrib->perm)) {
                   error("\"%s\" is not a directory", src);
                   return -1;
           }
           if (printflag)
                   printf("Retrieving %s\n", src);
   
           if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
                   mode = dirattrib->perm & 01777;
           else {
                   debug("Server did not send permissions for "
                       "directory \"%s\"", dst);
           }
   
           if (mkdir(dst, mode) == -1 && errno != EEXIST) {
                   error("mkdir %s: %s", dst, strerror(errno));
                   return -1;
           }
   
           if (do_readdir(conn, src, &dir_entries) == -1) {
                   error("%s: Failed to get directory contents", src);
                   return -1;
           }
   
           for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
                   filename = dir_entries[i]->filename;
   
                   new_dst = path_append(dst, filename);
                   new_src = path_append(src, filename);
   
                   if (S_ISDIR(dir_entries[i]->a.perm)) {
                           if (strcmp(filename, ".") == 0 ||
                               strcmp(filename, "..") == 0)
                                   continue;
                           if (download_dir_internal(conn, new_src, new_dst,
                               &(dir_entries[i]->a), pflag, printflag,
                               depth + 1) == -1)
                                   ret = -1;
                   } else if (S_ISREG(dir_entries[i]->a.perm) ) {
                           if (do_download(conn, new_src, new_dst,
                               &(dir_entries[i]->a), pflag) == -1) {
                                   error("Download of file %s to %s failed",
                                       new_src, new_dst);
                                   ret = -1;
                           }
                   } else
                           logit("%s: not a regular file\n", new_src);
   
                   xfree(new_dst);
                   xfree(new_src);
           }
   
           if (pflag) {
                   if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
                           struct timeval tv[2];
                           tv[0].tv_sec = dirattrib->atime;
                           tv[1].tv_sec = dirattrib->mtime;
                           tv[0].tv_usec = tv[1].tv_usec = 0;
                           if (utimes(dst, tv) == -1)
                                   error("Can't set times on \"%s\": %s",
                                       dst, strerror(errno));
                   } else
                           debug("Server did not send times for directory "
                               "\"%s\"", dst);
           }
   
           free_sftp_dirents(dir_entries);
   
           return ret;
   }
   
 int  int
   download_dir(struct sftp_conn *conn, char *src, char *dst,
       Attrib *dirattrib, int pflag, int printflag)
   {
           char *src_canon;
           int ret;
   
           if ((src_canon = do_realpath(conn, src)) == NULL) {
                   error("Unable to canonicalise path \"%s\"", src);
                   return -1;
           }
   
           ret = download_dir_internal(conn, src_canon, dst,
               dirattrib, pflag, printflag, 0);
           xfree(src_canon);
           return ret;
   }
   
   int
 do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,  do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
     int pflag)      int pflag)
 {  {
Line 1315 
Line 1437 
   
         return status;          return status;
 }  }
   
   static int
   upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
       int pflag, int printflag, int depth)
   {
           int ret = 0, status;
           DIR *dirp;
           struct dirent *dp;
           char *filename, *new_src, *new_dst;
           struct stat sb;
           Attrib a;
   
           if (depth >= MAX_DIR_DEPTH) {
                   error("Maximum directory depth exceeded: %d levels", depth);
                   return -1;
           }
   
           if (stat(src, &sb) == -1) {
                   error("Couldn't stat directory \"%s\": %s",
                       src, strerror(errno));
                   return -1;
           }
           if (!S_ISDIR(sb.st_mode)) {
                   error("\"%s\" is not a directory", src);
                   return -1;
           }
           if (printflag)
                   printf("Entering %s\n", src);
   
           attrib_clear(&a);
           stat_to_attrib(&sb, &a);
           a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
           a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
           a.perm &= 01777;
           if (!pflag)
                   a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
   
           status = do_mkdir(conn, dst, &a, 0);
           /*
            * we lack a portable status for errno EEXIST,
            * so if we get a SSH2_FX_FAILURE back we must check
            * if it was created successfully.
            */
           if (status != SSH2_FX_OK) {
                   if (status != SSH2_FX_FAILURE)
                           return -1;
                   if (do_stat(conn, dst, 0) == NULL)
                           return -1;
           }
   
           if ((dirp = opendir(src)) == NULL) {
                   error("Failed to open dir \"%s\": %s", src, strerror(errno));
                   return -1;
           }
   
           while (((dp = readdir(dirp)) != NULL) && !interrupted) {
                   if (dp->d_ino == 0)
                           continue;
                   filename = dp->d_name;
                   new_dst = path_append(dst, filename);
                   new_src = path_append(src, filename);
   
                   if (S_ISDIR(DTTOIF(dp->d_type))) {
                           if (strcmp(filename, ".") == 0 ||
                               strcmp(filename, "..") == 0)
                                   continue;
   
                           if (upload_dir_internal(conn, new_src, new_dst,
                               pflag, depth + 1, printflag) == -1)
                                   ret = -1;
                   } else if (S_ISREG(DTTOIF(dp->d_type)) ) {
                           if (do_upload(conn, new_src, new_dst, pflag) == -1) {
                                   error("Uploading of file %s to %s failed!",
                                       new_src, new_dst);
                                   ret = -1;
                           }
                   } else
                           logit("%s: not a regular file\n", filename);
                   xfree(new_dst);
                   xfree(new_src);
           }
   
           do_setstat(conn, dst, &a);
   
           (void) closedir(dirp);
           return ret;
   }
   
   int
   upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
       int pflag)
   {
           char *dst_canon;
           int ret;
   
           if ((dst_canon = do_realpath(conn, dst)) == NULL) {
                   error("Unable to canonicalise path \"%s\"", dst);
                   return -1;
           }
   
           ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
           xfree(dst_canon);
           return ret;
   }
   
   char *
   path_append(char *p1, char *p2)
   {
           char *ret;
           size_t len = strlen(p1) + strlen(p2) + 2;
   
           ret = xmalloc(len);
           strlcpy(ret, p1, len);
           if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
                   strlcat(ret, "/", len);
           strlcat(ret, p2, len);
   
           return(ret);
   }
   

Legend:
Removed from v.1.88  
changed lines
  Added in v.1.89