lib/krb5_wrap: use krb5_const_principal in smb_krb5_create_key_from_string.
[kai/samba-autobuild/.git] / source3 / libads / kerberos.c
index dc3d11a60c2d5e7c21c7d79f17a0c096a4e778e1..d5d8e2afc034f946589226466e584a87257a7789 100644 (file)
@@ -9,23 +9,31 @@
 
    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,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
-   
+
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "system/filesys.h"
+#include "smb_krb5.h"
+#include "../librpc/gen_ndr/ndr_misc.h"
+#include "libads/kerberos_proto.h"
+#include "libads/cldap.h"
+#include "secrets.h"
+#include "../lib/tsocket/tsocket.h"
 
 #ifdef HAVE_KRB5
 
+#define DEFAULT_KRB5_PORT 88
+
 #define LIBADS_CCACHE_NAME "MEMORY:libads"
 
 /*
@@ -45,9 +53,9 @@ kerb_prompter(krb5_context ctx, void *data,
        memset(prompts[0].reply->data, '\0', prompts[0].reply->length);
        if (prompts[0].reply->length > 0) {
                if (data) {
-                       strncpy(prompts[0].reply->data, (const char *)data,
+                       strncpy((char *)prompts[0].reply->data, (const char *)data,
                                prompts[0].reply->length-1);
-                       prompts[0].reply->length = strlen(prompts[0].reply->data);
+                       prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
                } else {
                        prompts[0].reply->length = 0;
                }
@@ -55,47 +63,14 @@ kerb_prompter(krb5_context ctx, void *data,
        return 0;
 }
 
-static BOOL smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx, 
-                                   DATA_BLOB *edata_blob, 
-                                   KRB5_EDATA_NTSTATUS *edata)
-{
-       BOOL ret = False;
-       prs_struct ps;
-
-       if (!mem_ctx || !edata_blob || !edata) 
-               return False;
-
-       if (!prs_init(&ps, edata_blob->length, mem_ctx, UNMARSHALL))
-               return False;
-
-       if (!prs_copy_data_in(&ps, (char *)edata_blob->data, edata_blob->length))
-               goto out;
-
-       prs_set_offset(&ps, 0);
-
-       if (!prs_ntstatus("ntstatus", &ps, 1, &edata->ntstatus))
-               goto out;
-
-       if (!prs_uint32("unknown1", &ps, 1, &edata->unknown1))
-               goto out;
-
-       if (!prs_uint32("unknown2", &ps, 1, &edata->unknown2)) /* only seen 00000001 here */
-               goto out;
-
-       ret = True;
- out:
-       prs_mem_free(&ps);
-
-       return ret;
-}
-
- static BOOL smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
+ static bool smb_krb5_get_ntstatus_from_krb5_error(krb5_error *error,
                                                   NTSTATUS *nt_status)
 {
        DATA_BLOB edata;
        DATA_BLOB unwrapped_edata;
        TALLOC_CTX *mem_ctx;
-       KRB5_EDATA_NTSTATUS parsed_edata;
+       struct KRB5_EDATA_NTSTATUS parsed_edata;
+       enum ndr_err_code ndr_err;
 
 #ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
        edata = data_blob(error->e_data->data, error->e_data->length);
@@ -121,7 +96,9 @@ static BOOL smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
 
        data_blob_free(&edata);
 
-       if (!smb_krb5_err_io_nstatus(mem_ctx, &unwrapped_edata, &parsed_edata)) {
+       ndr_err = ndr_pull_struct_blob_all(&unwrapped_edata, mem_ctx, 
+               &parsed_edata, (ndr_pull_flags_fn_t)ndr_pull_KRB5_EDATA_NTSTATUS);
+       if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
                data_blob_free(&unwrapped_edata);
                TALLOC_FREE(mem_ctx);
                return False;
@@ -138,11 +115,11 @@ static BOOL smb_krb5_err_io_nstatus(TALLOC_CTX *mem_ctx,
        return True;
 }
 
- static BOOL smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 
+ static bool smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(krb5_context ctx, 
                                                                  krb5_get_init_creds_opt *opt, 
                                                                  NTSTATUS *nt_status)
 {
-       BOOL ret = False;
+       bool ret = False;
        krb5_error *error = NULL;
 
 #ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
@@ -187,9 +164,10 @@ int kerberos_kinit_password_ext(const char *principal,
                                time_t *expire_time,
                                time_t *renew_till_time,
                                const char *cache_name,
-                               BOOL request_pac,
-                               BOOL add_netbios_addr,
-                               time_t renewable_time)
+                               bool request_pac,
+                               bool add_netbios_addr,
+                               time_t renewable_time,
+                               NTSTATUS *ntstatus)
 {
        krb5_context ctx = NULL;
        krb5_error_code code = 0;
@@ -209,14 +187,15 @@ int kerberos_kinit_password_ext(const char *principal,
                krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
        }
 
-       DEBUG(10,("kerberos_kinit_password: using [%s] as ccache and config [%s]\n",
+       DEBUG(10,("kerberos_kinit_password: as %s using [%s] as ccache and config [%s]\n",
+                       principal,
                        cache_name ? cache_name: krb5_cc_default_name(ctx),
                        getenv("KRB5_CONFIG")));
 
        if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) {
                goto out;
        }
-       
+
        if ((code = smb_krb5_parse_name(ctx, principal, &me))) {
                goto out;
        }
@@ -240,21 +219,23 @@ int kerberos_kinit_password_ext(const char *principal,
        }
 #endif
        if (add_netbios_addr) {
-               if ((code = smb_krb5_gen_netbios_krb5_address(&addr))) {
+               if ((code = smb_krb5_gen_netbios_krb5_address(&addr,
+                                                       lp_netbios_name()))) {
                        goto out;
                }
                krb5_get_init_creds_opt_set_address_list(opt, addr->addrs);
        }
 
-       if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), 
-                                                kerb_prompter, NULL, 0, NULL, opt))) {
+       if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, discard_const_p(char,password), 
+                                                kerb_prompter, discard_const_p(char, password),
+                                                0, NULL, opt))) {
                goto out;
        }
 
        if ((code = krb5_cc_initialize(ctx, cc, me))) {
                goto out;
        }
