Пример #1
0
def add_to_keychain_list(keychain_path):
    '''Ensure the keychain is in the search path. Returns True if we
    added the keychain to the list.'''

    # we use *foo to expand a list of keychain paths
    # pylint: disable=W0142

    added_keychain = False
    output = security('list-keychains', '-d', 'user')
    # Split the output and strip it of whitespace and leading/trailing
    # quotes, the result are absolute paths to keychains
    # Preserve the order in case we need to append to them
    search_keychains = [x.strip().strip('"')
                        for x in output.split('\n') if x.strip()]
    if not keychain_path in search_keychains:
        # Keychain is not in the search paths
        munkicommon.display_debug2('Adding client keychain to search path...')
        search_keychains.append(keychain_path)
        try:
            output = security(
                'list-keychains', '-d', 'user', '-s', *search_keychains)
            if output:
                munkicommon.display_debug2(output)
            added_keychain = True
        except SecurityError, err:
            munkicommon.display_error(
                'Could not add keychain %s to keychain list: %s',
                keychain_path, err)
            added_keychain = False
Пример #2
0
def read_signed_profile(profile_path):
    '''Attempts to read a (presumably) signed profile.'''

    # filed for future reference:
    # openssl smime -inform DER -verify -in Signed.mobileconfig
    #                           -noverify -out Unsigned.mobileconfig
    # will strip the signing from a signed profile
    # this might be a better approach
    # from: http://apple.stackexchange.com/questions/105981/
    #       how-do-i-view-or-verify-signed-mobileconfig-files-using-terminal

    # but... we're going to use an Apple-provided tool instead.

    cmd = ['/usr/bin/security', 'cms', '-D', '-i', profile_path]
    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    if proc.returncode:
        # security cms -D couldn't decode the file
        munkicommon.display_error(
            'Error reading profile %s: %s' % (profile_path, stderr))
        return {}
    try:
        return FoundationPlist.readPlistFromString(stdout)
    except FoundationPlist.NSPropertyListSerializationException, err:
        # not a valid plist
        munkicommon.display_error(
            'Error reading profile %s: %s' % (profile_path, err))
        return {}
Пример #3
0
def read_signed_profile(profile_path):
    '''Attempts to read a (presumably) signed profile.'''

    # filed for future reference:
    # openssl smime -inform DER -verify -in Signed.mobileconfig
    #                           -noverify -out Unsigned.mobileconfig
    # will strip the signing from a signed profile
    # this might be a better approach
    # from: https://apple.stackexchange.com/questions/105981/
    #       how-do-i-view-or-verify-signed-mobileconfig-files-using-terminal

    # but... we're going to use an Apple-provided tool instead.

    cmd = ['/usr/bin/security', 'cms', '-D', '-i', profile_path]
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    stdout, stderr = proc.communicate()
    if proc.returncode:
        # security cms -D couldn't decode the file
        munkicommon.display_error('Error reading profile %s: %s' %
                                  (profile_path, stderr))
        return {}
    try:
        return FoundationPlist.readPlistFromString(stdout)
    except FoundationPlist.NSPropertyListSerializationException, err:
        # not a valid plist
        munkicommon.display_error('Error reading profile %s: %s' %
                                  (profile_path, err))
        return {}
Пример #4
0
def cacheAppleSUScatalog():
    '''Caches a local copy of the current Apple SUS catalog.'''
    osvers = int(os.uname()[2].split('.')[0])
    munkisuscatalog = munkicommon.pref('SoftwareUpdateServerURL')
    prefs_catalogURL = getSoftwareUpdatePref('CatalogURL')
    if munkisuscatalog:
        # defined in Munki's prefs? use that
        catalogURL = munkisuscatalog
    elif prefs_catalogURL:
        # defined via MCX or
        # in /Library/Preferences/com.apple.SoftwareUpdate.plist
        catalogURL = prefs_catalogURL
    elif osvers == 9:
        # default catalog for Leopard
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-leopard.merged-1.sucatalog'
    elif osvers == 10:
        # default catalog for Snow Leopard
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-leopard-snowleopard.merged-1.sucatalog'
    elif osvers == 11:
        # default catalog for Lion
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog.gz'
    else:
        munkicommon.display_error(
            'Can\'t determine Software Update CatalogURL for Darwin '
            'version %s', osvers)
        return -1
    if not os.path.exists(swupdCacheDir(temp=False)):
        try:
            os.makedirs(swupdCacheDir(temp=False))
        except OSError, oserr:
            raise ReplicationError(oserr)
Пример #5
0
def remove_from_keychain_list(keychain_path):
    '''Remove keychain from the list of keychains'''

    # we use *foo to expand a list of keychain paths
    # pylint: disable=W0142

    output = security('list-keychains', '-d', 'user')
    # Split the output and strip it of whitespace and leading/trailing
    # quotes, the result are absolute paths to keychains
    # Preserve the order in case we need to append to them
    search_keychains = [
        x.strip().strip('"') for x in output.split('\n') if x.strip()
    ]
    if keychain_path in search_keychains:
        # Keychain is in the search path
        munkicommon.display_debug1('Removing %s from search path...',
                                   keychain_path)
        filtered_keychains = [
            keychain for keychain in search_keychains
            if keychain != keychain_path
        ]
        try:
            output = security('list-keychains', '-d', 'user', '-s',
                              *filtered_keychains)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
            munkicommon.display_error('Could not set new keychain list: %s',
                                      err)
Пример #6
0
def doAdobeRemoval(item):
    '''Wrapper for all the Adobe removal methods'''
    uninstallmethod = item['uninstall_method']
    itempath = ""
    if "uninstaller_item" in item:
        managedinstallbase = munkicommon.pref('ManagedInstallDir')
        itempath = os.path.join(managedinstallbase, 'Cache',
                                item["uninstaller_item"])
        if not os.path.exists(itempath):
            munkicommon.display_error("%s package for %s was "
                                      "missing from the cache."
                                      % (uninstallmethod, item['name']))
            return -1

    if uninstallmethod == "AdobeSetup":
        # CS3 uninstall
        retcode = runAdobeSetup(itempath, uninstalling=True)

    elif uninstallmethod == "AdobeUberUninstaller":
        # CS4 uninstall
        pkgname = item.get("adobe_package_name") or \
                  item.get("package_path","")
        retcode = runAdobeUberTool(itempath, pkgname, uninstalling=True)

    elif uninstallmethod == "AdobeCS5AAMEEPackage":
        # CS5 uninstall. Sheesh. Three releases, three methods.
        adobeInstallInfo = item.get('adobe_install_info')
        retcode = doAdobeCS5Uninstall(adobeInstallInfo)

    if retcode:
        munkicommon.display_error("Uninstall of %s failed." % item['name'])
    return retcode
Пример #7
0
def add_to_keychain_list(keychain_path):
    '''Ensure the keychain is in the search path. Returns True if we
    added the keychain to the list.'''

    # we use *foo to expand a list of keychain paths
    # pylint: disable=W0142

    added_keychain = False
    output = security('list-keychains', '-d', 'user')
    # Split the output and strip it of whitespace and leading/trailing
    # quotes, the result are absolute paths to keychains
    # Preserve the order in case we need to append to them
    search_keychains = [
        x.strip().strip('"') for x in output.split('\n') if x.strip()
    ]
    if not keychain_path in search_keychains:
        # Keychain is not in the search paths
        munkicommon.display_debug2('Adding client keychain to search path...')
        search_keychains.append(keychain_path)
        try:
            output = security('list-keychains', '-d', 'user', '-s',
                              *search_keychains)
            if output:
                munkicommon.display_debug2(output)
            added_keychain = True
        except SecurityError, err:
            munkicommon.display_error(
                'Could not add keychain %s to keychain list: %s',
                keychain_path, err)
            added_keychain = False
Пример #8
0
def remove_from_keychain_list(keychain_path):
    '''Remove keychain from the list of keychains'''

    # we use *foo to expand a list of keychain paths
    # pylint: disable=W0142

    output = security('list-keychains', '-d', 'user')
    # Split the output and strip it of whitespace and leading/trailing
    # quotes, the result are absolute paths to keychains
    # Preserve the order in case we need to append to them
    search_keychains = [x.strip().strip('"')
                        for x in output.split('\n') if x.strip()]
    if keychain_path in search_keychains:
        # Keychain is in the search path
        munkicommon.display_debug1(
            'Removing %s from search path...', keychain_path)
        filtered_keychains = [keychain for keychain in search_keychains
                              if keychain != keychain_path]
        try:
            output = security(
                'list-keychains', '-d', 'user', '-s', *filtered_keychains)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
            munkicommon.display_error(
                'Could not set new keychain list: %s', err)
