[Samba] Kerberos Keytab Code Update in 3.0.23
Gerald (Jerry) Carter
jerry at samba.org
Thu Jul 13 18:18:40 GMT 2006
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Scott Armstrong wrote:
> I've been using the built-in keytab management and it looks
> like the updated code no longer creates the userPrincipal
> in Active Directory.
I'm still working on the keytab code. There will be more
updates. Sorry I couldn't get everything done for 3.0.23.
You are correct. I'm going to see if we can slip my keytab
fixes into 3.0.23a.
Here's what has happened. 'net ads join' was rewritten to
be like WinXP using ms-rpc rather than doing ldap
modify requests. The end result is that non-admins
can now join Samba boxes to a domain just like they can
a Windows client (if the admin has granted the normal
privileges).
But now we are no longer guaranteed to be able to create
the UPN. You can however always run 'kinit -k machine$'.]
In Windows 2000 domains the UPN affects the DES salting
principal.
I can add code to attempt to add the UPN if you like
but since 'kinit -k machine$' always works, that seems
like a better solution.
> Whether this is an issue for others or not, it would be nice
> to have seen a reference to it in the release notes.
> Since having the user principal in the keytab and
> a cron job to renew the ticket are critical for me to use
> pam_krb5, I'm going to attempt to figure out what code
> needs to be added back from 3.0.22. In the defense
> of the authors, examining a Win2k3 server
> does not show the userPrincipal value being set, although I
> sort of considered this functionality to be the primary
> aim in using Samba for the keytab management.
I'm attaching a patch against 3.0.23. It does two
things:
* Removes the guesswork from deriving the DES salting
principal
* Cleans up the keytab generation and restricts keys
to the single DES and RC4-HMAC keys.
The resulting keytab looks like (i've removed the
realm names in the message for better formatting):
ktutil: list -e
slot KVNO Principal
- ---- ---- ---------------------------------------------------
1 6 host/suse10.plainjoe.org (DES cbc mode with CRC-32)
2 6 host/suse10.plainjoe.org (DES cbc mode with RSA-MD5)
3 6 host/suse10.plainjoe.org (ArcFour with HMAC/md5)
4 6 host/suse10 (DES cbc mode with CRC-32)
5 6 host/suse10 (DES cbc mode with RSA-MD5)
6 6 host/suse10 (ArcFour with HMAC/md5)
7 6 suse10$ (DES cbc mode with CRC-32)
8 6 suse10$ (DES cbc mode with RSA-MD5)
9 6 suse10$ (ArcFour with HMAC/md5)
If the machine has a UPN, that will be added as well.
So if you precreate the machine account with a UPN and join
the domain you would see it ni the list above.
> While I'm on my soap box, would it be possible to hear
> some clarification on the value of some of the principals
> created in the keytab (MIT Kerberos)?
> When I look at Active Directory using ADSI Edit, I see 4
> servicePrincipal values created as a result of "net ads join" -
> host/host, host/fqdn, cifs/host, cifs/fqdn.
I had the same reaction when I started looking at the
code. There should only be two. There are now (with
my latest changes).
Here's the deal. Windows will think principals such as
cifs/.... to the host/... SPN. See this URL:
http://support.microsoft.com/kb/326985/en-us
Now Windows doesn't actually store a keytab per say.
It just generates the keys on the fly. See this URL:
http://mailman.mit.edu/pipermail/kerberos/2005-July/008167.html
I feel that the current keytab generation is broken.
It is trying to entries to handles mutliple case
permutations.
> No offense intended, but what is the purpose of
> adding the variations of case especially with respect to
> the FQDN?
Too much guessing IMO.
> When I look at the tickets that are the result of
> making connections from one Win2K3 server to another,
> the principals simply reflect the form of the
> requests - ie \\FOO yields principal cifs/FOO at BAR.COM,
> \\foo.bar.com yields principal cifs/foo.bar.com at BAR.COM
> What am I missing?
My experience has been that the principals in the
service ticket match the SPN values in AD. I don't
see all of this case permutation people are claiming.
The patch is a work in progress so any feedback would
be appreciated.
cheers, jerry
=====================================================================
Samba ------- http://www.samba.org
Centeris ----------- http://www.centeris.com
"What man is a man who does not make the world better?" --Balian
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org
iD8DBQFEto6AIR7qMdg1EfYRAsYAAKC07PLnFv3PSFk1v1UrZdSlCj/L1gCgjjmY
hploWv3pzVjytOndavHaCeI=
=aht3
-----END PGP SIGNATURE-----
-------------- next part --------------
=== modified file 'source/include/rpc_ds.h'
--- source/include/rpc_ds.h
+++ source/include/rpc_ds.h
@@ -48,6 +48,13 @@
#define DSROLE_DOMAIN_MEMBER_SRV 3
#define DSROLE_BDC 4
#define DSROLE_PDC 5
+
+/* Settings for the domainFunctionality attribteu in the rootDSE */
+
+#define DS_DOMAIN_FUNCTION_2000 0
+#define DS_DOMAIN_FUCNTION_2003_MIXED 1
+#define DS_DOMAIN_FUNCTION_2003 2
+
typedef struct
=== modified file 'source/libads/kerberos.c'
--- source/libads/kerberos.c
+++ source/libads/kerberos.c
@@ -5,6 +5,7 @@
Copyright (C) Remus Koos 2001
Copyright (C) Nalin Dahyabhai <nalin at redhat.com> 2004.
Copyright (C) Jeremy Allison 2004.
+ Copyright (C) Gerald Carter 2006.
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
@@ -268,11 +269,85 @@
}
/************************************************************************
- Routine to get the salting principal for this service. Active
- Directory may use a non-obvious principal name to generate the salt
- when it determines the key to use for encrypting tickets for a service,
- and hopefully we detected that when we joined the domain.
- Caller must free if return is not null.
+ Return the standard DES salt key
+************************************************************************/
+
+char* kerberos_standard_des_salt( void )
+{
+ fstring salt;
+
+ fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
+ strlower_m( salt );
+ fstrcat( salt, lp_realm() );
+
+ return SMB_STRDUP( salt );
+}
+
+/************************************************************************
+************************************************************************/
+
+static char* des_salt_key( void )
+{
+ char *key;
+
+ asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
+
+ return key;
+}
+
+/************************************************************************
+************************************************************************/
+
+BOOL kerberos_secrets_store_des_salt( const char* salt )
+{
+ char* key;
+ BOOL ret;
+
+ if ( (key = des_salt_key()) == NULL ) {
+ DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
+ return False;
+ }
+
+ if ( !salt ) {
+ DEBUG(8,("kerberos_secrets_store_des_salt: deleting salt\n"));
+ secrets_delete( key );
+ return True;
+ }
+
+ DEBUG(3,("kerberos_secrets_store_des_salt: Storing salt \"%s\"\n", salt));
+
+ ret = secrets_store( key, salt, strlen(salt)+1 );
+
+ SAFE_FREE( key );
+
+ return ret;
+}
+
+/************************************************************************
+************************************************************************/
+
+char* kerberos_secrets_fetch_des_salt( void )
+{
+ char *salt, *key;
+
+ if ( (key = des_salt_key()) == NULL ) {
+ DEBUG(0,("kerberos_secrets_fetch_des_salt: failed to generate key!\n"));
+ return False;
+ }
+
+ salt = (char*)secrets_fetch( key, NULL );
+
+ SAFE_FREE( key );
+
+ return salt;
+}
+
+
+/************************************************************************
+ Routine to get the salting principal for this service. This is
+ maintained for backwards compatibilty with releases prior to 3.0.24.
+ Since we store the salting principal string only at join, we may have
+ to look for the older tdb keys. Caller must free if return is not null.
************************************************************************/
krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
@@ -281,23 +356,29 @@
{
char *unparsed_name = NULL, *salt_princ_s = NULL;
krb5_principal ret_princ = NULL;
-
- if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
- return (krb5_principal)NULL;
- }
-
- if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
- SAFE_FREE(unparsed_name);
- return (krb5_principal)NULL;
+
+ /* lookup new key first */
+
+ if ( (salt_princ_s = kerberos_secrets_fetch_des_salt()) == NULL ) {
+
+ /* look under the old key. If this fails, just use the standard key */
+
+ if (smb_krb5_unparse_name(context, host_princ, &unparsed_name) != 0) {
+ return (krb5_principal)NULL;
+ }
+ if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
+ /* fall back to host/machine.realm at REALM */
+ salt_princ_s = kerberos_standard_des_salt();
+ }
}
if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
- SAFE_FREE(unparsed_name);
- SAFE_FREE(salt_princ_s);
- return (krb5_principal)NULL;
- }
+ ret_princ = NULL;
+ }
+
SAFE_FREE(unparsed_name);
SAFE_FREE(salt_princ_s);
+
return ret_princ;
}
@@ -362,465 +443,9 @@
return ret;
}
-/************************************************************************
- Routine to get initial credentials as a service ticket for the local machine.
- Returns a buffer initialized with krb5_mk_req_extended.
- ************************************************************************/
-
-static krb5_error_code get_service_ticket(krb5_context ctx,
- krb5_ccache ccache,
- const char *service_principal,
- int enctype,
- krb5_data *p_outbuf)
-{
- krb5_creds creds, *new_creds = NULL;
- char *service_s = NULL;
- char *machine_account = NULL, *password = NULL;
- krb5_data in_data;
- krb5_auth_context auth_context = NULL;
- krb5_error_code err = 0;
-
- ZERO_STRUCT(creds);
-
- asprintf(&machine_account, "%s$@%s", global_myname(), lp_realm());
- if (machine_account == NULL) {
- goto out;
- }
- password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
- if (password == NULL) {
- goto out;
- }
- if ((err = kerberos_kinit_password(machine_account, password,
- 0, LIBADS_CCACHE_NAME)) != 0) {
- DEBUG(0,("get_service_ticket: kerberos_kinit_password %s failed: %s\n",
- machine_account,
- error_message(err)));
- goto out;
- }
-
- /* Ok - the above call has gotten a TGT. Now we need to get a service
- ticket to ourselves. */
-
- /* Set up the enctype and client and server principal fields for krb5_get_credentials. */
- kerberos_set_creds_enctype(&creds, enctype);
-
- if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) {
- DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n",
- error_message(err)));
- goto out;
- }
-
- if (strchr_m(service_principal, '@')) {
- asprintf(&service_s, "%s", service_principal);
- } else {
- asprintf(&service_s, "%s@%s", service_principal, lp_realm());
- }
-
- if ((err = smb_krb5_parse_name(ctx, service_s, &creds.server))) {
- DEBUG(0,("get_service_ticket: smb_krb5_parse_name %s failed: %s\n",
- service_s, error_message(err)));
- goto out;
- }
-
- if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) {
- DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n",
- service_s, enctype, error_message(err)));
- goto out;
- }
-
- memset(&in_data, '\0', sizeof(in_data));
- if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data,
- new_creds, p_outbuf)) != 0) {
- DEBUG(0,("get_service_ticket: krb5_mk_req_extended failed: %s\n",
- error_message(err)));
- goto out;
- }
-
- out:
-
- if (auth_context) {
- krb5_auth_con_free(ctx, auth_context);
- }
- if (new_creds) {
- krb5_free_creds(ctx, new_creds);
- }
- if (creds.server) {
- krb5_free_principal(ctx, creds.server);
- }
- if (creds.client) {
- krb5_free_principal(ctx, creds.client);
- }
-
- SAFE_FREE(service_s);
- SAFE_FREE(password);
- SAFE_FREE(machine_account);
- return err;
-}
-
-/************************************************************************
- Check if the machine password can be used in conjunction with the salting_principal
- to generate a key which will successfully decrypt the AP_REQ already
- gotten as a message to the local machine.
- ************************************************************************/
-
-static BOOL verify_service_password(krb5_context ctx,
- int enctype,
- const char *salting_principal,
- krb5_data *in_data)
-{
- BOOL ret = False;
- krb5_principal salting_kprinc = NULL;
- krb5_ticket *ticket = NULL;
- krb5_keyblock key;
- krb5_data passdata;
- char *salting_s = NULL;
- char *machine_account = NULL, *password = NULL;
- krb5_auth_context auth_context = NULL;
- krb5_error_code err;
-
- memset(&passdata, '\0', sizeof(passdata));
- memset(&key, '\0', sizeof(key));
-
- asprintf(&machine_account, "%s$@%s", global_myname(), lp_realm());
- if (machine_account == NULL) {
- goto out;
- }
- password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
- if (password == NULL) {
- goto out;
- }
-
- if (strchr_m(salting_principal, '@')) {
- asprintf(&salting_s, "%s", salting_principal);
- } else {
- asprintf(&salting_s, "%s@%s", salting_principal, lp_realm());
- }
-
- if ((err = smb_krb5_parse_name(ctx, salting_s, &salting_kprinc))) {
- DEBUG(0,("verify_service_password: smb_krb5_parse_name %s failed: %s\n",
- salting_s, error_message(err)));
- goto out;
- }
-
- passdata.length = strlen(password);
- passdata.data = (char*)password;
- if ((err = create_kerberos_key_from_string_direct(ctx, salting_kprinc, &passdata, &key, enctype))) {
- DEBUG(0,("verify_service_password: create_kerberos_key_from_string %d failed: %s\n",
- enctype, error_message(err)));
- goto out;
- }
-
- if ((err = krb5_auth_con_init(ctx, &auth_context)) != 0) {
- DEBUG(0,("verify_service_password: krb5_auth_con_init failed %s\n", error_message(err)));
- goto out;
- }
-
- if ((err = krb5_auth_con_setuseruserkey(ctx, auth_context, &key)) != 0) {
- DEBUG(0,("verify_service_password: krb5_auth_con_setuseruserkey failed %s\n", error_message(err)));
- goto out;
- }
-
- if (!(err = krb5_rd_req(ctx, &auth_context, in_data, NULL, NULL, NULL, &ticket))) {
- DEBUG(10,("verify_service_password: decrypted message with enctype %u salt %s!\n",
- (unsigned int)enctype, salting_s));
- ret = True;
- }
-
- out:
-
- memset(&passdata, 0, sizeof(passdata));
- krb5_free_keyblock_contents(ctx, &key);
- if (ticket != NULL) {
- krb5_free_ticket(ctx, ticket);
- }
- if (salting_kprinc) {
- krb5_free_principal(ctx, salting_kprinc);
- }
- SAFE_FREE(salting_s);
- SAFE_FREE(password);
- SAFE_FREE(machine_account);
- return ret;
-}
-
-/************************************************************************
- *
- * From the current draft of kerberos-clarifications:
- *
- * It is not possible to reliably generate a user's key given a pass
- * phrase without contacting the KDC, since it will not be known
- * whether alternate salt or parameter values are required.
- *
- * And because our server has a password, we have this exact problem. We
- * make multiple guesses as to which principal name provides the salt which
- * the KDC is using.
- *
- ************************************************************************/
-
-static void kerberos_derive_salting_principal_for_enctype(const char *service_principal,
- krb5_context ctx,
- krb5_ccache ccache,
- krb5_enctype enctype,
- krb5_enctype *enctypes)
-{
- char *salting_principals[3] = {NULL, NULL, NULL}, *second_principal = NULL;
- krb5_error_code err = 0;
- krb5_data outbuf;
- int i, j;
-
- memset(&outbuf, '\0', sizeof(outbuf));
-
- /* Check that the service_principal is useful. */
- if ((service_principal == NULL) || (strlen(service_principal) == 0)) {
- return;
- }
-
- /* Generate our first guess -- the principal as-given. */
- asprintf(&salting_principals[0], "%s", service_principal);
- if ((salting_principals[0] == NULL) || (strlen(salting_principals[0]) == 0)) {
- return;
- }
-
- /* Generate our second guess -- the computer's principal, as Win2k3. */
- asprintf(&second_principal, "host/%s.%s", global_myname(), lp_realm());
- if (second_principal != NULL) {
- strlower_m(second_principal);
- asprintf(&salting_principals[1], "%s@%s", second_principal, lp_realm());
- SAFE_FREE(second_principal);
- }
- if ((salting_principals[1] == NULL) || (strlen(salting_principals[1]) == 0)) {
- goto out;
- }
-
- /* Generate our third guess -- the computer's principal, as Win2k. */
- asprintf(&second_principal, "HOST/%s", global_myname());
- if (second_principal != NULL) {
- strlower_m(second_principal + 5);
- asprintf(&salting_principals[2], "%s@%s",
- second_principal, lp_realm());
- SAFE_FREE(second_principal);
- }
- if ((salting_principals[2] == NULL) || (strlen(salting_principals[2]) == 0)) {
- goto out;
- }
-
- /* Get a service ticket for ourselves into our memory ccache. */
- /* This will commonly fail if there is no principal by that name (and we're trying
- many names). So don't print a debug 0 error. */
-
- if ((err = get_service_ticket(ctx, ccache, service_principal, enctype, &outbuf)) != 0) {
- DEBUG(3, ("verify_service_password: get_service_ticket failed: %s\n",
- error_message(err)));
- goto out;
- }
-
- /* At this point we have a message to ourselves, salted only the KDC knows how. We
- have to work out what that salting is. */
-
- /* Try and find the correct salting principal. */
- for (i = 0; i < sizeof(salting_principals) / sizeof(salting_principals[i]); i++) {
- if (verify_service_password(ctx, enctype, salting_principals[i], &outbuf)) {
- break;
- }
- }
-
- /* If we failed to get a match, return. */
- if (i >= sizeof(salting_principals) / sizeof(salting_principals[i])) {
- goto out;
- }
-
- /* If we succeeded, store the principal for use for all enctypes which
- * share the same cipher and string-to-key function. Doing this here
- * allows servers which just pass a keytab to krb5_rd_req() to work
- * correctly. */
- for (j = 0; enctypes[j] != 0; j++) {
- if (enctype != enctypes[j]) {
- /* If this enctype isn't compatible with the one which
- * we used, skip it. */
-
- if (!kerberos_compatible_enctypes(ctx, enctypes[j], enctype))
- continue;
- }
- /* If the principal which gives us the proper salt is the one
- * which we would normally guess, don't bother noting anything
- * in the secrets tdb. */
- if (strcmp(service_principal, salting_principals[i]) != 0) {
- kerberos_secrets_store_salting_principal(service_principal,
- enctypes[j],
- salting_principals[i]);
- }
- }
-
- out :
-
- kerberos_free_data_contents(ctx, &outbuf);
- SAFE_FREE(salting_principals[0]);
- SAFE_FREE(salting_principals[1]);
- SAFE_FREE(salting_principals[2]);
- SAFE_FREE(second_principal);
-}
-
-/************************************************************************
- Go through all the possible enctypes for this principal.
- ************************************************************************/
-
-static void kerberos_derive_salting_principal_direct(krb5_context context,
- krb5_ccache ccache,
- krb5_enctype *enctypes,
- char *service_principal)
-{
- int i;
-
- /* Try for each enctype separately, because the rules are
- * different for different enctypes. */
- for (i = 0; enctypes[i] != 0; i++) {
- /* Delete secrets entry first. */
- kerberos_secrets_store_salting_principal(service_principal, 0, NULL);
-#ifdef ENCTYPE_ARCFOUR_HMAC
- if (enctypes[i] == ENCTYPE_ARCFOUR_HMAC) {
- /* Of course this'll always work, so just save
- * ourselves the effort. */
- continue;
- }
-#endif
- /* Try to figure out what's going on with this
- * principal. */
- kerberos_derive_salting_principal_for_enctype(service_principal,
- context,
- ccache,
- enctypes[i],
- enctypes);
- }
-}
-
-/************************************************************************
- Wrapper function for the above.
- ************************************************************************/
-
-BOOL kerberos_derive_salting_principal(char *service_principal)
-{
- krb5_context context = NULL;
- krb5_enctype *enctypes = NULL;
- krb5_ccache ccache = NULL;
- krb5_error_code ret = 0;
-
- initialize_krb5_error_table();
- if ((ret = krb5_init_context(&context)) != 0) {
- DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
- error_message(ret)));
- return False;
- }
- if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
- DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
- error_message(ret)));
- goto out;
- }
-
- if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
- DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n",
- LIBADS_CCACHE_NAME, error_message(ret)));
- goto out;
- }
-
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service_principal);
-
- out:
- if (enctypes) {
- free_kerberos_etypes(context, enctypes);
- }
- if (ccache) {
- krb5_cc_destroy(context, ccache);
- }
- if (context) {
- krb5_free_context(context);
- }
-
- return ret ? False : True;
-}
-
-/************************************************************************
- Core function to try and determine what salt is being used for any keytab
- keys.
- ************************************************************************/
-
-BOOL kerberos_derive_cifs_salting_principals(void)
-{
- fstring my_fqdn;
- char *service = NULL;
- krb5_context context = NULL;
- krb5_enctype *enctypes = NULL;
- krb5_ccache ccache = NULL;
- krb5_error_code ret = 0;
- BOOL retval = False;
-
- initialize_krb5_error_table();
- if ((ret = krb5_init_context(&context)) != 0) {
- DEBUG(1,("kerberos_derive_cifs_salting_principals: krb5_init_context failed. %s\n",
- error_message(ret)));
- return False;
- }
- if ((ret = get_kerberos_allowed_etypes(context, &enctypes)) != 0) {
- DEBUG(1,("kerberos_derive_cifs_salting_principals: get_kerberos_allowed_etypes failed. %s\n",
- error_message(ret)));
- goto out;
- }
-
- if ((ret = krb5_cc_resolve(context, LIBADS_CCACHE_NAME, &ccache)) != 0) {
- DEBUG(3, ("get_service_ticket: krb5_cc_resolve for %s failed: %s\n",
- LIBADS_CCACHE_NAME, error_message(ret)));
- goto out;
- }
-
- if (asprintf(&service, "%s$", global_myname()) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- if (asprintf(&service, "cifs/%s", global_myname()) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- if (asprintf(&service, "host/%s", global_myname()) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- if (asprintf(&service, "cifs/%s.%s", global_myname(), lp_realm()) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- if (asprintf(&service, "host/%s.%s", global_myname(), lp_realm()) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- name_to_fqdn(my_fqdn, global_myname());
- if (asprintf(&service, "cifs/%s", my_fqdn) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
- if (asprintf(&service, "host/%s", my_fqdn) != -1) {
- strlower_m(service);
- kerberos_derive_salting_principal_direct(context, ccache, enctypes, service);
- SAFE_FREE(service);
- }
-
- retval = True;
-
- out:
- if (enctypes) {
- free_kerberos_etypes(context, enctypes);
- }
- if (ccache) {
- krb5_cc_destroy(context, ccache);
- }
- if (context) {
- krb5_free_context(context);
- }
- return retval;
-}
+
+/************************************************************************
+************************************************************************/
int kerberos_kinit_password(const char *principal,
const char *password,
=== modified file 'source/libads/kerberos_keytab.c'
--- source/libads/kerberos_keytab.c
+++ source/libads/kerberos_keytab.c
@@ -9,6 +9,7 @@
Copyright (C) Rakesh Patel 2004
Copyright (C) Dan Perry 2004
Copyright (C) Jeremy Allison 2004
+ Copyright (C) Gerald Carter 2006
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
@@ -29,117 +30,32 @@
#ifdef HAVE_KRB5
+/* This MAX_NAME_LEN is a constant defined in krb5.h */
+#ifndef MAX_KEYTAB_NAME_LEN
+#define MAX_KEYTAB_NAME_LEN 1100
+#endif
+
+
/**********************************************************************
- Adds a single service principal, i.e. 'host' to the system keytab
-***********************************************************************/
-
-int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
+**********************************************************************/
+
+static int smb_krb5_kt_add_entry( krb5_context context, krb5_keytab keytab,
+ krb5_kvno kvno, const char *princ_s,
+ krb5_enctype *enctypes, krb5_data password )
{
krb5_error_code ret = 0;
- krb5_context context = NULL;
- krb5_keytab keytab = NULL;
krb5_kt_cursor cursor;
krb5_keytab_entry kt_entry;
krb5_principal princ = NULL;
- krb5_data password;
- krb5_enctype *enctypes = NULL;
- krb5_kvno kvno;
-
- char *principal = NULL;
- char *princ_s = NULL;
- char *password_s = NULL;
-#ifndef MAX_KEYTAB_NAME_LEN
-#define MAX_KEYTAB_NAME_LEN 1100
-#endif
- char keytab_name[MAX_KEYTAB_NAME_LEN]; /* This MAX_NAME_LEN is a constant defined in krb5.h */
- fstring my_fqdn;
int i;
char *ktprinc = NULL;
ZERO_STRUCT(kt_entry);
ZERO_STRUCT(cursor);
-
- initialize_krb5_error_table();
- ret = krb5_init_context(&context);
- if (ret) {
- DEBUG(1,("ads_keytab_add_entry: could not krb5_init_context: %s\n",error_message(ret)));
- return -1;
- }
-#ifdef HAVE_WRFILE_KEYTAB /* MIT */
- keytab_name[0] = 'W';
- keytab_name[1] = 'R';
- ret = krb5_kt_default_name(context, (char *) &keytab_name[2], MAX_KEYTAB_NAME_LEN - 4);
-#else /* Heimdal */
- ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
-#endif
- if (ret) {
- DEBUG(1,("ads_keytab_add_entry: krb5_kt_default_name failed (%s)\n", error_message(ret)));
- goto out;
- }
- DEBUG(2,("ads_keytab_add_entry: Using default system keytab: %s\n", (char *) &keytab_name));
- ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
- if (ret) {
- DEBUG(1,("ads_keytab_add_entry: krb5_kt_resolve failed (%s)\n", error_message(ret)));
- goto out;
- }
-
- /* retrieve the password */
- if (!secrets_init()) {
- DEBUG(1,("ads_keytab_add_entry: secrets_init failed\n"));
- ret = -1;
- goto out;
- }
- password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
- if (!password_s) {
- DEBUG(1,("ads_keytab_add_entry: failed to fetch machine password\n"));
- ret = -1;
- goto out;
- }
- password.data = password_s;
- password.length = strlen(password_s);
-
- /* Construct our principal */
- name_to_fqdn(my_fqdn, global_myname());
- strlower_m(my_fqdn);
-
- if (strchr_m(srvPrinc, '@')) {
- /* It's a fully-named principal. */
- asprintf(&princ_s, "%s", srvPrinc);
- } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
- /* It's the machine account, as used by smbclient clients. */
- asprintf(&princ_s, "%s@%s", srvPrinc, lp_realm());
- } else {
- /* It's a normal service principal. Add the SPN now so that we
- * can obtain credentials for it and double-check the salt value
- * used to generate the service's keys. */
- asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm());
- /* Update the directory with the SPN */
- DEBUG(3,("ads_keytab_add_entry: Attempting to add/update '%s'\n", princ_s));
- if (!ADS_ERR_OK(ads_add_service_principal_name(ads, global_myname(), srvPrinc))) {
- DEBUG(1,("ads_keytab_add_entry: ads_add_service_principal_name failed.\n"));
- goto out;
- }
- }
-
- ret = get_kerberos_allowed_etypes(context,&enctypes);
- if (ret) {
- DEBUG(1,("ads_keytab_add_entry: get_kerberos_allowed_etypes failed (%s)\n",error_message(ret)));
- goto out;
- }
-
- /* Guess at how the KDC is salting keys for this principal. */
- kerberos_derive_salting_principal(princ_s);
-
+
ret = smb_krb5_parse_name(context, princ_s, &princ);
if (ret) {
DEBUG(1,("ads_keytab_add_entry: smb_krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
- goto out;
- }
-
- kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
- if (kvno == -1) { /* -1 indicates failure, everything else is OK */
- DEBUG(1,("ads_keytab_add_entry: ads_get_kvno failed to determine the system's kvno.\n"));
- ret = -1;
goto out;
}
@@ -275,15 +191,8 @@
}
}
- krb5_kt_close(context, keytab);
- keytab = NULL; /* Done with keytab now. No double free. */
out:
-
- SAFE_FREE(principal);
- SAFE_FREE(password_s);
- SAFE_FREE(princ_s);
-
{
krb5_keytab_entry zero_kt_entry;
ZERO_STRUCT(zero_kt_entry);
@@ -294,10 +203,7 @@
if (princ) {
krb5_free_principal(context, princ);
}
- if (enctypes) {
- free_kerberos_etypes(context, enctypes);
- }
-
+
{
krb5_kt_cursor zero_csr;
ZERO_STRUCT(zero_csr);
@@ -305,6 +211,157 @@
krb5_kt_end_seq_get(context, keytab, &cursor);
}
}
+
+ return (int)ret;
+}
+
+
+/**********************************************************************
+ Adds a single service principal, i.e. 'host' to the system keytab
+***********************************************************************/
+
+int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
+{
+ krb5_error_code ret = 0;
+ krb5_context context = NULL;
+ krb5_keytab keytab = NULL;
+ krb5_data password;
+ krb5_kvno kvno;
+ krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 0, 0 };
+ char *princ_s = NULL, *short_princ_s = NULL;
+ char *password_s = NULL;
+ char *my_fqdn;
+ char keytab_name[MAX_KEYTAB_NAME_LEN];
+ TALLOC_CTX *ctx = NULL;
+ char *machine_name;
+
+#if defined(ENCTYPE_ARCFOUR_HMAC)
+ enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
+#endif
+
+ initialize_krb5_error_table();
+ ret = krb5_init_context(&context);
+ if (ret) {
+ DEBUG(1,("ads_keytab_add_entry: could not krb5_init_context: %s\n",error_message(ret)));
+ return -1;
+ }
+
+#ifdef HAVE_WRFILE_KEYTAB /* MIT */
+ keytab_name[0] = 'W';
+ keytab_name[1] = 'R';
+ ret = krb5_kt_default_name(context, (char *) &keytab_name[2], MAX_KEYTAB_NAME_LEN - 4);
+#else /* Heimdal */
+ ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
+#endif
+ if (ret) {
+ DEBUG(1,("ads_keytab_add_entry: krb5_kt_default_name failed (%s)\n", error_message(ret)));
+ goto out;
+ }
+ DEBUG(2,("ads_keytab_add_entry: Using default system keytab: %s\n", (char *) &keytab_name));
+ ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
+ if (ret) {
+ DEBUG(1,("ads_keytab_add_entry: krb5_kt_resolve failed (%s)\n", error_message(ret)));
+ goto out;
+ }
+
+ /* retrieve the password */
+ if (!secrets_init()) {
+ DEBUG(1,("ads_keytab_add_entry: secrets_init failed\n"));
+ ret = -1;
+ goto out;
+ }
+ password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
+ if (!password_s) {
+ DEBUG(1,("ads_keytab_add_entry: failed to fetch machine password\n"));
+ ret = -1;
+ goto out;
+ }
+ password.data = password_s;
+ password.length = strlen(password_s);
+
+ /* we need the dNSHostName value here */
+
+ if ( (ctx = talloc_init("ads_keytab_add_entry")) == NULL ) {
+ DEBUG(0,("ads_keytab_add_entry: talloc() failed!\n"));
+ ret = -1;
+ goto out;
+ }
+
+ if ( (my_fqdn = ads_get_dnshostname( ads, ctx, global_myname())) == NULL ) {
+ DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's dns name in AD!\n"));
+ ret = -1;
+ goto out;
+ }
+
+ if ( (machine_name = ads_get_samaccountname( ads, ctx, global_myname())) == NULL ) {
+ DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's short name in AD!\n"));
+ ret = -1;
+ goto out;
+ }
+ /*strip the trailing '$' */
+ machine_name[strlen(machine_name)-1] = '\0';
+
+ /* Construct our principal */
+
+ if (strchr_m(srvPrinc, '@')) {
+ /* It's a fully-named principal. */
+ asprintf(&princ_s, "%s", srvPrinc);
+ } else if (srvPrinc[strlen(srvPrinc)-1] == '$') {
+ /* It's the machine account, as used by smbclient clients. */
+ asprintf(&princ_s, "%s@%s", srvPrinc, lp_realm());
+ } else {
+ /* It's a normal service principal. Add the SPN now so that we
+ * can obtain credentials for it and double-check the salt value
+ * used to generate the service's keys. */
+
+ asprintf(&princ_s, "%s/%s@%s", srvPrinc, my_fqdn, lp_realm());
+ asprintf(&short_princ_s, "%s/%s@%s", srvPrinc, machine_name, lp_realm());
+
+ /* According to http://support.microsoft.com/kb/326985/en-us,
+ certain principal names are automatically mapped to the host/...
+ principal in the AD account. So only create these in the
+ keytab, not in AD. --jerry */
+
+ if ( !strequal( srvPrinc, "cifs" ) && !strequal(srvPrinc, "host" ) ) {
+ DEBUG(3,("ads_keytab_add_entry: Attempting to add/update '%s'\n", princ_s));
+
+ if (!ADS_ERR_OK(ads_add_service_principal_name(ads, global_myname(), my_fqdn, srvPrinc))) {
+ DEBUG(1,("ads_keytab_add_entry: ads_add_service_principal_name failed.\n"));
+ goto out;
+ }
+ }
+ }
+
+ kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
+ if (kvno == -1) { /* -1 indicates failure, everything else is OK */
+ DEBUG(1,("ads_keytab_add_entry: ads_get_kvno failed to determine the system's kvno.\n"));
+ ret = -1;
+ goto out;
+ }
+
+ /* add the fqdn principal to the keytab */
+
+ ret = smb_krb5_kt_add_entry( context, keytab, kvno, princ_s, enctypes, password );
+ if ( ret ) {
+ DEBUG(1,("ads_keytab_add_entry: Failed to add entry to keytab file\n"));
+ goto out;
+ }
+
+ /* add the short principal name if we have one */
+
+ if ( short_princ_s ) {
+ ret = smb_krb5_kt_add_entry( context, keytab, kvno, short_princ_s, enctypes, password );
+ if ( ret ) {
+ DEBUG(1,("ads_keytab_add_entry: Failed to add short entry to keytab file\n"));
+ goto out;
+ }
+ }
+
+out:
+ SAFE_FREE( princ_s );
+ SAFE_FREE( short_princ_s );
+ TALLOC_FREE( ctx );
+
if (keytab) {
krb5_kt_close(context, keytab);
}
@@ -440,94 +497,76 @@
krb5_kt_cursor cursor;
krb5_keytab_entry kt_entry;
krb5_kvno kvno;
- fstring my_fqdn, my_Fqdn, my_name, my_NAME, my_host_realm;
- char *p_fqdn;
int i, found = 0;
+ char *sam_account_name, *upn;
char **oldEntries = NULL, *princ_s[26];
+ TALLOC_CTX *ctx = NULL;
+ fstring machine_name;
memset(princ_s, '\0', sizeof(princ_s));
- ret = ads_keytab_add_entry(ads, "host");
- if (ret) {
+ fstrcpy( machine_name, global_myname() );
+
+ /* these are the main ones we need */
+
+ if ( (ret = ads_keytab_add_entry(ads, "host") ) != 0 ) {
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'host'.\n"));
return ret;
}
- ret = ads_keytab_add_entry(ads, "cifs");
- if (ret) {
+
+
+#if 0 /* don't create the CIFS/... keytab entries since no one except smbd
+ really needs them and we will fall back to verifying against secrets.tdb */
+
+ if ( (ret = ads_keytab_add_entry(ads, "cifs")) != 0 ) {
DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding 'cifs'.\n"));
return ret;
}
-
- fstrcpy(my_name, global_myname());
- strlower_m(my_name);
-
- fstrcpy(my_NAME, global_myname());
- strupper_m(my_NAME);
-
- my_fqdn[0] = '\0';
- name_to_fqdn(my_fqdn, global_myname());
- strlower_m(my_fqdn);
-
- p_fqdn = strchr_m(my_fqdn, '.');
- fstrcpy(my_Fqdn, my_NAME);
- if (p_fqdn) {
- fstrcat(my_Fqdn, p_fqdn);
- }
-
- fstrcpy(my_host_realm, my_name);
- fstrcat(my_host_realm, ".");
- fstrcat(my_host_realm, lp_realm());
- strlower_m(my_host_realm);
-
- asprintf(&princ_s[0], "%s$@%s", my_name, lp_realm());
- asprintf(&princ_s[1], "%s$@%s", my_NAME, lp_realm());
- asprintf(&princ_s[2], "host/%s@%s", my_name, lp_realm());
- asprintf(&princ_s[3], "host/%s@%s", my_NAME, lp_realm());
- asprintf(&princ_s[4], "host/%s@%s", my_fqdn, lp_realm());
- asprintf(&princ_s[5], "host/%s@%s", my_Fqdn, lp_realm());
- asprintf(&princ_s[6], "HOST/%s@%s", my_name, lp_realm());
- asprintf(&princ_s[7], "HOST/%s@%s", my_NAME, lp_realm());
- asprintf(&princ_s[8], "HOST/%s@%s", my_fqdn, lp_realm());
- asprintf(&princ_s[9], "HOST/%s@%s", my_Fqdn, lp_realm());
- asprintf(&princ_s[10], "cifs/%s@%s", my_name, lp_realm());
- asprintf(&princ_s[11], "cifs/%s@%s", my_NAME, lp_realm());
- asprintf(&princ_s[12], "cifs/%s@%s", my_fqdn, lp_realm());
- asprintf(&princ_s[13], "cifs/%s@%s", my_Fqdn, lp_realm());
- asprintf(&princ_s[14], "CIFS/%s@%s", my_name, lp_realm());
- asprintf(&princ_s[15], "CIFS/%s@%s", my_NAME, lp_realm());
- asprintf(&princ_s[16], "CIFS/%s@%s", my_fqdn, lp_realm());
- asprintf(&princ_s[17], "CIFS/%s@%s", my_Fqdn, lp_realm());
- asprintf(&princ_s[18], "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm());
- asprintf(&princ_s[19], "CIFS/%s.%s@%s", my_name, lp_realm(), lp_realm());
- asprintf(&princ_s[20], "host/%s.%s@%s", my_name, lp_realm(), lp_realm());
- asprintf(&princ_s[21], "HOST/%s.%s@%s", my_name, lp_realm(), lp_realm());
-
- /* when dnsdomain == realm, don't add duplicate principal */
- if (!strequal(my_host_realm, my_fqdn)) {
- asprintf(&princ_s[22], "cifs/%s@%s", my_host_realm, lp_realm());
- asprintf(&princ_s[23], "CIFS/%s@%s", my_host_realm, lp_realm());
- asprintf(&princ_s[24], "host/%s@%s", my_host_realm, lp_realm());
- asprintf(&princ_s[25], "HOST/%s@%s", my_host_realm, lp_realm());
- }
-
- for (i = 0; i < sizeof(princ_s) / sizeof(princ_s[0]); i++) {
- if (princ_s[i] != NULL) {
- ret = ads_keytab_add_entry(ads, princ_s[i]);
- if (ret != 0) {
- DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding '%s'.\n", princ_s[i]));
- }
- SAFE_FREE(princ_s[i]);
- }
- }
-
- kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
+#endif
+
+ if ( (ctx = talloc_init("ads_keytab_create_default")) == NULL ) {
+ DEBUG(0,("ads_keytab_create_default: talloc() failed!\n"));
+ return -1;
+ }
+
+ /* now add the userPrincipalName and sAMAccountName entries */
+
+ if ( (sam_account_name = ads_get_samaccountname( ads, ctx, machine_name)) == NULL ) {
+ DEBUG(0,("ads_keytab_add_entry: unable to determine machine account's name in AD!\n"));
+ TALLOC_FREE( ctx );
+ return -1;
+ }
+
+ if ( (ret = ads_keytab_add_entry(ads, sam_account_name )) != 0 ) {
+ DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding sAMAccountName (%s)\n",
+ sam_account_name));
+ return ret;
+ }
+
+ /* remember that not every machine account will have a upn */
+
+ upn = ads_get_upn( ads, ctx, machine_name);
+ if ( upn ) {
+ if ( (ret = ads_keytab_add_entry(ads, upn)) != 0 ) {
+ DEBUG(1,("ads_keytab_create_default: ads_keytab_add_entry failed while adding UPN (%s)\n",
+ upn));
+ TALLOC_FREE( ctx );
+ return ret;
+ }
+ }
+
+ TALLOC_FREE( ctx );
+
+ /* Now loop through the keytab and update any other existing entries... */
+
+ kvno = (krb5_kvno) ads_get_kvno(ads, machine_name);
if (kvno == -1) {
DEBUG(1,("ads_keytab_create_default: ads_get_kvno failed to determine the system's kvno.\n"));
return -1;
}
-
- DEBUG(3,("ads_keytab_create_default: Searching for keytab entries to preserve and update.\n"));
- /* Now loop through the keytab and update any other existing entries... */
+
+ DEBUG(3,("ads_keytab_create_default: Searching for keytab entries to "
+ "preserve and update.\n"));
ZERO_STRUCT(kt_entry);
ZERO_STRUCT(cursor);
=== modified file 'source/libads/kerberos_verify.c'
--- source/libads/kerberos_verify.c
+++ source/libads/kerberos_verify.c
@@ -202,8 +202,12 @@
BOOL auth_ok = False;
char *password_s = NULL;
krb5_data password;
- krb5_enctype *enctypes = NULL;
+ krb5_enctype enctypes[4] = { ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, 0, 0 };
int i;
+
+#if defined(ENCTYPE_ARCFOUR_HMAC)
+ enctypes[2] = ENCTYPE_ARCFOUR_HMAC;
+#endif
ZERO_STRUCTP(keyblock);
@@ -222,12 +226,6 @@
password.length = strlen(password_s);
/* CIFS doesn't use addresses in tickets. This would break NAT. JRA */
-
- if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) {
- DEBUG(1,("ads_secrets_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n",
- error_message(ret)));
- goto out;
- }
p_packet->length = ticket->length;
p_packet->data = (krb5_pointer)ticket->data;
@@ -273,8 +271,6 @@
}
out:
-
- free_kerberos_etypes(context, enctypes);
SAFE_FREE(password_s);
return auth_ok;
=== modified file 'source/libads/ldap.c'
--- source/libads/ldap.c
+++ source/libads/ldap.c
@@ -5,6 +5,7 @@
Copyright (C) Remus Koos 2001
Copyright (C) Jim McDonough <jmcd at us.ibm.com> 2002
Copyright (C) Guenther Deschner 2005
+ Copyright (C) Gerald Carter 2006
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
@@ -286,15 +287,8 @@
DEBUG(3,("Connected to LDAP server %s\n", inet_ntoa(ads->ldap_ip)));
if (!ads->auth.user_name) {
- /* have to use the userPrincipalName value here and
- not servicePrincipalName; found by Guenther Deschner @ Sernet.
-
- Is this still correct? The comment does not match
- the code. --jerry
-
- Yes it is :)
- - Guenther
- */
+ /* Must use the userPrincipalName value here or sAMAccountName
+ and not servicePrincipalName; found by Guenther Deschner */
asprintf(&ads->auth.user_name, "%s$", global_myname() );
}
@@ -1402,20 +1396,21 @@
* (found by hostname) in AD.
* @param ads An initialized ADS_STRUCT
* @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
+ * @param my_fqdn The fully qualified DNS name of the machine
* @param spn A string of the service principal to add, i.e. 'host'
* @return 0 upon sucess, or non-zero if a failure occurs
**/
-ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name, const char *spn)
+ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
+ const char *my_fqdn, const char *spn)
{
ADS_STATUS ret;
TALLOC_CTX *ctx;
LDAPMessage *res = NULL;
- char *host_spn, *psp1, *psp2, *psp3;
+ char *psp1, *psp2;
ADS_MODLIST mods;
- fstring my_fqdn;
char *dn_string = NULL;
- const char *servicePrincipalName[4] = {NULL, NULL, NULL, NULL};
+ const char *servicePrincipalName[3] = {NULL, NULL, NULL};
ret = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
@@ -1433,84 +1428,59 @@
return ADS_ERROR(LDAP_NO_MEMORY);
}
- name_to_fqdn(my_fqdn, machine_name);
- strlower_m(my_fqdn);
-
- if (!(host_spn = talloc_asprintf(ctx, "HOST/%s", my_fqdn))) {
+ /* add short name spn */
+
+ if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
talloc_destroy(ctx);
ads_msgfree(ads, res);
return ADS_ERROR(LDAP_NO_MEMORY);
}
-
- /* Add the extra principal */
- psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name);
- if (!psp1) {
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ADS_ERROR(LDAP_NO_MEMORY);
- }
-
strupper_m(psp1);
strlower_m(&psp1[strlen(spn)]);
- DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp1, machine_name));
servicePrincipalName[0] = psp1;
- psp2 = talloc_asprintf(ctx, "%s/%s.%s", spn, machine_name, ads->config.realm);
- if (!psp2) {
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ADS_ERROR(LDAP_NO_MEMORY);
- }
-
+
+ DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
+ psp1, machine_name));
+
+
+ /* add fully qualified spn */
+
+ if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto out;
+ }
strupper_m(psp2);
strlower_m(&psp2[strlen(spn)]);
- DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp2, machine_name));
servicePrincipalName[1] = psp2;
- /* Add another principal in case the realm != the DNS domain, so that
- * the KDC doesn't send "server principal unknown" errors to clients
- * which use the DNS name in determining service principal names. */
- psp3 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn);
- if (!psp3) {
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ADS_ERROR(LDAP_NO_MEMORY);
- }
-
- strupper_m(psp3);
- strlower_m(&psp3[strlen(spn)]);
- if (strcmp(psp2, psp3) != 0) {
- DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n", psp3, machine_name));
- servicePrincipalName[2] = psp3;
- }
-
- if (!(mods = ads_init_mods(ctx))) {
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ADS_ERROR(LDAP_NO_MEMORY);
- }
+ DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
+ psp2, machine_name));
+
+ if ( (mods = ads_init_mods(ctx)) == NULL ) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto out;
+ }
+
ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
if (!ADS_ERR_OK(ret)) {
DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ret;
- }
- dn_string = ads_get_dn(ads, res);
- if (!dn_string) {
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ADS_ERROR(LDAP_NO_MEMORY);
- }
+ goto out;
+ }
+
+ if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
+ ret = ADS_ERROR(LDAP_NO_MEMORY);
+ goto out;
+ }
+
ret = ads_gen_mod(ads, dn_string, mods);
ads_memfree(ads,dn_string);
if (!ADS_ERR_OK(ret)) {
DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
- talloc_destroy(ctx);
- ads_msgfree(ads, res);
- return ret;
- }
-
- talloc_destroy(ctx);
+ goto out;
+ }
+
+ out:
+ TALLOC_FREE( ctx );
ads_msgfree(ads, res);
return ret;
}
@@ -2241,13 +2211,9 @@
return timegm(&tm);
}
-/**
- * Find the servers name and realm - this can be done before authentication
- * The ldapServiceName field on w2k looks like this:
- * vnet3.home.samba.org:win2000-vnet3$@VNET3.HOME.SAMBA.ORG
- * @param ads connection to ads server
- * @return status of search
- **/
+/********************************************************************
+********************************************************************/
+
ADS_STATUS ads_current_time(ADS_STRUCT *ads)
{
const char *attrs[] = {"currentTime", NULL};
@@ -2257,7 +2223,7 @@
TALLOC_CTX *ctx;
ADS_STRUCT *ads_s = ads;
- if (!(ctx = talloc_init("ads_server_info"))) {
+ if (!(ctx = talloc_init("ads_current_time"))) {
return ADS_ERROR(LDAP_NO_MEMORY);
}
@@ -2277,14 +2243,12 @@
status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
if (!ADS_ERR_OK(status)) {
- talloc_destroy(ctx);
goto done;
}
timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
if (!timestr) {
ads_msgfree(ads, res);
- talloc_destroy(ctx);
status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
goto done;
}
@@ -2308,6 +2272,60 @@
ads_destroy( &ads_s );
}
talloc_destroy(ctx);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
+{
+ const char *attrs[] = {"domainFunctionality", NULL};
+ ADS_STATUS status;
+ void *res;
+ ADS_STRUCT *ads_s = ads;
+
+ *val = DS_DOMAIN_FUNCTION_2000;
+
+ /* establish a new ldap tcp session if necessary */
+
+ if ( !ads->ld ) {
+ if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
+ ads->server.ldap_server )) == NULL )
+ {
+ goto done;
+ }
+ ads_s->auth.flags = ADS_AUTH_ANON_BIND;
+ status = ads_connect( ads_s );
+ if ( !ADS_ERR_OK(status))
+ goto done;
+ }
+
+ /* If the attribute does not exist assume it is a Windows 2000
+ functional domain */
+
+ status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(status)) {
+ if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ status = ADS_SUCCESS;
+ }
+ goto done;
+ }
+
+ if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
+ DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
+ }
+ DEBUG(3,("ads_domain_func_level: %d\n", *val));
+
+
+ ads_msgfree(ads, res);
+
+done:
+ /* free any temporary ads connections */
+ if ( ads_s != ads ) {
+ ads_destroy( &ads_s );
+ }
return status;
}
@@ -2622,4 +2640,100 @@
return dn_count;
}
+/********************************************************************
+********************************************************************/
+
+char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
+{
+ LDAPMessage *res = NULL;
+ ADS_STATUS status;
+ int count = 0;
+ char *name = NULL;
+
+ status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
+ global_myname()));
+ goto out;
+ }
+
+ if ( (count = ads_count_replies(ads, res)) != 1 ) {
+ DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
+ goto out;
+ }
+
+ if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
+ DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
+ }
+
+out:
+ ads_msgfree(ads, res);
+
+ return name;
+}
+
+/********************************************************************
+********************************************************************/
+
+char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
+{
+ LDAPMessage *res = NULL;
+ ADS_STATUS status;
+ int count = 0;
+ char *name = NULL;
+
+ status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
+ global_myname()));
+ goto out;
+ }
+
+ if ( (count = ads_count_replies(ads, res)) != 1 ) {
+ DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
+ goto out;
+ }
+
+ if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
+ DEBUG(0,("ads_get_dnshostname: No userPrincipalName attribute!\n"));
+ }
+
+out:
+ ads_msgfree(ads, res);
+
+ return name;
+}
+
+/********************************************************************
+********************************************************************/
+
+char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
+{
+ LDAPMessage *res = NULL;
+ ADS_STATUS status;
+ int count = 0;
+ char *name = NULL;
+
+ status = ads_find_machine_acct(ads, (void **)(void *)&res, global_myname());
+ if (!ADS_ERR_OK(status)) {
+ DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
+ global_myname()));
+ goto out;
+ }
+
+ if ( (count = ads_count_replies(ads, res)) != 1 ) {
+ DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
+ goto out;
+ }
+
+ if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
+ DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
+ }
+
+out:
+ ads_msgfree(ads, res);
+
+ return name;
+}
+
#endif
=== modified file 'source/libads/util.c'
--- source/libads/util.c
+++ source/libads/util.c
@@ -48,14 +48,6 @@
goto failed;
}
- /* Determine if the KDC is salting keys for this principal in a
- * non-obvious way. */
- if (!kerberos_derive_salting_principal(host_principal)) {
- DEBUG(1,("Failed to determine correct salting principal for %s\n", host_principal));
- ret = ADS_ERROR_SYSTEM(EACCES);
- goto failed;
- }
-
failed:
SAFE_FREE(password);
return ret;
=== modified file 'source/utils/net_ads.c'
--- source/utils/net_ads.c
+++ source/utils/net_ads.c
@@ -1029,6 +1029,61 @@
return rc;
}
+/************************************************************************
+ ************************************************************************/
+
+static BOOL net_derive_salting_principal( TALLOC_CTX *ctx, ADS_STRUCT *ads )
+{
+ uint32 domain_func;
+ ADS_STATUS status;
+ fstring salt;
+ char *std_salt;
+ LDAPMessage *res = NULL;
+ const char *machine_name = global_myname();
+
+ status = ads_domain_func_level( ads, &domain_func );
+ if ( !ADS_ERR_OK(status) ) {
+ DEBUG(2,("Failed to determine domain functional level!\n"));
+ return False;
+ }
+
+ /* go ahead and setup the default salt */
+
+ if ( (std_salt = kerberos_standard_des_salt()) == NULL ) {
+ DEBUG(0,("net_derive_salting_principal: failed to obtain stanard DES salt\n"));
+ return False;
+ }
+
+ fstrcpy( salt, std_salt );
+ SAFE_FREE( std_salt );
+
+ /* if it's a Windows functional domain, we have to look for the UPN */
+
+ if ( domain_func == DS_DOMAIN_FUNCTION_2000 ) {
+ char *upn;
+ int count;
+
+ status = ads_find_machine_acct(ads, (void **)(void *)&res, machine_name);
+ if (!ADS_ERR_OK(status)) {
+ return False;
+ }
+
+ if ( (count = ads_count_replies(ads, res)) != 1 ) {
+ DEBUG(1,("net_set_machine_spn: %d entries returned!\n", count));
+ return False;
+ }
+
+ upn = ads_pull_string(ads, ctx, res, "userPrincipalName");
+ if ( upn ) {
+ fstrcpy( salt, upn );
+ }
+
+ ads_msgfree(ads, res);
+ }
+
+ return kerberos_secrets_store_des_salt( salt );
+}
+
/*******************************************************************
join a domain using ADS (LDAP mods)
********************************************************************/
@@ -1139,30 +1194,16 @@
/* don't fail */
}
-#if defined(HAVE_KRB5)
- if (asprintf(&machine_account, "%s$", global_myname()) == -1) {
- d_fprintf(stderr, "asprintf failed\n");
- ads_destroy(&ads);
- return -1;
- }
-
- if (!kerberos_derive_salting_principal(machine_account)) {
+ if ( !net_derive_salting_principal( ctx, ads ) ) {
DEBUG(1,("Failed to determine salting principal\n"));
ads_destroy(&ads);
return -1;
}
- if (!kerberos_derive_cifs_salting_principals()) {
- DEBUG(1,("Failed to determine salting principals\n"));
- ads_destroy(&ads);
- return -1;
- }
-
/* Now build the keytab, using the same ADS connection */
if (lp_use_kerberos_keytab() && ads_keytab_create_default(ads)) {
DEBUG(1,("Error creating host keytab!\n"));
}
-#endif
d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm);
More information about the samba
mailing list