Ejemplo n.º 1
0
def profile_needs_to_be_installed(identifier, hash_value):
    '''If any of these conditions is True, we should install the profile:
    1) identifier is not in the output of `profiles -C`
    2) We don't have a receipt for this profile identifier
    3) receipt's hash_value for identifier does not match ours
    4) ProfileInstallDate doesn't match the receipt'''
    if not profiles_supported():
        return False
    if not identifier_in_config_profile_info(identifier):
        munkicommon.display_debug2('Profile identifier %s is not installed.' %
                                   identifier)
        return True
    receipt = get_profile_receipt(identifier)
    if not receipt:
        munkicommon.display_debug2('No receipt for profile identifier %s.' %
                                   identifier)
        return True
    munkicommon.display_debug2('Receipt for %s:\n%s' % (identifier, receipt))
    if receipt.get('FileHash') != hash_value:
        munkicommon.display_debug2(
            'Receipt FileHash for profile identifier %s does not match.' %
            identifier)
        return True
    installed_dict = profile_info_for_installed_identifier(identifier)
    if (installed_dict.get('ProfileInstallDate') !=
            receipt.get('ProfileInstallDate')):
        munkicommon.display_debug2(
            'Receipt ProfileInstallDate for profile identifier %s does not '
            'match.' % identifier)
        return True
    return False
Ejemplo n.º 2
0
def profile_needs_to_be_installed(identifier, hash_value):
    '''If any of these conditions is True, we should install the profile:
    1) identifier is not in the output of `profiles -C`
    2) We don't have a receipt for this profile identifier
    3) receipt's hash_value for identifier does not match ours
    4) ProfileInstallDate doesn't match the receipt'''
    if not identifier_in_config_profile_info(identifier):
        munkicommon.display_debug2(
            'Profile identifier %s is not installed.' % identifier)
        return True
    receipt = get_profile_receipt(identifier)
    if not receipt:
        munkicommon.display_debug2(
            'No receipt for profile identifier %s.' % identifier)
        return True
    munkicommon.display_debug2('Receipt for %s:\n%s' % (identifier, receipt))
    if receipt.get('FileHash') != hash_value:
        munkicommon.display_debug2(
            'Receipt FileHash for profile identifier %s does not match.'
            % identifier)
        return True
    installed_dict = profile_info_for_installed_identifier(identifier)
    if (installed_dict.get('ProfileInstallDate')
            != receipt.get('ProfileInstallDate')):
        munkicommon.display_debug2(
            'Receipt ProfileInstallDate for profile identifier %s does not '
            'match.' % identifier)
        return True
    return False
Ejemplo n.º 3
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)
Ejemplo n.º 4
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
Ejemplo n.º 5
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
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
def profile_is_installed(identifier):
    '''If identifier is in the output of `profiles -C`
    return True, else return False'''
    if identifier_in_config_profile_info(identifier):
        munkicommon.display_debug2(
            'Profile identifier %s is installed.' % identifier)
        return True
    munkicommon.display_debug2(
        'Profile identifier %s is not installed.' % identifier)
    return False
Ejemplo n.º 8
0
def profile_is_installed(identifier):
    '''If identifier is in the output of `profiles -C`
    return True, else return False'''
    if identifier_in_config_profile_info(identifier):
        munkicommon.display_debug2('Profile identifier %s is installed.' %
                                   identifier)
        return True
    munkicommon.display_debug2('Profile identifier %s is not installed.' %
                               identifier)
    return False
