Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 46 additions & 16 deletions src/libgit2/transports/ssh_exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,16 @@ GIT_INLINE(int) ensure_transport_state(
}

static int get_ssh_cmdline(
git_str *out,
git_vector *args,
ssh_exec_subtransport *transport,
git_net_url *url,
const char *command)
{
git_remote *remote = ((transport_smart *)transport->owner)->owner;
git_repository *repo = remote->repo;
git_config *cfg;
git_str ssh_cmd = GIT_STR_INIT;
git_str ssh_cmd = GIT_STR_INIT, ssh_path = GIT_STR_INIT,
url_and_host = GIT_STR_INIT, remote_cmd = GIT_STR_INIT;
const char *default_ssh_cmd = "ssh";
int error;

Expand Down Expand Up @@ -158,17 +159,45 @@ static int get_ssh_cmdline(
else if ((error = git_config__get_string_buf(&ssh_cmd, cfg, "core.sshcommand")) < 0 && error != GIT_ENOTFOUND)
goto done;

error = git_str_printf(out, "%s %s %s \"%s%s%s\" \"%s '%s'\"",
ssh_cmd.size > 0 ? ssh_cmd.ptr : default_ssh_cmd,
url->port_specified ? "-p" : "",
url->port_specified ? url->port : "",
url->username ? url->username : "",
url->username ? "@" : "",
url->host,
command,
url->path);
git_error_clear();

if ((error = git_fs_path_find_executable(&ssh_path,
ssh_cmd.size > 0 ? ssh_cmd.ptr : default_ssh_cmd)) < 0)
goto done;

if ((error = git_vector_insert(args, git_str_detach(&ssh_path))) < 0)
goto done;

if (url->port_specified) {
char *p = git__strdup("-p");
char *port = git__strdup(url->port);

if (!p || !port ||
(error = git_vector_insert(args, p)) < 0 ||
(error = git_vector_insert(args, port)) < 0)
goto done;
}

if (url->username) {
if ((error = git_str_puts(&url_and_host, url->username)) < 0 ||
(error = git_str_putc(&url_and_host, '@')) < 0)
goto done;
}

if ((error = git_str_puts(&url_and_host, url->host)) < 0 ||
(error = git_vector_insert(args, git_str_detach(&url_and_host))) < 0)
goto done;

if ((error = git_str_puts(&remote_cmd, command)) < 0 ||
(error = git_str_puts(&remote_cmd, " '")) < 0 ||
(error = git_str_puts_escaped(&remote_cmd, url->path, "'!", "'\\", "'")) < 0 ||
(error = git_str_puts(&remote_cmd, "'")) < 0 ||
(error = git_vector_insert(args, git_str_detach(&remote_cmd))) < 0)
goto done;

done:
git_str_dispose(&remote_cmd);
git_str_dispose(&url_and_host);
git_str_dispose(&ssh_cmd);
git_config_free(cfg);
return error;
Expand All @@ -183,7 +212,7 @@ static int start_ssh(

git_process_options process_opts = GIT_PROCESS_OPTIONS_INIT;
git_net_url url = GIT_NET_URL_INIT;
git_str ssh_cmdline = GIT_STR_INIT;
git_vector args = GIT_VECTOR_INIT;
const char *command;
int error;

Expand Down Expand Up @@ -214,19 +243,20 @@ static int start_ssh(
if (error < 0)
goto done;

if ((error = get_ssh_cmdline(&ssh_cmdline, transport, &url, command)) < 0)
if ((error = get_ssh_cmdline(&args, transport, &url, command)) < 0)
goto done;

if ((error = git_process_new_from_cmdline(&transport->process,
ssh_cmdline.ptr, env, ARRAY_SIZE(env), &process_opts)) < 0 ||
if ((error = git_process_new(&transport->process,
(const char **)args.contents, args.length,
env, ARRAY_SIZE(env), &process_opts)) < 0 ||
(error = git_process_start(transport->process)) < 0) {
git_process_free(transport->process);
transport->process = NULL;
goto done;
}

done:
git_str_dispose(&ssh_cmdline);
git_vector_dispose_deep(&args);
git_net_url_dispose(&url);
return error;
}
Expand Down
72 changes: 66 additions & 6 deletions src/util/fs_path.c
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,37 @@ bool git_fs_path_isfile(const char *path)
return S_ISREG(st.st_mode) != 0;
}

#ifdef GIT_WIN32

bool git_fs_path_isexecutable(const char *path)
{
struct stat st;

GIT_ASSERT_ARG_WITH_RETVAL(path, false);

if (git__suffixcmp_icase(path, ".exe") != 0 &&
git__suffixcmp_icase(path, ".cmd") != 0)
return false;

return (p_stat(path, &st) == 0);
}

#else

bool git_fs_path_isexecutable(const char *path)
{
struct stat st;

GIT_ASSERT_ARG_WITH_RETVAL(path, false);
if (p_stat(path, &st) < 0)
return false;

return S_ISREG(st.st_mode) != 0 &&
((st.st_mode & S_IXUSR) != 0);
}

#endif

bool git_fs_path_islink(const char *path)
{
struct stat st;
Expand Down Expand Up @@ -2020,9 +2051,10 @@ int git_fs_path_owner_is_system(bool *out, const char *path)
return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_ADMINISTRATOR);
}

int git_fs_path_find_executable(git_str *fullpath, const char *executable)
{
#ifdef GIT_WIN32

static int find_executable(git_str *fullpath, const char *executable)
{
git_win32_path fullpath_w, executable_w;
int error;

Expand All @@ -2035,9 +2067,15 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
error = git_str_put_w(fullpath, fullpath_w, wcslen(fullpath_w));

return error;
}

#else

static int find_executable(git_str *fullpath, const char *executable)
{
git_str path = GIT_STR_INIT;
const char *current_dir, *term;
size_t current_dirlen;
bool found = false;

if (git__getenv(&path, "PATH") < 0)
Expand All @@ -2049,20 +2087,28 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)
if (! (term = strchr(current_dir, GIT_PATH_LIST_SEPARATOR)))
term = strchr(current_dir, '\0');

current_dirlen = term - current_dir;
git_str_clear(fullpath);
if (git_str_put(fullpath, current_dir, (term - current_dir)) < 0 ||
git_str_putc(fullpath, '/') < 0 ||

/* An empty path segment is treated as '.' */
if (current_dirlen == 0 && git_str_putc(fullpath, '.'))
return -1;
else if (current_dirlen != 0 &&
git_str_put(fullpath, current_dir, current_dirlen) < 0)
return -1;

if (git_str_putc(fullpath, '/') < 0 ||
git_str_puts(fullpath, executable) < 0)
return -1;

if (git_fs_path_isfile(fullpath->ptr)) {
if (git_fs_path_isexecutable(fullpath->ptr)) {
found = true;
break;
}

current_dir = term;

while (*current_dir == GIT_PATH_LIST_SEPARATOR)
if (*current_dir == GIT_PATH_LIST_SEPARATOR)
current_dir++;
}

Expand All @@ -2073,5 +2119,19 @@ int git_fs_path_find_executable(git_str *fullpath, const char *executable)

git_str_clear(fullpath);
return GIT_ENOTFOUND;
}

#endif

int git_fs_path_find_executable(git_str *fullpath, const char *executable)
{
/* For qualified paths we do not look in PATH */
if (strchr(executable, '/') != NULL) {
if (!git_fs_path_isexecutable(executable))
return GIT_ENOTFOUND;

return git_str_puts(fullpath, executable);
}

return find_executable(fullpath, executable);
}
6 changes: 6 additions & 0 deletions src/util/fs_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ extern bool git_fs_path_isdir(const char *path);
*/
extern bool git_fs_path_isfile(const char *path);

/**
* Check if the given path points to an executable.
* @return true or false
*/
extern bool git_fs_path_isexecutable(const char *path);

/**
* Check if the given path points to a symbolic link.
* @return true or false
Expand Down
4 changes: 4 additions & 0 deletions src/util/process.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ extern int git_process_new(
* are not thread safe, so you may not modify the environment during
* this call. You can avoid this by setting `exclude_env` in the
* options and providing the entire environment yourself.
*
* This function passes the `cmdline` to a shell and DOES NOT perform
* any safe escaping on the `cmdline` and SHOULD NOT be used on user
* input. Instead, use `git_process_new`.
*/
extern int git_process_new_from_cmdline(
git_process **out,
Expand Down
24 changes: 18 additions & 6 deletions src/util/str.c
Original file line number Diff line number Diff line change
Expand Up @@ -1065,10 +1065,13 @@ int git_str_puts_escaped(
git_str *buf,
const char *string,
const char *esc_chars,
const char *esc_with)
const char *esc_prefix,
const char *esc_suffix)
{
const char *scan;
size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
size_t total = 0, count, alloclen;
size_t esc_prefix_len = esc_prefix ? strlen(esc_prefix) : 0;
size_t esc_suffix_len = esc_suffix ? strlen(esc_suffix) : 0;

if (!string)
return 0;
Expand All @@ -1080,7 +1083,7 @@ int git_str_puts_escaped(
scan += count;
/* count run of escaped characters */
count = strspn(scan, esc_chars);
total += count * (esc_len + 1);
total += count * (esc_prefix_len + esc_suffix_len + 1);
scan += count;
}

Expand All @@ -1096,13 +1099,22 @@ int git_str_puts_escaped(
buf->size += count;

for (count = strspn(scan, esc_chars); count > 0; --count) {
/* copy escape sequence */
memmove(buf->ptr + buf->size, esc_with, esc_len);
buf->size += esc_len;
/* copy escape prefix sequence */
if (esc_prefix) {
memmove(buf->ptr + buf->size, esc_prefix, esc_prefix_len);
buf->size += esc_prefix_len;
}

/* copy character to be escaped */
buf->ptr[buf->size] = *scan;
buf->size++;
scan++;

/* copy escape suffix sequence */
if (esc_suffix) {
memmove(buf->ptr + buf->size, esc_suffix, esc_suffix_len);
buf->size += esc_suffix_len;
}
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/util/str.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,21 +268,23 @@ int git_str_splice(
* @param str String buffer to append data to
* @param string String to escape and append
* @param esc_chars Characters to be escaped
* @param esc_with String to insert in from of each found character
* @param esc_prefix String to insert as prefix of each found character
* @param esc_suffix String to insert as suffix of each found character
* @return 0 on success, <0 on failure (probably allocation problem)
*/
extern int git_str_puts_escaped(
git_str *str,
const char *string,
const char *esc_chars,
const char *esc_with);
const char *esc_prefix,
const char *esc_suffix);

/**
* Append string escaping characters that are regex special
*/
GIT_INLINE(int) git_str_puts_escape_regex(git_str *str, const char *string)
{
return git_str_puts_escaped(str, string, "^.[]$()|*+?{}\\", "\\");
return git_str_puts_escaped(str, string, "^.[]$()|*+?{}\\", "\\", NULL);
}

/**
Expand Down
17 changes: 15 additions & 2 deletions src/util/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,26 @@ int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
return prefixcmp(str, str_n, prefix, true);
}

int git__suffixcmp(const char *str, const char *suffix)
static int suffixcmp(const char *str, const char *suffix, bool icase)
{
size_t a = strlen(str);
size_t b = strlen(suffix);

if (a < b)
return -1;
return strcmp(str + (a - b), suffix);

return icase ? strcasecmp(str + (a - b), suffix) :
strcmp(str + (a - b), suffix);
}

int git__suffixcmp(const char *str, const char *suffix)
{
return suffixcmp(str, suffix, false);
}

int git__suffixcmp_icase(const char *str, const char *suffix)
{
return suffixcmp(str, suffix, true);
}

char *git__strtok(char **end, const char *sep)
Expand Down
1 change: 1 addition & 0 deletions src/util/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ extern int git__prefixcmp_icase(const char *str, const char *prefix);
extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix);
extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
extern int git__suffixcmp(const char *str, const char *suffix);
extern int git__suffixcmp_icase(const char *str, const char *suffix);

GIT_INLINE(int) git__signum(int val)
{
Expand Down
Loading
Loading