mirror of
https://github.com/munki/munki.git
synced 2026-04-30 09:19:31 -05:00
Add identity preferences for all URLs defined in Munki's preferences, not just SoftwareRepoURL
This commit is contained in:
@@ -20,11 +20,13 @@ keychain
|
||||
Created by Greg Neagle on 2014-06-09.
|
||||
Incorporating work and ideas from Michael Lynn here:
|
||||
https://gist.github.com/pudquick/7704254
|
||||
|
||||
and here:
|
||||
https://gist.github.com/pudquick/836a19b5ff17c5b7640d#file-cert_tricks-py
|
||||
"""
|
||||
|
||||
import base64
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
import munkicommon
|
||||
@@ -65,6 +67,29 @@ def write_file(stringdata, pathname):
|
||||
return ''
|
||||
|
||||
|
||||
def pem_cert_bytes(cert_path):
|
||||
'''Read in a base64 pem file, return raw bytestring of embedded
|
||||
certificate'''
|
||||
cert_data = read_file(cert_path)
|
||||
if not (('-----BEGIN CERTIFICATE-----' in cert_data) and
|
||||
('-----END CERTIFICATE-----' in cert_data)):
|
||||
raise Exception('Certificate does not appear to be .pem file')
|
||||
core_data = cert_data.split(
|
||||
'-----BEGIN CERTIFICATE', 1)[-1].replace('\r', '\n').split(
|
||||
'-----\n', 1)[-1].split('\n-----END CERTIFICATE-----', 1)[0]
|
||||
return base64.b64decode(''.join(core_data.split('\n')))
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_munki_server_cert_data():
|
||||
'''Attempt to get information we need from Munki's preferences or
|
||||
defaults. Returns a dictionary.'''
|
||||
@@ -110,9 +135,13 @@ def get_munki_client_cert_data():
|
||||
if os.path.exists(client_cert_path):
|
||||
cert_data['client_cert_path'] = client_cert_path
|
||||
break
|
||||
|
||||
cert_data['site_url'] = (
|
||||
munkicommon.pref('SoftwareRepoURL').rstrip('/') + '/')
|
||||
site_urls = []
|
||||
for key in ['SoftwareRepoURL', 'PackageURL', 'CatalogURL',
|
||||
'ManifestURL', 'IconURL', 'ClientResourceURL']:
|
||||
url = munkicommon.pref(key)
|
||||
if url:
|
||||
site_urls.append(url.rstrip('/') + '/')
|
||||
cert_data['site_urls'] = site_urls
|
||||
return cert_data
|
||||
|
||||
|
||||
@@ -167,12 +196,12 @@ def make_client_keychain(certdata=None):
|
||||
'''Builds a client cert keychain from existing client certs'''
|
||||
|
||||
if not certdata:
|
||||
# jusr grab data from Munki's preferences/defaults
|
||||
# just grab data from Munki's preferences/defaults
|
||||
certdata = get_munki_client_cert_data()
|
||||
|
||||
client_cert_path = certdata['client_cert_path']
|
||||
client_key_path = certdata['client_key_path']
|
||||
site_url = certdata['site_url']
|
||||
site_urls = certdata['site_urls']
|
||||
if not client_cert_path:
|
||||
# no client, so nothing to do
|
||||
munkicommon.display_debug1(
|
||||
@@ -222,6 +251,8 @@ def make_client_keychain(certdata=None):
|
||||
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)
|
||||
@@ -229,122 +260,80 @@ def make_client_keychain(certdata=None):
|
||||
# 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)
|
||||
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_path, '-A', '-k', abs_keychain_path)
|
||||
'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_path, err)
|
||||
|
||||
# set up identity preference linking the identity (cert and key)
|
||||
# to the site_url
|
||||
# First we need to find the existing identity in our keychain
|
||||
try:
|
||||
output = security('find-identity', abs_keychain_path)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
except SecurityError:
|
||||
pass
|
||||
if not ' 1 identities found' in output:
|
||||
munkicommon.display_error('No identities found!')
|
||||
else:
|
||||
# We have a solitary match and can configure / verify
|
||||
# the identity preference
|
||||
id_hash = re.findall(r'\W+1\)\W+([0-9A-F]+)\W', output)[0]
|
||||
# First, check to see if we have an identity already
|
||||
create_identity = False
|
||||
'Could not import %s: %s', client_cert_file, err)
|
||||
if combined_pem:
|
||||
# we created this; we should clean it up
|
||||
try:
|
||||
output = security(
|
||||
'get-identity-preference', '-s', site_url, '-Z')
|
||||
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)
|
||||
# No error, we found an identity
|
||||
# Check if it matches the one we want
|
||||
current_hash = re.match(
|
||||
r'SHA-1 hash:\W+([A-F0-9]+)\W', output).group(1)
|
||||
if id_hash != current_hash:
|
||||
# We only care if there's a different hash being used.
|
||||
# Remove the incorrect one.
|
||||
output = security(
|
||||
'set-identity-preference', '-n', '-s', site_url)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
# Signal that we want to create a new identity preference
|
||||
create_identity = True
|
||||
# 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 finding identity-preference
|
||||
create_identity = True
|
||||
#elif id_hash not in output:
|
||||
# # Non-zero error code and hash not detected in output
|
||||
# # Signal that we want to create a new identity preference
|
||||
# create_identity = True
|
||||
if create_identity:
|
||||
# This code was moved into a common block that both routes could
|
||||
# access as it's a little complicated.
|
||||
# security will only create an identity preference in the
|
||||
# default keychain - which means a default has to be
|
||||
# defined/selected. For normal users, this is login.keychain -
|
||||
# but for root there's no login.keychain and no default keychain
|
||||
# configured. So we'll handle the case of no default keychain
|
||||
# (just set one) as well as pre-existing default keychain
|
||||
# (in which case we set it long enough to create the preference,
|
||||
# then set it back)
|
||||
munkicommon.display_debug1('Creating identity preference...')
|
||||
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
|
||||
# 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)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
except SecurityError, err:
|
||||
munkicommon.display_error(
|
||||
'Could not set default keychain to %s failed: %s'
|
||||
% (abs_keychain_path, err))
|
||||
default_keychain = None
|
||||
# Create the identity preferences
|
||||
for url in site_urls:
|
||||
try:
|
||||
output = security(
|
||||
'default-keychain', '-s', abs_keychain_path)
|
||||
'set-identity-preference',
|
||||
'-s', url, '-Z', id_hash, abs_keychain_path)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
except SecurityError, err:
|
||||
munkicommon.display_error(
|
||||
'Could not set default keychain to %s failed: %s'
|
||||
% (abs_keychain_path, err))
|
||||
default_keychain = None
|
||||
# Create the identity preference
|
||||
try:
|
||||
output = security(
|
||||
'set-identity-preference', '-s', site_url, '-Z',
|
||||
id_hash, abs_keychain_path)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
except SecurityError, err:
|
||||
munkicommon.display_error(
|
||||
'Setting identity preference failed: %s' % err)
|
||||
if default_keychain:
|
||||
# We originally had a different one, set it back
|
||||
output = security(
|
||||
'default-keychain', '-s', default_keychain)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
'Setting identity preference for %s failed: %s'
|
||||
% (url, err))
|
||||
if default_keychain:
|
||||
# We originally had a different default, set it back
|
||||
output = security(
|
||||
'default-keychain', '-s', default_keychain)
|
||||
if output:
|
||||
munkicommon.display_debug2(output)
|
||||
|
||||
# we're done, clean up.
|
||||
if added_keychain:
|
||||
remove_from_keychain_list(abs_keychain_path)
|
||||
|
||||
Reference in New Issue
Block a user