Пример #9
0
def cacheAppleSUScatalog():
    '''Caches a local copy of the current Apple SUS catalog.'''
    osvers = int(os.uname()[2].split('.')[0])
    munkisuscatalog = munkicommon.pref('SoftwareUpdateServerURL')
    prefs_catalogURL = getSoftwareUpdatePref('CatalogURL')
    if munkisuscatalog:
        # defined in Munki's prefs? use that
        catalogURL = munkisuscatalog
    elif prefs_catalogURL:
        # defined via MCX or
        # in /Library/Preferences/com.apple.SoftwareUpdate.plist
        catalogURL = prefs_catalogURL
    elif osvers == 9:
        # default catalog for Leopard
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-leopard.merged-1.sucatalog'
    elif osvers == 10:
        # default catalog for Snow Leopard
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-leopard-snowleopard.merged-1.sucatalog'
    elif osvers == 11:
        # default catalog for Lion
        catalogURL = 'http://swscan.apple.com/content/catalogs/others/index-lion-snowleopard-leopard.merged-1.sucatalog.gz'
    else:
        munkicommon.display_error(
            'Can\'t determine Software Update CatalogURL for Darwin '
            'version %s', osvers)
        return -1
    if not os.path.exists(swupdCacheDir(temp=False)):
        try:
            os.makedirs(swupdCacheDir(temp=False))
        except OSError, oserr:
            raise ReplicationError(oserr)
Пример #10
0
def downloadAvailableUpdates():
    '''Downloads the available Apple updates using our local
    filtered sucatalog. Returns True if successful, False otherwise.'''
    msg = "Downloading available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
        'content/catalogs/local_download.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        return False

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    # get the OS version
    osvers = int(os.uname()[2].split('.')[0])
    if osvers == 9:
        retcode = leopardDownloadAvailableUpdates(catalogURL)
    else:
        retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-d', '-a'])

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
        return False
    return True
Пример #11
0
def config_profile_info(ignore_cache=False):
    '''Returns a dictionary representing the output of `profiles -C -o`'''
    global CONFIG_PROFILE_INFO
    if not profiles_supported():
        CONFIG_PROFILE_INFO = {}
        return CONFIG_PROFILE_INFO
    if not ignore_cache and CONFIG_PROFILE_INFO is not None:
        return CONFIG_PROFILE_INFO
    output_plist = os.path.join(
        tempfile.mkdtemp(dir=munkicommon.tmpdir()), 'profiles')
    cmd = ['/usr/bin/profiles', '-C', '-o', output_plist]
    proc = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Could not obtain configuration profile info: %s' % proc.stderr)
        CONFIG_PROFILE_INFO = {}
    else:
        try:
            CONFIG_PROFILE_INFO = FoundationPlist.readPlist(
                output_plist + '.plist')
        except BaseException, err:
            munkicommon.display_error(
                'Could not read configuration profile info: %s' % err)
            CONFIG_PROFILE_INFO = {}
        finally:
Пример #12
0
def getpkgkeys(pkgnames):
    """
    Given a list of receipt names, bom file names, or package ids,
    gets a list of pkg_keys from the pkgs table in our database.
    """
    # open connection and cursor to our database
    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()

    # check package names to make sure they're all in the database,
    # build our list of pkg_keys
    pkgerror = False
    pkgkeyslist = []
    for pkg in pkgnames:
        values_t = (pkg,)
        munkicommon.display_debug1("select pkg_key from pkgs where pkgid = %s", pkg)
        pkg_keys = curs.execute("select pkg_key from pkgs where pkgid = ?", values_t).fetchall()
        if not pkg_keys:
            # try pkgname
            munkicommon.display_debug1("select pkg_key from pkgs where pkgname = %s", pkg)
            pkg_keys = curs.execute("select pkg_key from pkgs where pkgname = ?", values_t).fetchall()
        if not pkg_keys:
            munkicommon.display_error("%s not found in database.", pkg)
            pkgerror = True
        else:
            for row in pkg_keys:
                # only want first column
                pkgkeyslist.append(row[0])
    if pkgerror:
        pkgkeyslist = []

    curs.close()
    conn.close()
    munkicommon.display_debug1("pkgkeys: %s", pkgkeyslist)
    return pkgkeyslist
Пример #13
0
def config_profile_info(ignore_cache=False):
    '''Returns a dictionary representing the output of `profiles -C -o`'''
    global CONFIG_PROFILE_INFO
    if not profiles_supported():
        CONFIG_PROFILE_INFO = {}
        return CONFIG_PROFILE_INFO
    if not ignore_cache and CONFIG_PROFILE_INFO is not None:
        return CONFIG_PROFILE_INFO
    output_plist = os.path.join(tempfile.mkdtemp(dir=munkicommon.tmpdir()),
                                'profiles')
    cmd = ['/usr/bin/profiles', '-C', '-o', output_plist]
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Could not obtain configuration profile info: %s' % proc.stderr)
        CONFIG_PROFILE_INFO = {}
    else:
        try:
            CONFIG_PROFILE_INFO = FoundationPlist.readPlist(output_plist +
                                                            '.plist')
        except BaseException, err:
            munkicommon.display_error(
                'Could not read configuration profile info: %s' % err)
            CONFIG_PROFILE_INFO = {}
        finally:
Пример #14
0
def downloadAvailableUpdates():
    '''Downloads the available Apple updates using our local
    filtered sucatalog. Returns True if successful, False otherwise.'''
    msg = "Downloading available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    # use our filtered local catalog
    catalogpath = os.path.join(swupdCacheDir(),
                               'content/catalogs/local_download.sucatalog')
    if not os.path.exists(catalogpath):
        munkicommon.display_error(
            'Missing local Software Update catalog at %s', catalogpath)
        return False

    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    # get the OS version
    osvers = int(os.uname()[2].split('.')[0])
    if osvers == 9:
        retcode = leopardDownloadAvailableUpdates(catalogURL)
    else:
        retcode = run_softwareupdate(['--CatalogURL', catalogURL, '-d', '-a'])

    if retcode:
        # there was an error
        munkicommon.display_error("softwareupdate error: %s" % retcode)
        return False
    return True
Пример #15
0
def pem_cert_sha1_digest(cert_path):
    '''Return SHA1 digest for pem certificate at path'''
    try:
        raw_bytes = pem_cert_bytes(cert_path)
        return hashlib.sha1(raw_bytes).hexdigest().upper()
    except BaseException, err:
        munkicommon.display_error('Error reading %s: %s' % (cert_path, err))
        return None
Пример #16
0
def pem_cert_sha1_digest(cert_path):
    '''Return SHA1 digest for pem certificate at path'''
    try:
        raw_bytes = pem_cert_bytes(cert_path)
        return hashlib.sha1(raw_bytes).hexdigest().upper()
    except BaseException, err:
        munkicommon.display_error('Error reading %s: %s' % (cert_path, err))
        return None
Пример #17
0
def read_file(pathname):
    '''Return the contents of pathname as a string'''
    try:
        fileobj = open(pathname, mode='r')
        data = fileobj.read()
        fileobj.close()
        return data
    except (OSError, IOError), err:
        munkicommon.display_error('Could not read %s: %s', pathname, err)
        return ''
Пример #18
0
def read_file(pathname):
    '''Return the contents of pathname as a string'''
    try:
        fileobj = open(pathname, mode='r')
        data = fileobj.read()
        fileobj.close()
        return data
    except (OSError, IOError), err:
        munkicommon.display_error(
            'Could not read %s: %s', pathname, err)
        return ''
Пример #19
0
def read_profile(profile_path):
    '''Reads a profile.'''
    try:
        return FoundationPlist.readPlist(profile_path)
    except FoundationPlist.NSPropertyListSerializationException:
        # possibly a signed profile
        return read_signed_profile(profile_path)
    except BaseException, err:
        munkicommon.display_error(
            'Error reading profile %s: %s' % (profile_path, err))
        return {}
Пример #20
0
def read_profile(profile_path):
    '''Reads a profile.'''
    try:
        return FoundationPlist.readPlist(profile_path)
    except FoundationPlist.NSPropertyListSerializationException:
        # possibly a signed profile
        return read_signed_profile(profile_path)
    except BaseException, err:
        munkicommon.display_error('Error reading profile %s: %s' %
                                  (profile_path, err))
        return {}
Пример #21
0
def writefile(stringdata, path):
    '''Writes string data to path.
    Returns the path on success, empty string on failure.'''
    try:
        fileobject = open(path, mode='w', buffering=1)
        print >> fileobject, stringdata.encode('UTF-8')
        fileobject.close()
        return path
    except (OSError, IOError):
        munkicommon.display_error("Couldn't write %s" % stringdata)
        return ""
Пример #22
0
def write_file(stringdata, pathname):
    '''Writes stringdata to pathname.
    Returns the pathname on success, empty string on failure.'''
    try:
        fileobject = open(pathname, mode='w')
        fileobject.write(stringdata)
        fileobject.close()
        return pathname
    except (OSError, IOError), err:
        munkicommon.display_error('Couldn\'t write %s to %s: %s', stringdata,
                                  pathname, err)
        return ''
Пример #23
0
def removepackages(
    pkgnames,
    forcedeletebundles=False,
    listfiles=False,
    rebuildpkgdb=False,
    noremovereceipts=False,
    noupdateapplepkgdb=False,
):
    """
    Our main function, called by installer.py to remove items based on
    receipt info.
    """
    if pkgnames == []:
        munkicommon.display_error("You must specify at least one package to remove!")
        return -2

    if not initDatabase(forcerebuild=rebuildpkgdb):
        munkicommon.display_error("Could not initialize receipt database.")
        return -3

    pkgkeyslist = getpkgkeys(pkgnames)
    if len(pkgkeyslist) == 0:
        return -4

    if munkicommon.stopRequested():
        return -128
    removalpaths = getpathstoremove(pkgkeyslist)
    if munkicommon.stopRequested():
        return -128

    if removalpaths:
        if listfiles:
            removalpaths.sort()
            for item in removalpaths:
                print "/" + item.encode("UTF-8")
        else:
            if munkicommon.munkistatusoutput:
                munkistatus.disableStopButton()
            removeFilesystemItems(removalpaths, forcedeletebundles)
    else:
        munkicommon.display_status_minor("Nothing to remove.")
        if munkicommon.munkistatusoutput:
            time.sleep(2)

    if not listfiles:
        if not noremovereceipts:
            removeReceipts(pkgkeyslist, noupdateapplepkgdb)
        if munkicommon.munkistatusoutput:
            munkistatus.enableStopButton()
            munkicommon.display_status_minor("Package removal complete.")
            time.sleep(2)

    return 0
Пример #24
0
def make_client_keychain(cert_info=None):
    '''Builds a client cert keychain from existing client certs'''

    if not cert_info:
        # just grab data from Munki's preferences/defaults
        cert_info = get_munki_client_cert_info()

    client_cert_path = cert_info['client_cert_path']
    client_key_path = cert_info['client_key_path']
    site_urls = cert_info['site_urls']
    if not client_cert_path:
        # no client, so nothing to do
        munkicommon.display_debug1(
            'No client cert info provided, '
            'so no client keychain will be created.')
        return
    else:
        munkicommon.display_debug1('Client cert path: %s', client_cert_path)
        munkicommon.display_debug1('Client key path:  %s', client_key_path)

    # to do some of the following options correctly, we need to be root
    # and have root's home.
    # check to see if we're root
    if os.geteuid() != 0:
        munkicommon.display_error(
            'Can\'t make our client keychain unless we are root!')
        return
    # switch HOME if needed to root's home
    original_home = os.environ.get('HOME')
    if original_home:
        os.environ['HOME'] = os.path.expanduser('~root')

    keychain_pass = (
        munkicommon.pref('KeychainPassword') or DEFAULT_KEYCHAIN_PASSWORD)
    abs_keychain_path = get_keychain_path()
    if os.path.exists(abs_keychain_path):
        os.unlink(abs_keychain_path)
    if not os.path.exists(os.path.dirname(abs_keychain_path)):
        os.makedirs(os.path.dirname(abs_keychain_path))
    # create a new keychain
    munkicommon.display_debug1('Creating client keychain...')
    try:
        output = security('create-keychain',
                          '-p', keychain_pass, abs_keychain_path)
        if output:
            munkicommon.display_debug2(output)
    except SecurityError, err:
        munkicommon.display_error(
            'Could not create keychain %s: %s', abs_keychain_path, err)
        if original_home:
            # switch it back
            os.environ['HOME'] = original_home
        return
Пример #25
0
def make_client_keychain(cert_info=None):
    '''Builds a client cert keychain from existing client certs'''

    if not cert_info:
        # just grab data from Munki's preferences/defaults
        cert_info = get_munki_client_cert_info()

    client_cert_path = cert_info['client_cert_path']
    client_key_path = cert_info['client_key_path']
    site_urls = cert_info['site_urls']
    if not client_cert_path:
        # no client, so nothing to do
        munkicommon.display_debug1(
            'No client cert info provided, '
            'so no client keychain will be created.')
        return
    else:
        munkicommon.display_debug1('Client cert path: %s', client_cert_path)
        munkicommon.display_debug1('Client key path:  %s', client_key_path)

    # to do some of the following options correctly, we need to be root
    # and have root's home.
    # check to see if we're root
    if os.geteuid() != 0:
        munkicommon.display_error(
            'Can\'t make our client keychain unless we are root!')
        return
    # switch HOME if needed to root's home
    original_home = os.environ.get('HOME')
    if original_home:
        os.environ['HOME'] = os.path.expanduser('~root')

    keychain_pass = (
        munkicommon.pref('KeychainPassword') or DEFAULT_KEYCHAIN_PASSWORD)
    abs_keychain_path = get_keychain_path()
    if os.path.exists(abs_keychain_path):
        os.unlink(abs_keychain_path)
    if not os.path.exists(os.path.dirname(abs_keychain_path)):
        os.makedirs(os.path.dirname(abs_keychain_path))
    # create a new keychain
    munkicommon.display_debug1('Creating client keychain...')
    try:
        output = security('create-keychain',
                          '-p', keychain_pass, abs_keychain_path)
        if output:
            munkicommon.display_debug2(output)
    except SecurityError, err:
        munkicommon.display_error(
            'Could not create keychain %s: %s', abs_keychain_path, err)
        if original_home:
            # switch it back
            os.environ['HOME'] = original_home
        return
Пример #26
0
def write_file(stringdata, pathname):
    '''Writes stringdata to pathname.
    Returns the pathname on success, empty string on failure.'''
    try:
        fileobject = open(pathname, mode='w')
        fileobject.write(stringdata)
        fileobject.close()
        return pathname
    except (OSError, IOError), err:
        munkicommon.display_error('Couldn\'t write %s to %s: %s',
                                  stringdata, pathname, err)
        return ''
Пример #27
0
def removepackages(pkgnames,
                   forcedeletebundles=False,
                   listfiles=False,
                   rebuildpkgdb=False,
                   noremovereceipts=False,
                   noupdateapplepkgdb=False):
    """
    Our main function, called by installer.py to remove items based on
    receipt info.
    """
    if pkgnames == []:
        munkicommon.display_error(
            "You must specify at least one package to remove!")
        return -2

    if not initDatabase(forcerebuild=rebuildpkgdb):
        munkicommon.display_error("Could not initialize receipt database.")
        return -3

    pkgkeyslist = getpkgkeys(pkgnames)
    if len(pkgkeyslist) == 0:
        return -4

    if munkicommon.stopRequested():
        return -128
    removalpaths = getpathstoremove(pkgkeyslist)
    if munkicommon.stopRequested():
        return -128

    if removalpaths:
        if listfiles:
            removalpaths.sort()
            for item in removalpaths:
                print "/" + item.encode('UTF-8')
        else:
            if munkicommon.munkistatusoutput:
                munkistatus.disableStopButton()
            removeFilesystemItems(removalpaths, forcedeletebundles)
    else:
        munkicommon.display_status_minor('Nothing to remove.')
        if munkicommon.munkistatusoutput:
            time.sleep(2)

    if not listfiles:
        if not noremovereceipts:
            removeReceipts(pkgkeyslist, noupdateapplepkgdb)
        if munkicommon.munkistatusoutput:
            munkistatus.enableStopButton()
            munkicommon.display_status_minor('Package removal complete.')
            time.sleep(2)

    return 0
Пример #28
0
def install_profile(profile_path):
    '''Installs a profile. Returns True on success, False otherwise'''
    cmd = ['/usr/bin/profiles', '-IF', profile_path]
    proc = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Profile %s installation failed: %s'
            % (os.path.basename(profile_path), proc.stderr))
        return False
    record_profile_receipt(profile_path)
    return True
Пример #29
0
def remove_profile(identifier):
    '''Removes a profile with the given identifier. Returns True on success,
    False otherwise'''
    cmd = ['/usr/bin/profiles', '-Rp', identifier]
    proc = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Profile %s removal failed: %s' % (identifier, proc.stderr))
        return False
    remove_profile_receipt(identifier)
    return True
