Skip to content

Commit 6397024

Browse files
authored
Merge pull request libgit2#6266 from libgit2/ethomson/ownership
Validate repository directory ownership
2 parents 7e8d9be + 4161ebd commit 6397024

File tree

14 files changed

+554
-92
lines changed

14 files changed

+554
-92
lines changed

include/git2/common.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,9 @@ typedef enum {
225225
GIT_OPT_SET_ODB_PACKED_PRIORITY,
226226
GIT_OPT_SET_ODB_LOOSE_PRIORITY,
227227
GIT_OPT_GET_EXTENSIONS,
228-
GIT_OPT_SET_EXTENSIONS
228+
GIT_OPT_SET_EXTENSIONS,
229+
GIT_OPT_GET_OWNER_VALIDATION,
230+
GIT_OPT_SET_OWNER_VALIDATION
229231
} git_libgit2_opt_t;
230232

231233
/**
@@ -463,6 +465,14 @@ typedef enum {
463465
* > to support repositories with the `noop` extension but does want
464466
* > to support repositories with the `newext` extension.
465467
*
468+
* opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled)
469+
* > Gets the owner validation setting for repository
470+
* > directories.
471+
*
472+
* opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled)
473+
* > Set that repository directories should be owned by the current
474+
* > user. The default is to validate ownership.
475+
*
466476
* @param option Option key
467477
* @param ... value to set the option
468478
* @return 0 on success, <0 on failure

include/git2/errors.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ typedef enum {
5757
GIT_RETRY = -32, /**< Internal only */
5858
GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */
5959
GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */
60-
GIT_EAPPLYFAIL = -35 /**< Patch application failed */
60+
GIT_EAPPLYFAIL = -35, /**< Patch application failed */
61+
GIT_EOWNER = -36 /**< The object is not owned by the current user */
6162
} git_error_code;
6263

6364
/**

src/libgit2/config.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,14 +1170,18 @@ int git_config_find_programdata(git_buf *path)
11701170

11711171
int git_config__find_programdata(git_str *path)
11721172
{
1173-
int ret;
1173+
bool is_safe;
11741174

1175-
ret = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA);
1175+
if (git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA) < 0 ||
1176+
git_fs_path_owner_is_system_or_current_user(&is_safe, path->ptr) < 0)
1177+
return -1;
11761178

1177-
if (ret != GIT_OK)
1178-
return ret;
1179+
if (!is_safe) {
1180+
git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership");
1181+
return -1;
1182+
}
11791183

1180-
return git_fs_path_validate_system_file_ownership(path->ptr);
1184+
return 0;
11811185
}
11821186

11831187
int git_config__global_location(git_str *buf)

src/libgit2/libgit2.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,14 @@ int git_libgit2_opts(int key, ...)
406406
}
407407
break;
408408

409+
case GIT_OPT_GET_OWNER_VALIDATION:
410+
*(va_arg(ap, int *)) = git_repository__validate_ownership;
411+
break;
412+
413+
case GIT_OPT_SET_OWNER_VALIDATION:
414+
git_repository__validate_ownership = (va_arg(ap, int) != 0);
415+
break;
416+
409417
default:
410418
git_error_set(GIT_ERROR_INVALID, "invalid option key");
411419
error = -1;

src/libgit2/repository.c

Lines changed: 103 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
# include "win32/w32_util.h"
4040
#endif
4141

42+
bool git_repository__validate_ownership = true;
4243
bool git_repository__fsync_gitdir = false;
4344

4445
static const struct {
@@ -65,6 +66,7 @@ static const struct {
6566

6667
static int check_repositoryformatversion(int *version, git_config *config);
6768
static int check_extensions(git_config *config, int version);
69+
static int load_global_config(git_config **config);
6870

6971
#define GIT_COMMONDIR_FILE "commondir"
7072
#define GIT_GITDIR_FILE "gitdir"
@@ -483,6 +485,63 @@ static int read_gitfile(git_str *path_out, const char *file_path)
483485
return error;
484486
}
485487

488+
typedef struct {
489+
const char *repo_path;
490+
git_str tmp;
491+
bool is_safe;
492+
} validate_ownership_data;
493+
494+
static int validate_ownership_cb(const git_config_entry *entry, void *payload)
495+
{
496+
validate_ownership_data *data = payload;
497+
498+
if (strcmp(entry->value, "") == 0)
499+
data->is_safe = false;
500+
501+
if (git_fs_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 &&
502+
strcmp(data->tmp.ptr, data->repo_path) == 0)
503+
data->is_safe = true;
504+
505+
return 0;
506+
}
507+
508+
static int validate_ownership(const char *repo_path)
509+
{
510+
git_config *config = NULL;
511+
validate_ownership_data data = { repo_path, GIT_STR_INIT, false };
512+
bool is_safe;
513+
int error;
514+
515+
if ((error = git_fs_path_owner_is_current_user(&is_safe, repo_path)) < 0) {
516+
if (error == GIT_ENOTFOUND)
517+
error = 0;
518+
519+
goto done;
520+
}
521+
522+
if (is_safe) {
523+
error = 0;
524+
goto done;
525+
}
526+
527+
if (load_global_config(&config) == 0) {
528+
error = git_config_get_multivar_foreach(config, "safe.directory", NULL, validate_ownership_cb, &data);
529+
530+
if (!error && data.is_safe)
531+
goto done;
532+
}
533+
534+
git_error_set(GIT_ERROR_CONFIG,
535+
"repository path '%s' is not owned by current user",
536+
repo_path);
537+
error = GIT_EOWNER;
538+
539+
done:
540+
git_config_free(config);
541+
git_str_dispose(&data.tmp);
542+
return error;
543+
}
544+
486545
static int find_repo(
487546
git_str *gitdir_path,
488547
git_str *workdir_path,
@@ -856,6 +915,7 @@ int git_repository_open_ext(
856915
gitlink = GIT_STR_INIT, commondir = GIT_STR_INIT;
857916
git_repository *repo = NULL;
858917
git_config *config = NULL;
918+
const char *validation_path;
859919
int version = 0;
860920

861921
if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
@@ -904,16 +964,24 @@ int git_repository_open_ext(
904964
if ((error = check_extensions(config, version)) < 0)
905965
goto cleanup;
906966

907-
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
967+
if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) {
908968
repo->is_bare = 1;
909-
else {
910-
969+
} else {
911970
if (config &&
912971
((error = load_config_data(repo, config)) < 0 ||
913972
(error = load_workdir(repo, config, &workdir)) < 0))
914973
goto cleanup;
915974
}
916975

976+
/*
977+
* Ensure that the git directory is owned by the current user.
978+
*/
979+
validation_path = repo->is_bare ? repo->gitdir : repo->workdir;
980+
981+
if (git_repository__validate_ownership &&
982+
(error = validate_ownership(validation_path)) < 0)
983+
goto cleanup;
984+
917985
cleanup:
918986
git_str_dispose(&gitdir);
919987
git_str_dispose(&workdir);
@@ -1607,13 +1675,40 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
16071675
return is_insensitive;
16081676
}
16091677

