[PATCH] Keytab patch for current CVS "samba" source.

Rakesh Patel rapatel at optonline.net
Tue Apr 6 19:00:11 GMT 2004


Attached is the keytab patch - simplified to just generate the keytab
if "keytab file" and "keytab update" are defined.  "net ads join" will
create the keytab when joining a domain and "net ads changetrustpw" will
update the keytab after a new key has been successfully updated in AD.

The samba utilities will continue to use secretes.tdb to automatically
validate incoming requests and will use "machine$@REALM" as per
the Microsoft implementation of CIFS - the Windows 2000/2003 KDC
will automatically handle support for various key names as it does 
currently.

NOTE: If you already have an existing keytab created using "ktpass" from the
Microsoft utilities, it will have a DES key, so it is best to remove 
that key
and the host entry in AD/Windows2000/Windows2003 and simply do a "net 
ads join"
to properly join the machine to the AD based domain. You should also ensure
that your /etc/hosts contains the FQDN for the machine and that the 
resolv.conf
has the proper search order to ensure the correct DNS domain is 
returned. Generally
the DNS should be configured to point to the DNS service for the Windows 
2000/2003
domain.

Caveats:  The patch is unlikely to support the ability to join one or 
more domains as is.
    Assumes DNS is properly configured and tested in a DNS integrated AD 
environment.
    Windows 2000 does not support key versions - untested with Windows 2000.


Rakesh Patel.

-------------- next part --------------
--- source/include/ads.h.ORIG	2004-03-24 12:40:21.000000000 -0500
+++ source/include/ads.h	2004-04-06 12:30:17.000000000 -0400
@@ -224,3 +224,12 @@
 #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/libads/kerberos_verify.c.ORIG	2004-02-02 22:23:15.000000000 -0500
+++ source/libads/kerberos_verify.c	2004-04-06 12:53:47.000000000 -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,8 +73,8 @@
 
 	password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
 	if (!password_s) {
-		DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n"));
-		return NT_STATUS_LOGON_FAILURE;
+	  DEBUG(1,("ads_verify_ticket: failed to fetch machine password for domain %s\n",lp_workgroup()));
+	  return NT_STATUS_LOGON_FAILURE;
 	}
 
 	password.data = password_s;
@@ -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",
@@ -214,11 +221,11 @@
 #endif
 
 	get_auth_data_from_tkt(auth_data, tkt);
-
+	
 	{
-		TALLOC_CTX *ctx = talloc_init("pac data");
-		decode_pac_data(auth_data, ctx);
-		talloc_destroy(ctx);
+	  TALLOC_CTX *ctx = talloc_init("pac data");
+	  decode_pac_data(auth_data, ctx);
+	  talloc_destroy(ctx);
 	}
 
 #if 0
@@ -268,4 +275,5 @@
 	return sret;
 }
 
+
 #endif /* HAVE_KRB5 */
