SecTranslocateUtilities.cpp   [plain text]


/*
 * Copyright (c) 2016 Apple Inc. All Rights Reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 *
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 *
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 *
 * @APPLE_LICENSE_HEADER_END@
 */

#include <string>
#include <vector>

#include <unistd.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <dlfcn.h>

#define __APPLE_API_PRIVATE
#include <quarantine.h>
#undef __APPLE_API_PRIVATE

#include <security_utilities/logging.h>
#include <security_utilities/unix++.h>
#include <security_utilities/cfutilities.h>

#include "SecTranslocateUtilities.hpp"

#define APP_TRANSLOCATION_DIR "/AppTranslocation/"

namespace Security {

using namespace Security::UnixPlusPlus;

namespace SecTranslocate {

using namespace std;

/* store the real path and fstatfs for the file descriptor. This throws if either fail */
void ExtendedAutoFileDesc::init()
{
    char absPath[MAXPATHLEN];
    if(isOpen())
    {
        UnixError::check(fstatfs(fd(), &fsInfo));
        fcntl(F_GETPATH, absPath);
        realPath = absPath;
        quarantined = false;
        qtn_flags = 0;
        quarantineFetched = false; //only fetch quarantine info when we need it
    }
}

bool ExtendedAutoFileDesc::isFileSystemType(const string &fsType) const
{
    notOpen(); //Throws if not Open
    
    return fsType == fsInfo.f_fstypename;
}

bool ExtendedAutoFileDesc::pathIsAbsolute() const
{
    notOpen(); //Throws if not Open
    
    return originalPath == realPath;
}
    
bool ExtendedAutoFileDesc::isMountPoint() const
{
    notOpen(); //Throws if not Open
    return realPath == fsInfo.f_mntonname;
}
bool ExtendedAutoFileDesc::isInPrefixDir(const string &prefixDir) const
{
    notOpen(); //Throws if not Open
    
    return strncmp(realPath.c_str(), prefixDir.c_str(), prefixDir.length()) == 0;
}

string ExtendedAutoFileDesc::getFsType() const
{
    notOpen(); //Throws if not Open
    
    return fsInfo.f_fstypename;
}
    
string ExtendedAutoFileDesc::getMountPoint() const
{
    notOpen(); //Throws if not Open
    
    return fsInfo.f_mntonname;
}
    
string ExtendedAutoFileDesc::getMountFromPath() const
{
    notOpen(); //Throws if not Open
    
    return fsInfo.f_mntfromname;
}

const string& ExtendedAutoFileDesc::getRealPath() const
{
    notOpen(); //Throws if not Open
    
    return realPath;
}

fsid_t const ExtendedAutoFileDesc::getFsid() const
{
    notOpen(); //Throws if not Open
    
    return fsInfo.f_fsid;
}

void ExtendedAutoFileDesc::fetchQuarantine()
{
    if(!quarantineFetched)
    {
        notOpen();

        qtn_file_t qf = qtn_file_alloc();

        if(qf)
        {
            if(0 == qtn_file_init_with_fd(qf, fd()))
            {
                quarantined = true;
                qtn_flags = qtn_file_get_flags(qf);
            }
            qtn_file_free(qf);
            quarantineFetched = true;
        }
        else
        {
            Syslog::error("SecTranslocate: failed to allocate memory for quarantine struct");
            UnixError::throwMe();
        }
    }
}

bool ExtendedAutoFileDesc::isQuarantined()
{
    notOpen();
    fetchQuarantine();

    return quarantined;
}

bool ExtendedAutoFileDesc::isUserApproved()
{
    notOpen();
    fetchQuarantine();

    return ((qtn_flags & QTN_FLAG_USER_APPROVED) == QTN_FLAG_USER_APPROVED);
}

bool ExtendedAutoFileDesc::shouldTranslocate()
{
    notOpen();
    fetchQuarantine();

    return ((qtn_flags & (QTN_FLAG_TRANSLOCATE | QTN_FLAG_DO_NOT_TRANSLOCATE)) == QTN_FLAG_TRANSLOCATE);
}

/* Take an absolute path and split it into a vector of path components */
vector<string> splitPath(const string &path)
{
    vector<string> out;
    size_t start = 0;
    size_t end = 0;
    size_t len = 0;
    
    if(path.empty() || path.front() != '/')
    {
        Syslog::error("SecTranslocate::splitPath: asked to split a non-absolute or empty path: %s",path.c_str());
        UnixError::throwMe(EINVAL);
    }
    
    while(end != string::npos)
    {
        end = path.find('/', start);
        len = (end == string::npos) ? end : (end - start);
        string temp = path.substr(start,len);
        
        if(!temp.empty())
        {
            out.push_back(temp);
        }
        start = end + 1;
    }
    
    return out;
}

/* Take a vector of path components and turn it into an absolute path */
string joinPath(vector<string>& path)
{
    string out = "";
    for(auto &i : path)
    {
        out += "/"+i;
    }
    return out;
}

string joinPathUpTo(vector<string> &path, size_t index)
{
    if (path.size() == 0 || index > path.size()-1)
    {
        Syslog::error("SecTranslocate::joinPathUpTo invalid index %lu (size %lu)",index, path.size()-1);
        UnixError::throwMe(EINVAL);
    }

    string out = "";
    for (size_t i = 0; i <= index; i++)
    {
        out += "/" + path[i];
    }

    return out;
}
    
/* Fully resolve the path provided */
string getRealPath(const string &path)
{
    char absPath[MAXPATHLEN];
    AutoFileDesc fd(path);
    fd.fcntl(F_GETPATH, absPath);
    return absPath;
}
    
/* Create a UUID string */
string makeUUID()
{
    CFRef<CFUUIDRef> newUUID = CFUUIDCreate(NULL);
    if (!newUUID)
    {
        UnixError::throwMe(ENOMEM);
    }
    
    CFRef<CFStringRef> str = CFUUIDCreateString(NULL, newUUID.get());
    if (!str)
    {
        UnixError::throwMe(ENOMEM);
    }
    
    return cfString(str);
}

void* checkedDlopen(const char* path, int mode)
{
    void* handle = dlopen(path, mode);

    if(handle == NULL)
    {
        Syslog::critical("SecTranslocate: failed to load library %s: %s", path, dlerror());
        UnixError::throwMe();
    }

    return handle;
}

void* checkedDlsym(void* handle, const char* symbol)
{
    void* result = dlsym(handle, symbol);

    if(result == NULL)
    {
        Syslog::critical("SecTranslocate: failed to load symbol %s: %s", symbol, dlerror());
        UnixError::throwMe();
    }
    return result;
}
    
/* Calculate the app translocation directory for the user inside the user's temp directory */
string translocationDirForUser()
{
    char userTempPath[MAXPATHLEN];
    
    if(confstr(_CS_DARWIN_USER_TEMP_DIR, userTempPath, sizeof(userTempPath)) == 0)
    {
        Syslog::error("SecTranslocate: Failed to get temp dir for user %d (error:%d)",
                      getuid(),
                      errno);
        UnixError::throwMe();
    }
    
    // confstr returns a path with a symlink, we want the resolved path */
    return getRealPath(userTempPath)+APP_TRANSLOCATION_DIR;
}

/* Get a file descriptor for the provided path. if the last component of the provided path doesn't
 exist, create it and then re-attempt to get the file descriptor.
 */
int getFDForDirectory(const string &directoryPath, bool *owned)
{
    FileDesc fd(directoryPath, O_RDONLY, FileDesc::modeMissingOk);
    if(!fd)
    {
        UnixError::check(mkdir(directoryPath.c_str(),0755));
        fd.open(directoryPath);
        /* owned means that the library created the directory rather than it being pre-existent.
         We just made a directory that didn't exist before, so set owned to true. */
        if(owned)
        {
            *owned = true;
        }
    }
    else if (owned)
    {
        *owned = false;
    }
    
    return fd;
}
}
}