-       
+
        if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) {
                goto out;
        }
@@ -267,6 +248,29 @@ int kerberos_kinit_password_ext(const char *principal,
                *renew_till_time = (time_t) my_creds.times.renew_till;
        }
  out:
+       if (ntstatus) {
+
+               NTSTATUS status;
+
+               /* fast path */
+               if (code == 0) {
+                       *ntstatus = NT_STATUS_OK;
+                       goto cleanup;
+               }
+
+               /* try to get ntstatus code out of krb5_error when we have it
+                * inside the krb5_get_init_creds_opt - gd */
+
+               if (opt && smb_krb5_get_ntstatus_from_krb5_error_init_creds_opt(ctx, opt, &status)) {
+                       *ntstatus = status;
+                       goto cleanup;
+               }
+
+               /* fall back to self-made-mapping */
+               *ntstatus = krb5_to_nt_status(code);
+       }
+
+ cleanup:
        krb5_free_cred_contents(ctx, &my_creds);
        if (me) {
                krb5_free_principal(ctx, me);
@@ -286,51 +290,6 @@ int kerberos_kinit_password_ext(const char *principal,
        return code;
 }
 
-
-
-/* run kinit to setup our ccache */
-int ads_kinit_password(ADS_STRUCT *ads)
-{
-       char *s;
-       int ret;
-       const char *account_name;
-       fstring acct_name;
-
-       if ( IS_DC ) {
-               /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */
-               account_name = lp_workgroup();
-       } else {
-               /* always use the sAMAccountName for security = domain */
-               /* global_myname()$@REA.LM */
-               if ( lp_security() == SEC_DOMAIN ) {
-                       fstr_sprintf( acct_name, "%s$", global_myname() );
-                       account_name = acct_name;
-               }
-               else 
-                       /* This looks like host/global_myname()@REA.LM */
-                       account_name = ads->auth.user_name;
-       }
-
-       if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) {
-               return KRB5_CC_NOMEM;
-       }
-
-       if (!ads->auth.password) {
-               SAFE_FREE(s);
-               return KRB5_LIBOS_CANTREADPWD;
-       }
-       
-       ret = kerberos_kinit_password_ext(s, ads->auth.password, ads->auth.time_offset,
-                       &ads->auth.tgt_expire, NULL, NULL, False, False, ads->auth.renewable);
-
-       if (ret) {
-               DEBUG(0,("kerberos_kinit_password %s failed: %s\n", 
-                        s, error_message(ret)));
-       }
-       SAFE_FREE(s);
-       return ret;
-}
-
 int ads_kdestroy(const char *cc_name)
 {
        krb5_error_code code;
@@ -343,7 +302,7 @@ int ads_kdestroy(const char *cc_name)
                        error_message(code)));
                return code;
        }
