Skip to content

Instantly share code, notes, and snippets.

@glarizza
Created August 14, 2012 23:59
Show Gist options
  • Save glarizza/3354042 to your computer and use it in GitHub Desktop.
Save glarizza/3354042 to your computer and use it in GitHub Desktop.
Setting the password with dsimport
require 'puppet'
require 'facter/util/plist'
require 'stringio'
require 'base64'
def convert_xml_to_binary(plist_data)
# This method will accept a hash that has been returned from Plist::parse_xml
# and convert it to a binary plist (string value).
Puppet.debug('Converting XML plist to binary')
Puppet.debug('Executing: \'plutil -convert binary1 -o - -\'')
IO.popen('plutil -convert binary1 -o - -', mode='r+') do |io|
io.write Plist::Emit.dump(plist_data)
io.close_write
@converted_plist = io.read
end
@converted_plist
end
def convert_binary_to_xml(plist_data)
# This method will accept a binary plist (as a string) and convert it to a
# hash via Plist::parse_xml.
Puppet.debug('Converting binary plist to XML')
Puppet.debug('Executing: \'plutil -convert xml1 -o - -\'')
IO.popen('plutil -convert xml1 -o - -', mode='r+') do |io|
io.write plist_data
io.close_write
@converted_plist = io.read
end
Puppet.debug('Converting XML values to a hash.')
@plist_hash = Plist::parse_xml(@converted_plist)
@plist_hash
end
## Setting the Password with dsimport
@resource = 'jeff'
value = "7ea7d592131f57b2c8f8bdbcec8d9df12128a386393a4f00c7619bac2622a44d451419d11da512d5915ab98e39718ac94083fe2efd6bf710a54d477f8ff735b12587192d"
binary_password_value = Base64.decode64([[value].pack("H*")].pack("m").strip)
archive_hash = Hash.new
archive_hash = { 'SALTED-SHA512' => StringIO.new }
archive_hash['SALTED-SHA512'].string = binary_password_value
binary_archive_hash = convert_xml_to_binary(archive_hash)
tmpdir = Dir.mktmpdir
password_file = File.join(tmpdir, 'binary_password')
cached_source = File.join(tmpdir, 'dsimportfile')
#junk = Marshal.dump(binary_archive_hash)
File.open(password_file, 'w') { |file| file.write(binary_archive_hash) }
File.open(cached_source, 'w') { |file| file.write("0x0A 0x5C 0x3A 0x2C dsRecTypeStandard:Users 2 dsAttrTypeStandard:RecordName externalbinary:dsAttrTypeStandard:ShadowHashData \n#{@resource}:#{password_file}") }
`/usr/bin/dsimport #{cached_source} /Local/Default O`
## Get the password from disk using dscl
shadow_hash_data = Plist.parse_xml(`/usr/bin/dscl -plist . read /Users/jeff ShadowHashData`)
binary_plist = Array(shadow_hash_data['dsAttrTypeNative:ShadowHashData'][0].delete(' ')).pack('H*')
embedded_binary_plist = convert_binary_to_xml(binary_plist)
key_from_disk = embedded_binary_plist['SALTED-SHA512'].string.unpack("H*")[0]
## Back out the data we calculated in the first block
backed_out_plist = convert_binary_to_xml(binary_archive_hash)
key_from_backup = backed_out_plist['SALTED-SHA512'].string.unpack("H*")[0]
## They don't match
puts 'THEY MATCH' if key_from_backup == key_from_disk
## Read the data we JUST wrote to disk and compare the keys:
## Read from disk
data_from_disk = File.open(password_file, "rb").read
converted_plist = convert_binary_to_xml(data_from_disk)
key_from_file = converted_plist['SALTED-SHA512'].string.unpack("H*")[0]
## Yes, this is a match
puts "Sure are!" if key_from_file == value
## Okay, so let's try just modifying the user's plist
users_plist = Plist::parse_xml(`plutil -convert xml1 -o /dev/stdout /var/db/dslocal/nodes/Default/users/jeff.plist`)
password_hash_plist = users_plist['ShadowHashData'][0].string
converted_hash_plist = convert_binary_to_xml(password_hash_plist)
#converted_hash_plist['SALTED-SHA512'].string = value.unpack('a2'*(value.size/2)).collect { |i| i.hex.chr }.join
converted_hash_plist['SALTED-SHA512'].string = Base64.decode64([[value].pack("H*")].pack("m").strip)
changed_plist = convert_xml_to_binary(converted_hash_plist)
users_plist['ShadowHashData'][0].string = changed_plist
Plist::Emit.save_plist(users_plist, '/var/db/dslocal/nodes/Default/users/jeff.plist')
`plutil -convert binary1 /var/db/dslocal/nodes/Default/users/jeff.plist`
## Great, now read directly from the plist
get_plist = Plist::parse_xml(`plutil -convert xml1 -o /dev/stdout /var/db/dslocal/nodes/Default/users/jeff.plist`)
get_password_hash_plist = get_plist['ShadowHashData'][0].string
get_converted_hash_plist = convert_binary_to_xml(get_password_hash_plist)
get_password_hash = get_converted_hash_plist['SALTED-SHA512'].string.unpack("H*")[0]
## Do they match? Yes they do.
puts "THEY MATCH" if value == get_password_hash
## Now, let's read from DSCL again...
shadow_hash_data = Plist.parse_xml(`/usr/bin/dscl -plist . read /Users/jeff ShadowHashData`)
binary_plist = Array(shadow_hash_data['dsAttrTypeNative:ShadowHashData'][0].delete(' ')).pack('H*')
embedded_binary_plist = convert_binary_to_xml(binary_plist)
key_from_disk = embedded_binary_plist['SALTED-SHA512'].string.unpack("H*")[0]
## Do they match? Yes they do.
puts "THEY MATCH" if value == key_from_disk
## In summary, we're setting incorrectly with dsimport
## Reading from dscl the correct value:
#62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 7ea7d592 131f57b2 c8f8bdbc ec8d9df1 2128a386 393a4f00 c7619bac 2622a44d 451419d1 #1da512d5 915ab98e 39718ac9 4083fe2e fd6bf710 a54d477f 8ff735b1 2587192d 080b1900 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000060
## Reading from dscl with the INCORRECT value:
#62706c69 73743030 d101025d 53414c54 45442d53 48413531 324f1044 4ebb58ad e6e87d16 2a410d01 685acbe0 2a8ba79a 4791327b ca153a67 86578057 6df98883 #37b33eca ede269f5 77c81d6b e96456df cf9f34d6 a6f1d919 03f46052 0e878346 080b1900 00000000 00010100 00000000 00000300 00000000 00000000 00000000 000060
delete this gist
Comments are parsed with GitHub Flavored Markdown
Write
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment