rugged 1.3.1 → 1.3.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -2024,78 +2024,226 @@ done:
2024
2024
  return supported;
2025
2025
  }
2026
2026
 
2027
- int git_path_validate_system_file_ownership(const char *path)
2027
+ static git_path_owner_t mock_owner = GIT_PATH_OWNER_NONE;
2028
+
2029
+ void git_path__set_owner(git_path_owner_t owner)
2030
+ {
2031
+ mock_owner = owner;
2032
+ }
2033
+
2034
+ #ifdef GIT_WIN32
2035
+ static PSID *sid_dup(PSID sid)
2036
+ {
2037
+ DWORD len;
2038
+ PSID dup;
2039
+
2040
+ len = GetLengthSid(sid);
2041
+
2042
+ if ((dup = git__malloc(len)) == NULL)
2043
+ return NULL;
2044
+
2045
+ if (!CopySid(len, dup, sid)) {
2046
+ git_error_set(GIT_ERROR_OS, "could not duplicate sid");
2047
+ git__free(dup);
2048
+ return NULL;
2049
+ }
2050
+
2051
+ return dup;
2052
+ }
2053
+
2054
+ static int current_user_sid(PSID *out)
2028
2055
  {
2029
- #ifndef GIT_WIN32
2030
- GIT_UNUSED(path);
2031
- return GIT_OK;
2032
- #else
2033
- git_win32_path buf;
2034
- PSID owner_sid;
2035
- PSECURITY_DESCRIPTOR descriptor = NULL;
2036
- HANDLE token;
2037
2056
  TOKEN_USER *info = NULL;
2038
- DWORD err, len;
2039
- int ret;
2057
+ HANDLE token = NULL;
2058
+ DWORD len = 0;
2059
+ int error = -1;
2040
2060
 
2041
- if (git_win32_path_from_utf8(buf, path) < 0)
2042
- return -1;
2061
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
2062
+ git_error_set(GIT_ERROR_OS, "could not lookup process information");
2063
+ goto done;
2064
+ }
2043
2065
 
2044
- err = GetNamedSecurityInfoW(buf, SE_FILE_OBJECT,
2045
- OWNER_SECURITY_INFORMATION |
2046
- DACL_SECURITY_INFORMATION,
2047
- &owner_sid, NULL, NULL, NULL, &descriptor);
2066
+ if (GetTokenInformation(token, TokenUser, NULL, 0, &len) ||
2067
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
2068
+ git_error_set(GIT_ERROR_OS, "could not lookup token metadata");
2069
+ goto done;
2070
+ }
2048
2071
 
2049
- if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) {
2050
- ret = GIT_ENOTFOUND;
2051
- goto cleanup;
2072
+ info = git__malloc(len);
2073
+ GIT_ERROR_CHECK_ALLOC(info);
2074
+
2075
+ if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
2076
+ git_error_set(GIT_ERROR_OS, "could not lookup current user");
2077
+ goto done;
2052
2078
  }
2053
2079
 