-  
+
        if (!cc_name) {
                if ((code = krb5_cc_default(ctx, &cc))) {
                        krb5_free_context(ctx);
@@ -379,8 +338,8 @@ static char *kerberos_secrets_fetch_salting_principal(const char *service, int e
        char *key = NULL;
        char *ret = NULL;
 
-       asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype);
-       if (!key) {
+       if (asprintf(&key, "%s/%s/enctype=%d",
+                    SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
                return NULL;
        }
        ret = (char *)secrets_fetch(key, NULL);
@@ -396,8 +355,8 @@ char* kerberos_standard_des_salt( void )
 {
        fstring salt;
 
-       fstr_sprintf( salt, "host/%s.%s@", global_myname(), lp_realm() );
-       strlower_m( salt );
+       fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
+       (void)strlower_m( salt );
        fstrcat( salt, lp_realm() );
 
        return SMB_STRDUP( salt );
@@ -410,7 +369,10 @@ static char* des_salt_key( void )
 {
        char *key;
 
-       asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL, lp_realm());
+       if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
+                    lp_realm()) == -1) {
+               return NULL;
+       }
 
        return key;
 }
@@ -418,10 +380,10 @@ static char* des_salt_key( void )
 /************************************************************************
 ************************************************************************/
 
-BOOL kerberos_secrets_store_des_salt( const char* salt )
+bool kerberos_secrets_store_des_salt( const char* salt )
 {
        char* key;
-       BOOL ret;
+       bool ret;
 
        if ( (key = des_salt_key()) == NULL ) {
                DEBUG(0,("kerberos_secrets_store_des_salt: failed to generate key!\n"));
@@ -446,13 +408,14 @@ BOOL kerberos_secrets_store_des_salt( const char* salt )
 /************************************************************************
 ************************************************************************/
 
+static
 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;
+               return NULL;
        }
 
        salt = (char*)secrets_fetch( key, NULL );
@@ -462,7 +425,6 @@ char* kerberos_secrets_fetch_des_salt( void )
        return salt;
 }
 
-
 /************************************************************************
  Routine to get the salting principal for this service.  This is 
  maintained for backwards compatibilty with releases prior to 3.0.24.
@@ -470,20 +432,21 @@ char* kerberos_secrets_fetch_des_salt( void )
  to look for the older tdb keys.  Caller must free if return is not null.
  ************************************************************************/
 
+static
 krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
                                                        krb5_principal host_princ,
                                                        int enctype)
 {
        char *unparsed_name = NULL, *salt_princ_s = NULL;
        krb5_principal ret_princ = 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) {
+               if (smb_krb5_unparse_name(talloc_tos(), context, host_princ, &unparsed_name) != 0) {
                        return (krb5_principal)NULL;
                }
                if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) {
@@ -495,13 +458,50 @@ krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
        if (smb_krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) {
                ret_princ = NULL;
        }
-       
-       SAFE_FREE(unparsed_name);
+
+       TALLOC_FREE(unparsed_name);
        SAFE_FREE(salt_princ_s);
-       
+
        return ret_princ;
 }
 
+int create_kerberos_key_from_string(krb5_context context,
+                                       krb5_principal host_princ,
+                                       krb5_data *password,
+                                       krb5_keyblock *key,
+                                       krb5_enctype enctype,
+                                       bool no_salt)
+{
+       krb5_principal salt_princ = NULL;
+       int ret;
+       /*
+        * Check if we've determined that the KDC is salting keys for this
+        * principal/enctype in a non-obvious way.  If it is, try to match
+        * its behavior.
+        */
+       if (no_salt) {
+               KRB5_KEY_DATA(key) = (KRB5_KEY_DATA_CAST *)SMB_MALLOC(password->length);
+               if (!KRB5_KEY_DATA(key)) {
+                       return ENOMEM;
+               }
+               memcpy(KRB5_KEY_DATA(key), password->data, password->length);
+               KRB5_KEY_LENGTH(key) = password->length;
+               KRB5_KEY_TYPE(key) = enctype;
+               return 0;
+       }
+       salt_princ = kerberos_fetch_salt_princ_for_host_princ(context, host_princ, enctype);
+       ret = smb_krb5_create_key_from_string(context,
+                                             salt_princ ? salt_princ : host_princ,
+                                             NULL,
+                                             password,
+                                             enctype,
+                                             key);
+       if (salt_princ) {
+               krb5_free_principal(context, salt_princ);
+       }
+       return ret;
+}
+
 /************************************************************************
  Routine to set the salting principal for this service.  Active
  Directory may use a non-obvious principal name to generate the salt
@@ -510,37 +510,43 @@ krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
  Setting principal to NULL deletes this entry.
  ************************************************************************/
 
-BOOL kerberos_secrets_store_salting_principal(const char *service,
+bool kerberos_secrets_store_salting_principal(const char *service,
                                              int enctype,
                                              const char *principal)
 {
        char *key = NULL;
-       BOOL ret = False;
+       bool ret = False;
        krb5_context context = NULL;
        krb5_principal princ = NULL;
        char *princ_s = NULL;
        char *unparsed_name = NULL;
+       krb5_error_code code;
 
-       krb5_init_context(&context);
-       if (!context) {
+       if (((code = krb5_init_context(&context)) != 0) || (context == NULL)) {
+               DEBUG(5, ("kerberos_secrets_store_salting_pricipal: kdb5_init_context failed: %s\n",
+                         error_message(code)));
                return False;
        }
        if (strchr_m(service, '@')) {
-               asprintf(&princ_s, "%s", service);
+               if (asprintf(&princ_s, "%s", service) == -1) {
+                       goto out;
+               }
        } else {
-               asprintf(&princ_s, "%s@%s", service, lp_realm());
+               if (asprintf(&princ_s, "%s@%s", service, lp_realm()) == -1) {
+                       goto out;
+               }
        }
 
        if (smb_krb5_parse_name(context, princ_s, &princ) != 0) {
                goto out;
-               
        }
-       if (smb_krb5_unparse_name(context, princ, &unparsed_name) != 0) {
+       if (smb_krb5_unparse_name(talloc_tos(), context, princ, &unparsed_name) != 0) {
                goto out;
        }
 
-       asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype);
-       if (!key)  {
+       if (asprintf(&key, "%s/%s/enctype=%d",
+                    SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
+           == -1) {
                goto out;
        }
 
@@ -554,7 +560,11 @@ BOOL kerberos_secrets_store_salting_principal(const char *service,
 
        SAFE_FREE(key);
        SAFE_FREE(princ_s);
-       SAFE_FREE(unparsed_name);
+       TALLOC_FREE(unparsed_name);
+
+       if (princ) {
+               krb5_free_principal(context, princ);
+       }
 
        if (context) {
                krb5_free_context(context);
@@ -580,89 +590,189 @@ int kerberos_kinit_password(const char *principal,
                                           cache_name,
                                           False,
                                           False,
-                                          0);
+                                          0,
+                                          NULL);
 }
 
+/************************************************************************
+************************************************************************/
+
 /************************************************************************
  Create a string list of available kdc's, possibly searching by sitename.
  Does DNS queries.
+
+ If "sitename" is given, the DC's in that site are listed first.
+
 ************************************************************************/
 
-static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sitename, struct in_addr primary_ip)
+static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
+                               const struct sockaddr_storage *addr)
 {
-       struct ip_service *ip_srv_site;
-       struct ip_service *ip_srv_nonsite;
-       int count_site, count_nonsite, i;
-       char *kdc_str = talloc_asprintf(mem_ctx, "\tkdc = %s\n",
-                                       inet_ntoa(primary_ip));
+       int i;
+
+       for (i=0; i<*num_addrs; i++) {
+               if (sockaddr_equal((const struct sockaddr *)&addrs[i],
+                                  (const struct sockaddr *)addr)) {
+                       return;
+               }
+       }
+       addrs[i] = *addr;
+       *num_addrs += 1;
+}
+
+/* print_canonical_sockaddr prints an ipv6 addr in the form of
+* [ipv6.addr]. This string, when put in a generated krb5.conf file is not
+* always properly dealt with by some older krb5 libraries. Adding the hard-coded
+* portnumber workarounds the issue. - gd */
+
+static char *print_canonical_sockaddr_with_port(TALLOC_CTX *mem_ctx,
+                                               const struct sockaddr_storage *pss)
+{
+       char *str = NULL;
+
+       str = print_canonical_sockaddr(mem_ctx, pss);
+       if (str == NULL) {
+               return NULL;
+       }
+
+       if (pss->ss_family != AF_INET6) {
+               return str;
+       }
+
+#if defined(HAVE_IPV6)
+       str = talloc_asprintf_append(str, ":88");
+#endif
+       return str;
+}
+
+static char *get_kdc_ip_string(char *mem_ctx,
+               const char *realm,
+               const char *sitename,
+               const struct sockaddr_storage *pss)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       int i;
+       struct ip_service *ip_srv_site = NULL;
+       struct ip_service *ip_srv_nonsite = NULL;
+       int count_site = 0;
+       int count_nonsite;
+       int num_dcs;
+       struct sockaddr_storage *dc_addrs;
+       struct tsocket_address **dc_addrs2 = NULL;
+       const struct tsocket_address * const *dc_addrs3 = NULL;
+       char *result = NULL;
+       struct netlogon_samlogon_response **responses = NULL;
+       NTSTATUS status;
+       char *kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n", "",
+                                       print_canonical_sockaddr_with_port(mem_ctx, pss));
 
        if (kdc_str == NULL) {
+               TALLOC_FREE(frame);
                return NULL;
        }
 
-       /* Get the KDC's only in this site. */
+       /*
+        * First get the KDC's only in this site, the rest will be
+        * appended later
+        */
 
        if (sitename) {
-
                get_kdc_list(realm, sitename, &ip_srv_site, &count_site);
-
-               for (i = 0; i < count_site; i++) {
-                       if (ip_equal(ip_srv_site[i].ip, primary_ip)) {
-                               continue;
-                       }
-                       /* Append to the string - inefficient but not done often. */
-                       kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
-                               kdc_str, inet_ntoa(ip_srv_site[i].ip));
-                       if (!kdc_str) {
-                               SAFE_FREE(ip_srv_site);
-                               return NULL;
-                       }
-               }
        }
 
        /* Get all KDC's. */
 
        get_kdc_list(realm, NULL, &ip_srv_nonsite, &count_nonsite);
 
-       for (i = 0; i < count_nonsite; i++) {
-               int j;
+       dc_addrs = talloc_array(talloc_tos(), struct sockaddr_storage,
+                               1 + count_site + count_nonsite);
+       if (dc_addrs == NULL) {
+               goto fail;
+       }
 
-               if (ip_equal(ip_srv_nonsite[i].ip, primary_ip)) {
-                       continue;
-               }
+       dc_addrs[0] = *pss;
+       num_dcs = 1;
 
-               /* Ensure this isn't an IP already seen (YUK! this is n*n....) */
-               for (j = 0; j < count_site; j++) {
-                       if (ip_equal(ip_srv_nonsite[i].ip, ip_srv_site[j].ip)) {
-                               break;
-                       }
-                       /* As the lists are sorted we can break early if nonsite > site. */
-                       if (ip_service_compare(&ip_srv_nonsite[i], &ip_srv_site[j]) > 0) {
-                               break;
-                       }
+       for (i=0; i<count_site; i++) {
+               add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_site[i].ss);
+       }
+
+       for (i=0; i<count_nonsite; i++) {
+               add_sockaddr_unique(dc_addrs, &num_dcs, &ip_srv_nonsite[i].ss);
+       }
+
+       dc_addrs2 = talloc_zero_array(talloc_tos(),
+                                     struct tsocket_address *,
+                                     num_dcs);
+       if (dc_addrs2 == NULL) {
+               goto fail;
+       }
+
+       for (i=0; i<num_dcs; i++) {
+               char addr[INET6_ADDRSTRLEN];
+               int ret;
+
+               print_sockaddr(addr, sizeof(addr), &dc_addrs[i]);
+
+               ret = tsocket_address_inet_from_strings(dc_addrs2, "ip",
+                                                       addr, LDAP_PORT,
+                                                       &dc_addrs2[i]);
+               if (ret != 0) {
+                       status = map_nt_error_from_unix(errno);
+                       DEBUG(2,("Failed to create tsocket_address for %s - %s\n",
+                                addr, nt_errstr(status)));
+                       goto fail;
                }
-               if (j != i) {
+       }
+
+       dc_addrs3 = (const struct tsocket_address * const *)dc_addrs2;
+
+       status = cldap_multi_netlogon(talloc_tos(),
+                       dc_addrs3, num_dcs,
+                       realm, lp_netbios_name(),
+                       NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX,
+                       MIN(num_dcs, 3), timeval_current_ofs(3, 0), &responses);
+       TALLOC_FREE(dc_addrs2);
+       dc_addrs3 = NULL;
+
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(10,("get_kdc_ip_string: cldap_multi_netlogon failed: "
+                         "%s\n", nt_errstr(status)));
+               goto fail;
+       }
+
+       kdc_str = talloc_strdup(mem_ctx, "");
+       if (kdc_str == NULL) {
+               goto fail;
+       }
+
+       for (i=0; i<num_dcs; i++) {
+               char *new_kdc_str;
+
+               if (responses[i] == NULL) {
                        continue;
                }
 
                /* Append to the string - inefficient but not done often. */
-               kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
-                       kdc_str, inet_ntoa(ip_srv_nonsite[i].ip));
-               if (!kdc_str) {
-                       SAFE_FREE(ip_srv_site);
-                       SAFE_FREE(ip_srv_nonsite);
-                       return NULL;
+               new_kdc_str = talloc_asprintf(mem_ctx, "%s\tkdc = %s\n",
+                                             kdc_str,
+                                             print_canonical_sockaddr_with_port(mem_ctx, &dc_addrs[i]));
+               if (new_kdc_str == NULL) {
+                       goto fail;
                }
+               TALLOC_FREE(kdc_str);
+               kdc_str = new_kdc_str;
        }
 
-
-       SAFE_FREE(ip_srv_site);
-       SAFE_FREE(ip_srv_nonsite);
-
        DEBUG(10,("get_kdc_ip_string: Returning %s\n",
                kdc_str ));
 
-       return kdc_str;
+       result = kdc_str;
+fail:
+       SAFE_FREE(ip_srv_site);
+       SAFE_FREE(ip_srv_nonsite);
+       TALLOC_FREE(frame);
+       return result;
 }
 
 /************************************************************************
@@ -672,10 +782,12 @@ static char *get_kdc_ip_string(char *mem_ctx, const char *realm, const char *sit
  run as root or will fail (which is a good thing :-).
 ************************************************************************/
 
-BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *domain,
-                                       const char *sitename, struct in_addr ip)
+bool create_local_private_krb5_conf_for_domain(const char *realm,
+                                               const char *domain,
+                                               const char *sitename,
+                                               const struct sockaddr_storage *pss)
 {
-       char *dname = talloc_asprintf(NULL, "%s/smb_krb5", lp_lockdir());
+       char *dname;
        char *tmpname = NULL;
        char *fname = NULL;
        char *file_contents = NULL;
@@ -684,59 +796,100 @@ BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *do
        ssize_t ret;
        int fd;
        char *realm_upper = NULL;
+       bool result = false;
+       char *aes_enctypes = NULL;
+       mode_t mask;
 
+       if (!lp_create_krb5_conf()) {
+               return false;
+       }
+
+       if (realm == NULL) {
+               DEBUG(0, ("No realm has been specified! Do you really want to "
+                         "join an Active Directory server?\n"));
+               return false;
+       }
+
+       if (domain == NULL || pss == NULL) {
+               return false;
+       }
+
+       dname = lock_path("smb_krb5");
        if (!dname) {
-               return False;
+               return false;
        }
        if ((mkdir(dname, 0755)==-1) && (errno != EEXIST)) {
                DEBUG(0,("create_local_private_krb5_conf_for_domain: "
                        "failed to create directory %s. Error was %s\n",
                        dname, strerror(errno) ));
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
-       tmpname = talloc_asprintf(dname, "%s/smb_tmp_krb5.XXXXXX", lp_lockdir());
+       tmpname = lock_path("smb_tmp_krb5.XXXXXX");
        if (!tmpname) {
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        fname = talloc_asprintf(dname, "%s/krb5.conf.%s", dname, domain);
        if (!fname) {
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        DEBUG(10,("create_local_private_krb5_conf_for_domain: fname = %s, realm = %s, domain = %s\n",
                fname, realm, domain ));
 
        realm_upper = talloc_strdup(fname, realm);
-       strupper_m(realm_upper);
+       if (!strupper_m(realm_upper)) {
+               goto done;
+       }
 
-       kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, ip);
+       kdc_ip_string = get_kdc_ip_string(dname, realm, sitename, pss);
        if (!kdc_ip_string) {
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
+       }
+
+       aes_enctypes = talloc_strdup(fname, "");
+       if (aes_enctypes == NULL) {
+               goto done;
+       }
+
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+       aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes256-cts-hmac-sha1-96 ");
+       if (aes_enctypes == NULL) {
+               goto done;
        }
-               
-       file_contents = talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n\n"
-                               "[realms]\n\t%s = {\n"
-                               "\t%s\t}\n",
-                               realm_upper, realm_upper, kdc_ip_string);
+#endif
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+       aes_enctypes = talloc_asprintf_append(aes_enctypes, "%s", "aes128-cts-hmac-sha1-96");
+       if (aes_enctypes == NULL) {
+               goto done;
+       }
+#endif
+
+       file_contents = talloc_asprintf(fname,
+                                       "[libdefaults]\n\tdefault_realm = %s\n"
+                                       "\tdefault_tgs_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+                                       "\tdefault_tkt_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n"
+                                       "\tpreferred_enctypes = %s RC4-HMAC DES-CBC-CRC DES-CBC-MD5\n\n"
+                                       "[realms]\n\t%s = {\n"
+                                       "\t%s\t}\n",
+                                       realm_upper, aes_enctypes, aes_enctypes, aes_enctypes,
+                                       realm_upper, kdc_ip_string);
 
        if (!file_contents) {
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        flen = strlen(file_contents);
 
-       fd = smb_mkstemp(tmpname);
+       mask = umask(S_IRWXO | S_IRWXG);
+       fd = mkstemp(tmpname);
+       umask(mask);
        if (fd == -1) {
                DEBUG(0,("create_local_private_krb5_conf_for_domain: smb_mkstemp failed,"
                        " for file %s. Errno %s\n",
                        tmpname, strerror(errno) ));
+               goto done;
        }
 
        if (fchmod(fd, 0644)==-1) {
@@ -745,8 +898,7 @@ BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *do
                        tmpname, strerror(errno) ));
                unlink(tmpname);
                close(fd);
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        ret = write(fd, file_contents, flen);
@@ -756,15 +908,13 @@ BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *do
                        (int)ret, (unsigned int)flen, strerror(errno) ));
                unlink(tmpname);
                close(fd);
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
        if (close(fd)==-1) {
                DEBUG(0,("create_local_private_krb5_conf_for_domain: close failed."
                        " Errno %s\n", strerror(errno) ));
                unlink(tmpname);
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        if (rename(tmpname, fname) == -1) {
@@ -772,71 +922,85 @@ BOOL create_local_private_krb5_conf_for_domain(const char *realm, const char *do
                        "of %s to %s failed. Errno %s\n",
                        tmpname, fname, strerror(errno) ));
                unlink(tmpname);
-               TALLOC_FREE(dname);
-               return False;
+               goto done;
        }
 
        DEBUG(5,("create_local_private_krb5_conf_for_domain: wrote "
-               "file %s with realm %s KDC = %s\n",
-               fname, realm_upper, inet_ntoa(ip) ));
+               "file %s with realm %s KDC list = %s\n",
+               fname, realm_upper, kdc_ip_string));
 
        /* Set the environment variable to this file. */
        setenv("KRB5_CONFIG", fname, 1);
 