Пример #30
0
def main():
    '''Used when calling removepackages.py directly from the command line.'''
    # command-line options
    p = optparse.OptionParser()
    p.set_usage('''Usage: %prog [options] package_id ...''')
    p.add_option('--forcedeletebundles', '-f', action='store_true',
                    help='Delete bundles even if they aren\'t empty.')
    p.add_option('--listfiles', '-l', action='store_true',
                    help='''List the filesystem objects to be removed,
                    but do not actually remove them.''')
    p.add_option('--rebuildpkgdb', action='store_true',
                    help='Force a rebuild of the internal package database.')
    p.add_option('--noremovereceipts', action='store_true',
                    help='''Do not remove receipts and boms from
                    /Library/Receipts and update internal package
                    database.''')
    p.add_option('--noupdateapplepkgdb', action='store_true',
                    help='''Do not update Apple\'s package database.
                    If --noremovereceipts is also given, this is implied''')
    p.add_option('--munkistatusoutput', '-m', action='store_true',
                    help='Output is formatted for use with MunkiStatus.')
    p.add_option('--verbose', '-v', action='count', default=1,
                    help='''More verbose output. May be specified multiple
                    times.''')

    # Get our options and our package names
    options, pkgnames = p.parse_args()

    # check to see if we're root
    if os.geteuid() != 0:
        munkicommon.display_error("You must run this as root!")
        exit(-1)

    # set the munkicommon globals
    munkicommon.munkistatusoutput = options.munkistatusoutput
    munkicommon.verbose = options.verbose

    if options.munkistatusoutput:
        pkgcount = len(pkgnames)
        munkistatus.message("Removing %s packages..." % pkgcount)
        munkistatus.detail("")

    retcode = removepackages(pkgnames,
                             forcedeletebundles=options.forcedeletebundles,
                             listfiles=options.listfiles,
                             rebuildpkgdb=options.rebuildpkgdb,
                             noremovereceipts=options.noremovereceipts,
                             noupdateapplepkgdb=options.noupdateapplepkgdb)
    if options.munkistatusoutput:
        munkistatus.quit()
    exit(retcode)
Пример #31
0
def main():
    '''Used when calling removepackages.py directly from the command line.'''
    # command-line options
    parser = optparse.OptionParser()
    parser.set_usage('''Usage: %prog [options] package_id ...''')
    parser.add_option('--forcedeletebundles', '-f', action='store_true',
                      help='Delete bundles even if they aren\'t empty.')
    parser.add_option('--listfiles', '-l', action='store_true',
                      help='List the filesystem objects to be removed, '
                      'but do not actually remove them.')
    parser.add_option('--rebuildpkgdb', action='store_true',
                      help='Force a rebuild of the internal package database.')
    parser.add_option('--noremovereceipts', action='store_true',
                      help='''Do not remove receipts and boms from
                      /Library/Receipts and update internal package
                      database.''')
    parser.add_option('--noupdateapplepkgdb', action='store_true',
                      help='Do not update Apple\'s package database. '
                      'If --noremovereceipts is also given, this is implied')
    parser.add_option('--munkistatusoutput', '-m', action='store_true',
                      help='Output is formatted for use with MunkiStatus.')
    parser.add_option('--verbose', '-v', action='count', default=1,
                      help='More verbose output. May be specified multiple '
                      'times.')

    # Get our options and our package names
    options, pkgnames = parser.parse_args()

    # check to see if we're root
    if os.geteuid() != 0:
        munkicommon.display_error("You must run this as root!")
        exit(-1)

    # set the munkicommon globals
    munkicommon.munkistatusoutput = options.munkistatusoutput
    munkicommon.verbose = options.verbose

    if options.munkistatusoutput:
        pkgcount = len(pkgnames)
        munkistatus.message("Removing %s packages..." % pkgcount)
        munkistatus.detail("")

    retcode = removepackages(pkgnames,
                             forcedeletebundles=options.forcedeletebundles,
                             listfiles=options.listfiles,
                             rebuildpkgdb=options.rebuildpkgdb,
                             noremovereceipts=options.noremovereceipts,
                             noupdateapplepkgdb=options.noupdateapplepkgdb)
    if options.munkistatusoutput:
        munkistatus.quit()
    exit(retcode)
Пример #32
0
def debug_output():
    '''Debugging output for keychain'''
    try:
        munkicommon.display_debug1('***Keychain list***')
        munkicommon.display_debug1(security('list-keychains', '-d', 'user'))
        munkicommon.display_debug1('***Default keychain info***')
        munkicommon.display_debug1(security('default-keychain', '-d', 'user'))
        keychainfile = get_keychain_path()
        if os.path.exists(keychainfile):
            munkicommon.display_debug1('***Info for %s***' % keychainfile)
            munkicommon.display_debug1(
                security('show-keychain-info', keychainfile))
    except SecurityError, err:
        munkicommon.display_error(err)
Пример #33
0
def remove_profile(identifier):
    '''Removes a profile with the given identifier. Returns True on success,
    False otherwise'''
    cmd = ['/usr/bin/profiles', '-Rp', identifier]
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error('Profile %s removal failed: %s' %
                                  (identifier, proc.stderr))
        return False
    remove_profile_receipt(identifier)
    return True
Пример #34
0
def debug_output():
    '''Debugging output for keychain'''
    try:
        munkicommon.display_debug1('***Keychain list***')
        munkicommon.display_debug1(security('list-keychains', '-d', 'user'))
        munkicommon.display_debug1('***Default keychain info***')
        munkicommon.display_debug1(security('default-keychain', '-d', 'user'))
        keychainfile = get_keychain_path()
        if os.path.exists(keychainfile):
            munkicommon.display_debug1('***Info for %s***' % keychainfile)
            munkicommon.display_debug1(
                security('show-keychain-info', keychainfile))
    except SecurityError, err:
        munkicommon.display_error(str(err))
Пример #35
0
def getAvailableUpdates():
    '''Returns a list of product IDs of available Apple updates'''
    msg = "Checking for available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    applicable_updates = os.path.join(swupdCacheDir(),
                                      'ApplicableUpdates.plist')
    if os.path.exists(applicable_updates):
        # remove any old item
        try:
            os.unlink(applicable_updates)
        except (OSError, IOError):
            pass

    # use our locally-cached Apple catalog
    catalogpath = os.path.join(swupdCacheDir(),
                               'content/catalogs/apple_index.sucatalog')
    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    su_options = ['--CatalogURL', catalogURL, '-l', '-f', applicable_updates]

    retcode = run_softwareupdate(su_options)
    if retcode:
        # there was an error
        osvers = int(os.uname()[2].split('.')[0])
        if osvers == 9:
            # always a non-zero retcode on Leopard
            pass
        else:
            munkicommon.display_error("softwareupdate error: %s" % retcode)
            return []

    if os.path.exists(applicable_updates):
        try:
            updatelist = FoundationPlist.readPlist(applicable_updates)
            if updatelist:
                results_array = updatelist.get('phaseResultsArray', [])
                return [
                    item['productKey'] for item in results_array
                    if 'productKey' in item
                ]
        except FoundationPlist.NSPropertyListSerializationException:
            return []
    return []
Пример #36
0
 def __init__(self):
     '''Adds CA certs as trusted to System keychain.
     Unlocks the munki.keychain if it exists.
     Makes sure the munki.keychain is in the search list.
     Creates a new client keychain if needed.'''
     add_ca_certs_to_system_keychain()
     self.keychain_path = get_keychain_path()
     if client_certs_exist() and os.path.exists(self.keychain_path):
         # we have client certs; we should build a keychain using them
         try:
             os.unlink(self.keychain_path)
         except (OSError, IOError), err:
             munkicommon.display_error(
                 'Could not remove pre-existing %s: %s'
                 % (self.keychain_path, err))
Пример #37
0
 def __init__(self):
     '''Adds CA certs as trusted to System keychain.
     Unlocks the munki.keychain if it exists.
     Makes sure the munki.keychain is in the search list.
     Creates a new client keychain if needed.'''
     add_ca_certs_to_system_keychain()
     self.keychain_path = get_keychain_path()
     if client_certs_exist() and os.path.exists(self.keychain_path):
         # we have client certs; we should build a keychain using them
         try:
             os.unlink(self.keychain_path)
         except (OSError, IOError), err:
             munkicommon.display_error(
                 'Could not remove pre-existing %s: %s'
                 % (self.keychain_path, err))
Пример #38
0
def getAvailableUpdates():
    '''Returns a list of product IDs of available Apple updates'''
    msg = "Checking for available Apple Software Updates..."
    if munkicommon.munkistatusoutput:
        munkistatus.message(msg)
        munkistatus.detail("")
        munkistatus.percent(-1)
        munkicommon.log(msg)
    else:
        munkicommon.display_status(msg)

    applicable_updates = os.path.join(swupdCacheDir(),
                                      'ApplicableUpdates.plist')
    if os.path.exists(applicable_updates):
        # remove any old item
        try:
            os.unlink(applicable_updates)
        except (OSError, IOError):
            pass

    # use our locally-cached Apple catalog
    catalogpath = os.path.join(swupdCacheDir(),
        'content/catalogs/apple_index.sucatalog')
    catalogURL = 'file://localhost' + urllib2.quote(catalogpath)
    su_options = ['--CatalogURL', catalogURL, '-l', '-f', applicable_updates]

    retcode = run_softwareupdate(su_options)
    if retcode:
        # there was an error
        osvers = int(os.uname()[2].split('.')[0])
        if osvers == 9:
            # always a non-zero retcode on Leopard
            pass
        else:
            munkicommon.display_error("softwareupdate error: %s" % retcode)
            return []

    if os.path.exists(applicable_updates):
        try:
            updatelist = FoundationPlist.readPlist(applicable_updates)
            if updatelist:
                results_array = updatelist.get('phaseResultsArray', [])
                return [item['productKey'] for item in results_array
                        if 'productKey' in item]
        except FoundationPlist.NSPropertyListSerializationException:
            return []
    return []
