Add a function to determine whether a path is owned by the current user

This function will be used in the next commit to prevent `setup_git_directory()` from discovering a repository in a directory that is owned by someone other than the current user. Note: We cannot simply use `st.st_uid` on Windows just like we do on Linux and other Unix-like platforms: according to https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/stat-functions this field is always zero on Windows (because Windows' idea of a user ID does not fit into a single numerical value). Therefore, we have to do something a little involved to replicate the same functionality there. Also note: On Windows, a user's home directory is not actually owned by said user, but by the administrator. For all practical purposes, it is under the user's control, though, therefore we pretend that it is owned by the user. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

Johannes Schindelin committed Mar 2, 2022 at 11:06 UTC bdc77d1d685be9c10b88abb281a42bc620548595
3 files changed +106
compat/mingw.c
+87
@@ -1,5 +1,6 @@
1 #include "../git-compat-util.h"
2 #include "win32.h"
3 +#include <aclapi.h>
4 #include <conio.h>
5 #include <wchar.h>
6 #include "../strbuf.h"
@@ -2601,6 +2602,92 @@ static void setup_windows_environment(void)
2602 }
2603 }
2604
2605 +static PSID get_current_user_sid(void)
2606 +{
2607 + HANDLE token;
2608 + DWORD len = 0;
2609 + PSID result = NULL;
2610 +
2611 + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
2612 + return NULL;
2613 +
2614 + if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
2615 + TOKEN_USER *info = xmalloc((size_t)len);
2616 + if (GetTokenInformation(token, TokenUser, info, len, &len)) {
2617 + len = GetLengthSid(info->User.Sid);
2618 + result = xmalloc(len);
2619 + if (!CopySid(len, result, info->User.Sid)) {
2620 + error(_("failed to copy SID (%ld)"),
2621 + GetLastError());
2622 + FREE_AND_NULL(result);
2623 + }
2624 + }
2625 + FREE_AND_NULL(info);
2626 + }
2627 + CloseHandle(token);
2628 +
2629 + return result;
2630 +}
2631 +
2632 +int is_path_owned_by_current_sid(const char *path)
2633 +{
2634 + WCHAR wpath[MAX_PATH];
2635 + PSID sid = NULL;
2636 + PSECURITY_DESCRIPTOR descriptor = NULL;
2637 + DWORD err;
2638 +
2639 + static wchar_t home[MAX_PATH];
2640 +
2641 + int result = 0;
2642 +
2643 + if (xutftowcs_path(wpath, path) < 0)
2644 + return 0;
2645 +
2646 + /*
2647 + * On Windows, the home directory is owned by the administrator, but for
2648 + * all practical purposes, it belongs to the user. Do pretend that it is
2649 + * owned by the user.
2650 + */
2651 + if (!*home) {
2652 + DWORD size = ARRAY_SIZE(home);
2653 + DWORD len = GetEnvironmentVariableW(L"HOME", home, size);
2654 + if (!len || len > size)
2655 + wcscpy(home, L"::N/A::");
2656 + }
2657 + if (!wcsicmp(wpath, home))
2658 + return 1;
2659 +
2660 + /* Get the owner SID */
2661 + err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
2662 + OWNER_SECURITY_INFORMATION |
2663 + DACL_SECURITY_INFORMATION,
2664 + &sid, NULL, NULL, NULL, &descriptor);
2665 +
2666 + if (err != ERROR_SUCCESS)
2667 + error(_("failed to get owner for '%s' (%ld)"), path, err);
2668 + else if (sid && IsValidSid(sid)) {
2669 + /* Now, verify that the SID matches the current user's */
2670 + static PSID current_user_sid;
2671 +
2672 + if (!current_user_sid)
2673 + current_user_sid = get_current_user_sid();
2674 +
2675 + if (current_user_sid &&
2676 + IsValidSid(current_user_sid) &&
2677 + EqualSid(sid, current_user_sid))
2678 + result = 1;
2679 + }
2680 +
2681 + /*
2682 + * We can release the security descriptor struct only now because `sid`
2683 + * actually points into this struct.
2684 + */
2685 + if (descriptor)
2686 + LocalFree(descriptor);
2687 +
2688 + return result;
2689 +}
2690 +
2691 int is_valid_win32_path(const char *path, int allow_literal_nul)
2692 {
2693 const char *p = path;
compat/mingw.h
+7
@@ -452,6 +452,13 @@ char *mingw_query_user_email(void);
452 #include <inttypes.h>
453 #endif
454
455 +/**
456 + * Verifies that the specified path is owned by the user running the
457 + * current process.
458 + */
459 +int is_path_owned_by_current_sid(const char *path);
460 +#define is_path_owned_by_current_user is_path_owned_by_current_sid
461 +
462 /**
463 * Verifies that the given path is a valid one on Windows.
464 *
git-compat-util.h
+12
@@ -392,6 +392,18 @@ static inline int git_offset_1st_component(const char *path)
392 #define is_valid_path(path) 1
393 #endif
394
395 +#ifndef is_path_owned_by_current_user
396 +static inline int is_path_owned_by_current_uid(const char *path)
397 +{
398 + struct stat st;
399 + if (lstat(path, &st))
400 + return 0;
401 + return st.st_uid == geteuid();
402 +}
403 +
404 +#define is_path_owned_by_current_user is_path_owned_by_current_uid
405 +#endif
406 +
407 #ifndef find_last_dir_sep
408 static inline char *git_find_last_dir_sep(const char *path)
409 {