PATCH: Improved Keytab Functionality - UPDATED

Rakesh Patel rapatel at optonline.net
Wed Apr 14 13:07:02 GMT 2004


Attached is a revised version of the patch that addresses some items -

1) Returns key version 1 if no version is found (for Windows 2000 or 
Windows 2003 without key version support).
2) Fix "net ads changetrustpw" to do an "update" of the keytab so that 
custom service keys such as "ldap", are retained and also utilize the 
correct key version number.

This version was also enhanced by Dan Perry to address some issues we 
discussed.


Rakesh Patel.



Rakesh Patel wrote:

> Just to be clear - Geunther Deschner of SuSE Linux submitted the patch 
> originally. :-)
>
> Dan, I'll test your version of the patch - I definitely believe it is 
> the direction we needed
> to head with keytab support based upon prior discussions on the list.  
> Hopefully the Samba
> team agrees and we can move this forward.
>
> Thanks!
> Rakesh Patel.
>
>
> Dan Perry wrote:
>
>> Hi all,
>>
>> Attached is patch for samba-3.03pre2 that enables use of the system 
>> keytab
>> file, and also adds some keytab management functions to the net utility.
>> This patch is heavily based on the one submitted a few days ago (and 
>> also a
>> few months ago) by Rakesh Patel.
>>
>> The main differences between the patches that Rakesh has submitted 
>> and the
>> one attached are as follows are this patch uses samba to 
>> automatically create
>> new keytab principals for any given principal, not just HOST and 
>> CIFS.  An
>> example of this 'keytab fetching' is shown below.   Also, the get_kvno
>> function in the original patch did not always work correctly, and was 
>> a very
>> dense piece of code.   I replaced this with a simple LDAP query for the
>> msDS-KeyVersionNumber attribute in Active Directory (present on every
>> computer account) that contains the current KVNO.  Rather then trying to
>> determine the KVNO using Kerberos API's, I simply query out the 
>> current KVNO
>> using LDAP.   Finally, I removed the 'keytab update' parameter, and made
>> updating the system keytab the default.   It seemed odd to have the 
>> option to
>> not update the system keytab.
>>
>> This extra functionality is extremely useful in my efforts to get 
>> Linux boxes
>> to participate in Active Directory, and hopefully others will find it 
>> useful
>> as well.  In case it helps, this patch is currently being used on Redhat
>> enterprise Linux 3 systems, running MIT Kerberos 1.3.2.   The domain 
>> used is
>> running Windows 2003.   I don't think that any of the changes in this 
>> patch
>> are depend on the versions of Kerberos or Active Directory, but I 
>> haven't
>> really tested outside of the environment at work. 
>> Let me know any comments you have on this patch.   
>> Thanks,
>> -Dan
>>
>>
>> ----------- Extended Keytab Fetching Example -----------------
>>
>> Where I work, we have a large Active Directory Domain, and also an 
>> NIS realm
>> to service the Linux systems.   It would be nice to consolidate the 
>> two sets
>> of accounts into one - so we are looking at moving to Active 
>> Directory only.
>> The biggest hurdle we have now in doing this is how to distribute 
>> keytabs;
>> Microsoft's ktpass.exe is goofy at best.   Rakesh's patch enabled me 
>> to use
>> samba's net utility to fetch HOST and CIFS principals and install 
>> them in the
>> system keytab file, so that other applications, such as openssh, 
>> could use
>> the principals.
>>
>> My patch allows any principal to be fetched.   In the case of our LDAP
>> servers, I can do the following:
>>
>> $ klist -k
>> Keytab Name: FILE:/etc/krb5.keytab
>> KVNO Principal
>> ---- ---------------------------------------------------------
>> $ kinit admin
>> Password for admin at REALM:
>> $ net ads join
>> JOINED 'LDAPSERVER' to realm 'REALM'
>> $ klist -k
>> KVNO Principal
>> ---- ---------------------------------------------------------
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>> $ net ads keytab create ldap
>> $ klist -k
>> KVNO Principal
>> ---- ---------------------------------------------------------
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 host/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 cifs/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>   2 ldap/ldapserver.realm at REALM
>>
>> What I've doing using the keytab fetching feature is to first join the
>> machine to the AD domain (standard 'net ads join'), which creates the 
>> HOST
>> and CIFS service principals in the system keytab.   Then, I want to 
>> add an
>> LDAP service principal, which I do using 'net ads keytab create ldap'.
>>
>>  
>>
>

-------------- next part --------------
--- source/param/loadparm.c.ORIG	2004-04-04 03:37:17.000000000 -0400
+++ source/param/loadparm.c	2004-04-13 11:06:04.891546000 -0400
@@ -119,6 +119,7 @@
 	char *szPasswdChat;
 	char *szLogFile;
 	char *szConfigFile;
+	char *szKeytabFile;
 	char *szSMBPasswdFile;
 	char *szPrivateDir;
 	char **szPassdbBackend;
@@ -1087,6 +1088,7 @@
 	{"delete share command", P_STRING, P_GLOBAL, &Globals.szDeleteShareCommand, NULL, NULL, FLAG_ADVANCED}, 
 
 	{"config file", P_STRING, P_GLOBAL, &Globals.szConfigFile, NULL, NULL, FLAG_HIDE}, 
+	{"keytab file", P_STRING, P_GLOBAL, &Globals.szKeytabFile, NULL, NULL, FLAG_HIDE},
 	{"preload", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED}, 
 	{"auto services", P_STRING, P_GLOBAL, &Globals.szAutoServices, NULL, NULL, FLAG_ADVANCED}, 
 	{"lock directory", P_STRING, P_GLOBAL, &Globals.szLockDir, NULL, NULL, FLAG_ADVANCED}, 
