source4/smbd: Do not overstamp the process model with "single"
[amitay/samba.git] / source4 / kdc / db-glue.c
index af9fa268b325c11e6df43ae5ba54a765bdd77e2d..9ac5a1d38f06d227ad1eb4bc37f64a9656cb3d92 100644 (file)
@@ -35,6 +35,9 @@
 #include "kdc/sdb.h"
 #include "kdc/samba_kdc.h"
 #include "kdc/db-glue.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+#include "lib/messaging/irpc.h"
+
 
 #define SAMBA_KVNO_GET_KRBTGT(kvno) \
        ((uint16_t)(((uint32_t)kvno) >> 16))
@@ -65,6 +68,52 @@ static const char *trust_attrs[] = {
        NULL
 };
 
+/*
+  send a message to the drepl server telling it to initiate a
+  REPL_SECRET getncchanges extended op to fetch the users secrets
+ */
+static void auth_sam_trigger_repl_secret(TALLOC_CTX *mem_ctx,
+                                  struct imessaging_context *msg_ctx,
+                                  struct tevent_context *event_ctx,
+                                  struct ldb_dn *user_dn)
+{
+        struct dcerpc_binding_handle *irpc_handle;
+        struct drepl_trigger_repl_secret r;
+        struct tevent_req *req;
+        TALLOC_CTX *tmp_ctx;
+
+        tmp_ctx = talloc_new(mem_ctx);
+        if (tmp_ctx == NULL) {
+                return;
+        }
+
+        irpc_handle = irpc_binding_handle_by_name(tmp_ctx, msg_ctx,
+                                                  "dreplsrv",
+                                                  &ndr_table_irpc);
+        if (irpc_handle == NULL) {
+                DEBUG(1,(__location__ ": Unable to get binding handle for dreplsrv\n"));
+                TALLOC_FREE(tmp_ctx);
+                return;
+        }
+
+        r.in.user_dn = ldb_dn_get_linearized(user_dn);
+
+        /*
+         * This seem to rely on the current IRPC implementation,
+         * which delivers the message in the _send function.
+         *
+         * TODO: we need a ONE_WAY IRPC handle and register
+         * a callback and wait for it to be triggered!
+         */
+        req = dcerpc_drepl_trigger_repl_secret_r_send(tmp_ctx,
+                                                      event_ctx,
+                                                      irpc_handle,
+                                                      &r);
+
+        /* we aren't interested in a reply */
+        talloc_free(req);
+        TALLOC_FREE(tmp_ctx);
+}
 
 static time_t ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, time_t default_val)
 {
@@ -196,6 +245,69 @@ static int samba_kdc_entry_destructor(struct samba_kdc_entry *p)
        return 0;
 }
 
+/*
+ * Sort keys in descending order of strength.
+ *
+ * Explanaton from Greg Hudson:
+ *
+ * To encrypt tickets only the first returned key is used by the MIT KDC.  The
+ * other keys just communicate support for session key enctypes, and aren't
+ * really used.  The encryption key for the ticket enc part doesn't have
+ * to be of a type requested by the client. The session key enctype is chosen
+ * based on the client preference order, limited by the set of enctypes present
+ * in the server keys (unless the string attribute is set on the server
+ * principal overriding that set).
+ */
+static int samba_kdc_sort_encryption_keys(struct sdb_entry_ex *entry_ex)
+{
+       unsigned int i, j, idx = 0;
+       static const krb5_enctype etype_list[] = {
+               ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+               ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+               ENCTYPE_DES3_CBC_SHA1,
+               ENCTYPE_ARCFOUR_HMAC,
+               ENCTYPE_DES_CBC_MD5,
+               ENCTYPE_DES_CBC_MD4,
+               ENCTYPE_DES_CBC_CRC,
+               ENCTYPE_NULL
+       };
+       size_t etype_len = ARRAY_SIZE(etype_list);
+       size_t keys_size = entry_ex->entry.keys.len;
+       struct sdb_key *keys = entry_ex->entry.keys.val;
+       struct sdb_key *sorted_keys;
+
+       sorted_keys = calloc(keys_size, sizeof(struct sdb_key));
+       if (sorted_keys == NULL) {
+               return -1;
+       }
+
+       for (i = 0; i < etype_len; i++) {
+               for (j = 0; j < keys_size; j++) {
+                       const struct sdb_key skey = keys[j];
+
+                       if (idx == keys_size) {
+                               break;
+                       }
+
+                       if (KRB5_KEY_TYPE(&skey.key) == etype_list[i]) {
+                               sorted_keys[idx] = skey;
+                               idx++;
+                       }
+               }
+       }
+
+       /* Paranoia: Something went wrong during data copy */
+       if (idx != keys_size) {
+               free(sorted_keys);
+               return -1;
+       }
+
+       free(entry_ex->entry.keys.val);
+       entry_ex->entry.keys.val = sorted_keys;
+
+       return 0;
+}
+
 static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                                    struct samba_kdc_db_context *kdc_db_ctx,
                                                    TALLOC_CTX *mem_ctx,
