9c4bc2c395
Reviewed-by: mikael
465 lines
13 KiB
C
465 lines
13 KiB
C
/*
|
|
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* Simple Windows utility to remove all non-owner access to a given file.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Access mask to represent any file access
|
|
*/
|
|
#define ANY_ACCESS (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE)
|
|
|
|
|
|
/*
|
|
* Print error message to stderr
|
|
*/
|
|
static void printLastError(const char* msg) {
|
|
int len;
|
|
char buf[128];
|
|
DWORD errval;
|
|
|
|
buf[0] = '\0';
|
|
len = sizeof(buf);
|
|
|
|
errval = GetLastError();
|
|
if (errval != 0) {
|
|
int n = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL, errval,
|
|
0, buf, len, NULL);
|
|
if (n > 3) {
|
|
/* Drop final '.', CR, LF */
|
|
if (buf[n - 1] == '\n') n--;
|
|
if (buf[n - 1] == '\r') n--;
|
|
if (buf[n - 1] == '.') n--;
|
|
buf[n] = '\0';
|
|
}
|
|
}
|
|
|
|
if (strlen(buf) > 0) {
|
|
fprintf(stderr, "revokeall %s: %s\n", msg, buf);
|
|
} else {
|
|
fprintf(stderr, "revokeall %s\n", msg);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Return a string that includes all the components of a given SID.
|
|
* See here for a description of the SID components :-
|
|
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/security/sid_components.asp
|
|
*/
|
|
static char *getTextualSid(SID* sid) {
|
|
SID_IDENTIFIER_AUTHORITY* sia;
|
|
DWORD i, count;
|
|
DWORD len;
|
|
char* name;
|
|
|
|
/*
|
|
* Get the identifier authority and the number of sub-authorities
|
|
*/
|
|
sia = GetSidIdentifierAuthority(sid);
|
|
count = *GetSidSubAuthorityCount(sid);
|
|
|
|
/*
|
|
* Allocate buffer for the string - buffer is :-
|
|
* S-SID_REVISION- + identifierAuthority- + subauthorities- + NULL
|
|
*/
|
|
len=(15 + 12 + (12 * count) + 1) * sizeof(char);
|
|
name = (char*)malloc(len);
|
|
if (name == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// S-SID_REVISION
|
|
snprintf(name, len, "S-%lu-", SID_REVISION );
|
|
|
|
// Identifier authority
|
|
if ((sia->Value[0] != 0) || (sia->Value[1] != 0))
|
|
{
|
|
snprintf(name + strlen(name), len - strlen(name),
|
|
"0x%02hx%02hx%02hx%02hx%02hx%02hx",
|
|
(USHORT)sia->Value[0],
|
|
(USHORT)sia->Value[1],
|
|
(USHORT)sia->Value[2],
|
|
(USHORT)sia->Value[3],
|
|
(USHORT)sia->Value[4],
|
|
(USHORT)sia->Value[5]);
|
|
}
|
|
else
|
|
{
|
|
snprintf(name + strlen(name), len - strlen(name), "%lu",
|
|
(ULONG)(sia->Value[5] ) +
|
|
(ULONG)(sia->Value[4] << 8) +
|
|
(ULONG)(sia->Value[3] << 16) +
|
|
(ULONG)(sia->Value[2] << 24) );
|
|
}
|
|
|
|
// finally, the sub-authorities
|
|
for (i=0 ; i<count; i++) {
|
|
snprintf(name + strlen(name), len - strlen(name), "-%lu",
|
|
*GetSidSubAuthority(sid, i) );
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/*
|
|
* Returns a string to represent the given security identifier (SID).
|
|
* If the account is known to the local computer then the account
|
|
* domain is returned. The format will be \\name or domain\\name depending
|
|
* on if the computer belongs to a domain.
|
|
* If the account name is not known then the textual representation of
|
|
* SID is returned -- eg: S-1-5-21-2818032319-470147023-1036452850-13037.
|
|
*/
|
|
static char *getSIDString(SID* sid) {
|
|
char domain[255];
|
|
char name[255];
|
|
DWORD domainLen = sizeof(domain);
|
|
DWORD nameLen = sizeof(name);
|
|
SID_NAME_USE use;
|
|
|
|
if(!IsValidSid(sid)) {
|
|
return strdup("<Invalid SID>");
|
|
}
|
|
|
|
if (LookupAccountSid(NULL, sid, name, &nameLen, domain, &domainLen, &use)) {
|
|
size_t len = strlen(name) + strlen(domain) + 3;
|
|
char* s = (char*)malloc(len);
|
|
if (s != NULL) {
|
|
strcpy(s, domain);
|
|
strcat(s, "\\\\");
|
|
strcat(s, name);
|
|
}
|
|
return s;
|
|
} else {
|
|
return getTextualSid(sid);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Returns 1 if the specified file is on a file system that supports
|
|
* persistent ACLs (On NTFS file systems returns true, on FAT32 file systems
|
|
* returns false), otherwise 0. Returns -1 if error.
|
|
*/
|
|
static int isSecuritySupported(const char* path) {
|
|
char* root;
|
|
char* p;
|
|
BOOL res;
|
|
DWORD dwMaxComponentLength;
|
|
DWORD dwFlags;
|
|
char fsName[128];
|
|
DWORD fsNameLength;
|
|
|
|
/*
|
|
* Get root directory. For UNCs the slash after the share name is required.
|
|
*/
|
|
root = strdup(path);
|
|
if (*root == '\\') {
|
|
/*
|
|
* \\server\share\file ==> \\server\share\
|
|
*/
|
|
int slashskip = 3;
|
|
p = root;
|
|
while ((*p == '\\') && (slashskip > 0)) {
|
|
char* p2;
|
|
p++;
|
|
p2 = strchr(p, '\\');
|
|
if ((p2 == NULL) || (*p2 != '\\')) {
|
|
free(root);
|
|
fprintf(stderr, "Malformed UNC");
|
|
return -1;
|
|
}
|
|
p = p2;
|
|
slashskip--;
|
|
}
|
|
if (slashskip != 0) {
|
|
free(root);
|
|
fprintf(stderr, "Malformed UNC");
|
|
return -1;
|
|
}
|
|
p++;
|
|
*p = '\0';
|
|
|
|
} else {
|
|
p = strchr(root, '\\');
|
|
|
|
/*
|
|
* Relative path so use current directory
|
|
*/
|
|
if (p == NULL) {
|
|
free(root);
|
|
root = malloc(255);
|
|
if (GetCurrentDirectory(255, root) == 0) {
|
|
printLastError("GetCurrentDirectory failed");
|
|
return -1;
|
|
}
|
|
p = strchr(root, '\\');
|
|
if (p == NULL) {
|
|
fprintf(stderr, "GetCurrentDirectory doesn't include drive letter!!!!\n");
|
|
return -1;
|
|
}
|
|
}
|
|
p++;
|
|
*p = '\0';
|
|
}
|
|
|
|
/*
|
|
* Get the volume information - this gives us the file system file and
|
|
* also tells us if the file system supports persistent ACLs.
|
|
*/
|
|
fsNameLength = sizeof(fsName)-1;
|
|
res = GetVolumeInformation(root,
|
|
NULL, // address of name of the volume, can be NULL
|
|
0, // length of volume name
|
|
NULL, // address of volume serial number, can be NULL
|
|
&dwMaxComponentLength,
|
|
&dwFlags,
|
|
fsName,
|
|
fsNameLength);
|
|
if (res == 0) {
|
|
printLastError("GetVolumeInformation failed");
|
|
free(root);
|
|
return -1;
|
|
}
|
|
|
|
free(root);
|
|
return (dwFlags & FS_PERSISTENT_ACLS) ? 1 : 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Returns the security descriptor for a file.
|
|
*/
|
|
static SECURITY_DESCRIPTOR* getFileSecurityDescriptor(const char* path) {
|
|
SECURITY_DESCRIPTOR* sd;
|
|
DWORD len = 0;
|
|
SECURITY_INFORMATION info =
|
|
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
|
|
|
|
GetFileSecurity(path, info , 0, 0, &len);
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
printLastError("GetFileSecurity failed");
|
|
return NULL;
|
|
}
|
|
sd = (SECURITY_DESCRIPTOR *)malloc(len);
|
|
if (sd == NULL) {
|
|
fprintf(stderr, "Out of memory");
|
|
} else {
|
|
if (!GetFileSecurity(path, info, sd, len, &len)) {
|
|
printLastError("GetFileSecurity failed");
|
|
free(sd);
|
|
return NULL;
|
|
}
|
|
}
|
|
return sd;
|
|
}
|
|
|
|
|
|
/*
|
|
* Revoke all access to the specific file
|
|
*/
|
|
static int revokeAll(const char* path) {
|
|
SECURITY_DESCRIPTOR* sd;
|
|
SID* owner;
|
|
ACL *acl;
|
|
BOOL defaulted, present;
|
|
ACL_SIZE_INFORMATION acl_size_info;
|
|
DWORD i, count;
|
|
char* str;
|
|
|
|
/*
|
|
* Get security descriptor for file; From security descriptor get the
|
|
* owner SID, and the DACL.
|
|
*/
|
|
sd = getFileSecurityDescriptor(path);
|
|
if (sd == NULL) {
|
|
return -1; /* error already reported */
|
|
}
|
|
if (!GetSecurityDescriptorOwner(sd, &owner, &defaulted)) {
|
|
printLastError("GetSecurityDescriptorOwner failed");
|
|
return -1;
|
|
}
|
|
str = getSIDString(owner);
|
|
if (str != NULL) {
|
|
printf("owner: %s\n", str);
|
|
free(str);
|
|
}
|
|
if (!GetSecurityDescriptorDacl(sd, &present, &acl, &defaulted)) {
|
|
printLastError("GetSecurityDescriptorDacl failed");
|
|
return -1;
|
|
}
|
|
if (!present) {
|
|
fprintf(stderr, "Security descriptor does not contain a DACL");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* If DACL is NULL there is no access to the file - we are done
|
|
*/
|
|
if (acl == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Iterate over the ACEs. For each "allow" type check that the SID
|
|
* matches the owner - if not we remove the ACE from the ACL
|
|
*/
|
|
if (!GetAclInformation(acl, (void *) &acl_size_info, sizeof(acl_size_info),
|
|
AclSizeInformation)) {
|
|
printLastError("GetAclInformation failed");
|
|
return -1;
|
|
}
|
|
count = acl_size_info.AceCount;
|
|
i = 0;
|
|
while (count > 0) {
|
|
void* ace;
|
|
ACCESS_ALLOWED_ACE *access;
|
|
SID* sid;
|
|
BOOL deleted;
|
|
|
|
if (!GetAce(acl, i, &ace)) {
|
|
printLastError("GetAce failed");
|
|
return -1;
|
|
}
|
|
if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType != ACCESS_ALLOWED_ACE_TYPE) {
|
|
i++;
|
|
count--;
|
|
continue;
|
|
}
|
|
access = (ACCESS_ALLOWED_ACE *)ace;
|
|
sid = (SID *) &access->SidStart;
|
|
|
|
|
|
deleted = FALSE;
|
|
if (!EqualSid(owner, sid)) {
|
|
/*
|
|
* If the ACE allows any access then the file then we
|
|
* delete it.
|
|
*/
|
|
if (access->Mask & ANY_ACCESS) {
|
|
str = getSIDString(sid);
|
|
if (str != NULL) {
|
|
printf("remove ALLOW %s\n", str);
|
|
free(str);
|
|
}
|
|
if (DeleteAce(acl, i) == 0) {
|
|
printLastError("DeleteAce failed");
|
|
return -1;
|
|
}
|
|
deleted = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!deleted) {
|
|
str = getSIDString(sid);
|
|
if (str != NULL) {
|
|
printf("ALLOW %s (access mask=%x)\n", str, access->Mask);
|
|
free(str);
|
|
}
|
|
|
|
/* onto the next ACE */
|
|
i++;
|
|
}
|
|
count--;
|
|
}
|
|
|
|
/*
|
|
* No changes - only owner has access
|
|
*/
|
|
if (i == acl_size_info.AceCount) {
|
|
printf("No changes.\n");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Create security descriptor and set its DACL to the version
|
|
* that we just edited
|
|
*/
|
|
if (!InitializeSecurityDescriptor(sd, SECURITY_DESCRIPTOR_REVISION)) {
|
|
printLastError("InitializeSecurityDescriptor failed");
|
|
return -1;
|
|
}
|
|
if (!SetSecurityDescriptorDacl(sd, present, acl, defaulted)) {
|
|
printLastError("SetSecurityDescriptorDacl failed");
|
|
return -1;
|
|
}
|
|
if (!SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd)) {
|
|
printLastError("SetFileSecurity failed");
|
|
return -1;
|
|
}
|
|
|
|
printf("File updated.\n");
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Convert slashes in the pathname to backslashes if needed.
|
|
*/
|
|
static char* convert_path(const char* p) {
|
|
int i = 0;
|
|
char* path = strdup(p);
|
|
while (p[i] != '\0') {
|
|
if (p[i] == '/') {
|
|
path[i] = '\\';
|
|
}
|
|
i++;
|
|
}
|
|
return path;
|
|
}
|
|
|
|
/*
|
|
* Usage: revokeall file
|
|
*/
|
|
int main( int argc, char *argv[])
|
|
{
|
|
int rc;
|
|
const char* path;
|
|
|
|
if (argc != 2) {
|
|
fprintf(stderr, "Usage: %s file\n", argv[0]);
|
|
return -1;
|
|
}
|
|
path = convert_path(argv[1]);
|
|
printf("Revoking all non-owner access to %s\n", path);
|
|
rc = isSecuritySupported(path);
|
|
if (rc != 1) {
|
|
if (rc == 0) {
|
|
printf("File security not supported on this file system\n");
|
|
}
|
|
return rc;
|
|
} else {
|
|
return revokeAll(path);
|
|
}
|
|
}
|