Пример #39
0
def install_profile(profile_path, profile_identifier):
    '''Installs a profile. Returns True on success, False otherwise'''
    cmd = ['/usr/bin/profiles', '-IF', profile_path]
    proc = subprocess.Popen(
        cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Profile %s installation failed: %s'
            % (os.path.basename(profile_path), proc.stderr))
        return False
    if profile_identifier:
        record_profile_receipt(profile_path, profile_identifier)
    else:
        munkicommon.display_warning(
            'No identifier for profile %s; cannot record an installation '
            'receipt.' % os.path.basename(profile_path))
    return True
Пример #40
0
def runAdobeUberTool(dmgpath, pkgname='', uninstalling=False):
    '''Runs either AdobeUberInstaller or AdobeUberUninstaller
    from a disk image and provides progress feedback.
    pkgname is the name of a directory at the top level of the dmg
    containing the AdobeUber tools and their XML files.'''

    munkicommon.display_status_minor(
        'Mounting disk image %s' % os.path.basename(dmgpath))
    mountpoints = mountAdobeDmg(dmgpath)
    if mountpoints:
        installroot = mountpoints[0]
        if uninstalling:
            ubertool = os.path.join(installroot, pkgname,
                                    "AdobeUberUninstaller")
        else:
            ubertool = os.path.join(installroot, pkgname,
                                    "AdobeUberInstaller")

        if os.path.exists(ubertool):
            info = getAdobePackageInfo(installroot)
            packagename = info['display_name']
            action = "Installing"
            if uninstalling:
                action = "Uninstalling"
            munkicommon.display_status_major('%s %s' % (action, packagename))
            if munkicommon.munkistatusoutput:
                munkistatus.detail('Starting %s' % os.path.basename(ubertool))

            # try to find and count the number of payloads
            # so we can give a rough progress indicator
            number_of_payloads = countPayloads(installroot)

            retcode = runAdobeInstallTool(
                [ubertool], number_of_payloads, killAdobeAIR=True)

        else:
            munkicommon.display_error("No %s found" % ubertool)
            retcode = -1

        munkicommon.unmountdmg(installroot)
        return retcode
    else:
        munkicommon.display_error("No mountable filesystems on %s" % dmgpath)
        return -1
Пример #41
0
def unlock_and_set_nonlocking(keychain_path):
    '''Unlocks the keychain and sets it to non-locking'''
    keychain_pass = (munkicommon.pref('KeychainPassword')
                     or DEFAULT_KEYCHAIN_PASSWORD)
    try:
        output = security('unlock-keychain', '-p', keychain_pass,
                          keychain_path)
        if output:
            munkicommon.display_debug2(output)
    except SecurityError, err:
        # some problem unlocking the keychain.
        munkicommon.display_error('Could not unlock %s: %s.', keychain_path,
                                  err)
        # delete it
        try:
            os.unlink(keychain_path)
        except OSError, err:
            munkicommon.display_error('Could not remove %s: %s.',
                                      keychain_path, err)
Пример #42
0
def add_ca_certs_to_system_keychain(certdata=None):
    '''Adds any CA certs as trusted root certs to System.keychain'''

    if not certdata:
        certdata = get_munki_server_cert_data()

    ca_cert_path = certdata['ca_cert_path']
    ca_dir_path = certdata['ca_dir_path']
    SYSTEM_KEYCHAIN = "/Library/Keychains/System.keychain"
    if not os.path.exists(SYSTEM_KEYCHAIN):
        munkicommon.display_warning('%s not found.', SYSTEM_KEYCHAIN)
        return

    if not ca_cert_path and not ca_dir_path:
        # no CA certs, so nothing to do
        munkicommon.display_debug2(
            'No CA cert info provided, so nothing to add to System keychain.')
        return
    else:
        munkicommon.display_debug2('CA cert path:     %s', ca_cert_path)
        munkicommon.display_debug2('CA dir path:      %s', ca_dir_path)

    # Add CA certs. security add-trusted-cert does the right thing even if
    # the cert is already added, so we just do it; checking to see if the
    # cert is already added is hard.
    certs_to_add = []
    if ca_cert_path:
        certs_to_add.append(ca_cert_path)
    if ca_dir_path:
        # add any pem files in the ca_dir_path directory
        for item in os.listdir(ca_dir_path):
            if item.endswith('.pem'):
                certs_to_add.append(os.path.join(ca_dir_path, item))
    for cert in certs_to_add:
        munkicommon.display_debug1('Adding CA cert %s...', cert)
        try:
            output = security('add-trusted-cert', '-d', '-k', SYSTEM_KEYCHAIN,
                              cert)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
            munkicommon.display_error(
                'Could not add CA cert %s into System keychain: %s', cert, err)
Пример #43
0
def store_profile_receipt_data(identifier, hash_value):
    '''Stores info for profile identifier.
    If hash_value is None, item is removed from the datastore.'''
    profile_data = profile_receipt_data()
    if hash_value is not None:
        profile_dict = profile_info_for_installed_identifier(identifier,
                                                             ignore_cache=True)
        install_date = profile_dict.get('ProfileInstallDate', 'UNKNOWN')
        profile_data[identifier] = {
            'FileHash': hash_value,
            'ProfileInstallDate': install_date
        }
    elif identifier in profile_data.keys():
        del profile_data[identifier]
    try:
        FoundationPlist.writePlist(profile_data, profile_receipt_data_path())
    except BaseException, err:
        munkicommon.display_error('Cannot update hash for %s: %s' %
                                  (identifier, err))
Пример #44
0
def store_profile_receipt_data(identifier, hash_value):
    '''Stores info for profile identifier.
    If hash_value is None, item is removed from the datastore.'''
    profile_data = profile_receipt_data()
    if hash_value is not None:
        profile_dict = profile_info_for_installed_identifier(identifier,
                                                             ignore_cache=True)
        install_date = profile_dict.get('ProfileInstallDate', 'UNKNOWN')
        profile_data[identifier] = {
            'FileHash': hash_value,
            'ProfileInstallDate': install_date
        }
    elif identifier in profile_data.keys():
        del profile_data[identifier]
    try:
        FoundationPlist.writePlist(profile_data, profile_receipt_data_path())
    except BaseException, err:
        munkicommon.display_error(
            'Cannot update hash for %s: %s' % (identifier, err))