@@ -1615,6 +1617,7 @@
 FN_GLOBAL_STRING(lp_display_charset, &Globals.display_charset)
 FN_GLOBAL_STRING(lp_logfile, &Globals.szLogFile)
 FN_GLOBAL_STRING(lp_configfile, &Globals.szConfigFile)
+FN_GLOBAL_STRING(lp_keytab_file, &Globals.szKeytabFile)
 FN_GLOBAL_STRING(lp_smb_passwd_file, &Globals.szSMBPasswdFile)
 FN_GLOBAL_STRING(lp_private_dir, &Globals.szPrivateDir)
 FN_GLOBAL_STRING(lp_serverstring, &Globals.szServerString)
--- source/include/ads.h.ORIG	2004-04-04 03:37:24.000000000 -0400
+++ source/include/ads.h	2004-04-13 11:06:04.811574000 -0400
@@ -224,3 +224,11 @@
 #ifndef HAVE_AP_OPTS_USE_SUBKEY
 #define AP_OPTS_USE_SUBKEY 0
 #endif
+
+/* handle different filebased keytab-types
+ * MIT uses "WRFILE:", heimdal uses "FILE:" */
+#ifdef HAVE_WRFILE_KEYTAB
+#define KRB5_KT_FILE_PREFIX "WRFILE"
+#else
+#define KRB5_KT_FILE_PREFIX "FILE"
+#endif
--- source/include/config.h.in.ORIG	2004-04-05 10:57:36.000000000 -0400
+++ source/include/config.h.in	2004-04-13 11:06:04.821560000 -0400
@@ -494,6 +494,9 @@
 /* Whether krb5_keytab_entry has keyblock member */
 #undef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
 
+/* Define to 1 if you have the `krb5_kt_compare' function. */
+#undef HAVE_KRB5_KT_COMPARE
+
 /* Define to 1 if you have the `krb5_locate_kdc' function. */
 #undef HAVE_KRB5_LOCATE_KDC
 
@@ -530,6 +533,9 @@
 /* Define to 1 if you have the `krb5_use_enctype' function. */
 #undef HAVE_KRB5_USE_ENCTYPE
 
+/* Whether the KV5M_KEYTAB option is available */
+#undef HAVE_KV5M_KEYTAB
+
 /* Define to 1 if you have the <langinfo.h> header file. */
 #undef HAVE_LANGINFO_H
 
@@ -1325,6 +1331,9 @@
 /* Whether we have a working cracklib */
 #undef HAVE_WORKING_CRACKLIB
 
+/* Whether the WRFILE:-keytab is supported */
+#undef HAVE_WRFILE_KEYTAB
+
 /* Whether xfs quota support is available */
 #undef HAVE_XFS_QUOTAS
 
