X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/fa7225c82381bac4432a6edf16f53b5370238d85..dd5fb164cf5b32c462296bc65e289e100f74b59a:/OSX/libsecurity_translocate/lib/SecTranslocateShared.cpp diff --git a/OSX/libsecurity_translocate/lib/SecTranslocateShared.cpp b/OSX/libsecurity_translocate/lib/SecTranslocateShared.cpp index f03c070e..9c1595ae 100644 --- a/OSX/libsecurity_translocate/lib/SecTranslocateShared.cpp +++ b/OSX/libsecurity_translocate/lib/SecTranslocateShared.cpp @@ -73,7 +73,9 @@ static void setMountPointQuarantineIfNecessary(const string &mountPoint, const s static string getMountpointFromAppPath(const string &appPath, const string &originalPath); static vector getMountTableSnapshot(); -static string mountExistsForUser(const string &translationDirForUser, const string &originalPath, const string &destMount); +static string mountExistsForUser(const string &translationDirForUser, + const TranslocationPath &originalPath, + const string &destMount); static void validateMountpoint(const string &mountpoint, bool owned=false); static string makeNewMountpoint(const string &translationDir); static string newAppPath (const string &mountPoint, const TranslocationPath &originalPath); @@ -118,7 +120,8 @@ TranslocationPath::TranslocationPath(string originalPath) /* don't translocate if it already is */ /* only consider translocation if the thing being asked about is marked for translocation */ - if(!fd.isFileSystemType(NULLFS_FSTYPE) && fd.isQuarantined() && fd.shouldTranslocate()) + /* Nullfs can't translocate other mount's roots so abort if its a mountpoint */ + if(!fd.isFileSystemType(NULLFS_FSTYPE) && fd.isQuarantined() && fd.shouldTranslocate() && !fd.isMountPoint()) { ExtendedAutoFileDesc &&outermost = findOuterMostCodeBundleForFD(fd); @@ -140,6 +143,8 @@ TranslocationPath::TranslocationPath(string originalPath) UnixError::throwMe(EINVAL); } + componentNameToTranslocate = toTranslocateComponents.back(); + for(size_t cnt = 0; cnt < originalComponents.size(); cnt++) { if (cnt < toTranslocateComponents.size()) @@ -455,7 +460,7 @@ static vector getMountTableSnapshot() and an optional destination mountpoint path. Check the mount table to see if a mount point already user, for this app. If a destMountPoint is provided, make sure it is for this user, and that exists for this the mountpoint found in the mount table is the same as the one requested */ -static string mountExistsForUser(const string &translationDirForUser, const string &originalPath, const string &destMountPoint) +static string mountExistsForUser(const string &translationDirForUser, const TranslocationPath &originalPath, const string &destMountPoint) { string result; // start empty @@ -489,6 +494,18 @@ static string mountExistsForUser(const string &translationDirForUser, const stri vector mntbuf = getMountTableSnapshot(); + /* Save the untranslocated inode number*/ + ExtendedAutoFileDesc::UnixStat untranslocatedStat; + + if (stat(originalPath.getPathToTranslocate().c_str(), &untranslocatedStat)) + { + errno_t err = errno; + Syslog::warning("SecTranslocate: failed to stat original path (%d): %s", + err, + originalPath.getPathToTranslocate().c_str()); + UnixError::throwMe(err); + } + for (auto &i : mntbuf) { string mountOnName = i.f_mntonname; @@ -501,7 +518,7 @@ static string mountExistsForUser(const string &translationDirForUser, const stri also make sure that this is a nullfs mount and that the mount point name is longer than the translation directory with something other than / */ - if (i.f_mntfromname == originalPath && //mount is for the requested path + if (i.f_mntfromname == originalPath.getPathToTranslocate() && //mount is for the requested path strcmp(i.f_fstypename, NULLFS_FSTYPE) == 0 && // mount is a nullfs mount lastNonSlashPos > translationDirForUser.length()-1 && // no shenanigans, there must be more directory here than just the translation dir strncmp(i.f_mntonname, translationDirForUser.c_str(), translationDirForUser.length()) == 0) //mount is inside the translocation dir @@ -517,6 +534,29 @@ static string mountExistsForUser(const string &translationDirForUser, const stri UnixError::throwMe(EEXIST); } } + /* + find the inode number for mountOnName+/d/appname + */ + string pathToTranslocatedApp = mountOnName+"/d/"+originalPath.getComponentNameToTranslocate(); + + ExtendedAutoFileDesc::UnixStat oldTranslocatedStat; + + if (stat(pathToTranslocatedApp.c_str(), &oldTranslocatedStat)) + { + /* We should have access to this path and it should be real so complain if thats not true. */ + errno_t err = errno; + Syslog::warning("SecTranslocate: expected app not inside mountpoint: %s (error: %d)", pathToTranslocatedApp.c_str(), err); + UnixError::throwMe(err); + } + + if(untranslocatedStat.st_ino != oldTranslocatedStat.st_ino) + { + /* We have two Apps with the same name at the same path but different inodes. This means that the + translocated path is broken and should be removed */ + destroyTranslocatedPathForUser(pathToTranslocatedApp); + continue; + } + result = mountOnName; break; } @@ -685,16 +725,8 @@ static void setMountPointQuarantineIfNecessary(const string &mountPoint, const s to the desired app in the new mountpoint, and sanity check that calculation */ static string newAppPath (const string &mountPoint, const TranslocationPath &originalPath) { - vector original = splitPath(originalPath.getPathToTranslocate()); - - if (original.size() == 0) - { - Syslog::error("SecTranslocate: Invalid originalPath: %s", originalPath.getPathToTranslocate().c_str()); - UnixError::throwMe(EINVAL); - } - string midPath = mountPoint+"/d"; - string outPath = originalPath.getTranslocatedPathToOriginalPath(midPath+"/"+original.back()); + string outPath = originalPath.getTranslocatedPathToOriginalPath(midPath+"/"+originalPath.getComponentNameToTranslocate()); /* ExtendedAutoFileDesc will throw if one of these doesn't exist or isn't accessible */ ExtendedAutoFileDesc mountFd(mountPoint); @@ -756,7 +788,7 @@ string translocatePathForUser(const TranslocationPath &originalPath, const strin destMountPoint = getMountpointFromAppPath(destPath, toTranslocate); //throws or returns a mountpoint } - mountpoint = mountExistsForUser(baseDirForUser, toTranslocate, destMountPoint); //throws, detects invalid destMountPoint string + mountpoint = mountExistsForUser(baseDirForUser, originalPath, destMountPoint); //throws, detects invalid destMountPoint string if (!mountpoint.empty()) { @@ -954,14 +986,15 @@ bool destroyTranslocatedPathsForUserOnVolume(const string &volumePath) bool cleanupError = false; string baseDirForUser = translocationDirForUser(); vector mountTable = getMountTableSnapshot(); + struct statfs sb; fsid_t unmountingFsid; + int haveUnmountingFsid = statfs(volumePath.c_str(), &sb); + int haveMntFromState = 0; - /* passing in an empty volume here will fail to open */ - ExtendedAutoFileDesc volume(volumePath, O_RDONLY, FileDesc::modeMissingOk); + memset(&unmountingFsid, 0, sizeof(unmountingFsid)); - if(volume.isOpen()) - { - unmountingFsid = volume.getFsid(); + if(haveUnmountingFsid == 0) { + unmountingFsid = sb.f_fsid; } for (auto &mnt : mountTable) @@ -975,17 +1008,17 @@ bool destroyTranslocatedPathsForUserOnVolume(const string &volumePath) if (strcmp(mnt.f_fstypename, NULLFS_FSTYPE) == 0 && strncmp(mnt.f_mntonname, baseDirForUser.c_str(), baseDirForUser.length()) == 0) { - ExtendedAutoFileDesc volumeToCheck(mnt.f_mntfromname, O_RDONLY, FileDesc::modeMissingOk); + haveMntFromState = statfs(mnt.f_mntfromname, &sb); - if (!volumeToCheck.isOpen()) + if (haveMntFromState != 0) { // In this case we are trying to unmount a translocation point that points to nothing. Force it. // Not forcing it currently hangs in UBC cleanup. (void)removeMountPoint(mnt.f_mntonname , true); } - else if (volume.isOpen()) + else if (haveUnmountingFsid == 0) { - fsid_t toCheckFsid = volumeToCheck.getFsid(); + fsid_t toCheckFsid = sb.f_fsid; if( memcmp(&unmountingFsid, &toCheckFsid, sizeof(fsid_t)) == 0) { if(removeMountPoint(mnt.f_mntonname) != 0) @@ -999,6 +1032,7 @@ bool destroyTranslocatedPathsForUserOnVolume(const string &volumePath) return !cleanupError; } + /* This is intended to be used periodically to clean up translocation points that aren't used anymore */ void tryToDestroyUnusedTranslocationMounts() {