--- source/libads/kerberos_keytab.c.ORIG	2004-04-06 13:05:08.000000000 -0400
+++ source/libads/kerberos_keytab.c	2004-04-06 13:14:00.000000000 -0400
@@ -0,0 +1,264 @@
+/* 
+   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
+
+krb5_kvno get_kvno(char *hprinc_s, char *pwd)
+{
+    krb5_context context;
+    krb5_error_code ret;
+    krb5_enctype etype;
+    krb5_ccache ccache;
+    krb5_creds in_creds, *out_creds;
+    krb5_ticket *ticket;
+
+    krb5_creds my_creds;
+    krb5_error_code code = 0;
+    krb5_get_init_creds_opt options;
+    krb5_principal hprinc;
+
+    char *myprinc_s = NULL;
+
+    krb5_kvno kvno;
+    
+      fstring myname;
+      struct hostent *hp;
+
+	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(&myprinc_s, "host/%s@%s", myname, lp_realm());
+
+    ret = krb5_init_context(&context);
+    if (ret) {
+	DEBUG(1,("while initializing krb5 library (%d)",ret));
+	SAFE_FREE(myprinc_s);
+	return(-1);
+    }
+
+    etype = 0;
+
+    ret = krb5_parse_name(context, myprinc_s, &hprinc);
+
+    if (ret) {
+
+      printf("while parsing name %s failed (%s)\n",
+
+	     myprinc_s, error_message(ret));
+
+	SAFE_FREE(myprinc_s);
+      return(-1);
+
+    }
+
+    ret = krb5_cc_default(context, &ccache);
+    if (ret) {
+	DEBUG(1,("while opening ccache (%d)",ret));
+	krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+	return(-1);
+    }
+
+    code = krb5_cc_initialize(context, ccache, hprinc);
+    if (code) {
+	DEBUG(1,("when initializing ccache (%d)",ret));
+      krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+      return(-1);
+    }
+                                                                                                                                                                                                                                             
+    krb5_get_init_creds_opt_init(&options);
+    memset(&my_creds, 0, sizeof(my_creds));
+ 
+    if ((code = krb5_copy_principal(context, hprinc, &my_creds.client))){
+	DEBUG(1,("while copying principal (%d)",code));
+	    krb5_cc_close(context, ccache);
+	    krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+      return(-1);
+    }
+                                                                                                                                                                                                                                             
+    if ((code = krb5_copy_principal(context, hprinc, &my_creds.server))){
+	DEBUG(1,("while copying principal (%d)",code));
+	    krb5_cc_close(context, ccache);
+	    krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+      return(-1);
+    }
+                                                                                                                                                                                                                                             
+    my_creds.times.starttime = 0;       /* start timer when request
+                                           gets to KDC */
+                                                                                                                                                                                                                                             
+    my_creds.times.renew_till = 0;
+
+    ret = krb5_get_init_creds_password(context,&my_creds, hprinc, pwd, NULL, 0, 0, myprinc_s, &options);
+
+    if (ret) {
+	DEBUG(1,("whule getting client creds (%d)",ret));
+	    krb5_cc_close(context, ccache);
+	    krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+	return(-1);
+    }
+
+    code = krb5_cc_store_cred(context, ccache, &my_creds);
+    if (code) {
+	DEBUG(1,("while storing credentials (%d)",ret));
+	    krb5_cc_close(context, ccache);
+	    krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+	return(-1);
+    }
+                                                                                                                                                                                                                                             
+
+	memset(&in_creds, 0, sizeof(in_creds));
+
+	if ((code = krb5_copy_principal(context, hprinc, &in_creds.server))){
+	  DEBUG(1,("while copying principal (%d)",code));
+	  krb5_cc_close(context, ccache);
+	  krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+	  return(-1);
+	}
+
+	if ((code = krb5_copy_principal(context, hprinc, &in_creds.client))){
+	  DEBUG(1,("while copying principal (%d)",code));
+	  krb5_cc_close(context, ccache);
+	  krb5_free_context(context);
+	SAFE_FREE(myprinc_s);
+	  return(-1);
+	}
+
+	/*	in_creds.client = hprinc; */
+
+	in_creds.keyblock.enctype = etype;
+
+	/* in_creds.server = hprinc; */
+
+	ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds);
+
+	/* we need a native ticket */
+	ret = krb5_decode_ticket(&out_creds->ticket, &ticket);
+	if (ret) {
+	  DEBUG(1,("whule decoding ticket (%d), myprinc_s: %s, error message: %s",ret,myprinc_s,error_message(ret)));
+
+	    krb5_free_creds(context, out_creds);
+	    krb5_cc_close(context, ccache);
+	    krb5_free_context(context);
+	    SAFE_FREE(myprinc_s);
+	    return(-1);
+	}
+	else {
+	  kvno = ticket->enc_part.kvno;
+	  DEBUG(1,("myprinc_s: %s,kvno: %d",myprinc_s,kvno));
+	  krb5_free_ticket(context, ticket);
+	  krb5_free_creds(context, out_creds);
+	}
+
+    krb5_cc_close(context, ccache);
+    krb5_free_context(context);
+    SAFE_FREE(myprinc_s);
+    if (kvno == 255 ) kvno = 1;
+    return(kvno);
+
+}
+
+
+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_keytab_entry entry;
+	krb5_kvno kvno = 1;
+	krb5_error_code ret;
+	krb5_keyblock *key;
+	int i;
+
+	kvno = (krb5_kvno ) get_kvno(host_princ_s,password.data);
+	DEBUG(10,("creating keytab: %s for %s, kvno: %d\n", keytab_name, host_princ_s, kvno));
+	printf("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
+
+#ifdef HAVE_KV5M_KEYTAB
+		entry.magic = KV5M_KEYTAB;
+#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) {
+		  int retval;
+			DEBUG(1,("adding entry to keytab failed (%s)\n", 
+				 error_message(ret)));
+			retval = krb5_kt_close(context, *keytab);
+			if (retval) {
+			  DEBUG(3, ("krb5_kt_close failed (%s)\n",
+				    error_message(retval)));
+ 			}
+			return ret;
+		}
+	}
+	krb5_free_keyblock(context, key);
+	
+	return 0;
+}
+
+#endif /* HAVE_KRB5 */
--- source/libads/ldap.c.ORIG	2004-02-08 06:46:32.000000000 -0500
+++ source/libads/ldap.c	2004-04-06 13:59:56.633657000 -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) {
@@ -1001,6 +1006,9 @@
 	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));
@@ -1012,7 +1020,14 @@
 
 	ret = ADS_ERROR(LDAP_NO_MEMORY);
 
-	if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", hostname)))
+	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;
 	if (!(host_upn = talloc_asprintf(ctx, "%s@%s", host_spn, ads->config.realm)))
 		goto done;
@@ -1060,7 +1075,7 @@
 		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");
@@ -1087,6 +1102,7 @@
 		}
 	}
 done:
+	free(fqdn);
 	talloc_destroy(ctx);
 	return ret;
 }
--- source/param/loadparm.c.ORIG	2004-04-02 13:44:03.000000000 -0500
+++ source/param/loadparm.c	2004-04-06 12:42:34.000000000 -0400
@@ -120,6 +120,8 @@
 	char *szPasswdChat;
 	char *szLogFile;
 	char *szConfigFile;
+	char *szKeytabFile;
+	BOOL bKeytabUpdate;
 	char *szSMBPasswdFile;
 	char *szPrivateDir;
 	char **szPassdbBackend;
@@ -1095,6 +1097,8 @@
 	{"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}, 
+	{"keytab update", P_BOOL, P_GLOBAL, &Globals.bKeytabUpdate, 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}, 
@@ -1540,6 +1544,8 @@
 	Globals.server_signing = False;
 
 	string_set(&Globals.smb_ports, SMB_PORTS);
+	
+	Globals.bKeytabUpdate = False;
 }
 
 static TALLOC_CTX *lp_talloc;
@@ -1629,6 +1635,8 @@
 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_BOOL(lp_keytab_update, &Globals.bKeytabUpdate)
 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/utils/net.h.ORIG	2004-02-26 06:34:33.000000000 -0500
+++ source/utils/net.h	2004-04-06 12:30:17.000000000 -0400
@@ -44,6 +44,8 @@
 extern int opt_flags;
 
 extern const char *opt_comment;
+extern const char *opt_keytab_file;
+extern int opt_keytab_update;
 
 extern const char *opt_target_workgroup;
 extern const char *opt_workgroup;
--- source/utils/net.c.ORIG	2004-03-18 02:33:26.000000000 -0500
+++ source/utils/net.c	2004-04-06 12:36:57.000000000 -0400
@@ -83,6 +83,9 @@
 
 extern BOOL AllowDebugChange;
 