--- source/libads/kerberos_keytab.c.ORIG	2004-04-13 11:43:19.769540000 -0400
+++ source/libads/kerberos_keytab.c	2004-04-13 11:42:58.619452000 -0400
@@ -0,0 +1,100 @@
+/* 
+   Unix SMB/CIFS implementation.
+   kerberos keytab utility library
+   Copyright (C) Andrew Tridgell 2001
+   Copyright (C) Remus Koos 2001
+   Copyright (C) Luke Howard 2003   
+   Copyright (C) Guenther Deschner 2003
+   Copyright (C) Jim McDonough (jmcd at us.ibm.com) 2003
+   
+   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 2 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.
+   
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+#ifdef HAVE_KRB5
+
+static void free_keytab(krb5_context context, krb5_keytab keytab)
+{
+        int ret = 0;
+        if (keytab) {        
+                ret = krb5_kt_close(context, keytab);
+	}
+        if (ret) {
+                DEBUG(3, ("krb5_kt_close failed (%s)\n",      
+                          error_message(ret)));
+        }
+}
+
+krb5_error_code create_keytab(	krb5_context context,
+				krb5_principal host_princ,
+				char *host_princ_s,
+				krb5_data password,
+				krb5_enctype *enctypes,
+				krb5_keytab *keytab,
+				char *keytab_name,
+				krb5_kvno kvno)
+{
+	krb5_keytab_entry entry;
+	krb5_error_code ret;
+	krb5_keyblock *key;
+	int i;
+
+	DEBUG(1,("creating keytab: %s for %s, kvno: %d\n", keytab_name, host_princ_s, kvno));	
+	ret = krb5_kt_resolve(context, keytab_name, keytab);
+	if (ret) { 
+		return ret;
+	}
+	if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
+		return ENOMEM;
+	}
+
+	/* add keytab entries for all encryption types */
+	for ( i=0; enctypes[i]; i++ ) {
+		
+		if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
+			continue;
+		}
+
+		entry.principal = host_princ;
+		entry.vno       = kvno;
+
+#if !defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) && !defined(HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK)
+#error krb5_keytab_entry has no key or keyblock member
+#endif
+
+#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEY 	/* MIT */
+		entry.key = *key; 
+#endif
+
+#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK 	/* Heimdal */
+		entry.keyblock = *key;
+#endif
+		DEBUG(10,("adding keytab-entry for (%s) with encryption type (%d) and version (%d)\n",
+                                host_princ_s, enctypes[i], entry.vno));
+                ret = krb5_kt_add_entry(context, *keytab, &entry);
+                if (ret) {
+                        DEBUG(1,("adding entry to keytab failed (%s)\n",
+                                 error_message(ret)));
+                        free_keytab(context, *keytab);
+                        return ret;
+                }
+	}
+	krb5_free_keyblock(context, key);
+
+	return 0;
+}
+
+#endif /* HAVE_KRB5 */
--- source/libads/ldap.c.ORIG	2004-04-04 03:37:37.000000000 -0400
+++ source/libads/ldap.c	2004-04-13 11:29:40.296643000 -0400
@@ -226,11 +226,16 @@
 	ldap_set_option(ads->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
 
 	if (!ads->auth.user_name) {
+		struct hostent *hp;
 		/* by default use the machine account */
 		fstring myname;
 		fstrcpy(myname, global_myname());
+		hp = gethostbyname(myname);
+		if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+		  	fstrcpy(myname,hp->h_name);
+		}
 		strlower_m(myname);
-		asprintf(&ads->auth.user_name, "HOST/%s", myname);
+		asprintf(&ads->auth.user_name, "host/%s", myname);
 	}
 
 	if (!ads->auth.realm) {
@@ -843,7 +848,7 @@
 
 /**
  * Add an array of string values to a mod list
- * @param ctx An initialized TALLOC_CTX
+vv * @param ctx An initialized TALLOC_CTX
  * @param mods An initialized ADS_MODLIST
  * @param name The attribute name to add
  * @param vals The array of string values to add - NULL means DELETE
@@ -979,66 +984,175 @@
 	return ads_build_path(org_unit, "\\/", "ou=", 1);
 }
 
+/* Adds an item to an attribute array */
+ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
+                           const char *name, const char **vals)
+{
+        return ads_modlist_add(ctx, mods, LDAP_MOD_ADD,
+                               name, (const void **) vals);
+}
+
+/* Determines the computer account's current KVNO
+ * via an LDAP lookup */
+uint32 ads_get_kvno(ADS_STRUCT *ads, const char *hostname)
+{
+        ADS_STATUS ret;
+        LDAPMessage *res;
+        uint32 kvno = -1;       /* -1 for a kvno indicates failure */
+        char *filter;
+        const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
+                                                                                                                                                             
+        DEBUG(4,("Searching for host %s\n", hostname));
+        if (asprintf(&filter, "(samAccountName=%s$)", hostname) == -1) {
+                return kvno;
+        }
+        ret = ads_search(ads, (void**) &res, filter, attrs);
+        free(filter);
+        if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
+                DEBUG(1,("Computer Account For %s not found.\n", hostname));
+		kvno = 1;
+                return kvno;
+        }
+        DEBUG(1,("Using: %s\n", ads_get_dn(ads, res)));
+                                                                                                                                                             
+        if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
+                DEBUG(1,("Error Determining KVNO!\n"));
+		kvno = 1;
+                return kvno;
+        }
+        DEBUG(1,("Looked Up KVNO of: %d\n", kvno));
+                                                                                                                                                             
+        return kvno;
+}
+
+/* This adds a service principal name to an existing
+ * copmputer account (found by hostname) in AD.  */
+int ads_add_spn(ADS_STRUCT *ads, const char *hostname, const char *spn)
+{
+        ADS_STATUS ret;
+        TALLOC_CTX *ctx;
+        LDAPMessage *res;
+        struct hostent *hp;
+        char *host_spn, *host_upn, *psp1, *psp2;
+        ADS_MODLIST mods;
+        const char *fqdn;
+        const char *servicePrincipalName[3] = {NULL, NULL, NULL};
+                                                                                                                                                             
+        ret = ads_find_machine_acct(ads, (void **)&res, hostname);
+        if (ADS_ERR_OK(ret) && ads_count_replies(ads, res) == 1) {
+                DEBUG(1,("Host account for %s found\n", hostname));
+                                                                                                                                                             
+                if (!(ctx = talloc_init("machine_account"))) {
+                        return -1;
+                }
+                                                                                                                                                             
+                hp = gethostbyname(hostname);
+                if (hp->h_name && strlen(hp->h_name) > 0) {
+                        fqdn = strdup(hp->h_name);
+                } else {
+                        fqdn = hostname;
+                }
+                if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", fqdn))) {
+                        goto done;
+                }
+                if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
+                        goto done;
+                }
+
+                /* Add the extra principal */
+                psp1 = talloc_asprintf(ctx, "%s/%s", spn, hostname);
+                strupper_m(&psp1[0]);
+                strlower_m(&psp1[strlen(spn)]);
+                DEBUG(1,("INFO: Adding %s to host %s\n", psp1, hostname));
+                servicePrincipalName[0] = psp1;
+                psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, hostname, ads->config.realm);
+                strupper_m(&psp2[0]);
+                strlower_m(&psp2[strlen(spn)]);
+                DEBUG(1,("INFO: Adding %s to host %s\n", psp2, hostname));
+                servicePrincipalName[1] = psp2;
+                                                                                                                                                             
+                if (!(mods = ads_init_mods(ctx))) {
+                        goto done;
+                }
+                ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
+                ret = ads_gen_mod(ads, ads_get_dn(ads, res), mods);
+                if (!ADS_ERR_OK(ret)) {
+                        DEBUG(1,("Error: Updating Service Principals in LDAP\n"));
+                } else {
+                        talloc_destroy(ctx);
+                        return 0;
+                }
+done:
+                talloc_destroy(ctx);
+                return -1;
+        } else {
+                DEBUG(1,("WARNING: Host Account for %s not found... skipping operation.\n",
+                                                        hostname));
+                DEBUG(1,("WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
+                                                        spn, hostname, ads->config.realm));
+                /* Report the failure so nothing continues */
+                return -1;
+        }
+}
 
 
 /*
   add a machine account to the ADS server
 */
 static ADS_STATUS ads_add_machine_acct(ADS_STRUCT *ads, const char *hostname, 
-				       uint32 account_type,
-				       const char *org_unit)
+				       uint32 account_type, const char *org_unit)
 {
 	ADS_STATUS ret, status;
-	char *host_spn, *host_upn, *new_dn, *samAccountName, *controlstr;
-	char *ou_str;
+	char *host_spn, *host_upn, *comp_dn, *samAccountName, *controlstr;
 	TALLOC_CTX *ctx;
 	ADS_MODLIST mods;
 	const char *objectClass[] = {"top", "person", "organizationalPerson",
 				     "user", "computer", NULL};
-	const char *servicePrincipalName[5] = {NULL, NULL, NULL, NULL, NULL};
-	char *psp, *psp2;
+	const char *servicePrincipalName[3] = {NULL, NULL, NULL};
+	char *psp;
 	unsigned acct_control;
-	unsigned exists=0;
+	unsigned exists = 0;
 	LDAPMessage *res;
+	struct hostent *hp;
+	char *fqdn;
 
-	status = ads_find_machine_acct(ads, (void **)&res, hostname);
-	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
-		DEBUG(0, ("Host account for %s already exists - modifying old account\n", hostname));
-		exists=1;
-	}
 