2054
- if (err != ERROR_SUCCESS) {
2080
+ if ((*out = sid_dup(info->User.Sid)))
2081
+ error = 0;
2082
+
2083
+ done:
2084
+ if (token)
2085
+ CloseHandle(token);
2086
+
2087
+ git__free(info);
2088
+ return error;
2089
+ }
2090
+
2091
+ static int file_owner_sid(PSID *out, const char *path)
2092
+ {
2093
+ git_win32_path path_w32;
2094
+ PSECURITY_DESCRIPTOR descriptor = NULL;
2095
+ PSID owner_sid;
2096
+ DWORD ret;
2097
+ int error = -1;
2098
+
2099
+ if (git_win32_path_from_utf8(path_w32, path) < 0)
2100
+ return -1;
2101
+
2102
+ ret = GetNamedSecurityInfoW(path_w32, SE_FILE_OBJECT,
2103
+ OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
2104
+ &owner_sid, NULL, NULL, NULL, &descriptor);
2105
+
2106
+ if (ret == ERROR_FILE_NOT_FOUND || ret == ERROR_PATH_NOT_FOUND)
2107
+ error = GIT_ENOTFOUND;
2108
+ else if (ret != ERROR_SUCCESS)
2055
2109
  git_error_set(GIT_ERROR_OS, "failed to get security information");
2056
- ret = GIT_ERROR;
2057
- goto cleanup;
2110
+ else if (!IsValidSid(owner_sid))
2111
+ git_error_set(GIT_ERROR_OS, "file owner is not valid");
2112
+ else if ((*out = sid_dup(owner_sid)))
2113
+ error = 0;
2114
+
2115
+ if (descriptor)
2116
+ LocalFree(descriptor);
2117
+
2118
+ return error;
2119
+ }
2120
+
2121
+ int git_path_owner_is(
2122
+ bool *out,
2123
+ const char *path,
2124
+ git_path_owner_t owner_type)
2125
+ {
2126
+ PSID owner_sid = NULL, user_sid = NULL;
2127
+ BOOL is_admin, admin_owned;
2128
+ int error;
2129
+
2130
+ if (mock_owner) {
2131
+ *out = ((mock_owner & owner_type) != 0);
2132
+ return 0;
2133
+ }
2134
+
2135
+ if ((error = file_owner_sid(&owner_sid, path)) < 0)
2136
+ goto done;
2137
+
2138
+ if ((owner_type & GIT_PATH_OWNER_CURRENT_USER) != 0) {
2139
+ if ((error = current_user_sid(&user_sid)) < 0)
2140
+ goto done;
2141
+
2142
+ if (EqualSid(owner_sid, user_sid)) {
2143
+ *out = true;
2144
+ goto done;
2145
+ }
2058
2146
  }
2059
2147
 
2060
- if (!IsValidSid(owner_sid)) {
2061
- git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is unknown");
2062
- ret = GIT_ERROR;
2063
- goto cleanup;
2148
+ admin_owned =
2149
+ IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
2150
+ IsWellKnownSid(owner_sid, WinLocalSystemSid);
2151
+
2152
+ if (admin_owned &&
2153
+ (owner_type & GIT_PATH_OWNER_ADMINISTRATOR) != 0) {
2154
+ *out = true;
2155
+ goto done;
2064
2156
  }
2065
2157
 
2066
- if (IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
2067
- IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
2068
- ret = GIT_OK;
2069
- goto cleanup;
2158
+ if (admin_owned &&
2159
+ (owner_type & GIT_PATH_USER_IS_ADMINISTRATOR) != 0 &&
2160
+ CheckTokenMembership(NULL, owner_sid, &is_admin) &&
2161
+ is_admin) {
2162
+ *out = true;
2163
+ goto done;
2070
2164
  }
2071
2165
 
2072
- /* Obtain current user's SID */
2073
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token) &&
2074
- !GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
2075
- info = git__malloc(len);
2076
- GIT_ERROR_CHECK_ALLOC(info);
2077
- if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
2078
- git__free(info);
2079
- info = NULL;
2080
- }
2166
+ *out = false;
2167
+
2168
+ done:
2169
+ git__free(owner_sid);
2170
+ git__free(user_sid);
2171
+ return error;
2172
+ }
2173
+
2174
+ #else
2175
+
2176
+ static int sudo_uid_lookup(uid_t *out)
2177
+ {
2178
+ git_buf uid_str = GIT_BUF_INIT;
2179
+ int64_t uid;
2180
+ int error;
2181
+
2182
+ if ((error = git__getenv(&uid_str, "SUDO_UID")) == 0 &&
2183
+ (error = git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10)) == 0 &&
2184
+ uid == (int64_t)((uid_t)uid)) {
2185
+ *out = (uid_t)uid;
2081
2186
  }
2082
2187
 
2083
- /*
2084
- * If the file is owned by the same account that is running the current
2085
- * process, it's okay to read from that file.
2086
- */
2087
- if (info && EqualSid(owner_sid, info->User.Sid))
2088
- ret = GIT_OK;
2089
- else {
2090
- git_error_set(GIT_ERROR_INVALID, "programdata configuration file owner is not valid");
2091
- ret = GIT_ERROR;
2188
+ git_buf_dispose(&uid_str);
2189
+ return error;
2190
+ }
2191
+
2192
+ int git_path_owner_is(
2193
+ bool *out,
2194
+ const char *path,
2195
+ git_path_owner_t owner_type)
2196
+ {
2197
+ struct stat st;
2198
+ uid_t euid, sudo_uid;
2199
+
2200
+ if (mock_owner) {
2201
+ *out = ((mock_owner & owner_type) != 0);
2202
+ return 0;
2092
2203
  }
2093
- git__free(info);
2094
2204
 
2095
- cleanup:
2096
- if (descriptor)
2097
- LocalFree(descriptor);
2205
+ euid = geteuid();
2206
+
2207
+ if (p_lstat(path, &st) != 0) {
2208
+ if (errno == ENOENT)
2209
+ return GIT_ENOTFOUND;
2210
+
2211
+ git_error_set(GIT_ERROR_OS, "could not stat '%s'", path);
2212
+ return -1;
2213
+ }
2214
+
2215
+ if ((owner_type & GIT_PATH_OWNER_CURRENT_USER) != 0 &&
2216
+ st.st_uid == euid) {
2217
+ *out = true;
2218
+ return 0;
2219
+ }
2220
+
2221
+ if ((owner_type & GIT_PATH_OWNER_ADMINISTRATOR) != 0 &&
2222
+ st.st_uid == 0) {
2223
+ *out = true;
2224
+ return 0;
2225
+ }
2226
+
2227
+ if ((owner_type & GIT_PATH_OWNER_RUNNING_SUDO) != 0 &&
2228
+ euid == 0 &&
2229
+ sudo_uid_lookup(&sudo_uid) == 0 &&
2230
+ st.st_uid == sudo_uid) {
2231
+ *out = true;
2232
+ return 0;
2233
+ }
2234
+
2235
+ *out = false;
2236
+ return 0;
2237
+ }
2098
2238
 
