/*-- Copyright (c) 1996-1997 Microsoft Corporation Module Name: calcscom.c Abstract: Support routines for dacls/sacls exes Author: 14-Dec-1996 (macm) Environment: User mode only. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: --*/ #include #include #include #include #include #include #include #include #include #include #include DWORD ConvertCmdlineRights( IN PSTR pszCmdline, IN PCACLS_STR_RIGHTS pRightsTable, IN INT cRights, OUT DWORD *pConvertedRights ) /*++ Routine Description: Parses the given command line string that corresponds to a given rights list. The individual righs entry are looked up in the rights table and added to the list of converted rights Arguments: pszCmdline - The list of string rights to convert pRightsTable - The mapping from the string rights to the new win32 rights. It is expected that the rights table string tags will all be in upper case upon function entry. cRights - The number of items in the rights table pConvertedRights - Where the converted access mask is returned Return Value: ERROR_SUCCESS -- Success ERROR_INVALID_PARAMETER -- An unexpected string right was encountered --*/ { DWORD dwErr = ERROR_SUCCESS; PSTR pszCurrent = pszCmdline; INT i; #if DBG INT j; #endif *pConvertedRights = 0; // // Allow empty lists // if (pszCurrent == NULL) { return(ERROR_SUCCESS); } // // Assert that the table is upper case as expected // #if DBG for (i = 0; i < cRights; i++) { for (j = 0; j < 2; j++) { if(toupper(pRightsTable[i].szRightsTag[j]) != pRightsTable[i].szRightsTag[j]) { dwErr = ERROR_INVALID_PARAMETER; break; } } } #endif while (dwErr == ERROR_SUCCESS && *pszCurrent != '\0') { dwErr = ERROR_INVALID_PARAMETER; for (i = 0; i < cRights; i++ ) { if (pRightsTable[i].szRightsTag[0] == toupper(*pszCurrent) && pRightsTable[i].szRightsTag[1] == toupper(*(pszCurrent + 1))) { dwErr = ERROR_SUCCESS; *pConvertedRights |= pRightsTable[i].Right; break; } } pszCurrent++; if (*pszCurrent != '\0') { pszCurrent++; } } return(dwErr); } DWORD ParseCmdline ( IN PSTR *ppszArgs, IN INT cArgs, IN INT cSkip, IN PCACLS_CMDLINE pCmdValues, IN INT cCmdValues ) /*++ Routine Description: Parses the command line against the given cmd values. Arguments: ppszArgs - The argument list cArgs - Count of arguments in the list cSkip - Number of initial arguments to skip pCmdValues - Command values list to process the command line against cCmdValues - Number of command values in the list Return Value: ERROR_SUCCESS -- Success ERROR_INVALID_PARAMETER -- An unexpected command line value was found --*/ { DWORD dwErr = ERROR_SUCCESS; INT i,j; i = cSkip; while (i < cArgs && dwErr == ERROR_SUCCESS) { if( *ppszArgs[i] == '/' || *ppszArgs[i] == '-') { for (j = 0; j < cCmdValues; j++) { if (_stricmp(ppszArgs[i] + 1, pCmdValues[j].pszSwitch) == 0) { if (pCmdValues[j].iIndex != -1) { dwErr = ERROR_INVALID_PARAMETER; } else { pCmdValues[j].iIndex = i; // // See if we need to skip some number of values // if (pCmdValues[j].fFindNextSwitch == TRUE ) { pCmdValues[j].cValues = 0; while (i + 1 < cArgs) { if (*ppszArgs[i + 1] != '/' && *ppszArgs[i + 1] != '-') { pCmdValues[j].cValues++; i++; } else { break; } } } } break; } } if (j == cCmdValues) { dwErr = ERROR_INVALID_PARAMETER; break; } } else { dwErr = ERROR_INVALID_PARAMETER; } i++; } return(dwErr); } DWORD ProcessOperation ( IN PSTR *ppszCmdline, IN PCACLS_CMDLINE pCmdInfo, IN ACCESS_MODE AccessMode, IN PCACLS_STR_RIGHTS pRightsTable, IN INT cRights, IN DWORD fInherit, IN PACL pOldAcl OPTIONAL, OUT PACL *ppNewAcl ) /*++ Routine Description: Performs an "operation", such as Grant, Revoke, Deny. It parses the given command values into User/Permission pairs, and then creates a new security descriptor. The returned security descriptor needs to be freed via LocalFree. Arguments: ppszCmdline - The command line argument list pCmdInfo - Information about where this operation lives in the comand line AccessMode - Type of operation (Grant/Revoke/Deny) to do pRightsTable - The mapping from the string rights to the new win32 rights. It is expected that the rights table string tags will all be in upper case upon function entry. cRights - The number of items in the rights table fInherit - Inheritance flags to apply pOldAcl - Optional. If present, this is the ACL off of the object in the edit case. ppNewAcl - Where the new ACL is returned. Return Value: ERROR_SUCCESS -- Success ERROR_INVALID_PARAMETER -- The switch was specified, but no user/perms pairs were found ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed. --*/ { DWORD dwErr = ERROR_SUCCESS; PEXPLICIT_ACCESS_A pNewAccess = NULL; PSTR pszRights; INT i; DWORD dwRights; // // Make sure we have valid parameters // if (pCmdInfo->iIndex != -1 && pCmdInfo->cValues == 0) { return(ERROR_INVALID_PARAMETER); } pNewAccess = (PEXPLICIT_ACCESS_A)LocalAlloc(LMEM_FIXED, sizeof(EXPLICIT_ACCESS_A) * pCmdInfo->cValues); if (pNewAccess == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } // // Otherwise, start parsing and converting... // for (i = 0; i < (INT)pCmdInfo->cValues && dwErr == ERROR_SUCCESS; i++) { pszRights = strchr(ppszCmdline[pCmdInfo->iIndex + i + 1], ':'); if (pszRights == NULL && AccessMode != REVOKE_ACCESS) { dwErr = ERROR_INVALID_PARAMETER; } else { if (pszRights != NULL) { *pszRights = '\0'; pszRights++; } dwErr = ConvertCmdlineRights(pszRights, pRightsTable, cRights, &dwRights); if (dwErr == ERROR_SUCCESS) { BuildExplicitAccessWithNameA(&pNewAccess[i], ppszCmdline[pCmdInfo->iIndex + i + 1], dwRights, AccessMode, fInherit); } } } // // If all of that worked, we'll apply it to the new security descriptor // if (dwErr == ERROR_SUCCESS) { dwErr = SetEntriesInAclA(pCmdInfo->cValues, pNewAccess, pOldAcl, ppNewAcl); } LocalFree(pNewAccess); return(dwErr); } DWORD SetAndPropagateFileRights ( IN PSTR pszFilePath, IN PACL pAcl, IN SECURITY_INFORMATION SeInfo, IN BOOL fPropagate, IN BOOL fContinueOnDenied, IN BOOL fBreadthFirst, IN DWORD fInherit ) /*++ Routine Description: This function will set [and propagate] the given acl to the specified path and optionally all of its children. In the event of an access denied error, this function may or may not terminate, depending on the state of the fContinueOnDenied flag. This function does a depth first search with a write on return. This function is recursive. Arguments: pszFilePath - The file path to set the ACL on pAcl - The acl to set SeInfo - Whether the DACL or SACL is being set fPropagate - Determines whether the function should propagate or not fContinueOnDenied - Determines the behavior when an access denied is encountered. fBreadthFirst - If TRUE, do a breadth first propagation. Otherwise, do a depth first fInherit - Optional inheritance flags to apply Return Value: ERROR_SUCCESS -- Success ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed. --*/ { DWORD dwErr = ERROR_SUCCESS; PSTR pszFullPath = NULL; PSTR pszSearchPath = NULL; HANDLE hFind = INVALID_HANDLE_VALUE; DWORD cPathLen = 0; WIN32_FIND_DATAA FindData; BOOL fRestoreWhack = FALSE; PACE_HEADER pAce; DWORD iAce; if ( fInherit != 0 ) { pAce = (PACE_HEADER)FirstAce(pAcl); for ( iAce = 0; iAce < pAcl->AceCount && dwErr == ERROR_SUCCESS; iAce++, pAce = (PACE_HEADER)NextAce(pAce) ) { pAce->AceFlags |= (UCHAR)fInherit; } } // // If we're doing a breadth first propagation, set the security first // if ( fBreadthFirst == TRUE ) { dwErr = SetNamedSecurityInfoA(pszFilePath, SE_FILE_OBJECT, SeInfo, NULL, NULL, SeInfo == DACL_SECURITY_INFORMATION ? pAcl : NULL, SeInfo == SACL_SECURITY_INFORMATION ? pAcl : NULL); } if (fPropagate == TRUE) { cPathLen = strlen(pszFilePath); pszSearchPath = (PSTR)LocalAlloc(LMEM_FIXED, cPathLen + 1 + 4); if (pszSearchPath == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { if (pszFilePath[cPathLen - 1] == '\\') { pszFilePath[cPathLen - 1] = '\0'; cPathLen--; fRestoreWhack = TRUE; } sprintf(pszSearchPath, "%s\\%s", pszFilePath, "*.*"); hFind = FindFirstFileA(pszSearchPath, &FindData); if (hFind == INVALID_HANDLE_VALUE) { dwErr = GetLastError(); } } // // Start processing all the files // while (dwErr == ERROR_SUCCESS) { // // Ignore the . and .. // if (strcmp(FindData.cFileName, ".") != 0 && strcmp(FindData.cFileName, "..") != 0) { // // Now, build the full path... // pszFullPath = (PSTR)LocalAlloc(LMEM_FIXED, cPathLen + 1 + strlen(FindData.cFileName) + 1); if (pszFullPath == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { sprintf(pszFullPath, "%s\\%s", pszFilePath, FindData.cFileName); // // Call ourselves // dwErr = SetAndPropagateFileRights(pszFullPath, pAcl, SeInfo, fPropagate, fContinueOnDenied, fBreadthFirst, fInherit); if (dwErr == ERROR_ACCESS_DENIED && fContinueOnDenied == TRUE) { dwErr = ERROR_SUCCESS; } } } if (dwErr == ERROR_SUCCESS && FindNextFile(hFind, &FindData) == FALSE) { dwErr = GetLastError(); } } if(dwErr == ERROR_NO_MORE_FILES) { dwErr = ERROR_SUCCESS; } } // // Cover the case where it is a file // if (dwErr == ERROR_DIRECTORY) { dwErr = ERROR_SUCCESS; } // // Now, do the set // if (dwErr == ERROR_SUCCESS && fBreadthFirst == FALSE) { dwErr = SetNamedSecurityInfoA(pszFilePath, SE_FILE_OBJECT, SeInfo, NULL, NULL, SeInfo == DACL_SECURITY_INFORMATION ? pAcl : NULL, SeInfo == SACL_SECURITY_INFORMATION ? pAcl : NULL); } if (fRestoreWhack == TRUE) { pszFilePath[cPathLen - 1] = '\\'; pszFilePath[cPathLen] = '\0'; } // // If necessary, restore the inheritance flags // if ( fInherit != 0 ) { pAce = (PACE_HEADER)FirstAce(pAcl); for ( iAce = 0; iAce < pAcl->AceCount && dwErr == ERROR_SUCCESS; iAce++, pAce = (PACE_HEADER)NextAce(pAce) ) { pAce->AceFlags &= (UCHAR)~fInherit; } } return(dwErr); } DWORD DisplayAcl ( IN PSTR pszPath, IN PACL pAcl, IN PCACLS_STR_RIGHTS pRightsTable, IN INT cRights ) /*++ Routine Description: This function will display the given acl to the screen Arguments: pszPath - The file path to be displayed pAcl - The Acl to display pRightsTable - List of available rights cRights - Number of rights in the list Return Value: ERROR_SUCCESS -- Success --*/ { DWORD dwErr = ERROR_SUCCESS; ACL_SIZE_INFORMATION AclSize; ACL_REVISION_INFORMATION AclRev; PKNOWN_ACE pAce; PSID pSid; DWORD iIndex; PSTR pszName; INT i,cPathLen, iSkip, j; PSTR pszAceTypes[] = {"ACCESS_ALLOWED_ACE_TYPE", "ACCESS_DENIED_ACE_TYPE", "SYSTEM_AUDIT_ACE_TYPE", "SYSTEM_ALARM_ACE_TYPE", "ACCESS_ALLOWED_COMPOUND_ACE_TYPE", "ACCESS_ALLOWED_OBJECT_ACE_TYPE", "ACCESS_DENIED_OBJECT_ACE_TYPE", "SYSTEM_AUDIT_OBJECT_ACE_TYPE", "SYSTEM_ALARM_OBJECT_ACE_TYPE"}; PSTR pszInherit[] = {"OBJECT_INHERIT_ACE", "CONTAINER_INHERIT_ACE", "NO_PROPAGATE_INHERIT_ACE", "INHERIT_ONLY_ACE", "INHERITED_ACE"}; fprintf(stdout, "%s: ", pszPath); cPathLen = strlen(pszPath) + 2; if (pAcl == NULL) { fprintf(stdout, "NULL acl\n"); } else { if (GetAclInformation(pAcl, &AclRev, sizeof(ACL_REVISION_INFORMATION), AclRevisionInformation) == FALSE) { return(GetLastError()); } if(GetAclInformation(pAcl, &AclSize, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation) == FALSE) { return(GetLastError()); } fprintf(stdout, "AclRevision: %lu\n", AclRev.AclRevision); fprintf(stdout, "%*sAceCount: %lu\n", cPathLen, " ", AclSize.AceCount); fprintf(stdout, "%*sInUse: %lu\n", cPathLen, " ", AclSize.AclBytesInUse); fprintf(stdout, "%*sFree: %lu\n", cPathLen, " ", AclSize.AclBytesFree); fprintf(stdout, "%*sFlags: %lu\n", cPathLen, " ", pAcl->Sbz1); // // Now, dump all of the aces // pAce = FirstAce(pAcl); for(iIndex = 0; iIndex < pAcl->AceCount; iIndex++) { cPathLen = strlen(pszPath) + 2; fprintf(stdout, " %*sAce [%3lu]: ", cPathLen, " ", iIndex); cPathLen += 13; fprintf(stdout, "Type: %s\n", pAce->Header.AceType > ACCESS_MAX_MS_ACE_TYPE ? "UNKNOWN ACE TYPE" : pszAceTypes[pAce->Header.AceType]); fprintf(stdout, "%*sFlags: ", cPathLen, " "); if ( pAce->Header.AceFlags == 0 ) { fprintf(stdout, "0\n"); } else { if (( pAce->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) != 0 ) { fprintf( stdout,"SUCCESSFUL_ACCESS_ACE_FLAG " ); } if (( pAce->Header.AceFlags & FAILED_ACCESS_ACE_FLAG) != 0 ) { if (( pAce->Header.AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) != 0 ) { fprintf( stdout, "| " ); } fprintf( stdout,"FAILED_ACCESS_ACE_FLAG" ); } iSkip = 0; if ( ( pAce->Header.AceFlags & (FAILED_ACCESS_ACE_FLAG | SUCCESSFUL_ACCESS_ACE_FLAG)) != 0 && ( pAce->Header.AceFlags & VALID_INHERIT_FLAGS) != 0 ) { iSkip = cPathLen + 7; } // // Now, the inheritance flags // for (j = 0; j < sizeof(pszInherit) / sizeof(PSTR) ; j++) { if ((pAce->Header.AceFlags & (1 << j)) != 0 ) { if (iSkip != 0) { fprintf(stdout, " | \n"); fprintf(stdout, "%*s", iSkip, " "); } fprintf(stdout, "%s", pszInherit[j]); if (iSkip == 0) { iSkip = cPathLen + 7; } } } fprintf( stdout,"\n" ); } fprintf(stdout, "%*sSize: 0x%lx\n", cPathLen, " ", pAce->Header.AceSize); fprintf(stdout, "%*sMask: ", cPathLen, " "); if (pAce->Mask == 0) { fprintf(stdout, "%*sNo access\n", cPathLen, " "); } else { iSkip = 0; for (i = 1 ;i < cRights ;i++ ) { if ((pAce->Mask & pRightsTable[i].Right) != 0) { if (iSkip != 0) { fprintf(stdout, "%*s", iSkip, " "); } else { iSkip = cPathLen + 7; } fprintf(stdout, "%s\n", pRightsTable[i].pszDisplayTag); } } } // // Lookup the account name and return it... // // // If it's an object ace, dump the guids // dwErr = TranslateAccountName((PSID)&(pAce->SidStart), &pszName); if (dwErr == ERROR_SUCCESS) { fprintf(stdout, "%*sUser: %s\n", cPathLen, " ", pszName); LocalFree(pszName); } fprintf( stdout, "\n" ); pAce = NextAce(pAce); } } return(dwErr); } DWORD TranslateAccountName ( IN PSID pSid, OUT PSTR *ppszName ) /*++ Routine Description: This function will "translate" a sid into a name by doing a LookupAccountSid on the sid Arguments: pSid - The sid to convert to a name ppszName - Where the name is returned. Must be freed via a call to LocalFree. Return Value: ERROR_SUCCESS -- Success ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed --*/ { DWORD dwErr = ERROR_SUCCESS; SID_NAME_USE esidtype; LPSTR pszDomain = NULL; LPSTR pszName = NULL; ULONG cName = 0; ULONG cDomain = 0; if (LookupAccountSidA(NULL, pSid, NULL, &cName, NULL, &cDomain, &esidtype) == FALSE) { dwErr = GetLastError(); if (dwErr == ERROR_INSUFFICIENT_BUFFER) { dwErr = ERROR_SUCCESS; // // Allocate for the name and the domain // pszName = (PSTR)LocalAlloc(LMEM_FIXED, cName); if (pszName != NULL) { pszDomain = (PSTR)LocalAlloc(LMEM_FIXED, cDomain); if (pszDomain == NULL) { LocalFree(pszName); pszName = NULL; } } if (pszName == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } if (dwErr == ERROR_SUCCESS) { if(LookupAccountSidA(NULL, pSid, pszName, &cName, pszDomain, &cDomain, &esidtype) == FALSE) { dwErr = GetLastError(); LocalFree(pszName); LocalFree(pszDomain); } } } else if (dwErr == ERROR_NONE_MAPPED) { UCHAR String[256]; UNICODE_STRING SidStr; NTSTATUS Status; dwErr = ERROR_SUCCESS; pszName = NULL; // // Ok, return the sid as a name // SidStr.Buffer = (PWSTR)String; SidStr.Length = SidStr.MaximumLength = 256; Status = RtlConvertSidToUnicodeString(&SidStr, pSid, FALSE); if (NT_SUCCESS(Status)) { pszName = (PSTR)LocalAlloc(LMEM_FIXED, wcstombs(NULL, SidStr.Buffer, wcslen(SidStr.Buffer) + 1) + 1); if (pszName == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { wcstombs(pszName, SidStr.Buffer, wcslen(SidStr.Buffer) + 1); } } else { dwErr = RtlNtStatusToDosError(Status); } } } if(dwErr == ERROR_SUCCESS) { ULONG cLen; if(pszDomain != NULL && *pszDomain != '\0') { cLen = strlen(pszDomain) + 1; cLen += strlen(pszName) + 1; *ppszName = (PSTR)LocalAlloc(LMEM_FIXED, cLen); if (*ppszName == NULL) { dwErr = ERROR_NOT_ENOUGH_MEMORY; } else { sprintf(*ppszName, "%s\\%s", pszDomain, pszName); } } else { *ppszName = pszName; pszName = NULL; } } LocalFree(pszDomain); LocalFree(pszName); return(dwErr); }