+       result = true;
+
 #if defined(OVERWRITE_SYSTEM_KRB5_CONF)
 
 #define SYSTEM_KRB5_CONF_PATH "/etc/krb5.conf"
        /* Insanity, sheer insanity..... */
 
        if (strequal(realm, lp_realm())) {
-               pstring linkpath;
-               int lret;
-
-               lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath, sizeof(linkpath)-1);
-               linkpath[sizeof(pstring)-1] = '\0';
-
-               if (lret == 0 || strcmp(linkpath, fname) == 0) {
-                       /* Symlink already exists. */
-                       TALLOC_FREE(dname);
-                       return True;
+               SMB_STRUCT_STAT sbuf;
+
+               if (sys_lstat(SYSTEM_KRB5_CONF_PATH, &sbuf, false) == 0) {
+                       if (S_ISLNK(sbuf.st_ex_mode) && sbuf.st_ex_size) {
+                               int lret;
+                               size_t alloc_size = sbuf.st_ex_size + 1;
+                               char *linkpath = talloc_array(talloc_tos(), char,
+                                               alloc_size);
+                               if (!linkpath) {
+                                       goto done;
+                               }
+                               lret = readlink(SYSTEM_KRB5_CONF_PATH, linkpath,
+                                               alloc_size - 1);
+                               if (lret == -1) {
+                                       TALLOC_FREE(linkpath);
+                                       goto done;
+                               }
+                               linkpath[lret] = '\0';
+
+                               if (strcmp(linkpath, fname) == 0) {
+                                       /* Symlink already exists. */
+                                       TALLOC_FREE(linkpath);
+                                       goto done;
+                               }
+                               TALLOC_FREE(linkpath);
+                       }
                }
 
                /* Try and replace with a symlink. */
                if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
+                       const char *newpath = SYSTEM_KRB5_CONF_PATH ".saved";
                        if (errno != EEXIST) {
                                DEBUG(0,("create_local_private_krb5_conf_for_domain: symlink "
                                        "of %s to %s failed. Errno %s\n",
                                        fname, SYSTEM_KRB5_CONF_PATH, strerror(errno) ));
-                               TALLOC_FREE(dname);
-                               return True; /* Not a fatal error. */
+                               goto done; /* Not a fatal error. */
                        }
 
-                       pstrcpy(linkpath, SYSTEM_KRB5_CONF_PATH);
-                       pstrcat(linkpath, ".saved");
-
                        /* Yes, this is a race conditon... too bad. */
-                       if (rename(SYSTEM_KRB5_CONF_PATH, linkpath) == -1) {
+                       if (rename(SYSTEM_KRB5_CONF_PATH, newpath) == -1) {
                                DEBUG(0,("create_local_private_krb5_conf_for_domain: rename "
                                        "of %s to %s failed. Errno %s\n",
-                                       SYSTEM_KRB5_CONF_PATH, linkpath,
+                                       SYSTEM_KRB5_CONF_PATH, newpath,
                                        strerror(errno) ));
-                               TALLOC_FREE(dname);
-                               return True; /* Not a fatal error. */
+                               goto done; /* Not a fatal error. */
                        }
 
-                       if (symlink(fname, "/etc/krb5.conf") == -1) {
+                       if (symlink(fname, SYSTEM_KRB5_CONF_PATH) == -1) {
                                DEBUG(0,("create_local_private_krb5_conf_for_domain: "
                                        "forced symlink of %s to /etc/krb5.conf failed. Errno %s\n",
                                        fname, strerror(errno) ));
-                               TALLOC_FREE(dname);
-                               return True; /* Not a fatal error. */
+                               goto done; /* Not a fatal error. */
                        }
                }
        }
 #endif
 
+done:
+       TALLOC_FREE(tmpname);
        TALLOC_FREE(dname);
 
-       return True;
+       return result;
 }
 #endif