2099
- return ret;
2100
2239
  #endif
2240
+
2241
+ int git_path_owner_is_current_user(bool *out, const char *path)
2242
+ {
2243
+ return git_path_owner_is(out, path, GIT_PATH_OWNER_CURRENT_USER);
2244
+ }
2245
+
2246
+ int git_path_owner_is_system(bool *out, const char *path)
2247
+ {
2248
+ return git_path_owner_is(out, path, GIT_PATH_OWNER_ADMINISTRATOR);
2101
2249
  }
@@ -722,16 +722,54 @@ int git_path_normalize_slashes(git_buf *out, const char *path);
722
722
 
723
723
  bool git_path_supports_symlinks(const char *dir);
724
724
 
725
+ typedef enum {
726
+ GIT_PATH_OWNER_NONE = 0,
727
+
728
+ /** The file must be owned by the current user. */
729
+ GIT_PATH_OWNER_CURRENT_USER = (1 << 0),
730
+
731
+ /** The file must be owned by the system account. */
732
+ GIT_PATH_OWNER_ADMINISTRATOR = (1 << 1),
733
+
734
+ /**
735
+ * The file may be owned by a system account if the current
736
+ * user is in an administrator group. Windows only; this is
737
+ * a noop on non-Windows systems.
738
+ */
739
+ GIT_PATH_USER_IS_ADMINISTRATOR = (1 << 2),
740
+
741
+ /**
742
+ * The file is owned by the current user, who is running `sudo`.
743
+ */
744
+ GIT_PATH_OWNER_RUNNING_SUDO = (1 << 3),
745
+
746
+ /** The file may be owned by another user. */
747
+ GIT_PATH_OWNER_OTHER = (1 << 4)
748
+ } git_path_owner_t;
749
+
750
+ /**
751
+ * Sets the mock ownership for files; subsequent calls to
752
+ * `git_path_owner_is_*` functions will return this data until
753
+ * cleared with `GIT_FS_PATH_OWNER_NONE`.
754
+ */
755
+ void git_path__set_owner(git_path_owner_t owner);
756
+
757
+ /** Verify that the file in question is owned by the given owner. */
758
+ int git_path_owner_is(
759
+ bool *out,
760
+ const char *path,
761
+ git_path_owner_t owner_type);
762
+
725
763
  /**
726
- * Validate a system file's ownership
727
- *
728
764
  * Verify that the file in question is owned by an administrator or system
729
- * account, or at least by the current user.
730
- *
731
- * This function returns 0 if successful. If the file is not owned by any of
732
- * these, or any other if there have been problems determining the file
733
- * ownership, it returns -1.
765
+ * account.
734
766
  */
735
- int git_path_validate_system_file_ownership(const char *path);
767
+ int git_path_owner_is_system(bool *out, const char *path);
768
+
769
+ /**
770
+ * Verify that the file in question is owned by the current user;
771
+ */
772
+
773
+ int git_path_owner_is_current_user(bool *out, const char *path);
736
774
 
737
775
  #endif
@@ -38,6 +38,7 @@
38
38
  # include "win32/w32_util.h"
39
39
  #endif
40
40
 
41
+ bool git_repository__validate_ownership = true;
41
42
  bool git_repository__fsync_gitdir = false;
42
43
 
