RIP BOOL. Convert BOOL -> bool. I found a few interesting
[tprouty/samba.git] / source3 / libads / kerberos_keytab.c
index 5ece6e6ab25dd46c1e192281ec38d49e59efa3ba..d0161ada011a89bf6902aa500a6098b7aaefb908 100644 (file)
@@ -9,10 +9,11 @@
    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
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -21,8 +22,7 @@
    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.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
 #ifdef HAVE_KRB5
 
 /**********************************************************************
- 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(context, NULL, enctypes, princ_s);
-
-       ret = krb5_parse_name(context, princ_s, &princ);
+       
+       ret = smb_krb5_parse_name(context, princ_s, &princ);
        if (ret) {
-               DEBUG(1,("ads_keytab_add_entry: 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;
+               DEBUG(1,("smb_krb5_kt_add_entry: smb_krb5_parse_name(%s) failed (%s)\n", princ_s, error_message(ret)));
                goto out;
        }
 
        /* Seek and delete old keytab entries */
        ret = krb5_kt_start_seq_get(context, keytab, &cursor);
        if (ret != KRB5_KT_END && ret != ENOENT ) {
-               DEBUG(3,("ads_keytab_add_entry: Will try to delete old keytab entries\n"));
+               DEBUG(3,("smb_krb5_kt_add_entry: Will try to delete old keytab entries\n"));
                while(!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) {
-                       BOOL compare_ok = False;
+                       bool compare_name_ok = False;
 
-                       ret = krb5_unparse_name(context, kt_entry.principal, &ktprinc);
+                       ret = smb_krb5_unparse_name(context, kt_entry.principal, &ktprinc);
                        if (ret) {
-                               DEBUG(1,("ads_keytab_add_entry: krb5_unparse_name failed (%s)\n", error_message(ret)));
+                               DEBUG(1,("smb_krb5_kt_add_entry: smb_krb5_unparse_name failed (%s)\n",
+                                       error_message(ret)));
                                goto out;
                        }
 
@@ -166,50 +76,65 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
                         */
 
 #ifdef HAVE_KRB5_KT_COMPARE
-                       compare_ok = ((krb5_kt_compare(context, &kt_entry, princ, 0, 0) == True) && (kt_entry.vno != kvno - 1));
+                       compare_name_ok = (krb5_kt_compare(context, &kt_entry, princ, 0, 0) == True);
 #else
-                       compare_ok = ((strcmp(ktprinc, princ_s) == 0) && (kt_entry.vno != kvno - 1));
+                       compare_name_ok = (strcmp(ktprinc, princ_s) == 0);
 #endif
-                       krb5_free_unparsed_name(context, ktprinc);
-                       ktprinc = NULL;
-
-                       if (compare_ok) {
-                               DEBUG(3,("ads_keytab_add_entry: Found old entry for principal: %s (kvno %d) - trying to remove it.\n",
-                                       princ_s, kt_entry.vno));
-                               ret = krb5_kt_end_seq_get(context, keytab, &cursor);
-                               ZERO_STRUCT(cursor);
-                               if (ret) {
-                                       DEBUG(1,("ads_keytab_add_entry: krb5_kt_end_seq_get() failed (%s)\n",
-                                               error_message(ret)));
-                                       goto out;
-                               }
-                               ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
-                               if (ret) {
-                                       DEBUG(1,("ads_keytab_add_entry: krb5_kt_remove_entry failed (%s)\n",
-                                               error_message(ret)));
-                                       goto out;
-                               }
-                               ret = krb5_kt_start_seq_get(context, keytab, &cursor);
-                               if (ret) {
-                                       DEBUG(1,("ads_keytab_add_entry: krb5_kt_start_seq failed (%s)\n",
-                                               error_message(ret)));
-                                       goto out;
-                               }
-                               ret = smb_krb5_kt_free_entry(context, &kt_entry);
-                               ZERO_STRUCT(kt_entry);
-                               if (ret) {
-                                       DEBUG(1,("ads_keytab_add_entry: krb5_kt_remove_entry failed (%s)\n",
-                                               error_message(ret)));
-                                       goto out;
+
+                       if (!compare_name_ok) {
+                               DEBUG(10,("smb_krb5_kt_add_entry: ignoring keytab entry principal %s, kvno = %d\n",
+                                       ktprinc, kt_entry.vno));
+                       }
+
+                       SAFE_FREE(ktprinc);
+
+                       if (compare_name_ok) {
+                               if (kt_entry.vno == kvno - 1) {
+                                       DEBUG(5,("smb_krb5_kt_add_entry: Saving previous (kvno %d) entry for principal: %s.\n",
+                                               kvno - 1, princ_s));
+                               } else {
+
+                                       DEBUG(5,("smb_krb5_kt_add_entry: Found old entry for principal: %s (kvno %d) - trying to remove it.\n",
+                                               princ_s, kt_entry.vno));
+                                       ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+                                       ZERO_STRUCT(cursor);
+                                       if (ret) {
+                                               DEBUG(1,("smb_krb5_kt_add_entry: krb5_kt_end_seq_get() failed (%s)\n",
+                                                       error_message(ret)));
+                                               goto out;
+                                       }
+                                       ret = krb5_kt_remove_entry(context, keytab, &kt_entry);
+                                       if (ret) {
+                                               DEBUG(1,("smb_krb5_kt_add_entry: krb5_kt_remove_entry failed (%s)\n",
+                                                       error_message(ret)));
+                                               goto out;
+                                       }
+
+                                       DEBUG(5,("smb_krb5_kt_add_entry: removed old entry for principal: %s (kvno %d).\n",
+                                               princ_s, kt_entry.vno));
+
+                                       ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+                                       if (ret) {
+                                               DEBUG(1,("smb_krb5_kt_add_entry: krb5_kt_start_seq failed (%s)\n",
+                                                       error_message(ret)));
+                                               goto out;
+                                       }
+                                       ret = smb_krb5_kt_free_entry(context, &kt_entry);
+                                       ZERO_STRUCT(kt_entry);
+                                       if (ret) {
+                                               DEBUG(1,("smb_krb5_kt_add_entry: krb5_kt_remove_entry failed (%s)\n",
+                                                       error_message(ret)));
+                                               goto out;
+                                       }
+                                       continue;
                                }
-                               continue;
                        }
 
                        /* Not a match, just free this entry and continue. */
                        ret = smb_krb5_kt_free_entry(context, &kt_entry);
                        ZERO_STRUCT(kt_entry);
                        if (ret) {
-                               DEBUG(1,("ads_keytab_add_entry: smb_krb5_kt_free_entry failed (%s)\n", error_message(ret)));
+                               DEBUG(1,("smb_krb5_kt_add_entry: smb_krb5_kt_free_entry failed (%s)\n", error_message(ret)));
                                goto out;
                        }
                }
@@ -217,7 +142,7 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
                ret = krb5_kt_end_seq_get(context, keytab, &cursor);
                ZERO_STRUCT(cursor);
                if (ret) {
-                       DEBUG(1,("ads_keytab_add_entry: krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
+                       DEBUG(1,("smb_krb5_kt_add_entry: krb5_kt_end_seq_get failed (%s)\n",error_message(ret)));
                        goto out;
                }
        }
@@ -248,26 +173,19 @@ int ads_keytab_add_entry(ADS_STRUCT *ads, const char *srvPrinc)
                kt_entry.principal = princ;
                kt_entry.vno       = kvno;
 
-               DEBUG(3,("ads_keytab_add_entry: adding keytab entry for (%s) with encryption type (%d) and version (%d)\n",
+               DEBUG(3,("smb_krb5_kt_add_entry: adding keytab entry for (%s) with encryption type (%d) and version (%d)\n",
                        princ_s, enctypes[i], kt_entry.vno));
                ret = krb5_kt_add_entry(context, keytab, &kt_entry);
                krb5_free_keyblock_contents(context, keyp);
                ZERO_STRUCT(kt_entry);
                if (ret) {
-                       DEBUG(1,("ads_keytab_add_entry: adding entry to keytab failed (%s)\n", error_message(ret)));
+                       DEBUG(1,("smb_krb5_kt_add_entry: adding entry to keytab failed (%s)\n", error_message(ret)));
                        goto out;
                }
        }
 
-       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);
@@ -278,10 +196,7 @@ out:
        if (princ) {
                krb5_free_principal(context, princ);
        }
-       if (enctypes) {
-               free_kerberos_etypes(context, enctypes);
-       }
-
+       
        {
                krb5_kt_cursor zero_csr;
                ZERO_STRUCT(zero_csr);
@@ -289,6 +204,144 @@ out:
                        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;
+       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;
+       }
+
+       ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
+       if (ret) {
+               DEBUG(1,("ads_keytab_add_entry: smb_krb5_open_keytab 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);
        }
@@ -310,7 +363,6 @@ int ads_keytab_flush(ADS_STRUCT *ads)
        krb5_kt_cursor cursor;
        krb5_keytab_entry kt_entry;
        krb5_kvno kvno;
-       char keytab_name[MAX_KEYTAB_NAME_LEN];
 
        ZERO_STRUCT(kt_entry);
        ZERO_STRUCT(cursor);
@@ -321,26 +373,10 @@ int ads_keytab_flush(ADS_STRUCT *ads)
                DEBUG(1,("ads_keytab_flush: could not krb5_init_context: %s\n",error_message(ret)));
                return ret;
        }
-#ifdef HAVE_WRFILE_KEYTAB
-       keytab_name[0] = 'W';
-       keytab_name[1] = 'R';
-       ret = krb5_kt_default_name(context, (char *) &keytab_name[2], MAX_KEYTAB_NAME_LEN - 4);
-#else
-       ret = krb5_kt_default_name(context, (char *) &keytab_name[0], MAX_KEYTAB_NAME_LEN - 2);
-#endif
-       if (ret) {
-               DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
-               goto out;
-       }
-       DEBUG(3,("ads_keytab_flush: Using default keytab: %s\n", (char *) &keytab_name));
-       ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
-       if (ret) {
-               DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
-               goto out;
-       }
-       ret = krb5_kt_resolve(context, (char *) &keytab_name, &keytab);
+
+       ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
        if (ret) {
-               DEBUG(1,("ads_keytab_flush: krb5_kt_default failed (%s)\n", error_message(ret)));
+               DEBUG(1,("ads_keytab_flush: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
                goto out;
        }
 
@@ -424,68 +460,81 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
        krb5_kt_cursor cursor;
        krb5_keytab_entry kt_entry;
        krb5_kvno kvno;
-       fstring my_fqdn, my_Fqdn, my_name, my_NAME;
        int i, found = 0;
-       char **oldEntries = NULL, *princ_s[18];;
+       char *sam_account_name, *upn;
+       char **oldEntries = NULL, *princ_s[26];
+       TALLOC_CTX *ctx = NULL;
+       fstring machine_name;
 
-       ret = ads_keytab_add_entry(ads, "host");
-       if (ret) {
+       memset(princ_s, '\0', sizeof(princ_s));
+
+       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;
        }
+#endif
 
-       fstrcpy(my_name, global_myname());
-       strlower_m(my_name);
-       fstrcpy(my_NAME, global_myname());
-       strupper_m(my_NAME);
-       name_to_fqdn(my_Fqdn, global_myname());
-       strlower_m(my_Fqdn);
-       memcpy(my_Fqdn, my_NAME, strlen(my_NAME));
-       name_to_fqdn(my_fqdn, global_myname());
-       strlower_m(my_fqdn);
-
-       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());
-
-       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]);
+       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;      
+       }
+
+       /* upper case the sAMAccountName to make it easier for apps to 
+          know what case to use in the keytab file */
+
+       strupper_m( sam_account_name ); 
+
+       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;
                }
        }
 
-       kvno = (krb5_kvno) ads_get_kvno(ads, global_myname());
+       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);
@@ -496,9 +545,10 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
                DEBUG(1,("ads_keytab_create_default: could not krb5_init_context: %s\n",error_message(ret)));
                return ret;
        }