Пример #45
0
def install_profile(profile_path, profile_identifier):
    '''Installs a profile. Returns True on success, False otherwise'''
    cmd = ['/usr/bin/profiles', '-IF', profile_path]
    proc = subprocess.Popen(cmd,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        munkicommon.display_error(
            'Profile %s installation failed: %s' %
            (os.path.basename(profile_path), proc.stderr))
        return False
    if profile_identifier:
        record_profile_receipt(profile_path, profile_identifier)
    else:
        munkicommon.display_warning(
            'No identifier for profile %s; cannot record an installation '
            'receipt.' % os.path.basename(profile_path))
    return True
Пример #46
0
def unlock_and_set_nonlocking(keychain_path):
    '''Unlocks the keychain and sets it to non-locking'''
    keychain_pass = (
        munkicommon.pref('KeychainPassword') or DEFAULT_KEYCHAIN_PASSWORD)
    try:
        output = security(
            'unlock-keychain', '-p', keychain_pass, keychain_path)
        if output:
            munkicommon.display_debug2(output)
    except SecurityError, err:
        # some problem unlocking the keychain.
        munkicommon.display_error(
            'Could not unlock %s: %s.', keychain_path, err)
        # delete it
        try:
            os.unlink(keychain_path)
        except OSError, err:
            munkicommon.display_error(
                'Could not remove %s: %s.', keychain_path, err)
Пример #47
0
def add_ca_certs_to_system_keychain(cert_info=None):
    '''Adds any CA certs as trusted root certs to System.keychain'''

    if not cert_info:
        cert_info = get_munki_server_cert_info()

    ca_cert_path = cert_info['ca_cert_path']
    ca_dir_path = cert_info['ca_dir_path']
    if not ca_cert_path and not ca_dir_path:
        # no CA certs, so nothing to do
        munkicommon.display_debug2(
            'No CA cert info provided, so nothing to add to System keychain.')
        return
    else:
        munkicommon.display_debug2('CA cert path: %s', ca_cert_path)
        munkicommon.display_debug2('CA dir path:  %s', ca_dir_path)

    SYSTEM_KEYCHAIN = "/Library/Keychains/System.keychain"
    if not os.path.exists(SYSTEM_KEYCHAIN):
        munkicommon.display_warning('%s not found.', SYSTEM_KEYCHAIN)
        return

    # Add CA certs. security add-trusted-cert does the right thing even if
    # the cert is already added, so we just do it; checking to see if the
    # cert is already added is hard.
    certs_to_add = []
    if ca_cert_path:
        certs_to_add.append(ca_cert_path)
    if ca_dir_path:
        # add any pem files in the ca_dir_path directory
        for item in os.listdir(ca_dir_path):
            if item.endswith('.pem'):
                certs_to_add.append(os.path.join(ca_dir_path, item))
    for cert in certs_to_add:
        munkicommon.display_debug1('Adding CA cert %s...', cert)
        try:
            output = security('add-trusted-cert', '-d',
                              '-k', SYSTEM_KEYCHAIN, cert)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
            munkicommon.display_error(
                'Could not add CA cert %s into System keychain: %s', cert, err)
Пример #48
0
def mountAdobeDmg(dmgpath):
    """
    Attempts to mount the dmg at dmgpath
    and returns a list of mountpoints
    """
    mountpoints = []
    dmgname = os.path.basename(dmgpath)
    proc = subprocess.Popen(['/usr/bin/hdiutil', 'attach', dmgpath,
                            '-nobrowse', '-noverify', '-plist'],
                            bufsize=1,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (pliststr, err) = proc.communicate()
    if err:
        munkicommon.display_error("Error %s mounting %s." % (err, dmgname))
    if pliststr:
        plist = FoundationPlist.readPlistFromString(pliststr)
        for entity in plist['system-entities']:
            if 'mount-point' in entity:
                mountpoints.append(entity['mount-point'])

    return mountpoints
Пример #49
0
def getpkgkeys(pkgnames):
    """
    Given a list of receipt names, bom file names, or package ids,
    gets a list of pkg_keys from the pkgs table in our database.
    """
    # open connection and cursor to our database
    conn = sqlite3.connect(packagedb)
    curs = conn.cursor()

    # check package names to make sure they're all in the database,
    # build our list of pkg_keys
    pkgerror = False
    pkgkeyslist = []
    for pkg in pkgnames:
        values_t = (pkg, )
        munkicommon.display_debug1("select pkg_key from pkgs where pkgid = %s",
                                   pkg)
        pkg_keys = curs.execute('select pkg_key from pkgs where pkgid = ?',
                                values_t).fetchall()
        if not pkg_keys:
            # try pkgname
            munkicommon.display_debug1(
                "select pkg_key from pkgs where pkgname = %s", pkg)
            pkg_keys = curs.execute(
                'select pkg_key from pkgs where pkgname = ?',
                values_t).fetchall()
        if not pkg_keys:
            munkicommon.display_error("%s not found in database.", pkg)
            pkgerror = True
        else:
            for row in pkg_keys:
                # only want first column
                pkgkeyslist.append(row[0])
    if pkgerror:
        pkgkeyslist = []

    curs.close()
    conn.close()
    munkicommon.display_debug1("pkgkeys: %s", pkgkeyslist)
    return pkgkeyslist
Пример #50
0
def doAdobeCS5Uninstall(adobeInstallInfo):
    '''Runs the locally-installed Adobe CS5 tools to remove CS5 products.
    We need the uninstallxml and the CS5 Setup.app.'''
    uninstallxml = adobeInstallInfo.get('uninstallxml')
    if not uninstallxml:
        munkicommon.display_error("No uninstall.xml in adobe_install_info")
        return -1
    payloadcount = adobeInstallInfo.get('payload_count', 0)
    path = os.path.join(munkicommon.tmpdir, "uninstall.xml")
    deploymentFile = writefile(uninstallxml, path)
    if not deploymentFile:
        return -1
    setupapp = "/Library/Application Support/Adobe/OOBE/PDApp/DWA/Setup.app"
    setup = os.path.join(setupapp, "Contents/MacOS/Setup")
    if not os.path.exists(setup):
        munkicommon.display_error("%s is not installed." % setupapp)
        return -1
    uninstall_cmd = [ setup,
                    '--mode=silent',
                    '--action=uninstall',
                    '--skipProcessCheck=1',
                    '--deploymentFile=%s' % deploymentFile ]
    munkicommon.display_status_minor('Running Adobe Uninstall')
    return runAdobeInstallTool(uninstall_cmd, payloadcount)
Пример #51
0
def runAdobeSetup(dmgpath, uninstalling=False):
    '''Runs the Adobe setup tool in silent mode from
    an Adobe update DMG or an Adobe CS3 install DMG'''
    munkicommon.display_status_minor(
        'Mounting disk image %s' % os.path.basename(dmgpath))
    mountpoints = mountAdobeDmg(dmgpath)
    if mountpoints:
        setup_path = findSetupApp(mountpoints[0])
        if setup_path:
            # look for install.xml or uninstall.xml at root
            deploymentfile = None
            installxml = os.path.join(mountpoints[0], "install.xml")
            uninstallxml = os.path.join(mountpoints[0], "uninstall.xml")
            if uninstalling:
                if os.path.exists(uninstallxml):
                    deploymentfile = uninstallxml
                else:
                    # we've been asked to uninstall,
                    # but found no uninstall.xml
                    # so we need to bail
                    munkicommon.unmountdmg(mountpoints[0])
                    munkicommon.display_error(
                              "%s doesn't appear to contain uninstall info." %
                               os.path.basename(dmgpath))
                    return -1
            else:
                if os.path.exists(installxml):
                    deploymentfile = installxml

            # try to find and count the number of payloads
            # so we can give a rough progress indicator
            number_of_payloads = countPayloads(mountpoints[0])
            munkicommon.display_status_minor('Running Adobe Setup')
            adobe_setup = [ setup_path, '--mode=silent',
                            '--skipProcessCheck=1' ]
            if deploymentfile:
                adobe_setup.append('--deploymentFile=%s' % deploymentfile)

            retcode = runAdobeInstallTool(adobe_setup, number_of_payloads)

        else:
            munkicommon.display_error(
                '%s doesn\'t appear to contain Adobe Setup.' %
                 os.path.basename(dmgpath))
            retcode = -1

        munkicommon.unmountdmg(mountpoints[0])
        return retcode
    else:
        munkicommon.display_error('No mountable filesystems on %s' % dmgpath)
        return -1
Пример #52
0
def runAdobeCS5PatchInstaller(dmgpath, copylocal=False):
    '''Runs the AdobePatchInstaller for CS5.
    Optionally can copy the DMG contents to the local disk
    to work around issues with the patcher.'''
    munkicommon.display_status_minor(
        'Mounting disk image %s' % os.path.basename(dmgpath))
    mountpoints = mountAdobeDmg(dmgpath)
    if mountpoints:
        if copylocal:
            # copy the update to the local disk before installing
            updatedir = tempfile.mkdtemp()
            retcode = subprocess.call(["/bin/cp", "-r",
                                            mountpoints[0], updatedir])
            # unmount diskimage
            munkicommon.unmountdmg(mountpoints[0])
            if retcode:
                munkicommon.display_error(
                                "Error copying items from %s" % dmgpath)
                return -1
            # remove the dmg file to free up space, since we don't need it
            # any longer
            unused_result = subprocess.call(["/bin/rm", dmgpath])
        else:
            updatedir = mountpoints[0]

        patchinstaller = findAdobePatchInstallerApp(updatedir)
        if patchinstaller:
            # try to find and count the number of payloads
            # so we can give a rough progress indicator
            number_of_payloads = countPayloads(updatedir)
            munkicommon.display_status_minor('Running Adobe Patch Installer')
            install_cmd = [ patchinstaller,
                            '--mode=silent',
                            '--skipProcessCheck=1' ]
            retcode = runAdobeInstallTool(install_cmd,
                                          number_of_payloads)
        else:
            munkicommon.display_error(
                    "%s doesn't appear to contain AdobePatchInstaller.app." %
                    os.path.basename(dmgpath))
            retcode = -1
        if copylocal:
            # clean up our mess
            unused_result = subprocess.call(["/bin/rm", "-rf", updatedir])
        else:
            munkicommon.unmountdmg(mountpoints[0])
        return retcode
    else:
        munkicommon.display_error("No mountable filesystems on %s" % dmgpath)
        return -1
Пример #53
0
def extractAppIconsFromFlatPkg(pkg_path):
    '''Extracts application icons from a flat package.
       Returns a list of paths to icns files.'''
    cmd = ['/usr/sbin/pkgutil', '--bom', pkg_path]
    proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
                            stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                            stderr=subprocess.STDOUT)
    output = proc.communicate()[0]
    if proc.returncode:
        munkicommon.display_error(u'Could not get bom files from %s', pkg_path)
        return []
    bomfilepaths = output.splitlines()
    pkg_dict = {}
    for bomfile in bomfilepaths:
        # bomfile path is of the form:
        # /tmp/FlashPlayer.pkg.boms.2Rxa1z/AdobeFlashPlayerComponent.pkg/Bom
        pkgname = os.path.basename(os.path.dirname(bomfile))
        if not pkgname.endswith(u'.pkg'):
            # no subpackages; this is a component pkg
            pkgname = ''
        cmd = ['/usr/bin/lsbom', '-s', bomfile]
        proc = subprocess.Popen(cmd, shell=False, bufsize=-1,
                                stdin=subprocess.PIPE, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        output = proc.communicate()[0]
        if proc.returncode:
            munkicommon.display_error(u'Could not lsbom %s', bomfile)
        # record paths to all app Info.plist files
        pkg_dict[pkgname] = [
            os.path.normpath(line)
            for line in output.decode('utf-8').splitlines()
            if line.endswith(u'.app/Contents/Info.plist')]
        if not pkg_dict[pkgname]:
            # remove empty lists
            del pkg_dict[pkgname]
    if not pkg_dict:
        return []
    icon_paths = []
    pkgtmp = os.path.join(tempfile.mkdtemp(dir=u'/tmp'), u'pkg')
    exporttmp = tempfile.mkdtemp(dir='/tmp')
    cmd = ['/usr/sbin/pkgutil', '--expand', pkg_path, pkgtmp]
    result = subprocess.call(cmd)
    if result == 0:
        for pkg in pkg_dict:
            archive_path = os.path.join(pkgtmp, pkg, u'Payload')
            err = extractAppBitsFromPkgArchive(archive_path, exporttmp)
            if err == 0:
                for info_path in pkg_dict[pkg]:
                    full_path = os.path.join(exporttmp, info_path)
                    # convert path to Info.plist to path to app
                    app_path = os.path.dirname(os.path.dirname(full_path))
                    icon_path = findIconForApp(app_path)
                    if icon_path:
                        icon_paths.append(icon_path)
            else:
                munkicommon.display_error(
                    u'pax could not read files from %s', archive_path)
                return []
    else:
        munkicommon.display_error(u'Could not expand %s', pkg_path)
    # clean up our expanded flat package; we no longer need it
    shutil.rmtree(pkgtmp)
    return icon_paths
Пример #54
0
def removeFilesystemItems(removalpaths, forcedeletebundles):
    """
    Attempts to remove all the paths in the array removalpaths
    """
    # we sort in reverse because we can delete from the bottom up,
    # clearing a directory before we try to remove the directory itself
    removalpaths.sort(reverse=True)
    removalerrors = ""
    removalcount = len(removalpaths)
    munkicommon.display_status_minor('Removing %s filesystem items' %
                                     removalcount)

    itemcount = len(removalpaths)
    itemindex = 0
    munkicommon.display_percent_done(itemindex, itemcount)

    for item in removalpaths:
        itemindex += 1
        pathtoremove = "/" + item
        # use os.path.lexists so broken links return true
        # so we can remove them
        if os.path.lexists(pathtoremove):
            munkicommon.display_detail("Removing: " + pathtoremove)
            if (os.path.isdir(pathtoremove)
                    and not os.path.islink(pathtoremove)):
                diritems = munkicommon.listdir(pathtoremove)
                if diritems == ['.DS_Store']:
                    # If there's only a .DS_Store file
                    # we'll consider it empty
                    ds_storepath = pathtoremove + "/.DS_Store"
                    try:
                        os.remove(ds_storepath)
                    except (OSError, IOError):
                        pass
                    diritems = munkicommon.listdir(pathtoremove)
                if diritems == []:
                    # directory is empty
                    try:
                        os.rmdir(pathtoremove)
                    except (OSError, IOError), err:
                        msg = "Couldn't remove directory %s - %s" % (
                            pathtoremove, err)
                        munkicommon.display_error(msg)
                        removalerrors = removalerrors + "\n" + msg
                else:
                    # the directory is marked for deletion but isn't empty.
                    # if so directed, if it's a bundle (like .app), we should
                    # remove it anyway - no use having a broken bundle hanging
                    # around
                    if forcedeletebundles and isBundle(pathtoremove):
                        munkicommon.display_warning(
                            "Removing non-empty bundle: %s", pathtoremove)
                        retcode = subprocess.call(
                            ['/bin/rm', '-r', pathtoremove])
                        if retcode:
                            msg = "Couldn't remove bundle %s" % pathtoremove
                            munkicommon.display_error(msg)
                            removalerrors = removalerrors + "\n" + msg
                    else:
                        # if this path is inside a bundle, and we've been
                        # directed to force remove bundles,
                        # we don't need to warn because it's going to be
                        # removed with the bundle.
                        # Otherwise, we should warn about non-empty
                        # directories.
                        if not insideBundle(pathtoremove) or \
                           not forcedeletebundles:
                            msg = \
                              "Did not remove %s because it is not empty." % \
                               pathtoremove
                            munkicommon.display_error(msg)
                            removalerrors = removalerrors + "\n" + msg

            else:
                # not a directory, just unlink it
                # I was using rm instead of Python because I don't trust
                # handling of resource forks with Python
                #retcode = subprocess.call(['/bin/rm', pathtoremove])
                # but man that's slow.
                # I think there's a lot of overhead with the
                # subprocess call. I'm going to use os.remove.
                # I hope I don't regret it.
                retcode = ''
                try:
                    os.remove(pathtoremove)
                except (OSError, IOError), err:
                    msg = "Couldn't remove item %s: %s" % (pathtoremove, err)
                    munkicommon.display_error(msg)
                    removalerrors = removalerrors + "\n" + msg
Пример #55
0
 def test_display_error_with_str_msg(self):
     munkicommon.display_error(MSG_STR)
Пример #56
0
 def test_display_error_with_unicode_msg_unicode_arg(self):
     munkicommon.display_error(MSG_UNI, ARG_UNI)
Пример #57
0
def ImportPackage(packagepath, curs):
    """
    Imports package data from the receipt at packagepath into
    our internal package database.
    """

    bompath = os.path.join(packagepath, 'Contents/Archive.bom')
    infopath = os.path.join(packagepath, 'Contents/Info.plist')
    pkgname = os.path.basename(packagepath)

    if not os.path.exists(packagepath):
        munkicommon.display_error("%s not found.", packagepath)
        return

    if not os.path.isdir(packagepath):
        # Every machine I've seen has a bogus BSD.pkg,
        # so we won't print a warning for that specific one.
        if pkgname != "BSD.pkg":
            munkicommon.display_warning(
                "%s is not a valid receipt. Skipping.", packagepath)
        return

    if not os.path.exists(bompath):
        # look in receipt's Resources directory
        bomname = os.path.splitext(pkgname)[0] + '.bom'
        bompath = os.path.join(packagepath, "Contents/Resources",
                                bomname)
        if not os.path.exists(bompath):
            munkicommon.display_warning(
                "%s has no BOM file. Skipping.", packagepath)
            return

    if not os.path.exists(infopath):
        munkicommon.display_warning(
            "%s has no Info.plist. Skipping.", packagepath)
        return

    timestamp = os.stat(packagepath).st_mtime
    owner = 0
    plist = FoundationPlist.readPlist(infopath)
    if "CFBundleIdentifier" in plist:
        pkgid = plist["CFBundleIdentifier"]
    elif "Bundle identifier" in plist:
        # special case for JAMF Composer generated packages. WTF?
        pkgid = plist["Bundle identifier"]
    else:
        pkgid = pkgname
    if "CFBundleShortVersionString" in plist:
        vers = plist["CFBundleShortVersionString"]
    elif "Bundle versions string, short" in plist:
        # another special case for JAMF Composer-generated packages. Wow.
        vers = plist["Bundle versions string, short"]
    else:
        vers = "1.0"
    if "IFPkgRelocatedPath" in plist:
        ppath = plist["IFPkgRelocatedPath"]
        ppath = ppath.lstrip('./').rstrip('/')
    else:
        ppath = ""

    values_t = (timestamp, owner, pkgid, vers, ppath, pkgname)
    curs.execute(
        '''INSERT INTO pkgs (timestamp, owner, pkgid, vers, ppath, pkgname)
           values (?, ?, ?, ?, ?, ?)''', values_t)
    pkgkey = curs.lastrowid

    cmd = ["/usr/bin/lsbom", bompath]
    proc = subprocess.Popen(cmd, shell=False, bufsize=1,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    while True:
        line =  proc.stdout.readline().decode('UTF-8')
        if not line and (proc.poll() != None):
            break

        try:
            item = line.rstrip("\n").split("\t")
            path = item[0]
            perms = item[1]
            uidgid = item[2].split("/")
            uid = uidgid[0]
            gid = uidgid[1]
        except IndexError:
            # we really only care about the path
            perms = "0000"
            uid = "0"
            gid = "0"

        try:
            if path != ".":
                # special case for MS Office 2008 installers
                if ppath == "tmp/com.microsoft.updater/office_location":
                    ppath = "Applications"

                # prepend the ppath so the paths match the actual install
                # locations
                path = path.lstrip("./")
                if ppath:
                    path = ppath + "/" + path

                values_t = (path, )
                row = curs.execute(
                    'SELECT path_key from paths where path = ?',
                                                        values_t).fetchone()
                if not row:
                    curs.execute(
                            'INSERT INTO paths (path) values (?)', values_t)
                    pathkey = curs.lastrowid
                else:
                    pathkey = row[0]

                values_t = (pkgkey, pathkey, uid, gid, perms)
                curs.execute(
                '''INSERT INTO pkgs_paths (pkg_key, path_key, uid, gid, perms)
                   values (?, ?, ?, ?, ?)''', values_t)
        except sqlite3.DatabaseError:
            pass
Пример #58
0
def initDatabase(forcerebuild=False):
    """
    Builds or rebuilds our internal package database.
    """
    if not shouldRebuildDB(packagedb) and not forcerebuild:
        return True

    munkicommon.display_status_minor(
        'Gathering information on installed packages')

    if os.path.exists(packagedb):
        try:
            os.remove(packagedb)
        except (OSError, IOError):
            munkicommon.display_error(
                "Could not remove out-of-date receipt database.")
            return False

    os_version = munkicommon.getOsVersion(as_tuple=True)
    pkgcount = 0
    receiptsdir = "/Library/Receipts"
    bomsdir = "/Library/Receipts/boms"
    if os.path.exists(receiptsdir):
        receiptlist = munkicommon.listdir(receiptsdir)
        for item in receiptlist:
            if item.endswith(".pkg"):
                pkgcount += 1
    if os.path.exists(bomsdir):
        bomslist = munkicommon.listdir(bomsdir)
        for item in bomslist:
            if item.endswith(".bom"):
                pkgcount += 1

    if os_version >= (10, 6): # Snow Leopard or later
        pkglist = []
        cmd = ['/usr/sbin/pkgutil', '--pkgs']
        proc = subprocess.Popen(cmd, shell=False, bufsize=1,
                                stdin=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        while True:
            line =  proc.stdout.readline()
            if not line and (proc.poll() != None):
                break

            pkglist.append(line.rstrip('\n'))
            pkgcount += 1

    conn = sqlite3.connect(packagedb)
    conn.text_factory = str
    curs = conn.cursor()
    CreateTables(curs)

    currentpkgindex = 0
    munkicommon.display_percent_done(0, pkgcount)

    if os.path.exists(receiptsdir):
        receiptlist = munkicommon.listdir(receiptsdir)
        for item in receiptlist:
            if munkicommon.stopRequested():
                curs.close()
                conn.close()
                #our package db isn't valid, so we should delete it
                os.remove(packagedb)
                return False

            if item.endswith(".pkg"):
                receiptpath = os.path.join(receiptsdir, item)
                munkicommon.display_detail("Importing %s...", receiptpath)
                ImportPackage(receiptpath, curs)
                currentpkgindex += 1
                munkicommon.display_percent_done(currentpkgindex, pkgcount)

    if os.path.exists(bomsdir):
        bomslist = munkicommon.listdir(bomsdir)
        for item in bomslist:
            if munkicommon.stopRequested():
                curs.close()
                conn.close()
                #our package db isn't valid, so we should delete it
                os.remove(packagedb)
                return False

            if item.endswith(".bom"):
                bompath = os.path.join(bomsdir, item)
                munkicommon.display_detail("Importing %s...", bompath)
                ImportBom(bompath, curs)
                currentpkgindex += 1
                munkicommon.display_percent_done(currentpkgindex, pkgcount)
    if os_version >= (10, 6):  # Snow Leopard or later
        for pkg in pkglist:
            if munkicommon.stopRequested():
                curs.close()
                conn.close()
                #our package db isn't valid, so we should delete it
                os.remove(packagedb)
                return False

            munkicommon.display_detail("Importing %s...", pkg)
            ImportFromPkgutil(pkg, curs)
            currentpkgindex += 1
            munkicommon.display_percent_done(currentpkgindex, pkgcount)

    # in case we didn't quite get to 100% for some reason
    if currentpkgindex < pkgcount:
        munkicommon.display_percent_done(pkgcount, pkgcount)

    # commit and close the db when we're done.
    conn.commit()
    curs.close()
    conn.close()
    return True
Пример #59
0
def removeFilesystemItems(removalpaths, forcedeletebundles):
    """
    Attempts to remove all the paths in the array removalpaths
    """
    # we sort in reverse because we can delete from the bottom up,
    # clearing a directory before we try to remove the directory itself
    removalpaths.sort(reverse=True)
    removalerrors = ""
    removalcount = len(removalpaths)
    munkicommon.display_status_minor(
        'Removing %s filesystem items' % removalcount)

    itemcount = len(removalpaths)
    itemindex = 0
    munkicommon.display_percent_done(itemindex, itemcount)

    for item in removalpaths:
        itemindex += 1
        pathtoremove = "/" + item
        # use os.path.lexists so broken links return true
        # so we can remove them
        if os.path.lexists(pathtoremove):
            munkicommon.display_detail("Removing: " + pathtoremove)
            if (os.path.isdir(pathtoremove) and \
               not os.path.islink(pathtoremove)):
                diritems = munkicommon.listdir(pathtoremove)
                if diritems == ['.DS_Store']:
                    # If there's only a .DS_Store file
                    # we'll consider it empty
                    ds_storepath = pathtoremove + "/.DS_Store"
                    try:
                        os.remove(ds_storepath)
                    except (OSError, IOError):
                        pass
                    diritems = munkicommon.listdir(pathtoremove)
                if diritems == []:
                    # directory is empty
                    try:
                        os.rmdir(pathtoremove)
                    except (OSError, IOError), err:
                        msg = "Couldn't remove directory %s - %s" % (
                            pathtoremove, err)
                        munkicommon.display_error(msg)
                        removalerrors = removalerrors + "\n" + msg
                else:
                    # the directory is marked for deletion but isn't empty.
                    # if so directed, if it's a bundle (like .app), we should
                    # remove it anyway - no use having a broken bundle hanging
                    # around
                    if (forcedeletebundles and isBundle(pathtoremove)):
                        munkicommon.display_warning(
                            "Removing non-empty bundle: %s", pathtoremove)
                        retcode = subprocess.call(['/bin/rm', '-r',
                                                   pathtoremove])
                        if retcode:
                            msg = "Couldn't remove bundle %s" % pathtoremove
                            munkicommon.display_error(msg)
                            removalerrors = removalerrors + "\n" + msg
                    else:
                        # if this path is inside a bundle, and we've been
                        # directed to force remove bundles,
                        # we don't need to warn because it's going to be
                        # removed with the bundle.
                        # Otherwise, we should warn about non-empty
                        # directories.
                        if not insideBundle(pathtoremove) or \
                           not forcedeletebundles:
                            msg = \
                              "Did not remove %s because it is not empty." % \
                               pathtoremove
                            munkicommon.display_error(msg)
                            removalerrors = removalerrors + "\n" + msg

            else:
                # not a directory, just unlink it
                # I was using rm instead of Python because I don't trust
                # handling of resource forks with Python
                #retcode = subprocess.call(['/bin/rm', pathtoremove])
                # but man that's slow.
                # I think there's a lot of overhead with the
                # subprocess call. I'm going to use os.remove.
                # I hope I don't regret it.
                retcode = ''
                try:
                    os.remove(pathtoremove)
                except (OSError, IOError), err:
                    msg = "Couldn't remove item %s: %s" % (pathtoremove, err)
                    munkicommon.display_error(msg)
                    removalerrors = removalerrors + "\n" + msg
Пример #60
0
    added_keychain = add_to_keychain_list(abs_keychain_path)
    unlock_and_set_nonlocking(abs_keychain_path)

    # Add client cert (and optionally key)
    client_cert_file = None
    combined_pem = None
    if client_key_path:
        # combine client cert and private key before we import
        cert_data = read_file(client_cert_path)
        key_data = read_file(client_key_path)
        # write the combined data
        combined_pem = os.path.join(munkicommon.tmpdir(), 'combined.pem')
        if write_file(cert_data + key_data, combined_pem):
            client_cert_file = combined_pem
        else:
            munkicommon.display_error(
                'Could not combine client cert and key for import!')
    else:
        client_cert_file = client_cert_path
    if client_cert_file:
        # client_cert_file is combined_pem or client_cert_file
        munkicommon.display_debug2('Importing client cert and key...')
        try:
            output = security(
                'import', client_cert_file, '-A', '-k', abs_keychain_path)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
            munkicommon.display_error(
                'Could not import %s: %s', client_cert_file, err)
    if combined_pem:
        # we created this; we should clean it up