more enctypes...
[metze/samba/wip.git] / source3 / libads / kerberos.c
index a47ab6c868c8b650ce423c7adeaad9b065cb4fab..bb6b91ee95e4066e14d44ef1cd8e2435e1df5460 100644 (file)
@@ -22,6 +22,7 @@
 */
 
 #include "includes.h"
+#include "libsmb/namequery.h"
 #include "system/filesys.h"
 #include "smb_krb5.h"
 #include "../librpc/gen_ndr/ndr_misc.h"
@@ -29,6 +30,8 @@
 #include "libads/cldap.h"
 #include "secrets.h"
 #include "../lib/tsocket/tsocket.h"
+#include "lib/util/asn1.h"
+#include "krb5_errs.h"
 
 #ifdef HAVE_KRB5
 
@@ -57,7 +60,7 @@ kerb_prompter(krb5_context ctx, void *data,
                 * version have looping detection and return with a proper error code.
                 */
 
-#if HAVE_KRB5_PROMPT_TYPE /* Heimdal */
+#if defined(HAVE_KRB5_PROMPT_TYPE) /* Heimdal */
                 if (prompts[0].type == KRB5_PROMPT_TYPE_NEW_PASSWORD &&
                     prompts[1].type == KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN) {
                        /*
@@ -98,96 +101,6 @@ kerb_prompter(krb5_context ctx, void *data,
        return 0;
 }
 
- 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;
-       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);
-#else
-       edata = data_blob(error->e_data.data, error->e_data.length);
-#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
-
-#ifdef DEVELOPER
-       dump_data(10, edata.data, edata.length);
-#endif /* DEVELOPER */
-
-       mem_ctx = talloc_init("smb_krb5_get_ntstatus_from_krb5_error");
-       if (mem_ctx == NULL) {
-               data_blob_free(&edata);
-               return False;
-       }
-
-       if (!unwrap_edata_ntstatus(mem_ctx, &edata, &unwrapped_edata)) {
-               data_blob_free(&edata);
-               TALLOC_FREE(mem_ctx);
-               return False;
-       }
-
-       data_blob_free(&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;
-       }
-
-       data_blob_free(&unwrapped_edata);
-
-       if (nt_status) {
-               *nt_status = parsed_edata.ntstatus;
-       }
-
-       TALLOC_FREE(mem_ctx);
-
-       return True;
-}
-
- 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;
-       krb5_error *error = NULL;
-
-#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR
-       ret = krb5_get_init_creds_opt_get_error(ctx, opt, &error);
-       if (ret) {
-               DEBUG(1,("krb5_get_init_creds_opt_get_error gave: %s\n", 
-                       error_message(ret)));
-               return False;
-       }
-#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_GET_ERROR */
-
-       if (!error) {
-               DEBUG(1,("no krb5_error\n"));
-               return False;
-       }
-
-#ifdef HAVE_E_DATA_POINTER_IN_KRB5_ERROR
-       if (!error->e_data) {
-#else
-       if (error->e_data.data == NULL) {
-#endif /* HAVE_E_DATA_POINTER_IN_KRB5_ERROR */
-               DEBUG(1,("no edata in krb5_error\n")); 
-               krb5_free_error(ctx, error);
-               return False;
-       }
-
-       ret = smb_krb5_get_ntstatus_from_krb5_error(error, nt_status);
-
-       krb5_free_error(ctx, error);
-
-       return ret;
-}
-
 /*
   simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL
   place in default cache location.
@@ -208,15 +121,19 @@ int kerberos_kinit_password_ext(const char *principal,
        krb5_error_code code = 0;
        krb5_ccache cc = NULL;
        krb5_principal me = NULL;
+       krb5_principal canon_princ = NULL;
        krb5_creds my_creds;
        krb5_get_init_creds_opt *opt = NULL;
        smb_krb5_addresses *addr = NULL;
 
        ZERO_STRUCT(my_creds);
 
-       initialize_krb5_error_table();
-       if ((code = krb5_init_context(&ctx)))
-               goto out;
+       code = smb_krb5_init_context_common(&ctx);
+       if (code != 0) {
+               DBG_ERR("kerberos init context failed (%s)\n",
+                       error_message(code));
+               return code;
+       }
 
        if (time_offset != 0) {
                krb5_set_real_time(ctx, time(NULL) + time_offset, 0);
@@ -235,12 +152,17 @@ int kerberos_kinit_password_ext(const char *principal,
                goto out;
        }
 
-       if ((code = smb_krb5_get_init_creds_opt_alloc(ctx, &opt))) {
+       if ((code = krb5_get_init_creds_opt_alloc(ctx, &opt))) {
                goto out;
        }
 
        krb5_get_init_creds_opt_set_renew_life(opt, renewable_time);
        krb5_get_init_creds_opt_set_forwardable(opt, True);
+
+       /* Turn on canonicalization for lower case realm support */
+#ifndef SAMBA4_USES_HEIMDAL /* MIT */
+       krb5_get_init_creds_opt_set_canonicalize(opt, true);
+#endif /* MIT */
 #if 0
        /* insane testing */
        krb5_get_init_creds_opt_set_tkt_life(opt, 60);
@@ -267,7 +189,12 @@ int kerberos_kinit_password_ext(const char *principal,
                goto out;
        }
 
-       if ((code = krb5_cc_initialize(ctx, cc, me))) {
+       canon_princ = me;
+#ifndef SAMBA4_USES_HEIMDAL /* MIT */
+       canon_princ = my_creds.client;
+#endif /* MIT */
+
+       if ((code = krb5_cc_initialize(ctx, cc, canon_princ))) {
                goto out;
        }
 
@@ -284,23 +211,12 @@ int kerberos_kinit_password_ext(const char *principal,
        }
  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);
        }