-	if (!(ctx = talloc_init("machine_account")))
+	if (!(ctx = talloc_init("machine_account"))) {
 		return ADS_ERROR(LDAP_NO_MEMORY);
-
+	}
 	ret = ADS_ERROR(LDAP_NO_MEMORY);
 
-	if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
-		goto done;
-	if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
+	status = ads_find_machine_acct(ads, (void **)&res, hostname);
+	if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
+		DEBUG(1, ("Host account for %s already exists - modifying old account\n", hostname));
+		exists = 1;
+		comp_dn = ads_get_dn(ads, res);
+	} else {
+		comp_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, 
+                     		ads_ou_string(NULL), ads->config.bind_path);
+	}
+	DEBUG(4,("INFO: Using %s for Computer Account.\n", comp_dn));
+
+	hp = gethostbyname(hostname);
+        if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+                fqdn = strdup(hp->h_name);
+        } else {
+                fqdn = strdup(hostname);
+        }	
+	if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", fqdn))) {
 		goto done;
-	ou_str = ads_ou_string(org_unit);
-	if (!ou_str) {
-		DEBUG(1, ("ads_ou_string returned NULL (malloc failure?)\n"));
+   	}
+	if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm))) {
 		goto done;
 	}
-	new_dn = talloc_asprintf(ctx, "cn=%s,%s,%s", hostname, ou_str, 
-				 ads->config.bind_path);
+
+	/* Always add a host spn, for completeness */
 	servicePrincipalName[0] = talloc_asprintf(ctx, "HOST/%s", hostname);
 	psp = talloc_asprintf(ctx, "HOST/%s.%s", 
 			      hostname, 
 			      ads->config.realm);
 	strlower_m(&psp[5]);
 	servicePrincipalName[1] = psp;
-	servicePrincipalName[2] = talloc_asprintf(ctx, "CIFS/%s", hostname);
-	psp2 = talloc_asprintf(ctx, "CIFS/%s.%s", 
-			       hostname, 
-			       ads->config.realm);
-	strlower_m(&psp2[5]);
-	servicePrincipalName[3] = psp2;
-
-	free(ou_str);
-	if (!new_dn)
-		goto done;
 
 	if (!(samAccountName = talloc_asprintf(ctx, "%s$", hostname)))
 		goto done;
@@ -1060,16 +1174,16 @@
 		ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
 		ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
 	}
-	ads_mod_str(ctx, &mods, "dNSHostName", hostname);
+	ads_mod_str(ctx, &mods, "dNSHostName", fqdn);
 	ads_mod_str(ctx, &mods, "userPrincipalName", host_upn);
 	ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
 	ads_mod_str(ctx, &mods, "operatingSystem", "Samba");
 	ads_mod_str(ctx, &mods, "operatingSystemVersion", SAMBA_VERSION_STRING);
 
 	if (!exists) 
-		ret = ads_gen_add(ads, new_dn, mods);
+		ret = ads_gen_add(ads, comp_dn, mods);
 	else
-		ret = ads_gen_mod(ads, new_dn, mods);
+		ret = ads_gen_mod(ads, comp_dn, mods);
 
 	if (!ADS_ERR_OK(ret))
 		goto done;
@@ -1079,7 +1193,7 @@
 	 * don't have enough rights to do it.
 	 */
 	if (!exists) {
-		status = ads_set_machine_sd(ads, hostname, new_dn);
+		status = ads_set_machine_sd(ads, hostname, comp_dn);
 	
 		if (!ADS_ERR_OK(status)) {
 			DEBUG(0, ("Warning: ads_set_machine_sd: %s\n",
@@ -1087,6 +1201,7 @@
 		}
 	}
 done:
+	free(fqdn);
 	talloc_destroy(ctx);
 	return ret;
 }
--- source/libads/kerberos_verify.c.ORIG	2004-04-04 03:37:37.000000000 -0400
+++ source/libads/kerberos_verify.c	2004-04-13 11:06:04.831548000 -0400
@@ -64,6 +64,8 @@
 	ZERO_STRUCTP(auth_data);
 	ZERO_STRUCTP(ap_rep);
 
+	struct hostent *hp;
+
 	if (!secrets_init()) {
 		DEBUG(1,("ads_verify_ticket: secrets_init failed\n"));
 		return NT_STATUS_LOGON_FAILURE;
@@ -71,7 +73,7 @@
 
 	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
 	if (!password_s) {
-		DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n"));
+		DEBUG(1,("ads_verify_ticket: failed to fetch machine password for domain %s\n",lp_workgroup()));
 		return NT_STATUS_LOGON_FAILURE;
 	}
 
@@ -103,8 +105,13 @@
 	}
 
 	fstrcpy(myname, global_myname());
+	/* The hostname in the machine principal must be FQDN as per RFC. */
+	hp = gethostbyname(myname);
+	if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+	  fstrcpy(myname,hp->h_name);
+	}
 	strlower_m(myname);
