How to determine the owner of both local and remote files






4.43/5 (11 votes)
Code to determine the owner of both local and remote files.
Introduction
I needed this code for a project I contributed to Blackball, Inc. I found all kinds of examples on the web, but they all fell short for various reasons. One problem was that most of the examples I found did not work on a remote location. Another issue was that many of the examples I found did not properly deal with 'UNC vs. Drive Letter' path issues... including drive letter paths that map to 'Physical Drives vs. Shares'. This code will also work on folders.
Demo Project
The demo project download is a quick way to take a look at how the code can be used.
If you just want to grab the code and start using it in your own project, you need to copy the file name getowner.cpp into your project folder and then add it to your project. The file is dependent on Win32 and RTL libraries.
Points of Interest
The code within the getowner.cpp file is really just a 'C' function and will work in a 'C' project if you move the variable declarations to the top of the function. The function is called GetOwner()
and is detailed in Figure 1. The GetOwner()
function takes three parameters: the path to the item in question, a pointer to a buffer to contain the result, and the size of the buffer in bytes. The numbered comments within the code spell out the intent of each step. Interesting APIs used are WNetGetUniversalName()
, GetFileSecurity()
, GetSecurityDescriptorOwner()
, and LookupAccountSid()
.
#include <windows.h> // For the 'Win32' API. #include <shlwapi.h> // For the 'Path' API. #include <winnetwk.h> // For the 'WNet' API. // Function name : GetOwner // Description : Determines the 'Owner' of a given file or folder. // Return type : UINT is S_OK if successful; A Win32 'ERROR_' value otherwise. // Argument : LPCWSTR szFileOrFolderPathName is the fully // qualified path of the file or folder to examine. // Argument : LPWSTR pUserNameBuffer is a pointer to a buffer // used to contain the resulting 'Owner' string. // Argument : int nSizeInBytes is the size of the buffer. UINT GetOwner(LPCWSTR szFileOrFolderPathName, LPWSTR pUserNameBuffer, int nSizeInBytes) { // 1) Validate the path: // 1.1) Length should not be 0. // 1.2) Path must point to an existing file or folder. if(!lstrlen(szFileOrFolderPathName) || !PathFileExist(szFileOrFolderPathName)) return ERROR_INVALID_PARAMETER; // 2) Validate the buffer: // 2.1) Size must not be 0. // 2.2) Pointer must not be NULL. if(nSizeInBytes<=0 || pUserNameBuffer==NULL) return ERROR_INVALID_PARAMETER; // 3) Convert the path to UNC if it is not already UNC // so that we can extract a machine name from it: // 3.1) Use a big buffer... some OS's can have a path that is 32768 chars in length. WCHAR szUNCPathName[32767] = {0}; // 3.2) If path is not UNC... if(!PathIsUNC(szFileOrFolderPathName)) { // 3.3) Mask the big buffer into a UNIVERSAL_NAME_INFO. DWORD dwUniSize = 32767*sizeof(WCHAR); UNIVERSAL_NAME_INFO* pUNI = (UNIVERSAL_NAME_INFO*)szUNCPathName; // 3.4) Attempt to get the UNC version of the path into the big buffer. if(!WNetGetUniversalName(szFileOrFolderPathName, UNIVERSAL_NAME_INFO_LEVEL, pUNI, &dwUniSize)) { // 3.5) If successful, copy the UNC version into the buffer. lstrcpy(szUNCPathName, pUNI->lpUniversalName); } else { // 3.6) If not successful, copy the original path into the buffer. lstrcpy(szUNCPathName,szFileOrFolderPathName); } } else { // 3.7) Path is already UNC, copy the original path into the buffer. lstrcpy(szUNCPathName,szFileOrFolderPathName); } // 4) If path is UNC (will not be the case for local physical // drive paths) we want to extract the machine name: // 4.1) Use a buffer bug enough to hold a machine name per Win32. WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH+1] = {0}; // 4.2) If path is UNC... if(PathIsUNC(szUNCPathName)) { // 4.3) Use PathFindNextComponent() to skip past the double backslashes. LPWSTR lpMachineName = PathFindNextComponent(szUNCPathName); // 4.4) Walk the the rest of the path to find the end of the machine name. int nPos = 0; LPWSTR lpNextSlash = lpMachineName; while((lpNextSlash[0] != L'\\') && (lpNextSlash[0] != L'\0')) { nPos++; lpNextSlash++; } // 4.5) Copyt the machine name into the buffer. lstrcpyn(szMachineName, lpMachineName, nPos+1); } // 5) Derive the 'Owner' by getting the owner's Security ID from // a Security Descriptor associated with the file or folder indicated in the path. // 5.1) Get a security descriptor for the file // or folder that contains the Owner Security Information. // 5.1.1) Use GetFileSecurity() with some null params to get the required buffer size. // 5.1.2) We don't really care about the return value. // 5.1.3) The error code must be ERROR_INSUFFICIENT_BUFFER for us to continue. unsigned long uSizeNeeded = 0; GetFileSecurity(szUNCPathName, OWNER_SECURITY_INFORMATION, 0, 0, &uSizeNeeded); UINT uRet = GetLastError(); if(uRet==ERROR_INSUFFICIENT_BUFFER && uSizeNeeded) { uRet = S_OK; // Clear the ERROR_INSUFFICIENT_BUFFER // 5.2) Allocate the buffer for the Security Descriptor, check for out of memory LPBYTE lpSecurityBuffer = (LPBYTE) malloc(uSizeNeeded * sizeof(BYTE)); if(!lpSecurityBuffer) { return ERROR_NOT_ENOUGH_MEMORY; } // 5.2) Get the Security Descriptor that contains // the Owner Security Information into the buffer, check for errors if(!GetFileSecurity(szUNCPathName, OWNER_SECURITY_INFORMATION, lpSecurityBuffer, uSizeNeeded, &uSizeNeeded)) { free(lpSecurityBuffer); return GetLastError(); } // 5.3) Get the the owner's Security ID (SID) // from the Security Descriptor, check for errors PSID pSID = NULL; BOOL bOwnerDefaulted = FALSE; if(!GetSecurityDescriptorOwner(lpSecurityBuffer, &pSID, &bOwnerDefaulted)) { free(lpSecurityBuffer); return GetLastError(); } // 5.4) Get the size of the buffers needed // for the owner information (domain and name) // 5.4.1) Use LookupAccountSid() with buffer sizes // set to zero to get the required buffer sizes. // 5.4.2) We don't really care about the return value. // 5.4.3) The error code must be ERROR_INSUFFICIENT_BUFFER for us to continue. LPWSTR pName = NULL; LPWSTR pDomain = NULL; unsigned long uNameLen = 0; unsigned long uDomainLen = 0; SID_NAME_USE sidNameUse = SidTypeUser; LookupAccountSid(szMachineName, pSID, pName, &uNameLen, pDomain, &uDomainLen, &sidNameUse); uRet = GetLastError(); if((uRet==ERROR_INSUFFICIENT_BUFFER) && uNameLen && uDomainLen) { uRet = S_OK; // Clear the ERROR_INSUFFICIENT_BUFFER // 5.5) Allocate the required buffers, check for out of memory pName = (LPWSTR) malloc(uNameLen * sizeof(WCHAR)); pDomain = (LPWSTR) malloc(uDomainLen * sizeof(WCHAR)); if(!pName || !pDomain) { free(lpSecurityBuffer); return ERROR_NOT_ENOUGH_MEMORY; } // 5.6) Get domain and username if(!LookupAccountSid(szMachineName, pSID, pName, &uNameLen, pDomain, &uDomainLen, &sidNameUse)) { free(pName); free(pDomain); free(lpSecurityBuffer); return GetLastError(); } // 5.7) Build the owner string from the domain and username if(nSizeBytes > ((uNameLen+uDomainLen+1)*sizeof(WCHAR))) { lstrcpy(pUserNameBuffer, pDomain); lstrcat(pUserNameBuffer, L"\\"); lstrcat(pUserNameBuffer, pName); } else { uRet = ERROR_INSUFFICIENT_BUFFER; } // 5.8) Release memory free(pName); free(pDomain); } // 5.9) Release memory free(lpSecurityBuffer); } return uRet; }
(Figure 1)
If I left out any details you think should be mentioned in the article, please let me know.
If you could take one last second to rate this article or even leave a comment, it would be much appreciated.
Thanks for reading!