Ejemplo n.º 9
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
Ejemplo n.º 10
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
Ejemplo n.º 11
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)
Ejemplo n.º 12
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)
Ejemplo n.º 13
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)
Ejemplo n.º 14
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)
Ejemplo n.º 15
0
                curldirectivepath])

    proc = subprocess.Popen(cmd, shell=False, bufsize=1,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    targetsize = 0
    downloadedpercent = -1
    donewithheaders = False
    maxheaders = 15

    while True:
        if not donewithheaders:
            info = proc.stdout.readline().strip('\r\n')
            if info:
                munkicommon.display_debug2(info)
                if info.startswith('HTTP/'):
                    header['http_result_code'] = info.split(None, 2)[1]
                    header['http_result_description'] = info.split(None, 2)[2]
                elif ': ' in info:
                    part = info.split(None, 1)
                    fieldname = part[0].rstrip(':').lower()
                    header[fieldname] = part[1]
            else:
                # we got an empty line; end of headers (or curl exited)
                if follow_redirects:
                    if header.get('http_result_code') in ['301', '302', '303']:
                        # redirect, so more headers are coming.
                        # Throw away the headers we've received so far
                        header = {}
                        header['http_result_code'] = '000'
Ejemplo n.º 16
0
def get_url(url, destinationpath,
            custom_headers=None, message=None, onlyifnewer=False,
            resume=False, follow_redirects=False):
    """Gets an HTTP or HTTPS URL and stores it in
    destination path. Returns a dictionary of headers, which includes
    http_result_code and http_result_description.
    Will raise CurlError if Gurl returns an error.
    Will raise HTTPError if HTTP Result code is not 2xx or 304.
    If destinationpath already exists, you can set 'onlyifnewer' to true to
    indicate you only want to download the file only if it's newer on the
    server.
    If you set resume to True, Gurl will attempt to resume an
    interrupted download."""

    tempdownloadpath = destinationpath + '.download'
    if os.path.exists(tempdownloadpath) and not resume:
        if resume and not os.path.exists(destinationpath):
            os.remove(tempdownloadpath)

    cache_data = None
    if onlyifnewer and os.path.exists(destinationpath):
        # create a temporary Gurl object so we can extract the
        # stored caching data so we can download only if the
        # file has changed on the server
        gurl_obj = Gurl.alloc().initWithOptions_({'file': destinationpath})
        cache_data = gurl_obj.get_stored_headers()
        del gurl_obj

    options = {'url': url,
               'file': tempdownloadpath,
               'follow_redirects': follow_redirects,
               'can_resume': resume,
               'additional_headers': header_dict_from_list(custom_headers),
               'download_only_if_changed': onlyifnewer,
               'cache_data': cache_data,
               'logging_function': munkicommon.display_debug2}
    munkicommon.display_debug2('Options: %s' % options)

    connection = Gurl.alloc().initWithOptions_(options)
    stored_percent_complete = -1
    stored_bytes_received = 0
    connection.start()
    try:
        while True:
            # if we did `while not connection.isDone()` we'd miss printing
            # messages and displaying percentages if we exit the loop first
            connection_done = connection.isDone()
            if message and connection.status and connection.status != 304:
                # log always, display if verbose is 1 or more
                # also display in MunkiStatus detail field
                munkicommon.display_status_minor(message)
                # now clear message so we don't display it again
                message = None
            if (str(connection.status).startswith('2')
                and connection.percentComplete != -1):
                if connection.percentComplete != stored_percent_complete:
                    # display percent done if it has changed
                    stored_percent_complete = connection.percentComplete
                    munkicommon.display_percent_done(
                                                stored_percent_complete, 100)
            elif connection.bytesReceived != stored_bytes_received:
                # if we don't have percent done info, log bytes received
                stored_bytes_received = connection.bytesReceived
                munkicommon.display_detail(
                    'Bytes received: %s', stored_bytes_received)
            if connection_done:
                break

    except (KeyboardInterrupt, SystemExit):
        # safely kill the connection then re-raise
        connection.cancel()
        raise
    except Exception, err: # too general, I know
        # Let us out! ... Safely! Unexpectedly quit dialogs are annoying...
        connection.cancel()
        # Re-raise the error as a GurlError
        raise GurlError(-1, str(err))
Ejemplo n.º 17
0
    unlock_and_set_nonlocking(abs_keychain_path)

    # Add client cert (and optionally key)
    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):
            munkicommon.display_debug1('Importing client cert and key...')
            try:
                output = security('import', combined_pem, '-A', '-k',
                                  abs_keychain_path)
                if output:
                    munkicommon.display_debug2(output)
            except SecurityError, err:
                munkicommon.display_error('Could not import %s: %s',
                                          combined_pem, err)
            os.unlink(combined_pem)
        else:
            munkicommon.display_error(
                'Could not combine client cert and key for import!')
    else:
        munkicommon.display_debug2('Importing client cert and key...')
        try:
            output = security('import', client_cert_path, '-A', '-k',
                              abs_keychain_path)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
Ejemplo n.º 18
0
def get_url(url,
            destinationpath,
            custom_headers=None,
            message=None,
            onlyifnewer=False,
            resume=False,
            follow_redirects=False):
    """Gets an HTTP or HTTPS URL and stores it in
    destination path. Returns a dictionary of headers, which includes
    http_result_code and http_result_description.
    Will raise CurlError if Gurl returns an error.
    Will raise HTTPError if HTTP Result code is not 2xx or 304.
    If destinationpath already exists, you can set 'onlyifnewer' to true to
    indicate you only want to download the file only if it's newer on the
    server.
    If you set resume to True, Gurl will attempt to resume an
    interrupted download."""

    tempdownloadpath = destinationpath + '.download'
    if os.path.exists(tempdownloadpath) and not resume:
        if resume and not os.path.exists(destinationpath):
            os.remove(tempdownloadpath)

    cache_data = None
    if onlyifnewer and os.path.exists(destinationpath):
        # create a temporary Gurl object so we can extract the
        # stored caching data so we can download only if the
        # file has changed on the server
        gurl_obj = Gurl.alloc().initWithOptions_({'file': destinationpath})
        cache_data = gurl_obj.get_stored_headers()
        del gurl_obj

    options = {
        'url': url,
        'file': tempdownloadpath,
        'follow_redirects': follow_redirects,
        'can_resume': resume,
        'additional_headers': header_dict_from_list(custom_headers),
        'download_only_if_changed': onlyifnewer,
        'cache_data': cache_data,
        'logging_function': munkicommon.display_debug2
    }
    munkicommon.display_debug2('Options: %s' % options)

    connection = Gurl.alloc().initWithOptions_(options)
    stored_percent_complete = -1
    stored_bytes_received = 0
    connection.start()
    try:
        while True:
            # if we did `while not connection.isDone()` we'd miss printing
            # messages and displaying percentages if we exit the loop first
            connection_done = connection.isDone()
            if message and connection.status and connection.status != 304:
                # log always, display if verbose is 1 or more
                # also display in MunkiStatus detail field
                munkicommon.display_status_minor(message)
                # now clear message so we don't display it again
                message = None
            if (str(connection.status).startswith('2')
                    and connection.percentComplete != -1):
                if connection.percentComplete != stored_percent_complete:
                    # display percent done if it has changed
                    stored_percent_complete = connection.percentComplete
                    munkicommon.display_percent_done(stored_percent_complete,
                                                     100)
            elif connection.bytesReceived != stored_bytes_received:
                # if we don't have percent done info, log bytes received
                stored_bytes_received = connection.bytesReceived
                munkicommon.display_detail('Bytes received: %s',
                                           stored_bytes_received)
            if connection_done:
                break

    except (KeyboardInterrupt, SystemExit):
        # safely kill the connection then re-raise
        connection.cancel()
        raise
    except Exception, err:  # too general, I know
        # Let us out! ... Safely! Unexpectedly quit dialogs are annoying...
        connection.cancel()
        # Re-raise the error as a GurlError
        raise GurlError(-1, str(err))
Ejemplo n.º 19
0
    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
        try:
            os.unlink(combined_pem)
        except (OSError, IOError):
            pass
Ejemplo n.º 20
0
def curl(url, destinationpath,
         cert_info=None, custom_headers=None, donotrecurse=False, etag=None,
         message=None, onlyifnewer=False, resume=False, follow_redirects=False):
    """Gets an HTTP or HTTPS URL and stores it in
    destination path. Returns a dictionary of headers, which includes
    http_result_code and http_result_description.
    Will raise CurlError if curl returns an error.
    Will raise HTTPError if HTTP Result code is not 2xx or 304.
    If destinationpath already exists, you can set 'onlyifnewer' to true to
    indicate you only want to download the file only if it's newer on the
    server.
    If you have an ETag from the current destination path, you can pass that
    to download the file only if it is different.
    Finally, if you set resume to True, curl will attempt to resume an
    interrupted download. You'll get an error if the existing file is
    complete; if the file has changed since the first download attempt, you'll
    get a mess."""

    header = {}
    header['http_result_code'] = '000'
    header['http_result_description'] = ''

    curldirectivepath = os.path.join(munkicommon.tmpdir, 'curl_temp')
    tempdownloadpath = destinationpath + '.download'

    # we're writing all the curl options to a file and passing that to
    # curl so we avoid the problem of URLs showing up in a process listing
    try:
        fileobj = open(curldirectivepath, mode='w')
        print >> fileobj, 'silent'          # no progress meter
        print >> fileobj, 'show-error'      # print error msg to stderr
        print >> fileobj, 'no-buffer'       # don't buffer output
        print >> fileobj, 'fail'            # throw error if download fails
        print >> fileobj, 'dump-header -'   # dump headers to stdout
        print >> fileobj, 'speed-time = 30' # give up if too slow d/l
        print >> fileobj, 'output = "%s"' % tempdownloadpath
        print >> fileobj, 'ciphers = HIGH,!ADH' #use only secure >=128 bit SSL
        print >> fileobj, 'url = "%s"' % url
        
        munkicommon.display_debug2('follow_redirects is %s', follow_redirects)
        if follow_redirects:
            print >> fileobj, 'location'    # follow redirects
        
        if cert_info:
            cacert = cert_info.get('cacert')
            capath = cert_info.get('capath')
            cert = cert_info.get('cert')
            key = cert_info.get('key')
            if cacert:
                if not os.path.isfile(cacert):
                    raise CurlError(-1, 'No CA cert at %s' % cacert)
                print >> fileobj, 'cacert = "%s"' % cacert
            if capath:
                if not os.path.isdir(capath):
                    raise CurlError(-2, 'No CA directory at %s' % capath)
                print >> fileobj, 'capath = "%s"' % capath
            if cert:
                if not os.path.isfile(cert):
                    raise CurlError(-3, 'No client cert at %s' % cert)
                print >> fileobj, 'cert = "%s"' % cert
            if key:
                if not os.path.isfile(key):
                    raise CurlError(-4, 'No client key at %s' % key)
                print >> fileobj, 'key = "%s"' % key

        if os.path.exists(destinationpath):
            if etag:
                escaped_etag = etag.replace('"','\\"')
                print >> fileobj, ('header = "If-None-Match: %s"'
                                                        % escaped_etag)
            elif onlyifnewer:
                print >> fileobj, 'time-cond = "%s"' % destinationpath
            else:
                os.remove(destinationpath)

        if os.path.exists(tempdownloadpath):
            if resume and not os.path.exists(destinationpath):
                # let's try to resume this download
                print >> fileobj, 'continue-at -'
                # if an existing etag, only resume if etags still match.
                tempetag = getxattr(tempdownloadpath, XATTR_ETAG)
                if tempetag:
                    # Note: If-Range is more efficient, but the response
                    # confuses curl (Error: 33 if etag not match).
                    escaped_etag = tempetag.replace('"','\\"')
                    print >> fileobj, ('header = "If-Match: %s"'
                                        % escaped_etag)
            else:
                os.remove(tempdownloadpath)

        # Add any additional headers specified in custom_headers
        # custom_headers must be an array of strings with valid HTTP
        # header format.
        if custom_headers:
            for custom_header in custom_headers:
                custom_header = custom_header.strip().encode('utf-8')
                if re.search(r'^[\w-]+:.+', custom_header):
                    print >> fileobj, ('header = "%s"' % custom_header)
                else:
                    munkicommon.display_warning(
                        'Skipping invalid HTTP header: %s' % custom_header)

        fileobj.close()
    except Exception, e:
        raise CurlError(-5, 'Error writing curl directive: %s' % str(e))
Ejemplo n.º 21
0
            curldirectivepath]

    proc = subprocess.Popen(cmd, shell=False, bufsize=1,
                            stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    targetsize = 0
    downloadedpercent = -1
    donewithheaders = False
    maxheaders = 15

    while True:
        if not donewithheaders:
            info = proc.stdout.readline().strip('\r\n')
            if info:
                munkicommon.display_debug2(info)
                if info.startswith('HTTP/'):
                    header['http_result_code'] = info.split(None, 2)[1]
                    header['http_result_description'] = info.split(None, 2)[2]
                elif ': ' in info:
                    part = info.split(None, 1)
                    fieldname = part[0].rstrip(':').lower()
                    header[fieldname] = part[1]
            else:
                # we got an empty line; end of headers (or curl exited)
                if follow_redirects:
                    if header.get('http_result_code') in ['301', '302', '303']:
                        # redirect, so more headers are coming.
                        # Throw away the headers we've received so far
                        header = {}
                        header['http_result_code'] = '000'
Ejemplo n.º 22
0
    unlock_and_set_nonlocking(abs_keychain_path)

    # Add client cert (and optionally key)
    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):
            munkicommon.display_debug1('Importing client cert and key...')
            try:
                output = security(
                    'import', combined_pem, '-A', '-k', abs_keychain_path)
                if output:
                    munkicommon.display_debug2(output)
            except SecurityError, err:
                munkicommon.display_error(
                    'Could not import %s: %s', combined_pem, err)
            os.unlink(combined_pem)
        else:
            munkicommon.display_error(
                'Could not combine client cert and key for import!')
    else:
        munkicommon.display_debug2('Importing client cert and key...')
        try:
            output = security(
                'import', client_cert_path, '-A', '-k', abs_keychain_path)
            if output:
                munkicommon.display_debug2(output)
        except SecurityError, err:
Ejemplo n.º 23
0
def curl(url, destinationpath,
         cert_info=None, custom_headers=None, donotrecurse=False, etag=None,
         message=None, onlyifnewer=False, resume=False, follow_redirects=False):
    """Gets an HTTP or HTTPS URL and stores it in
    destination path. Returns a dictionary of headers, which includes
    http_result_code and http_result_description.
    Will raise CurlError if curl returns an error.
    Will raise HTTPError if HTTP Result code is not 2xx or 304.
    If destinationpath already exists, you can set 'onlyifnewer' to true to
    indicate you only want to download the file only if it's newer on the
    server.
    If you have an ETag from the current destination path, you can pass that
    to download the file only if it is different.
    Finally, if you set resume to True, curl will attempt to resume an
    interrupted download. You'll get an error if the existing file is
    complete; if the file has changed since the first download attempt, you'll
    get a mess."""

    header = {}
    header['http_result_code'] = '000'
    header['http_result_description'] = ''

    curldirectivepath = os.path.join(munkicommon.tmpdir, 'curl_temp')
    tempdownloadpath = destinationpath + '.download'

    # we're writing all the curl options to a file and passing that to
    # curl so we avoid the problem of URLs showing up in a process listing
    try:
        fileobj = open(curldirectivepath, mode='w')
        print >> fileobj, 'silent'          # no progress meter
        print >> fileobj, 'show-error'      # print error msg to stderr
        print >> fileobj, 'no-buffer'       # don't buffer output
        print >> fileobj, 'fail'            # throw error if download fails
        print >> fileobj, 'dump-header -'   # dump headers to stdout
        print >> fileobj, 'speed-time = 30' # give up if too slow d/l
        print >> fileobj, 'output = "%s"' % tempdownloadpath
        print >> fileobj, 'ciphers = HIGH,!ADH' #use only secure >=128 bit SSL
        print >> fileobj, 'url = "%s"' % url

        munkicommon.display_debug2('follow_redirects is %s', follow_redirects)
        if follow_redirects:
            print >> fileobj, 'location'    # follow redirects

        if cert_info:
            cacert = cert_info.get('cacert')
            capath = cert_info.get('capath')
            cert = cert_info.get('cert')
            key = cert_info.get('key')
            if cacert:
                if not os.path.isfile(cacert):
                    raise CurlError(-1, 'No CA cert at %s' % cacert)
                print >> fileobj, 'cacert = "%s"' % cacert
            if capath:
                if not os.path.isdir(capath):
                    raise CurlError(-2, 'No CA directory at %s' % capath)
                print >> fileobj, 'capath = "%s"' % capath
            if cert:
                if not os.path.isfile(cert):
                    raise CurlError(-3, 'No client cert at %s' % cert)
                print >> fileobj, 'cert = "%s"' % cert
            if key:
                if not os.path.isfile(key):
                    raise CurlError(-4, 'No client key at %s' % key)
                print >> fileobj, 'key = "%s"' % key

        if os.path.exists(destinationpath):
            if etag:
                escaped_etag = etag.replace('"','\\"')
                print >> fileobj, ('header = "If-None-Match: %s"'
                                                        % escaped_etag)
            elif onlyifnewer:
                print >> fileobj, 'time-cond = "%s"' % destinationpath
            else:
                os.remove(destinationpath)

        if os.path.exists(tempdownloadpath):
            if resume and not os.path.exists(destinationpath):
                # let's try to resume this download
                print >> fileobj, 'continue-at -'
                # if an existing etag, only resume if etags still match.
                tempetag = getxattr(tempdownloadpath, XATTR_ETAG)
                if tempetag:
                    # Note: If-Range is more efficient, but the response
                    # confuses curl (Error: 33 if etag not match).
                    escaped_etag = tempetag.replace('"','\\"')
                    print >> fileobj, ('header = "If-Match: %s"'
                                        % escaped_etag)
            else:
                os.remove(tempdownloadpath)

        # Add any additional headers specified in custom_headers
        # custom_headers must be an array of strings with valid HTTP
        # header format.
        if custom_headers:
            for custom_header in custom_headers:
                custom_header = custom_header.strip().encode('utf-8')
                if re.search(r'^[\w-]+:.+', custom_header):
                    print >> fileobj, ('header = "%s"' % custom_header)
                else:
                    munkicommon.display_warning(
                        'Skipping invalid HTTP header: %s' % custom_header)

        fileobj.close()
    except Exception, e:
        raise CurlError(-5, 'Error writing curl directive: %s' % str(e))
Ejemplo n.º 24
0
    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
        try:
            os.unlink(combined_pem)
        except (OSError, IOError):
            pass