-	asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm());
+	asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm());
 	ret = krb5_parse_name(context, host_princ_s, &host_princ);
 	if (ret) {
 		DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n",
--- source/utils/net.c.ORIG	2004-04-04 03:37:39.000000000 -0400
+++ source/utils/net.c	2004-04-13 11:06:04.911540000 -0400
@@ -77,6 +77,7 @@
 BOOL opt_domaingroup = False;
 const char *opt_newntname = "";
 int opt_rid = 0;
+const char *opt_keytab_file = "";
 
 BOOL opt_have_ip = False;
 struct in_addr opt_dest_ip;
@@ -683,6 +684,7 @@
 		{"timeout",	't', POPT_ARG_INT,    &opt_timeout},
 		{"machine-pass",'P', POPT_ARG_NONE,   &opt_machine_pass},
 		{"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup},
+		{"keytabfile",  'k', POPT_ARG_STRING, &opt_keytab_file},
 
 		/* Options for 'net groupmap set' */
 		{"local",       'L', POPT_ARG_NONE,   &opt_localgroup},
--- source/utils/net.h.ORIG	2004-04-04 03:37:39.000000000 -0400
+++ source/utils/net.h	2004-04-13 11:06:04.911556000 -0400
@@ -44,6 +44,7 @@
 extern int opt_flags;
 
 extern const char *opt_comment;
+extern const char *opt_keytab_file;
 
 extern const char *opt_target_workgroup;
 extern const char *opt_workgroup;
--- source/utils/net_ads.c.ORIG	2004-04-04 03:37:38.000000000 -0400
+++ source/utils/net_ads.c	2004-04-13 11:18:17.874146000 -0400
@@ -55,6 +55,8 @@
 "\n\tperform a raw LDAP search and dump the results\n"
 "\nnet ads dn"\
 "\n\tperform a raw LDAP search and dump attributes of a particular DN\n"
+"\nnet ads keytab"\
+"\n\tcreates and updates the kerberos system keytab file\n"
 		);
 	return -1;
 }
@@ -738,11 +740,11 @@
 			d_printf("Using the name [%s] from the server.\n", short_domain_name);
 			d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name);
 		}
-	}
-	else
+	} else {
 		short_domain_name = lp_workgroup();
+	}
 	
-	d_printf("Using short domain name -- %s\n", short_domain_name);
+	DEBUG(1,("Using short domain name -- %s\n", short_domain_name));
 	
 	/*  HACK ALRET!  Store the sid and password under bother the lp_workgroup() 
 	    value from smb.conf and the string returned from the server.  The former is
@@ -771,10 +773,17 @@
 	
 	d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
 
+	/* Now build the keytab, using the same ADS connection */
+        if (ads_keytab_create(ads)) {         
+                DEBUG(0,("Error creating host keytab.\n"));
+        }
+
 	SAFE_FREE(password);
 	SAFE_FREE(machine_account);
 	if ( ctx )
 		talloc_destroy(ctx);
+	
+	ads_destroy(&ads);
 	return 0;
 }
 
@@ -1077,6 +1086,7 @@
     char *host_principal;
     char *hostname;
     ADS_STATUS ret;
+    struct hostent *hp;
 
     if (!secrets_init()) {
 	    DEBUG(1,("Failed to initialise secrets database\n"));
@@ -1092,6 +1102,12 @@
     }
 
     hostname = strdup(global_myname());
+    hp = gethostbyname(global_myname());
+    if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+      	hostname = strdup(hp->h_name);
+    } else {
+	hostname = strdup(global_myname());
+    }
     strlower_m(hostname);
     asprintf(&host_principal, "%s@%s", hostname, ads->config.realm);
     SAFE_FREE(hostname);
@@ -1105,8 +1121,16 @@
 	SAFE_FREE(host_principal);
 	return -1;
     }
-    
     d_printf("Password change for principal HOST/%s succeeded.\n", host_principal);
+    
+    DEBUG(3,("INFO: Attempting to update the appropriate system keytab entries with new password.\n"));
+
+    /* Now build the keytab, using the same ADS connection */
+    if (ads_keytab_update_entries("host",ads)) {         
+      DEBUG(0,("Error creating host keytab.\n"));
+    }
+
+    d_printf("The system keytab has been updated.\n");
     ads_destroy(&ads);
     SAFE_FREE(host_principal);
 
@@ -1231,6 +1255,466 @@
 }
 
 