@@ -261,6 +373,81 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
        entry_ex->entry.keys.val = NULL;
        entry_ex->entry.keys.len = 0;
+       entry_ex->entry.kvno = 0;
+
+       if ((ent_type == SAMBA_KDC_ENT_TYPE_CLIENT)
+           && (userAccountControl & UF_SMARTCARD_REQUIRED)) {
+               uint8_t secretbuffer[32];
+
+               /*
+                * Fake keys until we have a better way to reject
+                * non-pkinit requests.
+                *
+                * We just need to indicate which encryption types are
+                * supported.
+                */
+               generate_secret_buffer(secretbuffer, sizeof(secretbuffer));
+
+               allocated_keys = 3;
+               entry_ex->entry.keys.len = 0;
+               entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(struct sdb_key));
+               if (entry_ex->entry.keys.val == NULL) {
+                       ZERO_STRUCT(secretbuffer);
+                       ret = ENOMEM;
+                       goto out;
+               }
+
+               if (supported_enctypes & ENC_HMAC_SHA1_96_AES256) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+                                                             secretbuffer, 32,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENC_HMAC_SHA1_96_AES128) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+                                                             secretbuffer, 16,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               if (supported_enctypes & ENC_RC4_HMAC_MD5) {
+                       struct sdb_key key = {};
+
+                       ret = smb_krb5_keyblock_init_contents(context,
+                                                             ENCTYPE_ARCFOUR_HMAC,
+                                                             secretbuffer, 16,
+                                                             &key.key);
+                       if (ret) {
+                               ZERO_STRUCT(secretbuffer);
+                               goto out;
+                       }
+
+                       entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key;
+                       entry_ex->entry.keys.len++;
+               }
+
+               ret = 0;
+               goto out;
+       }
 
        kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0);
        if (is_rodc) {
@@ -289,9 +476,11 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                }
 
                if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) {
-                       NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
-                       ret = EINVAL;
-                       goto out;
+                       if (scb.sub.num_packages != 0) {
+                               NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb);
+                               ret = EINVAL;
+                               goto out;
+                       }
                }
 
                for (i=0; i < scb.sub.num_packages; i++) {
@@ -364,7 +553,8 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
        if (allocated_keys == 0) {
                if (kdc_db_ctx->rodc) {
                        /* We are on an RODC, but don't have keys for this account.  Signal this to the caller */
-                       /* TODO:  We need to call a generalised version of auth_sam_trigger_repl_secret from here */
+                       auth_sam_trigger_repl_secret(kdc_db_ctx, kdc_db_ctx->msg_ctx,
+                                                    kdc_db_ctx->ev_ctx, msg->dn);
                        return SDB_ERR_NOT_FOUND_HERE;
                }
 
@@ -421,9 +611,9 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
                                key.salt->type = KRB5_PW_SALT;
 
-                               ret = krb5_copy_data_contents(&key.salt->salt,
-                                                             salt.data,
-                                                             salt.length);
+                               ret = smb_krb5_copy_data_contents(&key.salt->salt,
+                                                                 salt.data,
+                                                                 salt.length);
                                if (ret) {
                                        free(key.salt);
                                        key.salt = NULL;
@@ -446,7 +636,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                        }
                        if (ret) {
                                if (key.salt) {
-                                       kerberos_free_data_contents(context, &key.salt->salt);
+                                       smb_krb5_free_data_contents(context, &key.salt->salt);
                                        free(key.salt);
                                        key.salt = NULL;
                                }
@@ -479,9 +669,9 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 
                                key.salt->type = KRB5_PW_SALT;
 
-                               ret = krb5_copy_data_contents(&key.salt->salt,
-                                                             salt.data,
-                                                             salt.length);
+                               ret = smb_krb5_copy_data_contents(&key.salt->salt,
+                                                                 salt.data,
+                                                                 salt.length);
                                if (ret) {
                                        free(key.salt);
                                        key.salt = NULL;
@@ -496,7 +686,7 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
                                                              &key.key);
                        if (ret) {
                                if (key.salt) {
-                                       kerberos_free_data_contents(context, &key.salt->salt);
+                                       smb_krb5_free_data_contents(context, &key.salt->salt);
                                        free(key.salt);
                                        key.salt = NULL;
                                }
@@ -511,6 +701,13 @@ static krb5_error_code samba_kdc_message2entry_keys(krb5_context context,
 out:
        if (ret != 0) {
                entry_ex->entry.keys.len = 0;
+       } else if (entry_ex->entry.keys.len > 0 &&
+                  entry_ex->entry.keys.val != NULL) {
+               ret = samba_kdc_sort_encryption_keys(entry_ex);
+               if (ret != 0) {
+                       entry_ex->entry.keys.len = 0;
+                       ret = ENOMEM;
+               }
        }
        if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) {
                free(entry_ex->entry.keys.val);
@@ -589,7 +786,6 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
        struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
        uint32_t userAccountControl;
        uint32_t msDS_User_Account_Control_Computed;
-       unsigned int i;
        krb5_error_code ret = 0;
        krb5_boolean is_computer = FALSE;
 
@@ -880,8 +1076,9 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
 
        } else {
                NTTIME must_change_time
-                       = samdb_result_force_password_change(kdc_db_ctx->samdb, mem_ctx,
-                                                            realm_dn, msg);
+                       = samdb_result_nttime(msg,
+                                       "msDS-UserPasswordExpiryTimeComputed",
+                                       0);
                if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) {
                        entry_ex->entry.pw_end = NULL;
                } else {
@@ -940,24 +1137,6 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
                goto out;
        }
 
-       entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
-       if (entry_ex->entry.etypes == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
-               goto out;
-       }
-       entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
-       entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
-       if (entry_ex->entry.etypes->val == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
-               goto out;
-       }
-       for (i=0; i < entry_ex->entry.etypes->len; i++) {
-               entry_ex->entry.etypes->val[i] = KRB5_KEY_TYPE(&entry_ex->entry.keys.val[i].key);
-       }
-
-
        p->msg = talloc_steal(p, msg);
 
 out:
@@ -1289,7 +1468,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                                                              ENCTYPE_AES256_CTS_HMAC_SHA1_96,
                                                              &key.key);
                        if (ret != 0) {
-                               kerberos_free_data_contents(context, &salt);
+                               smb_krb5_free_data_contents(context, &salt);
                                goto out;
                        }
 
@@ -1305,7 +1484,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                                                              ENCTYPE_AES128_CTS_HMAC_SHA1_96,
                                                              &key.key);
                        if (ret != 0) {
-                               kerberos_free_data_contents(context, &salt);
+                               smb_krb5_free_data_contents(context, &salt);
                                goto out;
                        }
 
@@ -1313,7 +1492,7 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
                        entry_ex->entry.keys.len++;
                }
 
-               kerberos_free_data_contents(context, &salt);
+               smb_krb5_free_data_contents(context, &salt);
        }
 
        if (password_hash != NULL) {
@@ -1344,22 +1523,12 @@ static krb5_error_code samba_kdc_trust_message2entry(krb5_context context,
 
        entry_ex->entry.max_renew = NULL;
 
-       entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes)));
-       if (entry_ex->entry.etypes == NULL) {
-               krb5_clear_error_message(context);
-               ret = ENOMEM;
-               goto out;
-       }
-       entry_ex->entry.etypes->len = entry_ex->entry.keys.len;
-       entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int));
-       if (entry_ex->entry.etypes->val == NULL) {
+       ret = samba_kdc_sort_encryption_keys(entry_ex);
+       if (ret != 0) {
                krb5_clear_error_message(context);
                ret = ENOMEM;
                goto out;
        }
-       for (i=0; i < entry_ex->entry.etypes->len; i++) {
-               entry_ex->entry.etypes->val[i] = KRB5_KEY_TYPE(&entry_ex->entry.keys.val[i].key);
-       }
 
        p->msg = talloc_steal(p, msg);
 
@@ -2558,6 +2727,7 @@ NTSTATUS samba_kdc_setup_db_ctx(TALLOC_CTX *mem_ctx, struct samba_kdc_base_conte
        }
        kdc_db_ctx->ev_ctx = base_ctx->ev_ctx;
        kdc_db_ctx->lp_ctx = base_ctx->lp_ctx;
+       kdc_db_ctx->msg_ctx = base_ctx->msg_ctx;
 
        /* get default kdc policy */
        lpcfg_default_kdc_policy(base_ctx->lp_ctx,