|
1 | 1 | #include "../git-compat-util.h"
|
2 | 2 | #include "win32.h"
|
| 3 | +#include <aclapi.h> |
| 4 | +#include <sddl.h> |
3 | 5 | #include <conio.h>
|
4 | 6 | #include <wchar.h>
|
5 | 7 | #include <winioctl.h>
|
@@ -3446,6 +3448,122 @@ static void setup_windows_environment(void)
|
3446 | 3448 | has_symlinks = 0;
|
3447 | 3449 | }
|
3448 | 3450 |
|
| 3451 | +static PSID get_current_user_sid(void) |
| 3452 | +{ |
| 3453 | + HANDLE token; |
| 3454 | + DWORD len = 0; |
| 3455 | + PSID result = NULL; |
| 3456 | + |
| 3457 | + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) |
| 3458 | + return NULL; |
| 3459 | + |
| 3460 | + if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) { |
| 3461 | + TOKEN_USER *info = xmalloc((size_t)len); |
| 3462 | + if (GetTokenInformation(token, TokenUser, info, len, &len)) { |
| 3463 | + len = GetLengthSid(info->User.Sid); |
| 3464 | + result = xmalloc(len); |
| 3465 | + if (!CopySid(len, result, info->User.Sid)) { |
| 3466 | + error(_("failed to copy SID (%ld)"), |
| 3467 | + GetLastError()); |
| 3468 | + FREE_AND_NULL(result); |
| 3469 | + } |
| 3470 | + } |
| 3471 | + FREE_AND_NULL(info); |
| 3472 | + } |
| 3473 | + CloseHandle(token); |
| 3474 | + |
| 3475 | + return result; |
| 3476 | +} |
| 3477 | + |
| 3478 | +int is_path_owned_by_current_sid(const char *path) |
| 3479 | +{ |
| 3480 | + WCHAR wpath[MAX_PATH]; |
| 3481 | + PSID sid = NULL; |
| 3482 | + PSECURITY_DESCRIPTOR descriptor = NULL; |
| 3483 | + DWORD err; |
| 3484 | + |
| 3485 | + static wchar_t home[MAX_PATH]; |
| 3486 | + |
| 3487 | + int result = 0; |
| 3488 | + |
| 3489 | + if (xutftowcs_path(wpath, path) < 0) |
| 3490 | + return 0; |
| 3491 | + |
| 3492 | + /* |
| 3493 | + * On Windows, the home directory is owned by the administrator, but for |
| 3494 | + * all practical purposes, it belongs to the user. Do pretend that it is |
| 3495 | + * owned by the user. |
| 3496 | + */ |
| 3497 | + if (!*home) { |
| 3498 | + DWORD size = ARRAY_SIZE(home); |
| 3499 | + DWORD len = GetEnvironmentVariableW(L"HOME", home, size); |
| 3500 | + if (!len || len > size) |
| 3501 | + wcscpy(home, L"::N/A::"); |
| 3502 | + } |
| 3503 | + if (!wcsicmp(wpath, home)) |
| 3504 | + return 1; |
| 3505 | + |
| 3506 | + /* Get the owner SID */ |
| 3507 | + err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT, |
| 3508 | + OWNER_SECURITY_INFORMATION | |
| 3509 | + DACL_SECURITY_INFORMATION, |
| 3510 | + &sid, NULL, NULL, NULL, &descriptor); |
| 3511 | + |
| 3512 | + if (err != ERROR_SUCCESS) |
| 3513 | + error(_("failed to get owner for '%s' (%ld)"), path, err); |
| 3514 | + else if (sid && IsValidSid(sid)) { |
| 3515 | + /* Now, verify that the SID matches the current user's */ |
| 3516 | + static PSID current_user_sid; |
| 3517 | + BOOL is_member; |
| 3518 | + |
| 3519 | + if (!current_user_sid) |
| 3520 | + current_user_sid = get_current_user_sid(); |
| 3521 | + |
| 3522 | + if (current_user_sid && |
| 3523 | + IsValidSid(current_user_sid) && |
| 3524 | + EqualSid(sid, current_user_sid)) |
| 3525 | + result = 1; |
| 3526 | + else if (IsWellKnownSid(sid, WinBuiltinAdministratorsSid) && |
| 3527 | + CheckTokenMembership(NULL, sid, &is_member) && |
| 3528 | + is_member) |
| 3529 | + /* |
| 3530 | + * If owned by the Administrators group, and the |
| 3531 | + * current user is an administrator, we consider that |
| 3532 | + * okay, too. |
| 3533 | + */ |
| 3534 | + result = 1; |
| 3535 | + else if (git_env_bool("GIT_TEST_DEBUG_UNSAFE_DIRECTORIES", 0)) { |
| 3536 | + LPSTR str1, str2, to_free1 = NULL, to_free2 = NULL; |
| 3537 | + |
| 3538 | + if (ConvertSidToStringSidA(sid, &str1)) |
| 3539 | + to_free1 = str1; |
| 3540 | + else |
| 3541 | + str1 = "(inconvertible)"; |
| 3542 | + |
| 3543 | + if (!current_user_sid) |
| 3544 | + str2 = "(none)"; |
| 3545 | + else if (!IsValidSid(current_user_sid)) |
| 3546 | + str2 = "(invalid)"; |
| 3547 | + else if (ConvertSidToStringSidA(current_user_sid, &str2)) |
| 3548 | + to_free2 = str2; |
| 3549 | + else |
| 3550 | + str2 = "(inconvertible)"; |
| 3551 | + warning("'%s' is owned by:\n\t'%s'\nbut the current user is:\n\t'%s'", path, str1, str2); |
| 3552 | + LocalFree(to_free1); |
| 3553 | + LocalFree(to_free2); |
| 3554 | + } |
| 3555 | + } |
| 3556 | + |
| 3557 | + /* |
| 3558 | + * We can release the security descriptor struct only now because `sid` |
| 3559 | + * actually points into this struct. |
| 3560 | + */ |
| 3561 | + if (descriptor) |
| 3562 | + LocalFree(descriptor); |
| 3563 | + |
| 3564 | + return result; |
| 3565 | +} |
| 3566 | + |
3449 | 3567 | int is_valid_win32_path(const char *path, int allow_literal_nul)
|
3450 | 3568 | {
|
3451 | 3569 | const char *p = path;
|
|
0 commit comments