@@ -313,8 +229,8 @@ int kerberos_kinit_password_ext(const char *principal,
        if (addr) {
                smb_krb5_free_addresses(ctx, addr);
        }
-       if (opt) {
-               smb_krb5_get_init_creds_opt_free(ctx, opt);
+       if (opt) {
+               krb5_get_init_creds_opt_free(ctx, opt);
        }
        if (cc) {
                krb5_cc_close(ctx, cc);
@@ -331,10 +247,10 @@ int ads_kdestroy(const char *cc_name)
        krb5_context ctx = NULL;
        krb5_ccache cc = NULL;
 
-       initialize_krb5_error_table();
-       if ((code = krb5_init_context (&ctx))) {
-               DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", 
-                       error_message(code)));
+       code = smb_krb5_init_context_common(&ctx);
+       if (code != 0) {
+               DBG_ERR("kerberos init context failed (%s)\n",
+                       error_message(code));
                return code;
        }
 
@@ -361,134 +277,6 @@ int ads_kdestroy(const char *cc_name)
        return code;
 }
 
-/************************************************************************
- Routine to fetch the salting principal for a 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.
- ************************************************************************/
-
-static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype)
-{
-       char *key = NULL;
-       char *ret = NULL;
-
-       if (asprintf(&key, "%s/%s/enctype=%d",
-                    SECRETS_SALTING_PRINCIPAL, service, enctype) == -1) {
-               return NULL;
-       }
-       ret = (char *)secrets_fetch(key, NULL);
-       SAFE_FREE(key);
-       return ret;
-}
-
-/************************************************************************
- Return the standard DES salt key
-************************************************************************/
-
-char* kerberos_standard_des_salt( void )
-{
-       fstring salt;
-
-       fstr_sprintf( salt, "host/%s.%s@", lp_netbios_name(), lp_realm() );
-       (void)strlower_m( salt );
-       fstrcat( salt, lp_realm() );
-
-       return SMB_STRDUP( salt );
-}
-
-/************************************************************************
-************************************************************************/
-
-static char* des_salt_key( void )
-{
-       char *key;
-
-       if (asprintf(&key, "%s/DES/%s", SECRETS_SALTING_PRINCIPAL,
-                    lp_realm()) == -1) {
-               return NULL;
-       }
-
-       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;
-}
-
-/************************************************************************
-************************************************************************/
-
-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 NULL;
-       }
-
-       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.
- ************************************************************************/
-
-char *kerberos_fetch_salt_princ_for_host_princ(krb5_context context,
-                                              const char *host_princ_s,
-                                              int enctype)
-{
-       char *salt_princ_s;
-       /* lookup new key first */
-
-       salt_princ_s = kerberos_secrets_fetch_des_salt();
-       if (salt_princ_s == NULL) {
-
-               /* look under the old key.  If this fails, just use the standard key */
-               salt_princ_s = kerberos_secrets_fetch_salting_principal(host_princ_s,
-                                                                       enctype);
-               if (salt_princ_s == NULL) {
-                       /* fall back to host/machine.realm@REALM */
-                       salt_princ_s = kerberos_standard_des_salt();
-               }
-       }
-
-       return salt_princ_s;
-}
-
 int create_kerberos_key_from_string(krb5_context context,
                                        krb5_principal host_princ,
                                        krb5_principal salt_princ,
@@ -522,78 +310,6 @@ int create_kerberos_key_from_string(krb5_context context,
        return ret;
 }
 
-/************************************************************************
- Routine to set 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.
- Setting principal to NULL deletes this entry.
- ************************************************************************/
-
-bool kerberos_secrets_store_salting_principal(const char *service,
-                                             int enctype,
-                                             const char *principal)
-{
-       char *key = NULL;
-       bool ret = False;
-       krb5_context context = NULL;
-       krb5_principal princ = NULL;
-       char *princ_s = NULL;
-       char *unparsed_name = NULL;
-       krb5_error_code code;
-
-       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, '@')) {
-               if (asprintf(&princ_s, "%s", service) == -1) {
-                       goto out;
-               }
-       } else {
-               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(talloc_tos(), context, princ, &unparsed_name) != 0) {
-               goto out;
-       }
-
-       if (asprintf(&key, "%s/%s/enctype=%d",
-                    SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype)
-           == -1) {
-               goto out;
-       }
-
-       if ((principal != NULL) && (strlen(principal) > 0)) {
-               ret = secrets_store(key, principal, strlen(principal) + 1);
-       } else {
-               ret = secrets_delete(key);
-       }
-
- out:
-
-       SAFE_FREE(key);
-       SAFE_FREE(princ_s);
-       TALLOC_FREE(unparsed_name);
-
-       if (princ) {
-               krb5_free_principal(context, princ);
-       }
-
-       if (context) {
-               krb5_free_context(context);
-       }
-
-       return ret;
-}
-
-
 /************************************************************************
 ************************************************************************/
 
