diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2023-02-14 16:36:19 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-14 16:36:19 +0000 |
commit | f7963f28cad7b2416a3cb0e55bc5adf85c82607b (patch) | |
tree | ec49ba5e08c74de903a9b99e712cf4e2192ec6bd /src | |
parent | 1119326aadfca4062679722b3ed975b569bcc0cf (diff) | |
parent | e0220e6a222b056ab44d68f41a62012077d2a3f3 (diff) | |
download | libgit2-f7963f28cad7b2416a3cb0e55bc5adf85c82607b.tar.gz |
Merge pull request #6455 from libgit2/ethomson/sysdir
Support the notion of a home directory separately from global configuration directory
Diffstat (limited to 'src')
-rw-r--r-- | src/libgit2/attrcache.c | 2 | ||||
-rw-r--r-- | src/libgit2/config.c | 2 | ||||
-rw-r--r-- | src/libgit2/config_file.c | 4 | ||||
-rw-r--r-- | src/libgit2/libgit2.c | 19 | ||||
-rw-r--r-- | src/libgit2/sysdir.c | 301 | ||||
-rw-r--r-- | src/libgit2/sysdir.h | 48 | ||||
-rw-r--r-- | src/libgit2/transports/ssh.c | 105 | ||||
-rw-r--r-- | src/util/futils.c | 3 | ||||
-rw-r--r-- | src/util/win32/findfile.c | 286 | ||||
-rw-r--r-- | src/util/win32/findfile.h | 22 |
10 files changed, 408 insertions, 384 deletions
diff --git a/src/libgit2/attrcache.c b/src/libgit2/attrcache.c index b16d95c3c..405944ed1 100644 --- a/src/libgit2/attrcache.c +++ b/src/libgit2/attrcache.c @@ -300,7 +300,7 @@ static int attr_cache__lookup_path( /* expand leading ~/ as needed */ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') { - if (! (error = git_sysdir_expand_global_file(&buf, &cfgval[2]))) + if (! (error = git_sysdir_expand_homedir_file(&buf, &cfgval[2]))) *out = git_str_detach(&buf); } else if (cfgval) { *out = git__strdup(cfgval); diff --git a/src/libgit2/config.c b/src/libgit2/config.c index 5c366e221..6d15a8db6 100644 --- a/src/libgit2/config.c +++ b/src/libgit2/config.c @@ -860,7 +860,7 @@ static int git_config__parse_path(git_str *out, const char *value) return -1; } - return git_sysdir_expand_global_file(out, value[1] ? &value[2] : NULL); + return git_sysdir_expand_homedir_file(out, value[1] ? &value[2] : NULL); } return git_str_sets(out, value); diff --git a/src/libgit2/config_file.c b/src/libgit2/config_file.c index 66fcb8ae2..932ca7601 100644 --- a/src/libgit2/config_file.c +++ b/src/libgit2/config_file.c @@ -528,7 +528,7 @@ static int included_path(git_str *out, const char *dir, const char *path) { /* From the user's home */ if (path[0] == '~' && path[1] == '/') - return git_sysdir_expand_global_file(out, &path[1]); + return git_sysdir_expand_homedir_file(out, &path[1]); return git_fs_path_join_unrooted(out, path, dir, NULL); } @@ -616,7 +616,7 @@ static int do_match_gitdir( git_fs_path_dirname_r(&pattern, cfg_file); git_str_joinpath(&pattern, pattern.ptr, condition + 2); } else if (condition[0] == '~' && git_fs_path_is_dirsep(condition[1])) - git_sysdir_expand_global_file(&pattern, condition + 1); + git_sysdir_expand_homedir_file(&pattern, condition + 1); else if (!git_fs_path_is_absolute(condition)) git_str_joinpath(&pattern, "**", condition); else diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c index 2fda0722e..f225122e5 100644 --- a/src/libgit2/libgit2.c +++ b/src/libgit2/libgit2.c @@ -414,6 +414,25 @@ int git_libgit2_opts(int key, ...) git_repository__validate_ownership = (va_arg(ap, int) != 0); break; + case GIT_OPT_GET_HOMEDIR: + { + git_buf *out = va_arg(ap, git_buf *); + git_str str = GIT_STR_INIT; + const git_str *tmp; + + if ((error = git_buf_tostr(&str, out)) < 0 || + (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 || + (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0) + break; + + error = git_buf_fromstr(out, &str); + } + break; + + case GIT_OPT_SET_HOMEDIR: + error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *)); + break; + default: git_error_set(GIT_ERROR_INVALID, "invalid option key"); error = -1; diff --git a/src/libgit2/sysdir.c b/src/libgit2/sysdir.c index 450cb509b..7838a6789 100644 --- a/src/libgit2/sysdir.c +++ b/src/libgit2/sysdir.c @@ -12,16 +12,262 @@ #include "fs_path.h" #include <ctype.h> #if GIT_WIN32 -#include "win32/findfile.h" +# include "fs_path.h" +# include "win32/path_w32.h" +# include "win32/utf-conv.h" #else -#include <unistd.h> -#include <pwd.h> +# include <unistd.h> +# include <pwd.h> #endif +#ifdef GIT_WIN32 +# define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" +# define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" + +static int expand_win32_path(git_win32_path dest, const wchar_t *src) +{ + DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16); + + if (!len || len > GIT_WIN_PATH_UTF16) + return -1; + + return 0; +} + +static int win32_path_to_utf8(git_str *dest, const wchar_t *src) +{ + git_win32_utf8_path utf8_path; + + if (git_win32_path_to_utf8(utf8_path, src) < 0) { + git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8"); + return -1; + } + + /* Convert backslashes to forward slashes */ + git_fs_path_mkposix(utf8_path); + + return git_str_sets(dest, utf8_path); +} + +static git_win32_path mock_registry; +static bool mock_registry_set; + +extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir) +{ + if (!mock_sysdir) { + mock_registry[0] = L'\0'; + mock_registry_set = false; + } else { + size_t len = wcslen(mock_sysdir); + + if (len > GIT_WIN_PATH_MAX) { + git_error_set(GIT_ERROR_INVALID, "mock path too long"); + return -1; + } + + wcscpy(mock_registry, mock_sysdir); + mock_registry_set = true; + } + + return 0; +} + +static int lookup_registry_key( + git_win32_path out, + const HKEY hive, + const wchar_t* key, + const wchar_t *value) +{ + HKEY hkey; + DWORD type, size; + int error = GIT_ENOTFOUND; + + /* + * Registry data may not be NUL terminated, provide room to do + * it ourselves. + */ + size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t)); + + if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0) + return GIT_ENOTFOUND; + + if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 && + type == REG_SZ && + size > 0 && + size < sizeof(git_win32_path)) { + size_t wsize = size / sizeof(wchar_t); + size_t len = wsize - 1; + + if (out[wsize - 1] != L'\0') { + len = wsize; + out[wsize] = L'\0'; + } + + if (out[len - 1] == L'\\') + out[len - 1] = L'\0'; + + if (_waccess(out, F_OK) == 0) + error = 0; + } + + RegCloseKey(hkey); + return error; +} + +static int find_sysdir_in_registry(git_win32_path out) +{ + if (mock_registry_set) { + if (mock_registry[0] == L'\0') + return GIT_ENOTFOUND; + + wcscpy(out, mock_registry); + return 0; + } + + if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || + lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0) + return 0; + + return GIT_ENOTFOUND; +} + +static int find_sysdir_in_path(git_win32_path out) +{ + size_t out_len; + + if (git_win32_path_find_executable(out, L"git.exe") < 0 && + git_win32_path_find_executable(out, L"git.cmd") < 0) + return GIT_ENOTFOUND; + + out_len = wcslen(out); + + /* Trim the file name */ + if (out_len <= CONST_STRLEN(L"git.exe")) + return GIT_ENOTFOUND; + + out_len -= CONST_STRLEN(L"git.exe"); + + if (out_len && out[out_len - 1] == L'\\') + out_len--; + + /* + * Git for Windows usually places the command in a 'bin' or + * 'cmd' directory, trim that. + */ + if (out_len >= CONST_STRLEN(L"\\bin") && + wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0) + out_len -= CONST_STRLEN(L"\\bin"); + else if (out_len >= CONST_STRLEN(L"\\cmd") && + wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0) + out_len -= CONST_STRLEN(L"\\cmd"); + + if (!out_len) + return GIT_ENOTFOUND; + + out[out_len] = L'\0'; + return 0; +} + +static int find_win32_dirs( + git_str *out, + const wchar_t* tmpl[]) +{ + git_win32_path path16; + git_str buf = GIT_STR_INIT; + + git_str_clear(out); + + for (; *tmpl != NULL; tmpl++) { + if (!expand_win32_path(path16, *tmpl) && + path16[0] != L'%' && + !_waccess(path16, F_OK)) { + win32_path_to_utf8(&buf, path16); + + if (buf.size) + git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); + } + } + + git_str_dispose(&buf); + + return (git_str_oom(out) ? -1 : 0); +} + +static int append_subdir(git_str *out, git_str *path, const char *subdir) +{ + static const char* architecture_roots[] = { + "", + "mingw64", + "mingw32", + NULL + }; + const char **root; + size_t orig_path_len = path->size; + + for (root = architecture_roots; *root; root++) { + if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) || + git_str_joinpath(path, path->ptr, subdir) < 0) + return -1; + + if (git_fs_path_exists(path->ptr) && + git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0) + return -1; + + git_str_truncate(path, orig_path_len); + } + + return 0; +} + +int git_win32__find_system_dirs(git_str *out, const char *subdir) +{ + git_win32_path pathdir, regdir; + git_str path8 = GIT_STR_INIT; + bool has_pathdir, has_regdir; + int error; + + has_pathdir = (find_sysdir_in_path(pathdir) == 0); + has_regdir = (find_sysdir_in_registry(regdir) == 0); + + if (!has_pathdir && !has_regdir) + return 0; + + /* + * Usually the git in the path is the same git in the registry, + * in this case there's no need to duplicate the paths. + */ + if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0) + has_regdir = false; + + if (has_pathdir) { + if ((error = win32_path_to_utf8(&path8, pathdir)) < 0 || + (error = append_subdir(out, &path8, subdir)) < 0) + goto done; + } + + if (has_regdir) { + if ((error = win32_path_to_utf8(&path8, regdir)) < 0 || + (error = append_subdir(out, &path8, subdir)) < 0) + goto done; + } + +done: + git_str_dispose(&path8); + return error; +} +#endif /* WIN32 */ + static int git_sysdir_guess_programdata_dirs(git_str *out) { #ifdef GIT_WIN32 - return git_win32__find_programdata_dirs(out); + static const wchar_t *programdata_tmpls[2] = { + L"%PROGRAMDATA%\\Git", + NULL, + }; + + return find_win32_dirs(out, programdata_tmpls); #else git_str_clear(out); return 0; @@ -75,10 +321,17 @@ out: } #endif -static int git_sysdir_guess_global_dirs(git_str *out) +static int git_sysdir_guess_home_dirs(git_str *out) { #ifdef GIT_WIN32 - return git_win32__find_global_dirs(out); + static const wchar_t *global_tmpls[4] = { + L"%HOME%\\", + L"%HOMEDRIVE%%HOMEPATH%\\", + L"%USERPROFILE%\\", + NULL, + }; + + return find_win32_dirs(out, global_tmpls); #else int error; uid_t uid, euid; @@ -114,10 +367,25 @@ static int git_sysdir_guess_global_dirs(git_str *out) #endif } +static int git_sysdir_guess_global_dirs(git_str *out) +{ + return git_sysdir_guess_home_dirs(out); +} + static int git_sysdir_guess_xdg_dirs(git_str *out) { #ifdef GIT_WIN32 - return git_win32__find_xdg_dirs(out); + static const wchar_t *global_tmpls[7] = { + L"%XDG_CONFIG_HOME%\\git", + L"%APPDATA%\\git", + L"%LOCALAPPDATA%\\git", + L"%HOME%\\.config\\git", + L"%HOMEDRIVE%%HOMEPATH%\\.config\\git", + L"%USERPROFILE%\\.config\\git", + NULL, + }; + + return find_win32_dirs(out, global_tmpls); #else git_str env = GIT_STR_INIT; int error; @@ -171,6 +439,7 @@ static struct git_sysdir__dir git_sysdir__dirs[] = { { GIT_STR_INIT, git_sysdir_guess_xdg_dirs }, { GIT_STR_INIT, git_sysdir_guess_programdata_dirs }, { GIT_STR_INIT, git_sysdir_guess_template_dirs }, + { GIT_STR_INIT, git_sysdir_guess_home_dirs } }; static void git_sysdir_global_shutdown(void) @@ -350,6 +619,12 @@ int git_sysdir_find_template_dir(git_str *path) path, NULL, GIT_SYSDIR_TEMPLATE, "template"); } +int git_sysdir_find_homedir(git_str *path) +{ + return git_sysdir_find_in_dirlist( + path, NULL, GIT_SYSDIR_HOME, "home directory"); +} + int git_sysdir_expand_global_file(git_str *path, const char *filename) { int error; @@ -361,3 +636,15 @@ int git_sysdir_expand_global_file(git_str *path, const char *filename) return error; } + +int git_sysdir_expand_homedir_file(git_str *path, const char *filename) +{ + int error; + + if ((error = git_sysdir_find_homedir(path)) == 0) { + if (filename) + error = git_str_joinpath(path, path->ptr, filename); + } + + return error; +} diff --git a/src/libgit2/sysdir.h b/src/libgit2/sysdir.h index 568f27940..1d15bbf43 100644 --- a/src/libgit2/sysdir.h +++ b/src/libgit2/sysdir.h @@ -57,10 +57,22 @@ extern int git_sysdir_find_programdata_file(git_str *path, const char *filename) extern int git_sysdir_find_template_dir(git_str *path); /** - * Expand the name of a "global" file (i.e. one in a user's home - * directory). Unlike `find_global_file` (above), this makes no - * attempt to check for the existence of the file, and is useful if - * you want the full path regardless of existence. + * Find the home directory. On Windows, this will look at the `HOME`, + * `HOMEPATH`, and `USERPROFILE` environment variables (in that order) + * and return the first path that is set and exists. On other systems, + * this will simply return the contents of the `HOME` environment variable. + * + * @param path buffer to write the full path into + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_homedir(git_str *path); + +/** + * Expand the name of a "global" file -- by default inside the user's + * home directory, but can be overridden by the user configuration. + * Unlike `find_global_file` (above), this makes no attempt to check + * for the existence of the file, and is useful if you want the full + * path regardless of existence. * * @param path buffer to write the full path into * @param filename name of file in the home directory @@ -68,13 +80,25 @@ extern int git_sysdir_find_template_dir(git_str *path); */ extern int git_sysdir_expand_global_file(git_str *path, const char *filename); +/** + * Expand the name of a file in the user's home directory. This + * function makes no attempt to check for the existence of the file, + * and is useful if you want the full path regardless of existence. + * + * @param path buffer to write the full path into + * @param filename name of file in the home directory + * @return 0 on success or -1 on error + */ +extern int git_sysdir_expand_homedir_file(git_str *path, const char *filename); + typedef enum { - GIT_SYSDIR_SYSTEM = 0, - GIT_SYSDIR_GLOBAL = 1, - GIT_SYSDIR_XDG = 2, + GIT_SYSDIR_SYSTEM = 0, + GIT_SYSDIR_GLOBAL = 1, + GIT_SYSDIR_XDG = 2, GIT_SYSDIR_PROGRAMDATA = 3, - GIT_SYSDIR_TEMPLATE = 4, - GIT_SYSDIR__MAX = 5 + GIT_SYSDIR_TEMPLATE = 4, + GIT_SYSDIR_HOME = 5, + GIT_SYSDIR__MAX = 6 } git_sysdir_t; /** @@ -110,4 +134,10 @@ extern int git_sysdir_set(git_sysdir_t which, const char *paths); */ extern int git_sysdir_reset(void); +/** Sets the registry system dir to a mock; for testing. */ +extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir); + +/** Find the given system dir; for testing. */ +extern int git_win32__find_system_dirs(git_str *out, const char *subdir); + #endif diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c index 85e779744..e90ab07e8 100644 --- a/src/libgit2/transports/ssh.c +++ b/src/libgit2/transports/ssh.c @@ -16,6 +16,7 @@ #include "netops.h" #include "smart.h" #include "streams/socket.h" +#include "sysdir.h" #include "git2/credential.h" #include "git2/sys/credential.h" @@ -421,7 +422,8 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char * return 0; } -#define KNOWN_HOSTS_FILE ".ssh/known_hosts" +#define SSH_DIR ".ssh" +#define KNOWN_HOSTS_FILE "known_hosts" /* * Load the known_hosts file. @@ -430,16 +432,14 @@ static int request_creds(git_credential **out, ssh_subtransport *t, const char * */ static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session) { - git_str path = GIT_STR_INIT, home = GIT_STR_INIT; + git_str path = GIT_STR_INIT, sshdir = GIT_STR_INIT; LIBSSH2_KNOWNHOSTS *known_hosts = NULL; int error; GIT_ASSERT_ARG(hosts); - if ((error = git__getenv(&home, "HOME")) < 0) - return error; - - if ((error = git_str_joinpath(&path, git_str_cstr(&home), KNOWN_HOSTS_FILE)) < 0) + if ((error = git_sysdir_expand_homedir_file(&sshdir, SSH_DIR)) < 0 || + (error = git_str_joinpath(&path, git_str_cstr(&sshdir), KNOWN_HOSTS_FILE)) < 0) goto out; if ((known_hosts = libssh2_knownhost_init(session)) == NULL) { @@ -461,34 +461,32 @@ static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session out: *hosts = known_hosts; - git_str_clear(&home); - git_str_clear(&path); + git_str_dispose(&sshdir); + git_str_dispose(&path); return error; } -static const char *hostkey_type_to_string(int type) +static void add_hostkey_pref_if_avail( + LIBSSH2_KNOWNHOSTS *known_hosts, + const char *hostname, + int port, + git_str *prefs, + int type, + const char *type_name) { - switch (type) { - case LIBSSH2_KNOWNHOST_KEY_SSHRSA: - return "ssh-rsa"; - case LIBSSH2_KNOWNHOST_KEY_SSHDSS: - return "ssh-dss"; -#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 - case LIBSSH2_KNOWNHOST_KEY_ECDSA_256: - return "ecdsa-sha2-nistp256"; - case LIBSSH2_KNOWNHOST_KEY_ECDSA_384: - return "ecdsa-sha2-nistp384"; - case LIBSSH2_KNOWNHOST_KEY_ECDSA_521: - return "ecdsa-sha2-nistp521"; -#endif -#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 - case LIBSSH2_KNOWNHOST_KEY_ED25519: - return "ssh-ed25519"; -#endif - } + struct libssh2_knownhost *host = NULL; + const char key = '\0'; + int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type; + int error; - return NULL; + error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, mask, &host); + if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + if (git_str_len(prefs) > 0) { + git_str_putc(prefs, ','); + } + git_str_puts(prefs, type_name); + } } /* @@ -496,27 +494,27 @@ static const char *hostkey_type_to_string(int type) * look it up with a nonsense key and using that mismatch to figure out what key * we do have stored for the host. * - * Returns the string to pass to libssh2_session_method_pref or NULL if we were - * unable to find anything or an error happened. + * Populates prefs with the string to pass to libssh2_session_method_pref. */ -static const char *find_hostkey_preference(LIBSSH2_KNOWNHOSTS *known_hosts, const char *hostname, int port) +static void find_hostkey_preference( + LIBSSH2_KNOWNHOSTS *known_hosts, + const char *hostname, + int port, + git_str *prefs) { - struct libssh2_knownhost *host = NULL; - /* Specify no key type so we don't filter on that */ - int type = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW; - const char key = '\0'; - int error; - /* - * In case of mismatch, we can find the type of key from known_hosts in - * the returned host's information as it means that an entry was found - * but our nonsense key obviously didn't match. + * The order here is important as it indicates the priority of what will + * be preferred. */ - error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, type, &host); - if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) - return hostkey_type_to_string(host->typemask & LIBSSH2_KNOWNHOST_KEY_MASK); - - return NULL; +#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519 + add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ED25519, "ssh-ed25519"); +#endif +#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256 + add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_256, "ecdsa-sha2-nistp256"); + add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_384, "ecdsa-sha2-nistp384"); + add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_521, "ecdsa-sha2-nistp521"); +#endif + add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_SSHRSA, "ssh-rsa"); } static int _git_ssh_session_create( @@ -526,11 +524,11 @@ static int _git_ssh_session_create( int port, git_stream *io) { - int rc = 0; + git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent); LIBSSH2_SESSION *s; LIBSSH2_KNOWNHOSTS *known_hosts; - git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent); - const char *keytype = NULL; + git_str prefs = GIT_STR_INIT; + int rc = 0; GIT_ASSERT_ARG(session); GIT_ASSERT_ARG(hosts); @@ -547,16 +545,17 @@ static int _git_ssh_session_create( return -1; } - if ((keytype = find_hostkey_preference(known_hosts, hostname, port)) != NULL) { + find_hostkey_preference(known_hosts, hostname, port, &prefs); + if (git_str_len(&prefs) > 0) { do { - rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, keytype); + rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, git_str_cstr(&prefs)); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { ssh_error(s, "failed to set hostkey preference"); goto on_error; } } - + git_str_dispose(&prefs); do { rc = libssh2_session_handshake(s, socket->s); @@ -753,7 +752,7 @@ static int check_certificate( if (error == GIT_PASSTHROUGH) { error = git_error_state_restore(&previous_error); } else if (error < 0 && !git_error_last()) { - git_error_set(GIT_ERROR_NET, "user canceled hostkey check"); + git_error_set(GIT_ERROR_NET, "unknown remote host key"); } git_error_state_free(&previous_error); @@ -1009,7 +1008,7 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ if (list == NULL && !libssh2_userauth_authenticated(session)) { - ssh_error(session, "Failed to retrieve list of SSH authentication methods"); + ssh_error(session, "remote rejected authentication"); return GIT_EAUTH; } diff --git a/src/util/futils.c b/src/util/futils.c index cb872de09..084f1cd28 100644 --- a/src/util/futils.c +++ b/src/util/futils.c @@ -13,9 +13,6 @@ #include "rand.h" #include <ctype.h> -#if GIT_WIN32 -#include "win32/findfile.h" -#endif #define GIT_FILEMODE_DEFAULT 0100666 diff --git a/src/util/win32/findfile.c b/src/util/win32/findfile.c deleted file mode 100644 index 725a90167..000000000 --- a/src/util/win32/findfile.c +++ /dev/null @@ -1,286 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#include "findfile.h" - -#include "path_w32.h" -#include "utf-conv.h" -#include "fs_path.h" - -#define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" -#define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" - -static int git_win32__expand_path(git_win32_path dest, const wchar_t *src) -{ - DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16); - - if (!len || len > GIT_WIN_PATH_UTF16) - return -1; - - return 0; -} - -static int win32_path_to_8(git_str *dest, const wchar_t *src) -{ - git_win32_utf8_path utf8_path; - - if (git_win32_path_to_utf8(utf8_path, src) < 0) { - git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8"); - return -1; - } - - /* Convert backslashes to forward slashes */ - git_fs_path_mkposix(utf8_path); - - return git_str_sets(dest, utf8_path); -} - -static git_win32_path mock_registry; -static bool mock_registry_set; - -extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir) -{ - if (!mock_sysdir) { - mock_registry[0] = L'\0'; - mock_registry_set = false; - } else { - size_t len = wcslen(mock_sysdir); - - if (len > GIT_WIN_PATH_MAX) { - git_error_set(GIT_ERROR_INVALID, "mock path too long"); - return -1; - } - - wcscpy(mock_registry, mock_sysdir); - mock_registry_set = true; - } - - return 0; -} - -static int lookup_registry_key( - git_win32_path out, - const HKEY hive, - const wchar_t* key, - const wchar_t *value) -{ - HKEY hkey; - DWORD type, size; - int error = GIT_ENOTFOUND; - - /* - * Registry data may not be NUL terminated, provide room to do - * it ourselves. - */ - size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t)); - - if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0) - return GIT_ENOTFOUND; - - if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 && - type == REG_SZ && - size > 0 && - size < sizeof(git_win32_path)) { - size_t wsize = size / sizeof(wchar_t); - size_t len = wsize - 1; - - if (out[wsize - 1] != L'\0') { - len = wsize; - out[wsize] = L'\0'; - } - - if (out[len - 1] == L'\\') - out[len - 1] = L'\0'; - - if (_waccess(out, F_OK) == 0) - error = 0; - } - - RegCloseKey(hkey); - return error; -} - -static int find_sysdir_in_registry(git_win32_path out) -{ - if (mock_registry_set) { - if (mock_registry[0] == L'\0') - return GIT_ENOTFOUND; - - wcscpy(out, mock_registry); - return 0; - } - - if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || - lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 || - lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 || - lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0) - return 0; - - return GIT_ENOTFOUND; -} - -static int find_sysdir_in_path(git_win32_path out) -{ - size_t out_len; - - if (git_win32_path_find_executable(out, L"git.exe") < 0 && - git_win32_path_find_executable(out, L"git.cmd") < 0) - return GIT_ENOTFOUND; - - out_len = wcslen(out); - - /* Trim the file name */ - if (out_len <= CONST_STRLEN(L"git.exe")) - return GIT_ENOTFOUND; - - out_len -= CONST_STRLEN(L"git.exe"); - - if (out_len && out[out_len - 1] == L'\\') - out_len--; - - /* - * Git for Windows usually places the command in a 'bin' or - * 'cmd' directory, trim that. - */ - if (out_len >= CONST_STRLEN(L"\\bin") && - wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0) - out_len -= CONST_STRLEN(L"\\bin"); - else if (out_len >= CONST_STRLEN(L"\\cmd") && - wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0) - out_len -= CONST_STRLEN(L"\\cmd"); - - if (!out_len) - return GIT_ENOTFOUND; - - out[out_len] = L'\0'; - return 0; -} - -static int win32_find_existing_dirs( - git_str* out, - const wchar_t* tmpl[]) -{ - git_win32_path path16; - git_str buf = GIT_STR_INIT; - - git_str_clear(out); - - for (; *tmpl != NULL; tmpl++) { - if (!git_win32__expand_path(path16, *tmpl) && - path16[0] != L'%' && - !_waccess(path16, F_OK)) { - win32_path_to_8(&buf, path16); - - if (buf.size) - git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); - } - } - - git_str_dispose(&buf); - - return (git_str_oom(out) ? -1 : 0); -} - -static int append_subdir(git_str *out, git_str *path, const char *subdir) -{ - static const char* architecture_roots[] = { - "", - "mingw64", - "mingw32", - NULL - }; - const char **root; - size_t orig_path_len = path->size; - - for (root = architecture_roots; *root; root++) { - if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) || - git_str_joinpath(path, path->ptr, subdir) < 0) - return -1; - - if (git_fs_path_exists(path->ptr) && - git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0) - return -1; - - git_str_truncate(path, orig_path_len); - } - - return 0; -} - -int git_win32__find_system_dirs(git_str *out, const char *subdir) -{ - git_win32_path pathdir, regdir; - git_str path8 = GIT_STR_INIT; - bool has_pathdir, has_regdir; - int error; - - has_pathdir = (find_sysdir_in_path(pathdir) == 0); - has_regdir = (find_sysdir_in_registry(regdir) == 0); - - if (!has_pathdir && !has_regdir) - return 0; - - /* - * Usually the git in the path is the same git in the registry, - * in this case there's no need to duplicate the paths. - */ - if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0) - has_regdir = false; - - if (has_pathdir) { - if ((error = win32_path_to_8(&path8, pathdir)) < 0 || - (error = append_subdir(out, &path8, subdir)) < 0) - goto done; - } - - if (has_regdir) { - if ((error = win32_path_to_8(&path8, regdir)) < 0 || - (error = append_subdir(out, &path8, subdir)) < 0) - goto done; - } - -done: - git_str_dispose(&path8); - return error; -} - -int git_win32__find_global_dirs(git_str *out) -{ - static const wchar_t *global_tmpls[4] = { - L"%HOME%\\", - L"%HOMEDRIVE%%HOMEPATH%\\", - L"%USERPROFILE%\\", - NULL, - }; - - return win32_find_existing_dirs(out, global_tmpls); -} - -int git_win32__find_xdg_dirs(git_str *out) -{ - static const wchar_t *global_tmpls[7] = { - L"%XDG_CONFIG_HOME%\\git", - L"%APPDATA%\\git", - L"%LOCALAPPDATA%\\git", - L"%HOME%\\.config\\git", - L"%HOMEDRIVE%%HOMEPATH%\\.config\\git", - L"%USERPROFILE%\\.config\\git", - NULL, - }; - - return win32_find_existing_dirs(out, global_tmpls); -} - -int git_win32__find_programdata_dirs(git_str *out) -{ - static const wchar_t *programdata_tmpls[2] = { - L"%PROGRAMDATA%\\Git", - NULL, - }; - - return win32_find_existing_dirs(out, programdata_tmpls); -} diff --git a/src/util/win32/findfile.h b/src/util/win32/findfile.h deleted file mode 100644 index 7b191d1fe..000000000 --- a/src/util/win32/findfile.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) the libgit2 contributors. All rights reserved. - * - * This file is part of libgit2, distributed under the GNU GPL v2 with - * a Linking Exception. For full terms see the included COPYING file. - */ - -#ifndef INCLUDE_win32_findfile_h__ -#define INCLUDE_win32_findfile_h__ - -#include "git2_util.h" - -/** Sets the mock registry root for Git for Windows for testing. */ -extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir); - -extern int git_win32__find_system_dirs(git_str *out, const char *subpath); -extern int git_win32__find_global_dirs(git_str *out); -extern int git_win32__find_xdg_dirs(git_str *out); -extern int git_win32__find_programdata_dirs(git_str *out); - -#endif - |