Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
git2
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
lvzhengyang
git2
Commits
63970244
Unverified
Commit
63970244
authored
Apr 12, 2022
by
Edward Thomson
Committed by
GitHub
Apr 12, 2022
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #6266 from libgit2/ethomson/ownership
Validate repository directory ownership
parents
7e8d9be0
4161ebdd
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
551 additions
and
89 deletions
+551
-89
include/git2/common.h
+11
-1
include/git2/errors.h
+2
-1
src/libgit2/config.c
+9
-5
src/libgit2/libgit2.c
+8
-0
src/libgit2/repository.c
+103
-22
src/libgit2/repository.h
+1
-0
src/util/fs_path.c
+210
-51
src/util/fs_path.h
+28
-8
tests/clar/clar_libgit2.c
+5
-0
tests/clar/clar_libgit2.h
+1
-0
tests/clar/main.c
+1
-0
tests/libgit2/repo/config.c
+0
-1
tests/libgit2/repo/open.c
+147
-0
tests/util/path.c
+25
-0
No files found.
include/git2/common.h
View file @
63970244
...
@@ -225,7 +225,9 @@ typedef enum {
...
@@ -225,7 +225,9 @@ typedef enum {
GIT_OPT_SET_ODB_PACKED_PRIORITY
,
GIT_OPT_SET_ODB_PACKED_PRIORITY
,
GIT_OPT_SET_ODB_LOOSE_PRIORITY
,
GIT_OPT_SET_ODB_LOOSE_PRIORITY
,
GIT_OPT_GET_EXTENSIONS
,
GIT_OPT_GET_EXTENSIONS
,
GIT_OPT_SET_EXTENSIONS
GIT_OPT_SET_EXTENSIONS
,
GIT_OPT_GET_OWNER_VALIDATION
,
GIT_OPT_SET_OWNER_VALIDATION
}
git_libgit2_opt_t
;
}
git_libgit2_opt_t
;
/**
/**
...
@@ -463,6 +465,14 @@ typedef enum {
...
@@ -463,6 +465,14 @@ typedef enum {
* > to support repositories with the `noop` extension but does want
* > to support repositories with the `noop` extension but does want
* > to support repositories with the `newext` extension.
* > to support repositories with the `newext` extension.
*
*
* opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled)
* > Gets the owner validation setting for repository
* > directories.
*
* opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled)
* > Set that repository directories should be owned by the current
* > user. The default is to validate ownership.
*
* @param option Option key
* @param option Option key
* @param ... value to set the option
* @param ... value to set the option
* @return 0 on success, <0 on failure
* @return 0 on success, <0 on failure
...
...
include/git2/errors.h
View file @
63970244
...
@@ -57,7 +57,8 @@ typedef enum {
...
@@ -57,7 +57,8 @@ typedef enum {
GIT_RETRY
=
-
32
,
/**< Internal only */
GIT_RETRY
=
-
32
,
/**< Internal only */
GIT_EMISMATCH
=
-
33
,
/**< Hashsum mismatch in object */
GIT_EMISMATCH
=
-
33
,
/**< Hashsum mismatch in object */
GIT_EINDEXDIRTY
=
-
34
,
/**< Unsaved changes in the index would be overwritten */
GIT_EINDEXDIRTY
=
-
34
,
/**< Unsaved changes in the index would be overwritten */
GIT_EAPPLYFAIL
=
-
35
/**< Patch application failed */
GIT_EAPPLYFAIL
=
-
35
,
/**< Patch application failed */
GIT_EOWNER
=
-
36
/**< The object is not owned by the current user */
}
git_error_code
;
}
git_error_code
;
/**
/**
...
...
src/libgit2/config.c
View file @
63970244
...
@@ -1170,14 +1170,18 @@ int git_config_find_programdata(git_buf *path)
...
@@ -1170,14 +1170,18 @@ int git_config_find_programdata(git_buf *path)
int
git_config__find_programdata
(
git_str
*
path
)
int
git_config__find_programdata
(
git_str
*
path
)
{
{
int
ret
;
bool
is_safe
;
ret
=
git_sysdir_find_programdata_file
(
path
,
GIT_CONFIG_FILENAME_PROGRAMDATA
);
if
(
git_sysdir_find_programdata_file
(
path
,
GIT_CONFIG_FILENAME_PROGRAMDATA
)
<
0
||
git_fs_path_owner_is_system_or_current_user
(
&
is_safe
,
path
->
ptr
)
<
0
)
return
-
1
;
if
(
ret
!=
GIT_OK
)
if
(
!
is_safe
)
{
return
ret
;
git_error_set
(
GIT_ERROR_CONFIG
,
"programdata path has invalid ownership"
);
return
-
1
;
}
return
git_fs_path_validate_system_file_ownership
(
path
->
ptr
)
;
return
0
;
}
}
int
git_config__global_location
(
git_str
*
buf
)
int
git_config__global_location
(
git_str
*
buf
)
...
...
src/libgit2/libgit2.c
View file @
63970244
...
@@ -406,6 +406,14 @@ int git_libgit2_opts(int key, ...)
...
@@ -406,6 +406,14 @@ int git_libgit2_opts(int key, ...)
}
}
break
;
break
;
case
GIT_OPT_GET_OWNER_VALIDATION
:
*
(
va_arg
(
ap
,
int
*
))
=
git_repository__validate_ownership
;
break
;
case
GIT_OPT_SET_OWNER_VALIDATION
:
git_repository__validate_ownership
=
(
va_arg
(
ap
,
int
)
!=
0
);
break
;
default:
default:
git_error_set
(
GIT_ERROR_INVALID
,
"invalid option key"
);
git_error_set
(
GIT_ERROR_INVALID
,
"invalid option key"
);
error
=
-
1
;
error
=
-
1
;
...
...
src/libgit2/repository.c
View file @
63970244
...
@@ -39,6 +39,7 @@
...
@@ -39,6 +39,7 @@
# include "win32/w32_util.h"
# include "win32/w32_util.h"
#endif
#endif
bool
git_repository__validate_ownership
=
true
;
bool
git_repository__fsync_gitdir
=
false
;
bool
git_repository__fsync_gitdir
=
false
;
static
const
struct
{
static
const
struct
{
...
@@ -65,6 +66,7 @@ static const struct {
...
@@ -65,6 +66,7 @@ static const struct {
static
int
check_repositoryformatversion
(
int
*
version
,
git_config
*
config
);
static
int
check_repositoryformatversion
(
int
*
version
,
git_config
*
config
);
static
int
check_extensions
(
git_config
*
config
,
int
version
);
static
int
check_extensions
(
git_config
*
config
,
int
version
);
static
int
load_global_config
(
git_config
**
config
);
#define GIT_COMMONDIR_FILE "commondir"
#define GIT_COMMONDIR_FILE "commondir"
#define GIT_GITDIR_FILE "gitdir"
#define GIT_GITDIR_FILE "gitdir"
...
@@ -483,6 +485,63 @@ static int read_gitfile(git_str *path_out, const char *file_path)
...
@@ -483,6 +485,63 @@ static int read_gitfile(git_str *path_out, const char *file_path)
return
error
;
return
error
;
}
}
typedef
struct
{
const
char
*
repo_path
;
git_str
tmp
;
bool
is_safe
;
}
validate_ownership_data
;
static
int
validate_ownership_cb
(
const
git_config_entry
*
entry
,
void
*
payload
)
{
validate_ownership_data
*
data
=
payload
;
if
(
strcmp
(
entry
->
value
,
""
)
==
0
)
data
->
is_safe
=
false
;
if
(
git_fs_path_prettify_dir
(
&
data
->
tmp
,
entry
->
value
,
NULL
)
==
0
&&
strcmp
(
data
->
tmp
.
ptr
,
data
->
repo_path
)
==
0
)
data
->
is_safe
=
true
;
return
0
;
}
static
int
validate_ownership
(
const
char
*
repo_path
)
{
git_config
*
config
=
NULL
;
validate_ownership_data
data
=
{
repo_path
,
GIT_STR_INIT
,
false
};
bool
is_safe
;
int
error
;
if
((
error
=
git_fs_path_owner_is_current_user
(
&
is_safe
,
repo_path
))
<
0
)
{
if
(
error
==
GIT_ENOTFOUND
)
error
=
0
;
goto
done
;
}
if
(
is_safe
)
{
error
=
0
;
goto
done
;
}
if
(
load_global_config
(
&
config
)
==
0
)
{
error
=
git_config_get_multivar_foreach
(
config
,
"safe.directory"
,
NULL
,
validate_ownership_cb
,
&
data
);
if
(
!
error
&&
data
.
is_safe
)
goto
done
;
}
git_error_set
(
GIT_ERROR_CONFIG
,
"repository path '%s' is not owned by current user"
,
repo_path
);
error
=
GIT_EOWNER
;
done:
git_config_free
(
config
);
git_str_dispose
(
&
data
.
tmp
);
return
error
;
}
static
int
find_repo
(
static
int
find_repo
(
git_str
*
gitdir_path
,
git_str
*
gitdir_path
,
git_str
*
workdir_path
,
git_str
*
workdir_path
,
...
@@ -856,6 +915,7 @@ int git_repository_open_ext(
...
@@ -856,6 +915,7 @@ int git_repository_open_ext(
gitlink
=
GIT_STR_INIT
,
commondir
=
GIT_STR_INIT
;
gitlink
=
GIT_STR_INIT
,
commondir
=
GIT_STR_INIT
;
git_repository
*
repo
=
NULL
;
git_repository
*
repo
=
NULL
;
git_config
*
config
=
NULL
;
git_config
*
config
=
NULL
;
const
char
*
validation_path
;
int
version
=
0
;
int
version
=
0
;
if
(
flags
&
GIT_REPOSITORY_OPEN_FROM_ENV
)
if
(
flags
&
GIT_REPOSITORY_OPEN_FROM_ENV
)
...
@@ -904,16 +964,24 @@ int git_repository_open_ext(
...
@@ -904,16 +964,24 @@ int git_repository_open_ext(
if
((
error
=
check_extensions
(
config
,
version
))
<
0
)
if
((
error
=
check_extensions
(
config
,
version
))
<
0
)
goto
cleanup
;
goto
cleanup
;
if
((
flags
&
GIT_REPOSITORY_OPEN_BARE
)
!=
0
)
if
((
flags
&
GIT_REPOSITORY_OPEN_BARE
)
!=
0
)
{
repo
->
is_bare
=
1
;
repo
->
is_bare
=
1
;
else
{
}
else
{
if
(
config
&&
if
(
config
&&
((
error
=
load_config_data
(
repo
,
config
))
<
0
||
((
error
=
load_config_data
(
repo
,
config
))
<
0
||
(
error
=
load_workdir
(
repo
,
config
,
&
workdir
))
<
0
))
(
error
=
load_workdir
(
repo
,
config
,
&
workdir
))
<
0
))
goto
cleanup
;
goto
cleanup
;
}
}
/*
* Ensure that the git directory is owned by the current user.
*/
validation_path
=
repo
->
is_bare
?
repo
->
gitdir
:
repo
->
workdir
;
if
(
git_repository__validate_ownership
&&
(
error
=
validate_ownership
(
validation_path
))
<
0
)
goto
cleanup
;
cleanup:
cleanup:
git_str_dispose
(
&
gitdir
);
git_str_dispose
(
&
gitdir
);
git_str_dispose
(
&
workdir
);
git_str_dispose
(
&
workdir
);
...
@@ -1607,36 +1675,53 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
...
@@ -1607,36 +1675,53 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
return
is_insensitive
;
return
is_insensitive
;
}
}
static
bool
are_symlinks_supported
(
const
char
*
wd_path
)
/*
* Return a configuration object with only the global and system
* configurations; no repository-level configuration.
*/
static
int
load_global_config
(
git_config
**
config
)
{
{
git_config
*
config
=
NULL
;
git_str
global_buf
=
GIT_STR_INIT
;
git_str
global_buf
=
GIT_STR_INIT
;
git_str
xdg_buf
=
GIT_STR_INIT
;
git_str
xdg_buf
=
GIT_STR_INIT
;
git_str
system_buf
=
GIT_STR_INIT
;
git_str
system_buf
=
GIT_STR_INIT
;
git_str
programdata_buf
=
GIT_STR_INIT
;
git_str
programdata_buf
=
GIT_STR_INIT
;
int
symlinks
=
0
;
int
error
;
/*
* To emulate Git for Windows, symlinks on Windows must be explicitly
* opted-in. We examine the system configuration for a core.symlinks
* set to true. If found, we then examine the filesystem to see if
* symlinks are _actually_ supported by the current user. If that is
* _not_ set, then we do not test or enable symlink support.
*/
#ifdef GIT_WIN32
git_config__find_global
(
&
global_buf
);
git_config__find_global
(
&
global_buf
);
git_config__find_xdg
(
&
xdg_buf
);
git_config__find_xdg
(
&
xdg_buf
);
git_config__find_system
(
&
system_buf
);
git_config__find_system
(
&
system_buf
);
git_config__find_programdata
(
&
programdata_buf
);
git_config__find_programdata
(
&
programdata_buf
);
if
(
load_config
(
&
config
,
NULL
,
error
=
load_config
(
config
,
NULL
,
path_unless_empty
(
&
global_buf
),
path_unless_empty
(
&
global_buf
),
path_unless_empty
(
&
xdg_buf
),
path_unless_empty
(
&
xdg_buf
),
path_unless_empty
(
&
system_buf
),
path_unless_empty
(
&
system_buf
),
path_unless_empty
(
&
programdata_buf
))
<
0
)
path_unless_empty
(
&
programdata_buf
));
goto
done
;
git_str_dispose
(
&
global_buf
);
git_str_dispose
(
&
xdg_buf
);
git_str_dispose
(
&
system_buf
);
git_str_dispose
(
&
programdata_buf
);
if
(
git_config_get_bool
(
&
symlinks
,
config
,
"core.symlinks"
)
<
0
||
!
symlinks
)
return
error
;
}
static
bool
are_symlinks_supported
(
const
char
*
wd_path
)
{
git_config
*
config
=
NULL
;
int
symlinks
=
0
;
/*
* To emulate Git for Windows, symlinks on Windows must be explicitly
* opted-in. We examine the system configuration for a core.symlinks
* set to true. If found, we then examine the filesystem to see if
* symlinks are _actually_ supported by the current user. If that is
* _not_ set, then we do not test or enable symlink support.
*/
#ifdef GIT_WIN32
if
(
load_global_config
(
&
config
)
<
0
||
git_config_get_bool
(
&
symlinks
,
config
,
"core.symlinks"
)
<
0
||
!
symlinks
)
goto
done
;
goto
done
;
#endif
#endif
...
@@ -1644,10 +1729,6 @@ static bool are_symlinks_supported(const char *wd_path)
...
@@ -1644,10 +1729,6 @@ static bool are_symlinks_supported(const char *wd_path)
goto
done
;
goto
done
;
done:
done:
git_str_dispose
(
&
global_buf
);
git_str_dispose
(
&
xdg_buf
);
git_str_dispose
(
&
system_buf
);
git_str_dispose
(
&
programdata_buf
);
git_config_free
(
config
);
git_config_free
(
config
);
return
symlinks
!=
0
;
return
symlinks
!=
0
;
}
}
...
...
src/libgit2/repository.h
View file @
63970244
...
@@ -34,6 +34,7 @@
...
@@ -34,6 +34,7 @@
#define GIT_DIR_SHORTNAME "GIT~1"
#define GIT_DIR_SHORTNAME "GIT~1"
extern
bool
git_repository__fsync_gitdir
;
extern
bool
git_repository__fsync_gitdir
;
extern
bool
git_repository__validate_ownership
;
/** Cvar cache identifiers */
/** Cvar cache identifiers */
typedef
enum
{
typedef
enum
{
...
...
src/util/fs_path.c
View file @
63970244
...
@@ -1785,82 +1785,241 @@ done:
...
@@ -1785,82 +1785,241 @@ done:
return
supported
;
return
supported
;
}
}
int
git_fs_path_validate_system_file_ownership
(
const
char
*
path
)
static
git_fs_path__mock_owner_t
mock_owner
=
GIT_FS_PATH_MOCK_OWNER_NONE
;
void
git_fs_path__set_owner
(
git_fs_path__mock_owner_t
owner
)
{
mock_owner
=
owner
;
}
#ifdef GIT_WIN32
static
PSID
*
sid_dup
(
PSID
sid
)
{
DWORD
len
;
PSID
dup
;
len
=
GetLengthSid
(
sid
);
if
((
dup
=
git__malloc
(
len
))
==
NULL
)
return
NULL
;
if
(
!
CopySid
(
len
,
dup
,
sid
))
{
git_error_set
(
GIT_ERROR_OS
,
"could not duplicate sid"
);
git__free
(
dup
);
return
NULL
;
}
return
dup
;
}
static
int
current_user_sid
(
PSID
*
out
)
{
{
#ifndef GIT_WIN32
GIT_UNUSED
(
path
);
return
GIT_OK
;
#else
git_win32_path
buf
;
PSID
owner_sid
;
PSECURITY_DESCRIPTOR
descriptor
=
NULL
;
HANDLE
token
;
TOKEN_USER
*
info
=
NULL
;
TOKEN_USER
*
info
=
NULL
;
DWORD
err
,
len
;
HANDLE
token
=
NULL
;
int
ret
;
DWORD
len
=
0
;
int
error
=
-
1
;
if
(
git_win32_path_from_utf8
(
buf
,
path
)
<
0
)
if
(
!
OpenProcessToken
(
GetCurrentProcess
(),
TOKEN_QUERY
,
&
token
))
{
git_error_set
(
GIT_ERROR_OS
,
"could not lookup process information"
);
goto
done
;
}
if
(
GetTokenInformation
(
token
,
TokenUser
,
NULL
,
0
,
&
len
)
||
GetLastError
()
!=
ERROR_INSUFFICIENT_BUFFER
)
{
git_error_set
(
GIT_ERROR_OS
,
"could not lookup token metadata"
);
goto
done
;
}
info
=
git__malloc
(
len
);
GIT_ERROR_CHECK_ALLOC
(
info
);
if
(
!
GetTokenInformation
(
token
,
TokenUser
,
info
,
len
,
&
len
))
{
git_error_set
(
GIT_ERROR_OS
,
"could not lookup current user"
);
goto
done
;
}
if
((
*
out
=
sid_dup
(
info
->
User
.
Sid
)))
error
=
0
;
done:
if
(
token
)
CloseHandle
(
token
);
git__free
(
info
);
return
error
;
}
static
int
file_owner_sid
(
PSID
*
out
,
const
char
*
path
)
{
git_win32_path
path_w32
;
PSECURITY_DESCRIPTOR
descriptor
=
NULL
;
PSID
owner_sid
;
DWORD
ret
;
int
error
=
-
1
;
if
(
git_win32_path_from_utf8
(
path_w32
,
path
)
<
0
)
return
-
1
;
return
-
1
;
err
=
GetNamedSecurityInfoW
(
buf
,
SE_FILE_OBJECT
,
ret
=
GetNamedSecurityInfoW
(
path_w32
,
SE_FILE_OBJECT
,
OWNER_SECURITY_INFORMATION
|
OWNER_SECURITY_INFORMATION
|
DACL_SECURITY_INFORMATION
,
DACL_SECURITY_INFORMATION
,
&
owner_sid
,
NULL
,
NULL
,
NULL
,
&
descriptor
);
&
owner_sid
,
NULL
,
NULL
,
NULL
,
&
descriptor
);
if
(
err
==
ERROR_FILE_NOT_FOUND
||
err
==
ERROR_PATH_NOT_FOUND
)
{
if
(
ret
==
ERROR_FILE_NOT_FOUND
||
ret
==
ERROR_PATH_NOT_FOUND
)
ret
=
GIT_ENOTFOUND
;
error
=
GIT_ENOTFOUND
;
goto
cleanup
;
else
if
(
ret
!=
ERROR_SUCCESS
)
git_error_set
(
GIT_ERROR_OS
,
"failed to get security information"
);
else
if
(
!
IsValidSid
(
owner_sid
))
git_error_set
(
GIT_ERROR_OS
,
"file owner is not valid"
);
else
if
((
*
out
=
sid_dup
(
owner_sid
)))
error
=
0
;
if
(
descriptor
)
LocalFree
(
descriptor
);
return
error
;
}
int
git_fs_path_owner_is_current_user
(
bool
*
out
,
const
char
*
path
)
{
PSID
owner_sid
=
NULL
,
user_sid
=
NULL
;
int
error
=
-
1
;
if
(
mock_owner
)
{
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
);
return
0
;
}
}
if
(
err
!=
ERROR_SUCCESS
)
{
if
((
error
=
file_owner_sid
(
&
owner_sid
,
path
))
<
0
||
git_error_set
(
GIT_ERROR_OS
,
"failed to get security information"
);
(
error
=
current_user_sid
(
&
user_sid
))
<
0
)
ret
=
GIT_ERROR
;
goto
done
;
goto
cleanup
;
*
out
=
EqualSid
(
owner_sid
,
user_sid
);
error
=
0
;
done:
git__free
(
owner_sid
);
git__free
(
user_sid
);
return
error
;
}
int
git_fs_path_owner_is_system
(
bool
*
out
,
const
char
*
path
)
{
PSID
owner_sid
;
if
(
mock_owner
)
{
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_SYSTEM
);
return
0
;
}
}
if
(
!
IsValidSid
(
owner_sid
))
{
if
(
file_owner_sid
(
&
owner_sid
,
path
)
<
0
)
git_error_set
(
GIT_ERROR_INVALID
,
"programdata configuration file owner is unknown"
);
return
-
1
;
ret
=
GIT_ERROR
;
goto
cleanup
;
*
out
=
IsWellKnownSid
(
owner_sid
,
WinBuiltinAdministratorsSid
)
||
IsWellKnownSid
(
owner_sid
,
WinLocalSystemSid
);
git__free
(
owner_sid
);
return
0
;
}
int
git_fs_path_owner_is_system_or_current_user
(
bool
*
out
,
const
char
*
path
)
{
PSID
owner_sid
=
NULL
,
user_sid
=
NULL
;
int
error
=
-
1
;
if
(
mock_owner
)
{
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_SYSTEM
||
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
);
return
0
;
}
}
if
(
file_owner_sid
(
&
owner_sid
,
path
)
<
0
)
goto
done
;
if
(
IsWellKnownSid
(
owner_sid
,
WinBuiltinAdministratorsSid
)
||
if
(
IsWellKnownSid
(
owner_sid
,
WinBuiltinAdministratorsSid
)
||
IsWellKnownSid
(
owner_sid
,
WinLocalSystemSid
))
{
IsWellKnownSid
(
owner_sid
,
WinLocalSystemSid
))
{
ret
=
GIT_OK
;
*
out
=
1
;
goto
cleanup
;
error
=
0
;
goto
done
;
}
}
/* Obtain current user's SID */
if
(
current_user_sid
(
&
user_sid
)
<
0
)
if
(
OpenProcessToken
(
GetCurrentProcess
(),
TOKEN_QUERY
,
&
token
)
&&
goto
done
;
!
GetTokenInformation
(
token
,
TokenUser
,
NULL
,
0
,
&
len
))
{
info
=
git__malloc
(
len
);
*
out
=
EqualSid
(
owner_sid
,
user_sid
);
GIT_ERROR_CHECK_ALLOC
(
info
);
error
=
0
;
if
(
!
GetTokenInformation
(
token
,
TokenUser
,
info
,
len
,
&
len
))
{
git__free
(
info
);
done:
info
=
NULL
;
git__free
(
owner_sid
);
git__free
(
user_sid
);
return
error
;
}
#else
static
int
fs_path_owner_is
(
bool
*
out
,
const
char
*
path
,
uid_t
*
uids
,
size_t
uids_len
)
{
struct
stat
st
;
size_t
i
;
*
out
=
false
;
if
(
p_lstat
(
path
,
&
st
)
!=
0
)
{
if
(
errno
==
ENOENT
)
return
GIT_ENOTFOUND
;
git_error_set
(
GIT_ERROR_OS
,
"could not stat '%s'"
,
path
);
return
-
1
;
}
for
(
i
=
0
;
i
<
uids_len
;
i
++
)
{
if
(
uids
[
i
]
==
st
.
st_uid
)
{
*
out
=
true
;
break
;
}
}
}
}
/*
return
0
;
* If the file is owned by the same account that is running the current
}
* process, it's okay to read from that file.
*/
int
git_fs_path_owner_is_current_user
(
bool
*
out
,
const
char
*
path
)
if
(
info
&&
EqualSid
(
owner_sid
,
info
->
User
.
Sid
))
{
ret
=
GIT_OK
;
uid_t
userid
=
geteuid
();
else
{
git_error_set
(
GIT_ERROR_INVALID
,
"programdata configuration file owner is not valid"
);
if
(
mock_owner
)
{
ret
=
GIT_ERROR
;
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
);
return
0
;
}
}
git__free
(
info
);
cleanup
:
return
fs_path_owner_is
(
out
,
path
,
&
userid
,
1
);
if
(
descriptor
)
}
LocalFree
(
descriptor
);
return
ret
;
int
git_fs_path_owner_is_system
(
bool
*
out
,
const
char
*
path
)
#endif
{
uid_t
userid
=
0
;
if
(
mock_owner
)
{
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_SYSTEM
);
return
0
;
}
return
fs_path_owner_is
(
out
,
path
,
&
userid
,
1
);
}
int
git_fs_path_owner_is_system_or_current_user
(
bool
*
out
,
const
char
*
path
)
{
uid_t
userids
[
2
]
=
{
geteuid
(),
0
};
if
(
mock_owner
)
{
*
out
=
(
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_SYSTEM
||
mock_owner
==
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
);
return
0
;
}
return
fs_path_owner_is
(
out
,
path
,
userids
,
2
);
}
}
#endif
int
git_fs_path_find_executable
(
git_str
*
fullpath
,
const
char
*
executable
)
int
git_fs_path_find_executable
(
git_str
*
fullpath
,
const
char
*
executable
)
{
{
#ifdef GIT_WIN32
#ifdef GIT_WIN32
...
...
src/util/fs_path.h
View file @
63970244
...
@@ -731,17 +731,37 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path);
...
@@ -731,17 +731,37 @@ int git_fs_path_normalize_slashes(git_str *out, const char *path);
bool
git_fs_path_supports_symlinks
(
const
char
*
dir
);
bool
git_fs_path_supports_symlinks
(
const
char
*
dir
);
typedef
enum
{
GIT_FS_PATH_MOCK_OWNER_NONE
=
0
,
/* do filesystem lookups as normal */
GIT_FS_PATH_MOCK_OWNER_SYSTEM
=
1
,
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
=
2
,
GIT_FS_PATH_MOCK_OWNER_OTHER
=
3
}
git_fs_path__mock_owner_t
;
/**
* Sets the mock ownership for files; subsequent calls to
* `git_fs_path_owner_is_*` functions will return this data until cleared
* with `GIT_FS_PATH_MOCK_OWNER_NONE`.
*/
void
git_fs_path__set_owner
(
git_fs_path__mock_owner_t
owner
);
/**
/**
* Validate a system file's ownership
*
* Verify that the file in question is owned by an administrator or system
* Verify that the file in question is owned by an administrator or system
* account, or at least by the current user.
* account.
*
*/
* This function returns 0 if successful. If the file is not owned by any of
int
git_fs_path_owner_is_system
(
bool
*
out
,
const
char
*
path
);
* these, or any other if there have been problems determining the file
* ownership, it returns -1.
/**
* Verify that the file in question is owned by the current user;
*/
int
git_fs_path_owner_is_current_user
(
bool
*
out
,
const
char
*
path
);
/**
* Verify that the file in question is owned by an administrator or system
* account _or_ the current user;
*/
*/
int
git_fs_path_
validate_system_file_ownership
(
const
char
*
path
);
int
git_fs_path_
owner_is_system_or_current_user
(
bool
*
out
,
const
char
*
path
);
/**
/**
* Search the current PATH for the given executable, returning the full
* Search the current PATH for the given executable, returning the full
...
...
tests/clar/clar_libgit2.c
View file @
63970244
...
@@ -599,6 +599,11 @@ void cl_sandbox_set_search_path_defaults(void)
...
@@ -599,6 +599,11 @@ void cl_sandbox_set_search_path_defaults(void)
git_str_dispose
(
&
path
);
git_str_dispose
(
&
path
);
}
}
void
cl_sandbox_disable_ownership_validation
(
void
)
{
git_libgit2_opts
(
GIT_OPT_SET_OWNER_VALIDATION
,
0
);
}
#ifdef GIT_WIN32
#ifdef GIT_WIN32
bool
cl_sandbox_supports_8dot3
(
void
)
bool
cl_sandbox_supports_8dot3
(
void
)
{
{
...
...
tests/clar/clar_libgit2.h
View file @
63970244
...
@@ -222,6 +222,7 @@ void cl_fake_home(void);
...
@@ -222,6 +222,7 @@ void cl_fake_home(void);
void
cl_fake_home_cleanup
(
void
*
);
void
cl_fake_home_cleanup
(
void
*
);
void
cl_sandbox_set_search_path_defaults
(
void
);
void
cl_sandbox_set_search_path_defaults
(
void
);
void
cl_sandbox_disable_ownership_validation
(
void
);
#ifdef GIT_WIN32
#ifdef GIT_WIN32
# define cl_msleep(x) Sleep(x)
# define cl_msleep(x) Sleep(x)
...
...
tests/clar/main.c
View file @
63970244
...
@@ -26,6 +26,7 @@ int main(int argc, char *argv[])
...
@@ -26,6 +26,7 @@ int main(int argc, char *argv[])
cl_global_trace_register
();
cl_global_trace_register
();
cl_sandbox_set_search_path_defaults
();
cl_sandbox_set_search_path_defaults
();
cl_sandbox_disable_ownership_validation
();
/* Run the test suite */
/* Run the test suite */
res
=
clar_test_run
();
res
=
clar_test_run
();
...
...
tests/libgit2/repo/config.c
View file @
63970244
...
@@ -28,7 +28,6 @@ void test_repo_config__cleanup(void)
...
@@ -28,7 +28,6 @@ void test_repo_config__cleanup(void)
cl_assert
(
!
git_fs_path_isdir
(
"alternate"
));
cl_assert
(
!
git_fs_path_isdir
(
"alternate"
));
cl_fixture_cleanup
(
"empty_standard_repo"
);
cl_fixture_cleanup
(
"empty_standard_repo"
);
}
}
void
test_repo_config__can_open_global_when_there_is_no_file
(
void
)
void
test_repo_config__can_open_global_when_there_is_no_file
(
void
)
...
...
tests/libgit2/repo/open.c
View file @
63970244
...
@@ -3,13 +3,30 @@
...
@@ -3,13 +3,30 @@
#include "sysdir.h"
#include "sysdir.h"
#include <ctype.h>
#include <ctype.h>
static
int
validate_ownership
=
0
;
static
git_buf
config_path
=
GIT_BUF_INIT
;
void
test_repo_open__initialize
(
void
)
{
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_GET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
&
config_path
));
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_GET_OWNER_VALIDATION
,
&
validate_ownership
));
}
void
test_repo_open__cleanup
(
void
)
void
test_repo_open__cleanup
(
void
)
{
{
cl_git_sandbox_cleanup
();
cl_git_sandbox_cleanup
();
cl_fixture_cleanup
(
"empty_standard_repo"
);
cl_fixture_cleanup
(
"__global_config"
);
if
(
git_fs_path_isdir
(
"alternate"
))
if
(
git_fs_path_isdir
(
"alternate"
))
git_futils_rmdir_r
(
"alternate"
,
NULL
,
GIT_RMDIR_REMOVE_FILES
);
git_futils_rmdir_r
(
"alternate"
,
NULL
,
GIT_RMDIR_REMOVE_FILES
);
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_NONE
);
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
config_path
.
ptr
));
git_buf_dispose
(
&
config_path
);
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_OWNER_VALIDATION
,
validate_ownership
));
}
}
void
test_repo_open__bare_empty_repo
(
void
)
void
test_repo_open__bare_empty_repo
(
void
)
...
@@ -453,3 +470,133 @@ void test_repo_open__force_bare(void)
...
@@ -453,3 +470,133 @@ void test_repo_open__force_bare(void)
git_repository_free
(
barerepo
);
git_repository_free
(
barerepo
);
}
}
void
test_repo_open__validates_dir_ownership
(
void
)
{
git_repository
*
repo
;
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_OWNER_VALIDATION
,
1
));
cl_fixture_sandbox
(
"empty_standard_repo"
);
cl_git_pass
(
cl_rename
(
"empty_standard_repo/.gitted"
,
"empty_standard_repo/.git"
));
/* When the current user owns the repo config, that's acceptable */
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_CURRENT_USER
);
cl_git_pass
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
git_repository_free
(
repo
);
/* When the system user owns the repo config, fail */
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_SYSTEM
);
cl_git_fail
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
/* When an unknown user owns the repo config, fail */
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_OTHER
);
cl_git_fail
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
}
void
test_repo_open__can_allowlist_dirs_with_problematic_ownership
(
void
)
{
git_repository
*
repo
;
git_str
config_path
=
GIT_STR_INIT
,
config_filename
=
GIT_STR_INIT
,
config_data
=
GIT_STR_INIT
;
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_OWNER_VALIDATION
,
1
));
cl_fixture_sandbox
(
"empty_standard_repo"
);
cl_git_pass
(
cl_rename
(
"empty_standard_repo/.gitted"
,
"empty_standard_repo/.git"
));
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_OTHER
);
cl_git_fail
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
/* Add safe.directory options to the global configuration */
git_str_joinpath
(
&
config_path
,
clar_sandbox_path
(),
"__global_config"
);
cl_must_pass
(
p_mkdir
(
config_path
.
ptr
,
0777
));
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
config_path
.
ptr
);
git_str_joinpath
(
&
config_filename
,
config_path
.
ptr
,
".gitconfig"
);
git_str_printf
(
&
config_data
,
"[foo]
\n
"
\
"
\t
bar = Foobar
\n
"
\
"
\t
baz = Baz!
\n
"
\
"[safe]
\n
"
\
"
\t
directory = /non/existent/path
\n
"
\
"
\t
directory = /
\n
"
\
"
\t
directory = c:
\\\\
temp
\n
"
\
"
\t
directory = %s/%s
\n
"
\
"
\t
directory = /tmp
\n
"
\
"[bar]
\n
"
\
"
\t
foo = barfoo
\n
"
,
clar_sandbox_path
(),
"empty_standard_repo"
);
cl_git_rewritefile
(
config_filename
.
ptr
,
config_data
.
ptr
);
cl_git_pass
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
git_repository_free
(
repo
);
git_str_dispose
(
&
config_path
);
git_str_dispose
(
&
config_filename
);
git_str_dispose
(
&
config_data
);
}
void
test_repo_open__can_reset_safe_directory_list
(
void
)
{
git_repository
*
repo
;
git_str
config_path
=
GIT_STR_INIT
,
config_filename
=
GIT_STR_INIT
,
config_data
=
GIT_STR_INIT
;
cl_git_pass
(
git_libgit2_opts
(
GIT_OPT_SET_OWNER_VALIDATION
,
1
));
cl_fixture_sandbox
(
"empty_standard_repo"
);
cl_git_pass
(
cl_rename
(
"empty_standard_repo/.gitted"
,
"empty_standard_repo/.git"
));
git_fs_path__set_owner
(
GIT_FS_PATH_MOCK_OWNER_OTHER
);
cl_git_fail
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
/* Add safe.directory options to the global configuration */
git_str_joinpath
(
&
config_path
,
clar_sandbox_path
(),
"__global_config"
);
cl_must_pass
(
p_mkdir
(
config_path
.
ptr
,
0777
));
git_libgit2_opts
(
GIT_OPT_SET_SEARCH_PATH
,
GIT_CONFIG_LEVEL_GLOBAL
,
config_path
.
ptr
);
git_str_joinpath
(
&
config_filename
,
config_path
.
ptr
,
".gitconfig"
);
/* The blank resets our sandbox directory and opening fails */
git_str_printf
(
&
config_data
,
"[foo]
\n
"
\
"
\t
bar = Foobar
\n
"
\
"
\t
baz = Baz!
\n
"
\
"[safe]
\n
"
\
"
\t
directory = %s/%s
\n
"
\
"
\t
directory =
\n
"
\
"
\t
directory = /tmp
\n
"
\
"[bar]
\n
"
\
"
\t
foo = barfoo
\n
"
,
clar_sandbox_path
(),
"empty_standard_repo"
);
cl_git_rewritefile
(
config_filename
.
ptr
,
config_data
.
ptr
);
cl_git_fail
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
/* The blank resets tmp and allows subsequent declarations to succeed */
git_str_clear
(
&
config_data
);
git_str_printf
(
&
config_data
,
"[foo]
\n
"
\
"
\t
bar = Foobar
\n
"
\
"
\t
baz = Baz!
\n
"
\
"[safe]
\n
"
\
"
\t
directory = /tmp
\n
"
\
"
\t
directory =
\n
"
\
"
\t
directory = %s/%s
\n
"
\
"[bar]
\n
"
\
"
\t
foo = barfoo
\n
"
,
clar_sandbox_path
(),
"empty_standard_repo"
);
cl_git_rewritefile
(
config_filename
.
ptr
,
config_data
.
ptr
);
cl_git_pass
(
git_repository_open
(
&
repo
,
"empty_standard_repo"
));
git_repository_free
(
repo
);
git_str_dispose
(
&
config_path
);
git_str_dispose
(
&
config_filename
);
git_str_dispose
(
&
config_data
);
}
tests/util/path.c
View file @
63970244
...
@@ -737,3 +737,28 @@ void test_path__find_exe_in_path(void)
...
@@ -737,3 +737,28 @@ void test_path__find_exe_in_path(void)
git_str_dispose
(
&
sandbox_path
);
git_str_dispose
(
&
sandbox_path
);
git__free
(
orig_path
);
git__free
(
orig_path
);
}
}
void
test_path__validate_current_user_ownership
(
void
)
{
bool
is_cur
;
cl_must_pass
(
p_mkdir
(
"testdir"
,
0777
));
cl_git_pass
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"testdir"
));
cl_assert_equal_i
(
is_cur
,
1
);
cl_git_rewritefile
(
"testfile"
,
"This is a test file."
);
cl_git_pass
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"testfile"
));
cl_assert_equal_i
(
is_cur
,
1
);
#ifdef GIT_WIN32
cl_git_pass
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"C:
\\
"
));
cl_assert_equal_i
(
is_cur
,
0
);
cl_git_fail
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"c:
\\
path
\\
does
\\
not
\\
exist"
));
#else
cl_git_pass
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"/"
));
cl_assert_equal_i
(
is_cur
,
0
);
cl_git_fail
(
git_fs_path_owner_is_current_user
(
&
is_cur
,
"/path/does/not/exist"
));
#endif
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment