Esempio n. 1
0
def import_middleware():
    '''Check munki folder for a python file that starts with 'middleware'.
    If the file exists and has a callable 'process_request_options' attribute,
    the module is loaded under the 'middleware' name'''
    required_function_name = 'process_request_options'
    munki_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    for filename in os.listdir(munki_dir):
        if (filename.startswith('middleware')
                and os.path.splitext(filename)[1] == '.py'):
            name = os.path.splitext(filename)[0]
            filepath = os.path.join(munki_dir, filename)
            _tmp = imp.load_source(name, filepath)
            if hasattr(_tmp, required_function_name):
                if callable(getattr(_tmp, required_function_name)):
                    munkicommon.display_debug1(
                        'Loading middleware module %s' % filename)
                    globals()['middleware'] = _tmp
                    return
                else:
                    munkicommon.display_warning(
                        '%s attribute in %s is not callable.'
                        % (required_function_name, filepath))
                    munkicommon.display_warning('Ignoring %s' % filepath)
            else:
                munkicommon.display_warning(
                    '%s does not have a %s function'
                    % (filepath, required_function_name))
                munkicommon.display_warning('Ignoring %s' % filepath)
    return
Esempio n. 2
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)
Esempio n. 3
0
def import_middleware():
    '''Check munki folder for a python file that starts with 'middleware'.
    If the file exists and has a callable 'process_request_options' attribute,
    the module is loaded under the 'middleware' name'''
    required_function_name = 'process_request_options'
    munki_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    for filename in os.listdir(munki_dir):
        if (filename.startswith('middleware')
                and os.path.splitext(filename)[1] == '.py'):
            name = os.path.splitext(filename)[0]
            filepath = os.path.join(munki_dir, filename)
            _tmp = imp.load_source(name, filepath)
            if hasattr(_tmp, required_function_name):
                if callable(getattr(_tmp, required_function_name)):
                    munkicommon.display_debug1('Loading middleware module %s' %
                                               filename)
                    globals()['middleware'] = _tmp
                    return
                else:
                    munkicommon.display_warning(
                        '%s attribute in %s is not callable.' %
                        (required_function_name, filepath))
                    munkicommon.display_warning('Ignoring %s' % filepath)
            else:
                munkicommon.display_warning('%s does not have a %s function' %
                                            (filepath, required_function_name))
                munkicommon.display_warning('Ignoring %s' % filepath)
    return
Esempio n. 4
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)
Esempio n. 5
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)
Esempio n. 6
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)
Esempio n. 7
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
Esempio n. 8
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
Esempio n. 9
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
Esempio n. 10
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
Esempio n. 11
0
    if combined_pem:
        # we created this; we should clean it up
        try:
            os.unlink(combined_pem)
        except (OSError, IOError):
            pass

    id_hash = pem_cert_sha1_digest(client_cert_path)
    if not id_hash:
        munkicommon.display_error(
            'Cannot create keychain identity preference.')
    else:
        # set up identity preference(s) linking the identity (cert and key)
        # to the various urls

        munkicommon.display_debug1('Creating identity preferences...')
        try:
            output = security('default-keychain')
            if output:
                munkicommon.display_debug2(output)
            # One is defined, remember the path
            default_keychain = [
                x.strip().strip('"')
                for x in output.split('\n') if x.strip()][0]
        except SecurityError, err:
            # error raised if there is no default
            default_keychain = None
        # Temporarily assign the default keychain to ours
        try:
            output = security(
                'default-keychain', '-s', abs_keychain_path)
Esempio n. 12
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))
Esempio n. 13
0
    if combined_pem:
        # we created this; we should clean it up
        try:
            os.unlink(combined_pem)
        except (OSError, IOError):
            pass

    id_hash = pem_cert_sha1_digest(client_cert_path)
    if not id_hash:
        munkicommon.display_error(
            'Cannot create keychain identity preference.')
    else:
        # set up identity preference(s) linking the identity (cert and key)
        # to the various urls

        munkicommon.display_debug1('Creating identity preferences...')
        try:
            output = security('default-keychain')
            if output:
                munkicommon.display_debug2(output)
            # One is defined, remember the path
            default_keychain = [
                x.strip().strip('"')
                for x in output.split('\n') if x.strip()][0]
        except SecurityError, err:
            # error raised if there is no default
            default_keychain = None
        # Temporarily assign the default keychain to ours
        try:
            output = security(
                'default-keychain', '-s', abs_keychain_path)
Esempio n. 14
0
        # Gurl returned an error
        munkicommon.display_detail(
            'Download error %s: %s', connection.error.code(), 
            connection.error.localizedDescription())
        if connection.SSLerror:
            munkicommon.display_detail(
                'SSL error detail: %s', str(connection.SSLerror))
            keychain.debug_output()
        munkicommon.display_detail('Headers: %s', connection.headers)
        if os.path.exists(tempdownloadpath) and not resume:
            os.remove(tempdownloadpath)
        raise GurlError(connection.error.code(), 
                        connection.error.localizedDescription())

    if connection.response != None:
        munkicommon.display_debug1('Status: %s', connection.status)
        munkicommon.display_debug1('Headers: %s', connection.headers)
    if connection.redirection != []:
        munkicommon.display_debug1('Redirection: %s', connection.redirection)

    temp_download_exists = os.path.isfile(tempdownloadpath)
    connection.headers['http_result_code'] = str(connection.status)
    description = NSHTTPURLResponse.localizedStringForStatusCode_(
                                                            connection.status)
    connection.headers['http_result_description'] = description

    if str(connection.status).startswith('2') and temp_download_exists:
        os.rename(tempdownloadpath, destinationpath)
        return connection.headers
    elif connection.status == 304:
        # unchanged on server
Esempio n. 15
0
        # Gurl returned an error
        munkicommon.display_detail('Download error %s: %s',
                                   connection.error.code(),
                                   connection.error.localizedDescription())
        if connection.SSLerror:
            munkicommon.display_detail('SSL error detail: %s',
                                       str(connection.SSLerror))
            keychain.debug_output()
        munkicommon.display_detail('Headers: %s', connection.headers)
        if os.path.exists(tempdownloadpath) and not resume:
            os.remove(tempdownloadpath)
        raise GurlError(connection.error.code(),
                        connection.error.localizedDescription())

    if connection.response != None:
        munkicommon.display_debug1('Status: %s', connection.status)
        munkicommon.display_debug1('Headers: %s', connection.headers)
    if connection.redirection != []:
        munkicommon.display_debug1('Redirection: %s', connection.redirection)

    temp_download_exists = os.path.isfile(tempdownloadpath)
    connection.headers['http_result_code'] = str(connection.status)
    description = NSHTTPURLResponse.localizedStringForStatusCode_(
        connection.status)
    connection.headers['http_result_description'] = description

    if str(connection.status).startswith('2') and temp_download_exists:
        os.rename(tempdownloadpath, destinationpath)
        return connection.headers
    elif connection.status == 304:
        # unchanged on server
Esempio n. 16
0
                      onlyifnewer=getonlyifnewer,
                      resume=resume,
                      follow_redirects=follow_redirects)

    except CurlError, err:
        err = 'Error %s: %s' % tuple(err)
        raise CurlDownloadError(err)

    except HTTPError, err:
        err = 'HTTP result %s: %s' % tuple(err)
        raise CurlDownloadError(err)

    err = None
    if header['http_result_code'] == '304':
        # not modified, return existing file
        munkicommon.display_debug1('%s already exists and is up-to-date.'
                                        % destinationpath)
        # file is in cache and is unchanged, so we return False
        return False
    else:
        if header.get('last-modified'):
            # set the modtime of the downloaded file to the modtime of the
            # file on the server
            modtimestr = header['last-modified']
            modtimetuple = time.strptime(modtimestr,
                                         '%a, %d %b %Y %H:%M:%S %Z')
            modtimeint = calendar.timegm(modtimetuple)
            os.utime(destinationpath, (time.time(), modtimeint))
        if header.get('etag'):
            # store etag in extended attribute for future use
            xattr.setxattr(destinationpath, XATTR_ETAG, header['etag'])
        return True
Esempio n. 17
0
            os.environ['HOME'] = original_home
        return

    # Ensure the keychain is in the search path and unlocked
    added_keychain = add_to_keychain_list(abs_keychain_path)
    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:
Esempio n. 18
0
                      onlyifnewer=getonlyifnewer,
                      resume=resume,
                      follow_redirects=follow_redirects)

    except CurlError, err:
        err = 'Error %s: %s' % tuple(err)
        raise CurlDownloadError(err)

    except HTTPError, err:
        err = 'HTTP result %s: %s' % tuple(err)
        raise CurlDownloadError(err)

    err = None
    if header['http_result_code'] == '304':
        # not modified, return existing file
        munkicommon.display_debug1('%s already exists and is up-to-date.'
                                        % destinationpath)
        # file is in cache and is unchanged, so we return False
        return False
    else:
        if header.get('last-modified'):
            # set the modtime of the downloaded file to the modtime of the
            # file on the server
            modtimestr = header['last-modified']
            modtimetuple = time.strptime(modtimestr,
                                         '%a, %d %b %Y %H:%M:%S %Z')
            modtimeint = calendar.timegm(modtimetuple)
            os.utime(destinationpath, (time.time(), modtimeint))
        if header.get('etag'):
            # store etag in extended attribute for future use
            xattr.setxattr(destinationpath, XATTR_ETAG, header['etag'])
        return True
Esempio n. 19
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)
Esempio n. 20
0
            os.environ['HOME'] = original_home
        return

    # Ensure the keychain is in the search path and unlocked
    added_keychain = add_to_keychain_list(abs_keychain_path)
    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:
Esempio n. 21
0
    if connection.error != None:
        # Gurl returned an error
        munkicommon.display_detail(
            "Download error %s: %s", connection.error.code(), connection.error.localizedDescription()
        )
        if connection.SSLerror:
            munkicommon.display_detail("SSL error detail: %s", str(connection.SSLerror))
            keychain.debug_output()
        munkicommon.display_detail("Headers: %s", connection.headers)
        if os.path.exists(tempdownloadpath) and not resume:
            os.remove(tempdownloadpath)
        raise GurlError(connection.error.code(), connection.error.localizedDescription())

    if connection.response != None:
        munkicommon.display_debug1("Status: %s", connection.status)
        munkicommon.display_debug1("Headers: %s", connection.headers)
    if connection.redirection != []:
        munkicommon.display_debug1("Redirection: %s", connection.redirection)

    temp_download_exists = os.path.isfile(tempdownloadpath)
    connection.headers["http_result_code"] = str(connection.status)
    description = NSHTTPURLResponse.localizedStringForStatusCode_(connection.status)
    connection.headers["http_result_description"] = description

    if str(connection.status).startswith("2") and temp_download_exists:
        os.rename(tempdownloadpath, destinationpath)
        return connection.headers
    elif connection.status == 304:
        # unchanged on server
        munkicommon.display_debug1("Item is unchanged on the server.")