+const char *opt_keytab_file = "";
+int opt_keytab_update = 0;
+
 uint32 get_sec_channel_type(const char *param) 
 {
 	if (!(param && *param)) {
@@ -684,6 +687,8 @@
 		{"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},
+ 		{"keytabupdate",'K', POPT_ARG_NONE,   &opt_keytab_update},
 
 		/* Options for 'net groupmap set' */
 		{"local",       'L', POPT_ARG_NONE,   &opt_localgroup},
--- source/utils/net_ads.c.ORIG	2004-03-13 22:47:55.000000000 -0500
+++ source/utils/net_ads.c	2004-04-06 12:30:17.000000000 -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\tdisplays or creates a kerberos keytab-file\n"
 		);
 	return -1;
 }
@@ -775,6 +777,15 @@
 	SAFE_FREE(machine_account);
 	if ( ctx )
 		talloc_destroy(ctx);
+
+	if ( opt_keytab_update == 1 || lp_keytab_update() == 1 ) {
+	  int retval;
+	  retval = (int) ads_keytab_create();
+	  if ( retval ) {
+	    d_printf("Error (%d) creating host keytab!!",retval);
+	  }
+	}
+
 	return 0;
 }
 
@@ -1077,6 +1088,8 @@
     char *host_principal;
     char *hostname;
     ADS_STATUS ret;
+    
+    struct hostent *hp;
 
     if (!secrets_init()) {
 	    DEBUG(1,("Failed to initialise secrets database\n"));
@@ -1091,7 +1104,13 @@
 	    return -1;
     }
 
-    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);
@@ -1110,6 +1129,14 @@
     ads_destroy(&ads);
     SAFE_FREE(host_principal);
 
+    if ( opt_keytab_update == 1  || lp_keytab_update() == 1 ) {
+      int retval;
+	retval = (int) ads_keytab_create();
+	if ( retval ) {
+	  d_printf("Error (%d) creating/updating host keytab!",retval); 
+	}
+    }
+
     return 0;
 }
 
@@ -1231,6 +1258,261 @@
 }
 
 
