Ping?
Christian Heckendorf wrote:
> Several sftp(1) commands will fail if the remote pwd contains glob(3)
> meta characters. This is due to glob being called on the concatenated
> pwd and command argument in order to generate a file list to operate
> on. However, the pwd is not escaped before calling glob, so glob
> will offer no useful results and sftp will error "not found".
>
> The following commands will not work in some or all conditions when
> called from one of these meta character directories: get, rm, chmod,
> chown, chgrp, ls, and tab-completions.
>
> The diff below extends the make_absolute() function to optionally
> add escape characters to the pwd before generating the absolute
> path version of the argument which will be sent to glob. I'd be
> happy to rewrite if a different solution is preferable.
>
> --
> Christian
>
>
> Index: sftp.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/ssh/sftp.c,v
> retrieving revision 1.171
> diff -u -p -r1.171 sftp.c
> --- sftp.c 20 Aug 2015 22:32:42 -0000 1.171
> +++ sftp.c 27 Dec 2015 21:03:08 -0000
> @@ -154,6 +154,10 @@ struct CMD {
> #define REMOTE 1
> #define LOCAL 2
>
> +/* Options for handling glob meta chars in pwd */
> +#define NOESCAPE_META 0
> +#define ESCAPE_META 1
> +
> static const struct CMD cmds[] = {
> { "bye", I_QUIT, NOARGS },
> { "cd", I_CHDIR, REMOTE },
> @@ -330,14 +334,63 @@ path_strip(char *path, char *strip)
> return (xstrdup(path));
> }
>
> +/* Escape any glob(3) meta characters in the pwd */
> static char *
> -make_absolute(char *p, char *pwd)
> +escape_glob_meta(char *pwd)
> {
> - char *abs_str;
> + size_t i, j, expand_len, len;
> + char *esc_pwd;
> +
> + len = strlen(pwd);
> +
> + expand_len = 0;
> + for (i = 0; i < len; i++) {
> + switch (pwd[i]) {
> + case '*':
> + case '?':
> + case '[':
> + case '\\':
> + expand_len++;
> + break;
> + }
> + }
> +
> + esc_pwd = xmalloc(len + expand_len + 1);
> +
> + for (i = j = 0; i < len; i++) {
> + switch (pwd[i]) {
> + case '*':
> + case '?':
> + case '[':
> + case '\\':
> + esc_pwd[j++] = '\\';
> + /* FALLTHROUGH */
> + default:
> + esc_pwd[j++] = pwd[i];
> + break;
> + }
> + }
> + esc_pwd[j] = 0;
> +
> + return esc_pwd;
> +}
> +
> +static char *
> +make_absolute(char *p, char *pwd, int escopt)
> +{
> + char *abs_str, *tmp_pwd;
>
> /* Derelativise */
> if (p && p[0] != '/') {
> - abs_str = path_append(pwd, p);
> + if(escopt == ESCAPE_META)
> + tmp_pwd = escape_glob_meta(pwd);
> + else
> + tmp_pwd = pwd;
> +
> + abs_str = path_append(tmp_pwd, p);
> +
> + if(escopt == ESCAPE_META)
> + free(tmp_pwd);
> free(p);
> return(abs_str);
> } else
> @@ -573,7 +626,7 @@ process_get(struct sftp_conn *conn, char
> int i, r, err = 0;
>
> abs_src = xstrdup(src);
> - abs_src = make_absolute(abs_src, pwd);
> + abs_src = make_absolute(abs_src, pwd, ESCAPE_META);
> memset(&g, 0, sizeof(g));
>
> debug3("Looking up %s", abs_src);
> @@ -660,7 +713,7 @@ process_put(struct sftp_conn *conn, char
>
> if (dst) {
> tmp_dst = xstrdup(dst);
> - tmp_dst = make_absolute(tmp_dst, pwd);
> + tmp_dst = make_absolute(tmp_dst, pwd, NOESCAPE_META);
> }
>
> memset(&g, 0, sizeof(g));
> @@ -707,7 +760,8 @@ process_put(struct sftp_conn *conn, char
> } else if (tmp_dst) {
> abs_dst = path_append(tmp_dst, filename);
> } else {
> - abs_dst = make_absolute(xstrdup(filename), pwd);
> + abs_dst = make_absolute(xstrdup(filename), pwd,
> + NOESCAPE_META);
> }
> free(tmp);
>
> @@ -1417,20 +1471,20 @@ parse_dispatch_command(struct sftp_conn
> rflag, aflag, fflag);
> break;
> case I_RENAME:
> - path1 = make_absolute(path1, *pwd);
> - path2 = make_absolute(path2, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> + path2 = make_absolute(path2, *pwd, NOESCAPE_META);
> err = do_rename(conn, path1, path2, lflag);
> break;
> case I_SYMLINK:
> sflag = 1;
> case I_LINK:
> if (!sflag)
> - path1 = make_absolute(path1, *pwd);
> - path2 = make_absolute(path2, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> + path2 = make_absolute(path2, *pwd, NOESCAPE_META);
> err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2);
> break;
> case I_RM:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, ESCAPE_META);
> remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
> for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
> if (!quiet)
> @@ -1441,18 +1495,18 @@ parse_dispatch_command(struct sftp_conn
> }
> break;
> case I_MKDIR:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> attrib_clear(&a);
> a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
> a.perm = 0777;
> err = do_mkdir(conn, path1, &a, 1);
> break;
> case I_RMDIR:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> err = do_rmdir(conn, path1);
> break;
> case I_CHDIR:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> if ((tmp = do_realpath(conn, path1)) == NULL) {
> err = 1;
> break;
> @@ -1489,14 +1543,14 @@ parse_dispatch_command(struct sftp_conn
> if (*path1 != '/')
> tmp = *pwd;
>
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, ESCAPE_META);
> err = do_globbed_ls(conn, path1, tmp, lflag);
> break;
> case I_DF:
> /* Default to current directory if no path specified */
> if (path1 == NULL)
> path1 = xstrdup(*pwd);
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, NOESCAPE_META);
> err = do_df(conn, path1, hflag, iflag);
> break;
> case I_LCHDIR:
> @@ -1527,7 +1581,7 @@ parse_dispatch_command(struct sftp_conn
> printf("Local umask: %03lo\n", n_arg);
> break;
> case I_CHMOD:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, ESCAPE_META);
> attrib_clear(&a);
> a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
> a.perm = n_arg;
> @@ -1542,7 +1596,7 @@ parse_dispatch_command(struct sftp_conn
> break;
> case I_CHOWN:
> case I_CHGRP:
> - path1 = make_absolute(path1, *pwd);
> + path1 = make_absolute(path1, *pwd, ESCAPE_META);
> remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g);
> for (i = 0; g.gl_pathv[i] && !interrupted; i++) {
> if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) {
> @@ -1814,7 +1868,7 @@ complete_match(EditLine *el, struct sftp
>
> memset(&g, 0, sizeof(g));
> if (remote != LOCAL) {
> - tmp = make_absolute(tmp, remote_path);
> + tmp = make_absolute(tmp, remote_path, ESCAPE_META);
> remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
> } else
> glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g);
> @@ -2032,7 +2086,7 @@ interactive_loop(struct sftp_conn *conn,
>
> if (file1 != NULL) {
> dir = xstrdup(file1);
> - dir = make_absolute(dir, remote_path);
> + dir = make_absolute(dir, remote_path, NOESCAPE_META);
>
> if (remote_is_dir(conn, dir) && file2 == NULL) {
> if (!quiet)
> @@ -2046,6 +2100,10 @@ interactive_loop(struct sftp_conn *conn,
> return (-1);
> }
> } else {
> + free(dir);
> + dir = xstrdup(file1);
> + dir = make_absolute(dir, remote_path, ESCAPE_META);
> +
> /* XXX this is wrong wrt quoting */
> snprintf(cmd, sizeof cmd, "get%s %s%s%s",
> global_aflag ? " -a" : "", dir,
>