[PATCH] keytab management for ADS mode.
Rakesh Patel
rapatel at optonline.net
Thu Jan 22 00:54:25 GMT 2004
Attached is a revised version of the patch, which addresses some minor bugs.
Testing for the code was done on Solaris 8 and Solaris 9 joined to a
"native" Windows2003 domain.
smbclient was tested against both smbd and a W2K3 domain controller with
file services enabled.
An XP client in a trusted W2K3 domain with user authenticed in the same
domain as the Solaris boxes was used
to test cross-realm kerberos credentials to the samba server (and for
PuTTY/GSSAPI-SSPI to openssh as well).
The Solaris machines use openldap+Cyrus SASL + MIT krb5 verion 1.3.1
along with samba 3.0.2rc1 with
the attached patch.
NOTE: When smbclient communicates with a server, it obtains credentials
as specified by the server.
Windows2003 SMB/CIFS services utilizes NetBIOS naming conventions and
supports use of kerberos
principals of the form "machine$@REALM", where machine is the base name
(not fully qualified) which
is used for NetBIOS naming. When smbclient communicates with smbd, smbd
will return tickets using
host/machine.domain at REALM, where machine.domain is the fully-qualified
name returned by DNS.
It is important to note that if you use /etc/hosts first before DNS, the
fully-qualified names should be
listed first.
Ideally, smbd should probably return cifs/machine.domain at REALM [which is
what the XP client sees]
to smbclient, however there is no impact in functionality with
host/machine.domain at REALM as the
service principal.
There were a number of requests for keytab support - hopefully the patch
can be cleanly integrated after
further testing in various environments.
Rakesh Patel.
RakeshPatel at tdwaterhouse.com wrote:
> The attached patch is based upon the patches posted by Guenther
> Deschner of SuSE Linux AG.
>
>
>
> The patch is based upon 3.0.2rc1 and implements the management of MIT
> keytab files through the use of "net ads" commands. Testing is
> required for Heimdal.
>
>
>
> New configuration options:
>
>
>
> Keytab file = /etc/krb5.keytab ; specify the keytab file to be utilized.
>
>
>
> Keytab update = yes/no ; specify whether "net ads changetrustpw"
> should update the keytab file or not.
>
> If keytab update is set to 'yes',
> net ads join will automatically update/create the keytab file.
>
>
>
> As long as the keytab file is specified, "net ads keytab create" will
> create or update the keytab file using the current key version.
>
>
>
> The patch utilizes the version number of the host key to ensure the
> keytab has the correct version. Windows 2000
>
> Did not support key version numbers, so the patch needs to be tested
> against a Windows 2000 KDC to ensure it does not result in
> segmentation violations or invalid key version numbers.
>
>
>
> The intent of the patch is to utilize your Windows 2003 KDC/AD for
> managing the host keytab and ensure Samba tools/services and other
> Kerberos services can function simultaneously. The secrets.tdb is
> utilized to ensure the ability to generate a new random key and update
> the keytab appropriately. The advantage of utilizing the Samba
> facilities include support for RC4-HMAC, support for random passwords
> as well as the ability to automate and periodically change the host
> key(s). This was not possible when using "ktpass" as provided by
> Microsoft.
>
>
>
> Please email comments/suggestions/fixes to rapatel at optonline.net
> <mailto:rapatel at optonline.net> or the samba-technical list.
>
>
>
> Rakesh Patel.
>
>
>
>
>
-------------- next part --------------
--- source/include/ads.h.ORIG 2004-01-16 12:47:52.000000000 -0500
+++ source/include/ads.h 2004-01-20 08:06:29.636599000 -0500
@@ -221,3 +221,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/ldap.c.ORIG 2004-01-06 16:08:40.000000000 -0500
+++ source/libads/ldap.c 2004-01-20 10:05:48.993851000 -0500
@@ -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 = 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");
--- source/libads/kerberos_verify.c.ORIG 2004-01-16 12:47:52.000000000 -0500
+++ source/libads/kerberos_verify.c 2004-01-21 15:20:42.789618000 -0500
@@ -38,8 +38,8 @@
}
}
-#ifdef HAVE_MEMORY_KEYTAB
-static krb5_error_code create_keytab(krb5_context context,
+
+krb5_error_code create_keytab(krb5_context context,
krb5_principal host_princ,
char *host_princ_s,
krb5_data password,
@@ -53,6 +53,7 @@
krb5_keyblock *key;
int i;
+ kvno = get_kvno(host_princ_s,password.data);
DEBUG(10,("creating keytab: %s\n", keytab_name));
ret = krb5_kt_resolve(context, keytab_name, keytab);
if (ret)
@@ -61,7 +62,7 @@
if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
return ENOMEM;
}
-
+
/* add keytab entries for all encryption types */
for ( i=0; enctypes[i]; i++ ) {
@@ -84,8 +85,11 @@
entry.keyblock = *key;
#endif
- DEBUG(10,("adding keytab-entry for (%s) with encryption type (%d)\n",
- host_princ_s, enctypes[i]));
+#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) {
DEBUG(1,("adding entry to keytab failed (%s)\n",
@@ -98,7 +102,7 @@
return 0;
}
-#endif
+
static BOOL setup_keytab(krb5_context context,
krb5_principal host_princ,
@@ -110,17 +114,11 @@
char *keytab_name = NULL;
krb5_error_code ret;
- /* check if we have to setup a keytab - not currently enabled
- I've put this in so that the else block below functions
- the same way that it will when this code is turned on */
- if (0 /* will later be *lp_keytab() */) {
+ /* check if we have to setup a keytab */
+ if (*lp_keytab_file()) {
/* use a file-keytab */
- asprintf(&keytab_name, "%s:%s",
- ""
- /* KRB5_KT_FILE_PREFIX, "FILE" or
- "WRFILE" depending on HEeimdal or MIT */,
- "" /* will later be lp_keytab() */);
+ asprintf(&keytab_name, "%s:%s", KRB5_KT_FILE_PREFIX, lp_keytab_file());
DEBUG(10,("will use filebased keytab: %s\n", keytab_name));
ret = krb5_kt_resolve(context, keytab_name, keytab);
@@ -193,6 +191,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;
@@ -200,7 +200,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;
}
@@ -232,8 +232,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",
@@ -306,11 +311,7 @@
packet.data = (krb5_pointer)ticket->data;
if (!(ret = krb5_rd_req(context, &auth_context, &packet,
-#ifdef HAVE_MEMORY_KEYTAB
- host_princ,
-#else
- NULL,
-#endif
+ NULL,
keytab, NULL, &tkt))) {
DEBUG(10,("ads_verify_ticket: enc type [%u] decrypted message !\n",
(unsigned int)enctypes[i] ));
@@ -406,4 +407,166 @@
return sret;
}
+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)",ret));
+ 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)",ret));
+ 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)",ret));
+ 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)",ret));
+ 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);
+ return(kvno);
+
+}
#endif /* HAVE_KRB5 */
--- source/param/loadparm.c.ORIG 2004-01-16 12:47:53.000000000 -0500
+++ source/param/loadparm.c 2004-01-20 08:06:29.696597000 -0500
@@ -119,6 +119,8 @@
char *szPasswdChat;
char *szLogFile;
char *szConfigFile;
+ char *szKeytabFile;
+ BOOL bKeytabUpdate;
char *szSMBPasswdFile;
char *szPrivateDir;
char **szPassdbBackend;
@@ -1086,6 +1088,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},
@@ -1526,6 +1530,8 @@
Globals.server_signing = False;
string_set(&Globals.smb_ports, SMB_PORTS);
+
+ Globals.bKeytabUpdate = False;
}
static TALLOC_CTX *lp_talloc;
@@ -1614,6 +1620,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/smbd/negprot.c.ORIG 2003-08-15 16:39:59.000000000 -0400
+++ source/smbd/negprot.c 2004-01-21 15:15:03.797996000 -0500
@@ -209,9 +209,11 @@
if (lp_security() != SEC_ADS) {
blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE");
} else {
- asprintf(&principal, "%s$@%s", guid, lp_realm());
- blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
- free(principal);
+ struct hostent *hp;
+ hp = gethostbyname(guid);
+ asprintf(&principal, "host/%s@%s", hp->h_name, lp_realm());
+ blob = spnego_gen_negTokenInit(guid, OIDs_krb5, principal);
+ free(principal);
}
memcpy(p, blob.data, blob.length);
len = blob.length;
--- source/utils/net.c.ORIG 2003-12-04 16:38:40.000000000 -0500
+++ source/utils/net.c 2004-01-20 08:06:29.736594000 -0500
@@ -73,6 +73,8 @@
int opt_timeout = 0;
const char *opt_target_workgroup = NULL;
int opt_machine_pass = 0;
+const char *opt_keytab_file = "";
+int opt_keytab_update = 0;
BOOL opt_have_ip = False;
struct in_addr opt_dest_ip;
@@ -656,6 +658,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},
POPT_COMMON_SAMBA
{ 0, 0, 0, 0}
};
--- source/utils/net_ads.c.ORIG 2004-01-16 12:47:53.000000000 -0500
+++ source/utils/net_ads.c 2004-01-21 15:30:54.792547000 -0500
@@ -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/utils/net.h.ORIG 2003-11-07 12:37:40.000000000 -0500
+++ source/utils/net.h 2004-01-20 08:06:29.746618000 -0500
@@ -42,6 +42,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/configure.in.ORIG 2004-01-16 12:47:52.000000000 -0500
+++ source/configure.in 2004-01-20 08:06:29.786599000 -0500
@@ -2610,6 +2610,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
@@ -2714,6 +2715,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>],
@@ -2873,6 +2886,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
More information about the samba-technical
mailing list