+int ads_keytab_add_entry(const char *srvPrinc, ADS_STRUCT *ads)
+{
+	krb5_error_code ret;
+	krb5_context context;
+	krb5_keytab keytab;
+	krb5_kt_cursor cursor;
+	krb5_keytab_entry entry;
+	krb5_principal princ;
+	krb5_data password;
+	krb5_enctype *enctypes = NULL;
+	krb5_kvno kvno;
+	
+	char *principal = NULL;
+	char *keytab_name = NULL;
+	char *princ_s = NULL;
+	char *password_s = NULL;
+	fstring myname;
+
+	struct hostent *hp;
+        ADS_STATUS rc;
+
+	/* resolve the right keytab */
+	if (*opt_keytab_file) {
+		asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, opt_keytab_file);
+	} else if (*lp_keytab_file()) {
+		asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, lp_keytab_file());
+	} else {
+		printf("no known keytab\n");
+		return -1;
+	}
+	DEBUG(1,("creating keytab: %s\n",keytab_name));
+
+	initialize_krb5_error_table();
+	ret = krb5_init_context(&context);
+	if (ret) {
+		DEBUG(1,("could not krb5_init_context: %s\n",error_message(ret)));
+		return -1;
+	}
+	ret = krb5_kt_resolve(context,keytab_name,&keytab);
+	if (ret) {
+		DEBUG(1,("krb5_kt_resolve failed (%s)\n",error_message(ret)));
+		return -1;
+	}
+
+	ret = get_kerberos_allowed_etypes(context,&enctypes);
+	if (ret) {
+		DEBUG(1,("get_kerberos_allowed_etypes failed (%s)\n",error_message(ret)));
+		return -1;
+	}
+
+	/* retrieve the password */
+	if (!secrets_init()) {
+		DEBUG(1,("secrets_init failed\n"));
+		return -1;
+	}
+	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+	if (!password_s) {
+		DEBUG(1,("failed to fetch machine password\n"));
+		return -1;
+	}
+	password.data = password_s;
+	password.length = strlen(password_s);
+
+	/* construct our principal */
+	fstrcpy(myname, global_myname());
+	hp = gethostbyname(myname);
+	if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+	  fstrcpy(myname,hp->h_name);
+	}
+	strlower_m(myname);
+	asprintf(&princ_s, "%s/%s@%s", srvPrinc, myname, lp_realm());
+	ret = krb5_parse_name(context, princ_s, &princ);
+	if (ret) {
+		DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
+		return -1;
+	}
+
+	/* seek and delete old keytab entries */
+	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+	if (ret != KRB5_KT_END && ret != ENOENT ) {
+		DEBUG(1,("will try to delete old entries\n"));
+		while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
+		  	int retval = 0;
+		  	char *ktprinc = NULL;
+		  	krb5_unparse_name(context, entry.principal, &ktprinc);
+#ifdef HAVE_KRB5_KT_COMPARE
+		  	if ( krb5_kt_compare(context, &entry, princ, 0, 0) == True )
+#else
+	          	if ( strcmp(ktprinc,princ_s) == 0 )
+#endif
+		  	{
+			    	DEBUG(1,("found old entry for principal: %s (deleting)\n", princ_s));
+
+			    	/* MIT kludge - can't remove keytab entry while scanning entries... */ 
+			    	ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_remove_entry(context, keytab, &entry);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_start_seq failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_free_entry(context, &entry);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	free(ktprinc);
+			    	continue;
+		  	}
+		  	ret = krb5_kt_free_entry(context, &entry);
+		  	if (ret) { 
+		    		DEBUG(1,("krb5_kt_free_entry failed (%s)\n",error_message(ret)));
+			}
+		  	free(ktprinc); 
+	        }
+		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+		if (ret) {
+			DEBUG(1,("krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
+		}
+	}
+
+	kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
+
+	/* store new entries in keytab */
+	ret = create_keytab(context, princ, princ_s, password, enctypes, &keytab, keytab_name, kvno);
+	if (ret) {
+		DEBUG(1,("could not create keytab: %s\n",error_message(ret)));
+		return -1;
+	}
+
+	/* Update the LDAP with the SPN */
+	DEBUG(1,("Attempting to add/update '%s'\n", princ_s));
+        if (ads_add_spn(ads, global_myname(), srvPrinc) != 0) {
+                ads_destroy(&ads);
+                return -1;
+        }
+
+	return 0;		
+}
+
+int ads_keytab_flush()
+{
+	krb5_error_code ret;
+        krb5_context context;
+        krb5_keytab keytab;
+        krb5_kt_cursor cursor;
+        krb5_keytab_entry entry;
+        char *keytab_name = NULL;
+                                                                                                                                                             
+        /* resolve the right keytab */
+        if (*opt_keytab_file) {
+                asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, opt_keytab_file);
+        } else if (*lp_keytab_file()) {
+                asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, lp_keytab_file());
+        } else {
+                DEBUG(1,("ERROR: no known keytab\n"));
+                return -1;
+        }
+        DEBUG(1,("Updating keytab: %s\n",keytab_name));
+
+        initialize_krb5_error_table();
+        ret = krb5_init_context(&context);
+        if (ret) {
+                DEBUG(1,("could not krb5_init_context: %s\n",error_message(ret)));
+                return -1;
+        }
+        ret = krb5_kt_resolve(context,keytab_name,&keytab);
+        if (ret) {
+                DEBUG(1,("krb5_kt_resolve failed (%s)\n",error_message(ret)));
+                return -1;
+        }
+        /* seek and delete old keytab entries */
+        ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+        if (ret != KRB5_KT_END && ret != ENOENT ) {
+                DEBUG(1,("Will try to delete ALL entries\n"));
+                while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
+                        int retval = 0;
+
+                        /* Can't remove keytab entry while scanning entries... */
+                        ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+                        if (ret) {
+                                DEBUG(1,("krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
+                                return -1;
+                        }
+                        ret = krb5_kt_remove_entry(context, keytab, &entry);
+                        if (ret) {
+                                DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+                                return -1;
+                        }
+                        ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+                        if (ret) {
+                                DEBUG(1,("krb5_kt_start_seq failed (%s)\n",error_message(ret)));
+                                return -1;
+                        }
+                        ret = krb5_kt_free_entry(context, &entry);
+                        if (ret) {
+                                DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+                                return -1;
+                        }
+                        continue;
+                }
+        }
+
+	return 0;
+}
+
+int ads_keytab_create(ADS_STRUCT *ads)
+{
+        if (ads_keytab_flush()) {
+                return -1;
+        }
+        if (ads_keytab_add_entry("host", ads)) {
+                return -1;
+        }
+        if (ads_keytab_add_entry("cifs", ads)) {
+                return -1;
+        }
+        return 0;
+}
+
+int ads_keytab_update_entries(const char *srvPrinc, ADS_STRUCT *ads)
+{
+	krb5_error_code ret;
+	krb5_context context;
+	krb5_keytab keytab;
+	krb5_kt_cursor cursor;
+	krb5_keytab_entry entry;
+	krb5_principal princ;
+	krb5_data password;
+	krb5_enctype *enctypes = NULL;
+	krb5_kvno kvno;
+	
+	char *principal = NULL;
+	char *keytab_name = NULL;
+	char *princ_s = NULL;
+	char *password_s = NULL;
+	char *keynames[500];
+	int count = 0, i = 0;
+	fstring myname;
+
+	struct hostent *hp;
+        ADS_STATUS rc;
+
+	/* resolve the right keytab */
+	if (*opt_keytab_file) {
+		asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, opt_keytab_file);
+	} else if (*lp_keytab_file()) {
+		asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, lp_keytab_file());
+	} else {
+		printf("no known keytab\n");
+		return -1;
+	}
+	DEBUG(1,("updating keytab: %s\n",keytab_name));
+
+	initialize_krb5_error_table();
+	ret = krb5_init_context(&context);
+	if (ret) {
+		DEBUG(1,("could not krb5_init_context: %s\n",error_message(ret)));
+		return -1;
+	}
+	ret = krb5_kt_resolve(context,keytab_name,&keytab);
+	if (ret) {
+		DEBUG(1,("krb5_kt_resolve failed (%s)\n",error_message(ret)));
+		return -1;
+	}
+
+	ret = get_kerberos_allowed_etypes(context,&enctypes);
+	if (ret) {
+		DEBUG(1,("get_kerberos_allowed_etypes failed (%s)\n",error_message(ret)));
+		return -1;
+	}
+
+	/* retrieve the password */
+	if (!secrets_init()) {
+		DEBUG(1,("secrets_init failed\n"));
+		return -1;
+	}
+	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+	if (!password_s) {
+		DEBUG(1,("failed to fetch machine password\n"));
+		return -1;
+	}
+	password.data = password_s;
+	password.length = strlen(password_s);
+
+	/* construct our principal */
+	fstrcpy(myname, global_myname());
+	hp = gethostbyname(myname);
+	if ( hp->h_name && strlen(hp->h_name) > 0 ) {
+	  fstrcpy(myname,hp->h_name);
+	}
+	strlower_m(myname);
+	asprintf(&princ_s, "%s/%s@%s", srvPrinc, myname, lp_realm());
+	ret = krb5_parse_name(context, princ_s, &princ);
+	if (ret) {
+		DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
+		return -1;
+	}
+
+	/* seek and delete old keytab entries */
+	/* build list of keys to update in the process */
+	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+	if (ret != KRB5_KT_END && ret != ENOENT ) {
+		DEBUG(1,("will try to delete old entries\n"));
+		while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){
+		  	int retval = 0;
+		  	char *ktprinc = NULL;
+			char *cp;
+			char *princp;
+		  	krb5_unparse_name(context, entry.principal, &ktprinc);
+			princp = strchr(princ_s,'/');  
+			cp = strchr(ktprinc,'/');  
+			DEBUG(1,("comparing keynames %s and %s\n",princp, cp));
+	          	if ( strncmp(cp,princp,strlen(princp)) == 0 )
+		  	{
+			  int found = 0;
+			    	DEBUG(1,("found old entry for principal: %s (deleting)\n", ktprinc));
+				for ( i = 0 ; i < count ; i++ ) {
+				  if ( strncmp ( keynames[i], ktprinc, strlen(keynames[i])) == 0 ) {
+				    found = 1;
+				  }
+				}
+				if ( found == 0 ) {
+				  keynames[count++] = strdup(ktprinc);
+				}
+
+			    	/* MIT kludge - can't remove keytab entry while scanning entries... */ 
+			    	ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_end_seq_get() failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_remove_entry(context, keytab, &entry);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_start_seq failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	ret = krb5_kt_free_entry(context, &entry);
+			    	if (ret) {
+					DEBUG(1,("krb5_kt_remove_entry failed (%s)\n",error_message(ret)));
+					return -1;
+			    	}
+			    	free(ktprinc);
+			    	continue;
+		  	}
+		  	ret = krb5_kt_free_entry(context, &entry);
+		  	if (ret) { 
+		    		DEBUG(1,("krb5_kt_free_entry failed (%s)\n",error_message(ret)));
+			}
+		  	free(ktprinc); 
+	        }
+		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+		if (ret) {
+			DEBUG(1,("krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
+		}
+	}
+
+	kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
+
+	/* store new entries in keytab */
+	for ( i = 0 ; i < count ; i++ ) { 
+	  krb5_principal keyprinc;
+	  DEBUG(1,("parsing keyname (%s)\n", keynames[i]));
+	  ret = krb5_parse_name(context, keynames[i], &keyprinc);
+	  if (ret) {
+	    DEBUG(1,("krb5_parse_name(%s) failed (%s)\n", keynames[i], error_message(ret)));
+	    return -1;
+	  }
+	  DEBUG(1,("creating keyname (%s)\n", keynames[i]));
+	  ret = create_keytab(context, keyprinc, keynames[i], password, enctypes, &keytab, keytab_name, kvno);
+	  if (ret) {
+	    DEBUG(1,("could not create keytab entry %s: %s\n",keynames[i],error_message(ret)));
+	    return -1;
+	  }
+	  free(keynames[i]);
+	}
+
+	/* Update the LDAP with the SPN */
+	DEBUG(1,("Attempting to add/update '%s'\n", princ_s));
+        if (ads_add_spn(ads, global_myname(), srvPrinc) != 0) {
+                ads_destroy(&ads);
+                return -1;
+        }
+
+	return 0;		
+}
+
+static int net_ads_keytab_usage(int argc, const char **argv)
+{
+        d_printf(
+                "net ads keytab <COMMAND>\n"\
+                "<COMMAND> can be either:\n"\
+                "  CREATE    Creates a fresh keytab\n"\
+                "  ADD       Adds new service principal\n"\
+		"  FLUSH     Removes all keytab entries\n"
+		"  HELP      Prints this help message\n"\
+		"The ADD command will take arguments, the other commands\n"\
+		"will not take any arguments.   The arguments given to ADD\n"\
+		"should be a list of principals to add.  For example, \n"\
+		"   net ads keytab add srv1 srv2\n"\
+		"will add principals for the services srv1 and srv2 to the\n"\
+		"system's keytab.\n"\
+                "\n"
+        );
+	return -1;
+}
+
+static int net_ads_keytab_add(int argc, const char **argv)
+{
+	int i;
+        int ret = 0;
+        ADS_STRUCT *ads;
+
+	DEBUG(2,("Processing principals to add...\n"));
+	if (!(ads = ads_startup())) {
+	  	return -1;
+	}
+	for (i = 0; i < argc; i++) {
+		ret |= ads_keytab_add_entry(argv[i], ads);
+	}
+	ads_destroy(&ads);		
+	return ret;
+}
+
+static int net_ads_keytab_create()
+{	
+	ADS_STRUCT *ads;
+
+	if (!(ads = ads_startup())) {                     
+              	return -1;               
+      	}
+	return ads_keytab_create(ads);
+}
+
+int net_ads_keytab(int argc, const char **argv)
+{
+        struct functable func[] = {
+                {"CREATE", net_ads_keytab_create},
+                {"ADD", net_ads_keytab_add},
+                {"FLUSH", ads_keytab_flush},
+                {"HELP", net_ads_keytab_usage},
+                {NULL, NULL}            
+        };
+        return net_run_function(argc, argv, func, net_ads_keytab_usage);
+}
+
+
 int net_ads_help(int argc, const char **argv)
 {
 	struct functable func[] = {
@@ -1269,6 +1753,7 @@
 		{"DN", net_ads_dn},
 		{"WORKGROUP", net_ads_workgroup},
 		{"LOOKUP", net_ads_lookup},
+		{"KEYTAB", net_ads_keytab},
 		{"HELP", net_ads_help},
 		{NULL, NULL}
 	};
@@ -1278,6 +1763,11 @@
 
 #else
 
+int net_ads_keytab(int argc, const char **argv)
+{
+	return net_ads_noads();
+}
+
 static int net_ads_noads(void)
 {
 	d_printf("ADS support not compiled in\n");
--- source/configure.in.ORIG	2004-04-04 03:37:25.000000000 -0400
+++ source/configure.in	2004-04-13 11:06:04.811558000 -0400
@@ -2655,6 +2655,7 @@
   # now see if we can find the krb5 libs in standard paths
   # or as specified above
   AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_mk_req_extended)
+  AC_CHECK_LIB_EXT(krb5, KRB5_LIBS, krb5_kt_compare)
 
   ########################################################
   # now see if we can find the gssapi libs in standard paths
@@ -2759,6 +2760,18 @@
               [Whether the AP_OPTS_USE_SUBKEY ap option is available])
   fi
 