@@ -625,10 +341,10 @@ int kerberos_kinit_password(const char *principal,
 
 ************************************************************************/
 
-static void add_sockaddr_unique(struct sockaddr_storage *addrs, int *num_addrs,
+static void add_sockaddr_unique(struct sockaddr_storage *addrs, size_t *num_addrs,
                                const struct sockaddr_storage *addr)
 {
-       int i;
+       size_t i;
 
        for (i=0; i<*num_addrs; i++) {
                if (sockaddr_equal((const struct sockaddr *)&addrs[i],
@@ -671,12 +387,12 @@ static char *get_kdc_ip_string(char *mem_ctx,
                const struct sockaddr_storage *pss)
 {
        TALLOC_CTX *frame = talloc_stackframe();
-       int i;
+       size_t 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;
+       size_t num_dcs;
        struct sockaddr_storage *dc_addrs;
        struct tsocket_address **dc_addrs2 = NULL;
        const struct tsocket_address * const *dc_addrs3 = NULL;
@@ -737,7 +453,7 @@ static char *get_kdc_ip_string(char *mem_ctx,
                                      struct tsocket_address *,
                                      num_dcs);
 
-       DEBUG(10, ("%d additional KDCs to test\n", num_dcs));
+       DBG_DEBUG("%zu additional KDCs to test\n", num_dcs);
        if (num_dcs == 0) {
                goto out;
        }
@@ -883,6 +599,28 @@ static char *get_enctypes(TALLOC_CTX *mem_ctx)
 }
 #endif
 
+uint32_t kerberos_supported_encryption_types(void)
+{
+       uint32_t encryption_types = 0;
+
+       if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
+           lp_kerberos_encryption_types() == KERBEROS_ETYPES_STRONG) {
+#ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+               encryption_types |= ENC_HMAC_SHA1_96_AES128;
+#endif
+#ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+               encryption_types |= ENC_HMAC_SHA1_96_AES256;
+#endif
+       }
+
+       if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_ALL ||
+           lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
+               encryption_types |= ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
+       }
+
+       return encryption_types;
+}
+
 bool create_local_private_krb5_conf_for_domain(const char *realm,
                                                const char *domain,
                                                const char *sitename,
@@ -899,6 +637,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
        char *realm_upper = NULL;
        bool result = false;
        char *enctypes = NULL;
+       const char *include_system_krb5 = "";
        mode_t mask;
 
        if (!lp_create_krb5_conf()) {
@@ -915,7 +654,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
                return false;
        }
 
-       dname = lock_path("smb_krb5");
+       dname = lock_path(talloc_tos(), "smb_krb5");
        if (!dname) {
                return false;
        }
@@ -926,7 +665,7 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
                goto done;
        }
 
-       tmpname = lock_path("smb_tmp_krb5.XXXXXX");
+       tmpname = lock_path(talloc_tos(), "smb_tmp_krb5.XXXXXX");
        if (!tmpname) {
                goto done;
        }
@@ -954,13 +693,25 @@ bool create_local_private_krb5_conf_for_domain(const char *realm,
                goto done;
        }
 
+#if !defined(SAMBA4_USES_HEIMDAL)
+       if (lp_include_system_krb5_conf()) {
+               include_system_krb5 = "include /etc/krb5.conf";
+       }
+#endif
+
        file_contents =
-           talloc_asprintf(fname, "[libdefaults]\n\tdefault_realm = %s\n"
-                                  "%s"
-                                  "\tdns_lookup_realm = false\n\n"
-                                  "[realms]\n\t%s = {\n"
-                                  "%s\t}\n",
-                           realm_upper, enctypes, realm_upper, kdc_ip_string);
+           talloc_asprintf(fname,
+                           "[libdefaults]\n\tdefault_realm = %s\n"
+                           "%s"
+                           "\tdns_lookup_realm = false\n\n"
+                           "[realms]\n\t%s = {\n"
+                           "%s\t}\n"
+                           "%s\n",
+                           realm_upper,
+                           enctypes,
+                           realm_upper,
+                           kdc_ip_string,
+                           include_system_krb5);
 
        if (!file_contents) {
                goto done;