How to import passwords to Heimdal?

Steve Gaarder gaarder1 at math.cornell.edu
Thu Dec 8 09:07:42 MST 2011


I have created a procedure and a couple Python scripts to synchronize a 
password from an MIT KDC to a Samba4 domain controller.  The procedure is 
below, the scripts are attached, and everything is also here:

http://www.math.cornell.edu/~gaarder/mit-samba-sync.html

Note that this assumes that the user already exists in Samba, and that the CN 
of the user is the same as the username. (If you create the user with 
samba-tool and specify a surname, you should use the --use-username-as-cn 
option.)

Step 1 - export the user from MIT:

On the MIT KDC, use kdb5_util to dump the user's key data. Use the -b7 dump 
format option. E.g.:

kdb5_util dump -b7 USERNAME.mit USERNAME at REALM.WHATSAMATTAU.EDU

Step 2 - transfer the dump file to the Samba domain controller via your 
preferred method.

Step 3 - remove unneeded keys

A Kerberos principal's key may be encoded in several different ways. The only 
one Samba needs is arcfour-hmac-md5 (type 23), and the conversion code may 
choke on other types. The Python script filterdump.py will remove all but the 
first type 23 key; use it thus:

python filterdump.py <USERNAME.mit >USERNAME.mitdump

Step 4 - decrypt and convert the dump to Heimdal format

The MIT tools do not provide a way to get an unencrypted key, so we have to 
convert to Heimdal format to decrypt it. This step and the next one can be done 
with one script - see below.

Step 5 - extract the key, convert it to base64, and put it in the Samba 
database

Step 4 and this step can be done with the script syncdump.py. This script 
requires the Heimdal tools hprop and hpropd and copy of the key that was used 
to encrypt the dump. This key is the same as the MIT master key (unless you 
used the -mkey_convert option when you made the dump) and needs to be in keytab 
format (older MIT "stash" format will not work). Heimdal's ktutil can be used 
to create the key; make sure that the enctype is the same. Run this script (as 
root) thus:

python syncdump.py USERNAME.mitdump

That's it!

-- 
Steve Gaarder
System Administrator, Dept of Mathematics
Cornell University, Ithaca, NY, USA
gaarder at math.cornell.edu
-------------- next part --------------
# This program takes an MIT dump and removes all keys except the first
# type 23 (arcfour) key that it finds.  This is done so that it can be
# passed to hprop without the latter choking on key types it doesn't support.

""" Copyright (C) 2011 Steven Gaarder and Cornell University.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    For a copy of the GNU General Public License,  see 
    <http://www.gnu.org/licenses/>."""


import sys, string

for line in sys.stdin.readlines():
    foundkey = False
    f = string.split(line,"\t")
    if f[0] != "princ":
        sys.stdout.write(line)
        continue
    principal_length = f[2]
    num_tl = f[3]
    for j in range(0,4):
        sys.stdout.write(f[j] + "\t")
    num_keys = f[4]
    sys.stdout.write("1\t") # we will only output one key
    num_extra = f[5]
    principal = f[6]
    for j in range(5,15):
        sys.stdout.write(f[j] +"\t")
    # tl_data starts at 15
    i = 15
    for t in range(int(num_tl)):
        sys.stdout.write(f[i] + "\t" + f[i+1] + "\t" +f[i+2] + "\t")
        i = i + 3
    for k in range(int(num_keys)):
        keystr = ""
        type23 = False
        key_data_version = f[i]
        i = i + 1
        kvno = f[i]
        i = i + 1
        keystr = key_data_version + "\t" + kvno
        for v in range(int(key_data_version)):
           if f[i] == "23":
               type23 = True
           keystr = keystr + "\t" + f[i] + "\t" + f[i+1] + "\t" + f[i+2]
           i = i + 3
        if type23 and not foundkey:
            sys.stdout.write(keystr)
            foundkey = True
    #print the rest
    for j in range(i, len(f)):
        sys.stdout.write("\t" + f[j])

    
        

-------------- next part --------------
# This program extracts a type 23 key from an MIT dump and installs the
# password in a Samba 4 LDB database.  The dump file name should be
# given on the command line.
# A copy of the key (in keytab format) used to encrypt the MIT dump must be 
# in /usr/local/private/x-key.  This key must have the same enctype as
# the one used to encrypt.
# We assume that Samba 4 is intalled in /usr/local/samba and that Heimdal
# is in /usr/heimdal.

""" Copyright (C) 2011 Steven Gaarder and Cornell University.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    For a copy of the GNU General Public License,  see 
    <http://www.gnu.org/licenses/>."""

import sys
import subprocess
import re
import base64
import StringIO

myrealm = "REALM.WHATSAMATTAU.EDU"
mybase = "dc=realm,dc=whatsamattau,dc=edu"
infile = sys.argv[1]

# Use hprop and hpropd to get a Heimdal-format key

pout = subprocess.Popen(["/usr/heimdal/libexec/hprop --database=" + infile + " --source=mit-dump --decrypt --master-key=/usr/local/private/x-key --stdout|/usr/heimdal/libexec/hpropd -n --print"], shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

outlist = pout.stdout.readlines()

pat = re.compile("(\w+)@" + myrealm + ".*\d+:\d*:23:(\w+):")

mat = pat.search(outlist[0])

username =  mat.group(1)
b64key = base64.b64encode( mat.group(2).decode("hex"))

# check to see that the user is in the system


ldbfilename = "/usr/local/samba/private/sam.ldb.d/" + mybase.upper() + ".ldb"

pout = subprocess.Popen(["/usr/local/samba/bin/ldbsearch", "-H", ldbfilename, "-b", "CN=Users," + mybase, "CN=" + username, "cn" ], shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

outlist = pout.stdout.readlines()

respat = re.compile("returned\s(\d+)\srecords")

found = 0
for line in outlist:
    resmat = respat.search(line)
    if resmat != None:
        found =  resmat.group(1)

if int(found) <= 0:
    print "User not found in database"
    sys.exit(1)

# run it

pinn = subprocess.Popen(["/usr/local/samba/bin/ldbmodify", "-H", ldbfilename, "-b", "CN=Users," + mybase,  "-i", "--nosync", "--controls=relax:0" ], shell=False, stdin=subprocess.PIPE)

ldifstr="dn: CN=" + username + ",CN=Users," + mybase +"\n" + "changetype: modify\n" + "replace: unicodePwd\n" + "unicodePwd:: " + b64key +"\n"

pinn.communicate(input=ldifstr)






More information about the samba-technical mailing list