+  AC_CACHE_CHECK([for KV5M_KEYTAB],
+                 samba_cv_HAVE_KV5M_KEYTAB,[
+    AC_TRY_COMPILE([#include <krb5.h>],
+      [krb5_keytab_entry entry; entry.magic = KV5M_KEYTAB;],
+      samba_cv_HAVE_KV5M_KEYTAB=yes,
+      samba_cv_HAVE_KV5M_KEYTAB=no)])
+
+  if test x"$samba_cv_HAVE_KV5M_KEYTAB" = x"yes"; then
+      AC_DEFINE(HAVE_KV5M_KEYTAB,1,
+             [Whether the KV5M_KEYTAB option is available])
+  fi
+
   AC_CACHE_CHECK([for the krb5_princ_component macro],
                 samba_cv_HAVE_KRB5_PRINC_COMPONENT,[
     AC_TRY_LINK([#include <krb5.h>],
@@ -2896,6 +2909,27 @@
   AC_MSG_RESULT(no)
 )
 
+AC_CACHE_CHECK([for WRFILE: keytab support],
+		 samba_cv_HAVE_WRFILE_KEYTAB,[
+    AC_TRY_RUN([
+#include<krb5.h>
+  main()
+  {
+    krb5_context context;
+    krb5_keytab keytab;
+    
+    krb5_init_context(&context);
+    if (krb5_kt_resolve(context, "WRFILE:/tmp/whatever", &keytab))
+      exit(0);
+    exit(1);
+  }], 
+  samba_cv_HAVE_WRFILE_KEYTAB=no,
+  samba_cv_HAVE_WRFILE_KEYTAB=yes)])
+
+  if test x"$samba_cv_HAVE_WRFILE_KEYTAB" = x"yes"; then
+      AC_DEFINE(HAVE_WRFILE_KEYTAB,1,
+               [Whether the WRFILE:-keytab is supported])
+  fi
 
 #################################################
 # check for a PAM clear-text auth, accounts, password and session support
--- source/Makefile.in.ORIG	2004-04-04 03:37:15.000000000 -0400
+++ source/Makefile.in	2004-04-13 11:06:04.841585000 -0400
@@ -209,7 +209,7 @@
              libads/disp_sec.o libads/ads_utils.o libads/ldap_utils.o \
 	     libads/ads_ldap.o libads/authdata.o
 
-LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o
+LIBADS_SERVER_OBJ = libads/util.o libads/kerberos_verify.o libads/kerberos_keytab.o
 
 SECRETS_OBJ = passdb/secrets.o passdb/machine_sid.o 
 
@@ -656,7 +656,7 @@
 		libsmb/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \
 		libads/kerberos_verify.o $(SECRETS_OBJ) lib/server_mutex.o \
 		libads/authdata.o rpc_parse/parse_prs.o rpc_parse/parse_misc.o \
-		libsmb/doserr.o
+		libsmb/doserr.o libads/kerberos_keytab.o
 
 ######################################################################
 # now the rules...
@@ -1179,7 +1179,7 @@
 
 bin/wbinfo at EXEEXT@: $(WBINFO_OBJ) @BUILD_POPT@ bin/.dummy
 	@echo Linking $@
-	@$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@ -lcrypto
+	@$(LINK) -o $@ $(WBINFO_OBJ) $(LIBS) @POPTLIBS@
 
 bin/ntlm_auth at EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
 		$(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy


More information about the samba-technical mailing list