-       ret = krb5_kt_default(context, &keytab);
+
+       ret = smb_krb5_open_keytab(context, NULL, True, &keytab);
        if (ret) {
-               DEBUG(1,("ads_keytab_create_default: krb5_kt_default failed (%s)\n",error_message(ret)));
+               DEBUG(1,("ads_keytab_create_default: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
                goto done;
        }
 
@@ -523,7 +573,7 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
        if (!found) {
                goto done;
        }
-       oldEntries = (char **) malloc(found * sizeof(char *));
+       oldEntries = SMB_MALLOC_ARRAY(char *, found );
        if (!oldEntries) {
                DEBUG(1,("ads_keytab_create_default: Failed to allocate space to store the old keytab entries (malloc failed?).\n"));
                ret = -1;
@@ -539,9 +589,9 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
                                char *p;
 
                                /* This returns a malloc'ed string in ktprinc. */
-                               ret = krb5_unparse_name(context, kt_entry.principal, &ktprinc);
+                               ret = smb_krb5_unparse_name(context, kt_entry.principal, &ktprinc);
                                if (ret) {
-                                       DEBUG(1,("krb5_unparse_name failed (%s)\n", error_message(ret)));
+                                       DEBUG(1,("smb_krb5_unparse_name failed (%s)\n", error_message(ret)));
                                        goto done;
                                }
                                /*
@@ -564,12 +614,12 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
                                                break;
                                        }
                                        if (!strcmp(oldEntries[i], ktprinc)) {
-                                               krb5_free_unparsed_name(context, ktprinc);
+                                               SAFE_FREE(ktprinc);
                                                break;
                                        }
                                }
                                if (i == found) {
-                                       krb5_free_unparsed_name(context, ktprinc);
+                                       SAFE_FREE(ktprinc);
                                }
                        }
                        smb_krb5_kt_free_entry(context, &kt_entry);
@@ -578,7 +628,7 @@ int ads_keytab_create_default(ADS_STRUCT *ads)
                ret = 0;
                for (i = 0; oldEntries[i]; i++) {
                        ret |= ads_keytab_add_entry(ads, oldEntries[i]);
-                       krb5_free_unparsed_name(context, oldEntries[i]);
+                       SAFE_FREE(oldEntries[i]);
                }
                krb5_kt_end_seq_get(context, keytab, &cursor);
        }
@@ -610,4 +660,104 @@ done:
        }
        return ret;
 }
+
+/**********************************************************************
+ List system keytab.
+***********************************************************************/
+
+int ads_keytab_list(const char *keytab_name)
+{
+       krb5_error_code ret = 0;
+       krb5_context context = NULL;
+       krb5_keytab keytab = NULL;
+       krb5_kt_cursor cursor;
+       krb5_keytab_entry kt_entry;
+
+       ZERO_STRUCT(kt_entry);
+       ZERO_STRUCT(cursor);
+
+       initialize_krb5_error_table();
+       ret = krb5_init_context(&context);
+       if (ret) {
+               DEBUG(1,("ads_keytab_list: could not krb5_init_context: %s\n",error_message(ret)));
+               return ret;
+       }
+
+       ret = smb_krb5_open_keytab(context, keytab_name, False, &keytab);
+       if (ret) {
+               DEBUG(1,("ads_keytab_list: smb_krb5_open_keytab failed (%s)\n", error_message(ret)));
+               goto out;
+       }
+
+       ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+       if (ret) {
+               goto out;
+       }
+
+       printf("Vno  Type        Principal\n");
+
+       while (krb5_kt_next_entry(context, keytab, &kt_entry, &cursor) == 0) {
+       
+               char *princ_s = NULL;
+               char *etype_s = NULL;
+               krb5_enctype enctype = 0;
+
+               ret = smb_krb5_unparse_name(context, kt_entry.principal, &princ_s);
+               if (ret) {
+                       goto out;
+               }
+
+               enctype = smb_get_enctype_from_kt_entry(&kt_entry);
+
+               ret = smb_krb5_enctype_to_string(context, enctype, &etype_s);
+               if (ret) {
+                       SAFE_FREE(princ_s);
+                       goto out;
+               }
+
+               printf("%3d  %s\t\t %s\n", kt_entry.vno, etype_s, princ_s);
+
+               SAFE_FREE(princ_s);
+               SAFE_FREE(etype_s);
+
+               ret = smb_krb5_kt_free_entry(context, &kt_entry);
+               if (ret) {
+                       goto out;
+               }
+       }
+
+       ret = krb5_kt_end_seq_get(context, keytab, &cursor);
+       if (ret) {
+               goto out;
+       }
+
+       /* Ensure we don't double free. */
+       ZERO_STRUCT(kt_entry);
+       ZERO_STRUCT(cursor);
+out:
+
+       {
+               krb5_keytab_entry zero_kt_entry;
+               ZERO_STRUCT(zero_kt_entry);
+               if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) {
+                       smb_krb5_kt_free_entry(context, &kt_entry);
+               }
+       }
+       {
+               krb5_kt_cursor zero_csr;
+               ZERO_STRUCT(zero_csr);
+               if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) {
+                       krb5_kt_end_seq_get(context, keytab, &cursor);  
+               }
+       }
+
+       if (keytab) {
+               krb5_kt_close(context, keytab);
+       }
+       if (context) {
+               krb5_free_context(context);
+       }
+       return ret;
+}
+
 #endif /* HAVE_KRB5 */