1610-
static bool are_symlinks_supported(const char *wd_path)
1678+
/*
1679+
* Return a configuration object with only the global and system
1680+
* configurations; no repository-level configuration.
1681+
*/
1682+
static int load_global_config(git_config **config)
16111683
{
1612-
git_config *config = NULL;
16131684
git_str global_buf = GIT_STR_INIT;
16141685
git_str xdg_buf = GIT_STR_INIT;
16151686
git_str system_buf = GIT_STR_INIT;
16161687
git_str programdata_buf = GIT_STR_INIT;
1688+
int error;
1689+
1690+
git_config__find_global(&global_buf);
1691+
git_config__find_xdg(&xdg_buf);
1692+
git_config__find_system(&system_buf);
1693+
git_config__find_programdata(&programdata_buf);
1694+
1695+
error = load_config(config, NULL,
1696+
path_unless_empty(&global_buf),
1697+
path_unless_empty(&xdg_buf),
1698+
path_unless_empty(&system_buf),
1699+
path_unless_empty(&programdata_buf));
1700+
1701+
git_str_dispose(&global_buf);
1702+
git_str_dispose(&xdg_buf);
1703+
git_str_dispose(&system_buf);
1704+
git_str_dispose(&programdata_buf);
1705+
1706+
return error;
1707+
}
1708+
1709+
static bool are_symlinks_supported(const char *wd_path)
1710+
{
1711+
git_config *config = NULL;
16171712
int symlinks = 0;
16181713

16191714
/*
@@ -1624,30 +1719,16 @@ static bool are_symlinks_supported(const char *wd_path)
16241719
* _not_ set, then we do not test or enable symlink support.
16251720
*/
16261721
#ifdef GIT_WIN32
1627-
git_config__find_global(&global_buf);
1628-
git_config__find_xdg(&xdg_buf);
1629-
git_config__find_system(&system_buf);
1630-
git_config__find_programdata(&programdata_buf);
1631-
1632-
if (load_config(&config, NULL,
1633-
path_unless_empty(&global_buf),
1634-
path_unless_empty(&xdg_buf),
1635-
path_unless_empty(&system_buf),
1636-
path_unless_empty(&programdata_buf)) < 0)
1637-
goto done;
1638-
1639-
if (git_config_get_bool(&symlinks, config, "core.symlinks") < 0 || !symlinks)
1722+
if (load_global_config(&config) < 0 ||
1723+
git_config_get_bool(&symlinks, config, "core.symlinks") < 0 ||
1724+
!symlinks)
16401725
goto done;
16411726
#endif
16421727

16431728
if (!(symlinks = git_fs_path_supports_symlinks(wd_path)))
16441729
goto done;
16451730

16461731
done:
1647-
git_str_dispose(&global_buf);
1648-
git_str_dispose(&xdg_buf);
1649-
git_str_dispose(&system_buf);
1650-
git_str_dispose(&programdata_buf);
16511732
git_config_free(config);
16521733
return symlinks != 0;
16531734
}

src/libgit2/repository.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#define GIT_DIR_SHORTNAME "GIT~1"
3535

3636
extern bool git_repository__fsync_gitdir;
37+
extern bool git_repository__validate_ownership;
3738

3839
/** Cvar cache identifiers */
3940
typedef enum {

0 commit comments

Comments
 (0)