+int ads_keytab_create()
+{
+	krb5_error_code ret;
+	krb5_context context;
+	krb5_keytab keytab;
+	krb5_kt_cursor cursor;
+	krb5_keytab_entry entry;
+	krb5_principal host_princ;
+	krb5_principal cifs_princ;
+	krb5_data password;
+	krb5_enctype *enctypes = NULL;
+	
+	char *principal = NULL;
+	char *keytab_name = NULL;
+	char *host_princ_s = NULL;
+	char *cifs_princ_s = NULL;
+	char *password_s = NULL;
+	fstring myname;
+
+	struct hostent *hp;
+
+	/* 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;
+	}
+	printf("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(&host_princ_s, "host/%s@%s", myname, lp_realm());
+	asprintf(&cifs_princ_s, "cifs/%s@%s", myname, lp_realm());
+	ret = krb5_parse_name(context, host_princ_s, &host_princ);
+	if (ret) {
+		DEBUG(1,("krb5_parse_name(%s) failed (%s)\n",host_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 ) {
+		printf("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, host_princ, 0, 0) == True )
+#else
+	          if ( strcmp(ktprinc,host_princ_s) == 0 || strcmp(ktprinc,cifs_princ_s) == 0 )
+#endif
+		  {
+		    if ( strcmp(ktprinc,host_princ_s) == 0) {
+			    printf("found old entry for principal: %s (deleting)\n", host_princ_s);
+		    }
+		    if ( strcmp(ktprinc,cifs_princ_s) == 0 ) {
+			    printf("found old entry for principal: %s (deleting)\n", cifs_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)));
+	}
+
+	/* store new entries in keytab */
+	ret = create_keytab(context, host_princ, host_princ_s, password, enctypes, &keytab, keytab_name);
+	if (ret) {
+		DEBUG(1,("could not create keytab: %s\n",error_message(ret)));
+		return -1;
+	}
+	ret = krb5_parse_name(context, cifs_princ_s, &cifs_princ);
+	if (ret) {
+		DEBUG(1,("krb5_parse_name failed (%s)\n",error_message(ret)));
+	}
+	ret = create_keytab(context, cifs_princ, cifs_princ_s, password, enctypes, &keytab, keytab_name);
+	if (ret) {
+		DEBUG(1,("could not update keytab: %s\n",error_message(ret)));
+		return -1;
+	}
+
+	return 0;
+		
+}
+
+static int net_ads_keytab_usage(int argc, const char **argv)
+{
+	d_printf(
+"\nnet ads keytab"
+"\n\tlist the contents of the keytab"
+"\nnet ads keytab create"
+"\n\twill create a new keytab"
+"\n\nthe default location of your keytab (defined in smb.conf)"
+"\ncan be overridden with --keytab=/my/keytab\n\n");
+	return -1;
+}
+
+int net_ads_keytab(int argc, const char **argv)
+{
+	struct functable func[] = {
+		{"CREATE", ads_keytab_create},
+		{"HELP", net_ads_keytab_usage},
+		{NULL, NULL}
+	};
+
+	if (argc == 0) {
+
+		/* list the keytab */
+		krb5_error_code ret;
+		krb5_context context;
+		krb5_keytab keytab;
+		krb5_kt_cursor cursor;
+		krb5_keytab_entry entry;
+		char enctype_s[256];
+		char *principal = NULL;
+		char *keytab_name = NULL;
+
+		/* resolve the correct 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");
+			free(keytab_name);
+			return -1;
+		} 
+		printf("\nlisting 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,("could not krb5_kt_resolve: %s\n",error_message(ret)));
+			return -1;
+		}
+
+		/* iterate through all entries and print some data */
+		ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+		if (ret) {
+			DEBUG(1,("could not krb5_kt_start_seq_get: %s\n",error_message(ret)));
+			return -1;
+		}
+
+		printf("kvno, principal, timestamp, enctype \n");
+		printf("-----------------------------------\n");
+		while ((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
+			krb5_unparse_name(context, entry.principal, &principal);
+#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+			ret = krb5_enctype_to_string(context, entry.keyblock.keytype, &enctype_s);
+#else 
+			memset(enctype_s,0x00,256);
+			ret = krb5_enctype_to_string(entry.key.enctype, enctype_s, 256);
+#endif
+			if (ret)
+				DEBUG(1,("could not krb5_enctype_to_string: %s\n",error_message(ret)));
+			printf("%d, %s, (%s), %s\n", 
+					entry.vno, principal, http_timestring(entry.timestamp), enctype_s);
+			free(principal);
+			krb5_kt_free_entry(context, &entry);
+		}
+
+		ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+		if (ret) {
+			DEBUG(1,("could not krb5_kt_end_seq_get: %s\n",error_message(ret)));
+			return -1;
+		}
+
+		free(keytab_name);
+		free(enctype_s);
+	
+		ret = krb5_kt_close(context, keytab);
+		if (ret) {
+			DEBUG(1,("could not krb5_kt_close: %s\n",error_message(ret)));
+			return -1;
+		}
+
+		return 0;
+	}
+	return net_run_function(argc, argv, func, net_ads_keytab_usage);
+}
+
 int net_ads_help(int argc, const char **argv)
 {
 	struct functable func[] = {
@@ -1270,6 +1552,7 @@
 		{"WORKGROUP", net_ads_workgroup},
 		{"LOOKUP", net_ads_lookup},
 		{"HELP", net_ads_help},
+		{"KEYTAB", net_ads_keytab},
 		{NULL, NULL}
 	};
 	
@@ -1314,6 +1597,11 @@
 	return net_ads_noads();
 }
 
+int net_ads_keytab(int argc, const char **argv)
+{
+	return net_ads_noads();
+}
+
 /* this one shouldn't display a message */
 int net_ads_check(void)
 {
--- source/configure.in.ORIG	2004-03-26 17:29:09.000000000 -0500
+++ source/configure.in	2004-04-06 12:30:17.000000000 -0400
@@ -2660,6 +2660,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
@@ -2764,6 +2765,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>],
@@ -2901,6 +2914,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-01 07:33:04.000000000 -0500
+++ source/Makefile.in	2004-04-06 14:08:55.355881000 -0400
@@ -212,7 +212,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 
 
@@ -668,7 +668,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...
@@ -1201,7 +1201,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