43
44
  static const struct {
@@ -64,6 +65,7 @@ static const struct {
64
65
 
65
66
  static int check_repositoryformatversion(int *version, git_config *config);
66
67
  static int check_extensions(git_config *config, int version);
68
+ static int load_global_config(git_config **config);
67
69
 
68
70
  #define GIT_COMMONDIR_FILE "commondir"
69
71
  #define GIT_GITDIR_FILE "gitdir"
@@ -482,6 +484,116 @@ static int read_gitfile(git_buf *path_out, const char *file_path)
482
484
  return error;
483
485
  }
484
486
 
487
+ typedef struct {
488
+ const char *repo_path;
489
+ git_buf tmp;
490
+ bool *is_safe;
491
+ } validate_ownership_data;
492
+
493
+ static int validate_ownership_cb(const git_config_entry *entry, void *payload)
494
+ {
495
+ validate_ownership_data *data = payload;
496
+
497
+ if (strcmp(entry->value, "") == 0)
498
+ *data->is_safe = false;
499
+
500
+ if (git_path_prettify_dir(&data->tmp, entry->value, NULL) == 0 &&
501
+ strcmp(data->tmp.ptr, data->repo_path) == 0)
502
+ *data->is_safe = true;
503
+
504
+ return 0;
505
+ }
506
+
507
+ static int validate_ownership_config(bool *is_safe, const char *path)
508
+ {
509
+ validate_ownership_data ownership_data = {
510
+ path, GIT_BUF_INIT, is_safe
511
+ };
512
+ git_config *config;
513
+ int error;
514
+
515
+ if (load_global_config(&config) != 0)
516
+ return 0;
517
+
518
+ error = git_config_get_multivar_foreach(config,
519
+ "safe.directory", NULL,
520
+ validate_ownership_cb,
521
+ &ownership_data);
522
+
523
+ git_config_free(config);
524
+ git_buf_dispose(&ownership_data.tmp);
525
+
526
+ return error;
527
+ }
528
+
529
+ static int validate_ownership_path(bool *is_safe, const char *path)
530
+ {
531
+ git_path_owner_t owner_level =
532
+ GIT_PATH_OWNER_CURRENT_USER |
533
+ GIT_PATH_USER_IS_ADMINISTRATOR |
534
+ GIT_PATH_OWNER_RUNNING_SUDO;
535
+ int error = 0;
536
+
537
+ if (path)
538
+ error = git_path_owner_is(is_safe, path, owner_level);
539
+
540
+ if (error == GIT_ENOTFOUND) {
541
+ *is_safe = true;
542
+ error = 0;
543
+ }
544
+
545
+ return error;
546
+ }
547
+
548
+ static int validate_ownership(git_repository *repo)
549
+ {
550
+ const char *validation_paths[3] = { NULL }, *path;
551
+ size_t validation_len = 0, i;
552
+ bool is_safe = false;
553
+ int error = 0;
554
+
555
+ /*
556
+ * If there's a worktree, validate the permissions to it *and*
557
+ * the git directory, and use the worktree as the configuration
558
+ * key for allowlisting the directory. In a bare setup, only
559
+ * look at the gitdir and use that as the allowlist. So we
560
+ * examine all `validation_paths` but use only the first as
561
+ * the configuration lookup.
562
+ */
563
+
564
+ if (repo->workdir)
565
+ validation_paths[validation_len++] = repo->workdir;
566
+
567
+ if (repo->gitlink)
568
+ validation_paths[validation_len++] = repo->gitlink;
569
+
570
+ validation_paths[validation_len++] = repo->gitdir;
571
+
572
+ for (i = 0; i < validation_len; i++) {
573
+ path = validation_paths[i];
574
+
575
+ if ((error = validate_ownership_path(&is_safe, path)) < 0)
576
+ goto done;
577
+
578
+ if (!is_safe)
579
+ break;
580
+ }
581
+
582
+ if (is_safe ||
583
+ (error = validate_ownership_config(&is_safe, validation_paths[0])) < 0)
584
+ goto done;
585
+
586
+ if (!is_safe) {
587
+ git_error_set(GIT_ERROR_CONFIG,
588
+ "repository path '%s' is not owned by current user",
589
+ path);
590
+ error = GIT_EOWNER;
591
+ }
592
+
593
+ done:
594
+ return error;
595
+ }
596
+
485
597
  static int find_repo(
486
598
  git_buf *gitdir_path,
487
599
  git_buf *workdir_path,
@@ -903,16 +1015,23 @@ int git_repository_open_ext(
903
1015
  if ((error = check_extensions(config, version)) < 0)
904
1016
  goto cleanup;
905
1017
 
906
- if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
1018
+ if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) {
907
1019
  repo->is_bare = 1;
908
- else {
909
-
1020
+ } else {
910
1021
  if (config &&
911
1022
  ((error = load_config_data(repo, config)) < 0 ||
912
1023
  (error = load_workdir(repo, config, &workdir)) < 0))
913
1024
  goto cleanup;
914
1025
  }
915
1026
 
1027
+ /*
1028
+ * Ensure that the git directory and worktree are
1029
+ * owned by the current user.
1030
+ */
1031
+ if (git_repository__validate_ownership &&
1032
+ (error = validate_ownership(repo)) < 0)
1033
+ goto cleanup;
1034
+
916
1035
  cleanup:
917
1036
  git_buf_dispose(&gitdir);
918
1037
  git_buf_dispose(&workdir);
@@ -1609,13 +1728,40 @@ static bool is_filesystem_case_insensitive(const char *gitdir_path)
1609
1728
  return is_insensitive;
1610
1729
  }
1611
1730
 
1612
- static bool are_symlinks_supported(const char *wd_path)
1731
+ /*
1732
+ * Return a configuration object with only the global and system
1733
+ * configurations; no repository-level configuration.
1734
+ */
1735
+ static int load_global_config(git_config **config)
1613
1736
  {
1614
- git_config *config = NULL;
1615
1737
  git_buf global_buf = GIT_BUF_INIT;
1616
1738
  git_buf xdg_buf = GIT_BUF_INIT;
1617
1739
  git_buf system_buf = GIT_BUF_INIT;
1618
1740
  git_buf programdata_buf = GIT_BUF_INIT;
1741
+ int error;
1742
+
1743
+ git_config_find_global(&global_buf);
1744
+ git_config_find_xdg(&xdg_buf);
1745
+ git_config_find_system(&system_buf);
1746
+ git_config_find_programdata(&programdata_buf);
1747
+
1748
+ error = load_config(config, NULL,
1749
+ path_unless_empty(&global_buf),
1750
+ path_unless_empty(&xdg_buf),
1751
+ path_unless_empty(&system_buf),
1752
+ path_unless_empty(&programdata_buf));
1753
+
1754
+ git_buf_dispose(&global_buf);
1755
+ git_buf_dispose(&xdg_buf);
1756
+ git_buf_dispose(&system_buf);
1757
+ git_buf_dispose(&programdata_buf);
1758
+
1759
+ return error;
1760
+ }
1761
+
1762
+ static bool are_symlinks_supported(const char *wd_path)
1763
+ {
1764
+ git_config *config = NULL;
1619
1765
  int symlinks = 0;
1620
1766
 
1621
1767
  /*
@@ -1626,19 +1772,9 @@ static bool are_symlinks_supported(const char *wd_path)
1626
1772
  * _not_ set, then we do not test or enable symlink support.
1627
1773
  */
1628
1774
  #ifdef GIT_WIN32
1629
- git_config_find_global(&global_buf);
1630
- git_config_find_xdg(&xdg_buf);
1631
- git_config_find_system(&system_buf);
1632
- git_config_find_programdata(&programdata_buf);
1633
-
1634
- if (load_config(&config, NULL,
1635
- path_unless_empty(&global_buf),
1636
- path_unless_empty(&xdg_buf),
1637
- path_unless_empty(&system_buf),
1638
- path_unless_empty(&programdata_buf)) < 0)
1639
- goto done;
1640
-
1641
- if (git_config_get_bool(&symlinks, config, "core.symlinks") < 0 || !symlinks)
1775
+ if (load_global_config(&config) < 0 ||
1776
+ git_config_get_bool(&symlinks, config, "core.symlinks") < 0 ||
1777
+ !symlinks)
1642
1778
  goto done;
1643
1779
  #endif
1644
1780
 
@@ -1646,10 +1782,6 @@ static bool are_symlinks_supported(const char *wd_path)
1646
1782
  goto done;
1647
1783
 
1648
1784
  done:
1649
- git_buf_dispose(&global_buf);
1650
- git_buf_dispose(&xdg_buf);
1651
- git_buf_dispose(&system_buf);
1652
- git_buf_dispose(&programdata_buf);
1653
1785
  git_config_free(config);
1654
1786
  return symlinks != 0;
1655
1787
  }
@@ -34,6 +34,7 @@
34
34
  #define GIT_DIR_SHORTNAME "GIT~1"
35
35
 
36
36
  extern bool git_repository__fsync_gitdir;
37
+ extern bool git_repository__validate_ownership;
37
38
 
38
39
  /** Cvar cache identifiers */
39
40
  typedef enum {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rugged
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.3.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Chacon
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-01-28 00:00:00.000000000 Z
12
+ date: 2022-07-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake-compiler
@@ -697,7 +697,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
697
697
  - !ruby/object:Gem::Version
698
698
  version: '0'
699
699
  requirements: []
700
- rubygems_version: 3.4.0.dev
700
+ rubygems_version: 3.3.7
701
701
  signing_key:
702
702
  specification_version: 4
703
703
  summary: Rugged is a Ruby binding to the libgit2 linkable library