summaryrefslogtreecommitdiffstats
path: root/source4/kdc/mit-kdb
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/kdc/mit-kdb
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/kdc/mit-kdb')
-rw-r--r--source4/kdc/mit-kdb/kdb_samba.c182
-rw-r--r--source4/kdc/mit-kdb/kdb_samba.h184
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_change_pwd.c59
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_common.c114
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_masterkey.c69
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_pac.c115
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_policies.c769
-rw-r--r--source4/kdc/mit-kdb/kdb_samba_principals.c397
-rw-r--r--source4/kdc/mit-kdb/wscript_build22
9 files changed, 1911 insertions, 0 deletions
diff --git a/source4/kdc/mit-kdb/kdb_samba.c b/source4/kdc/mit-kdb/kdb_samba.c
new file mode 100644
index 0000000..0ff1bfe
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba.c
@@ -0,0 +1,182 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/samba_kdc.h"
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+static krb5_error_code kdb_samba_init_library(void)
+{
+ return 0;
+}
+
+static krb5_error_code kdb_samba_fini_library(void)
+{
+ return 0;
+}
+
+static krb5_error_code kdb_samba_init_module(krb5_context context,
+ char *conf_section,
+ char **db_args,
+ int mode)
+{
+ /* TODO mit_samba_context_init */
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+ int rc;
+
+ rc = mit_samba_context_init(&mit_ctx);
+ if (rc != 0) {
+ return ENOMEM;
+ }
+
+
+ code = krb5_db_set_context(context, mit_ctx);
+
+ return code;
+}
+static krb5_error_code kdb_samba_fini_module(krb5_context context)
+{
+ struct mit_samba_context *mit_ctx;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return 0;
+ }
+
+ mit_samba_context_free(mit_ctx);
+
+ return 0;
+}
+
+static krb5_error_code kdb_samba_db_create(krb5_context context,
+ char *conf_section,
+ char **db_args)
+{
+ /* NOTE: used only by kadmin */
+ return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code kdb_samba_db_destroy(krb5_context context,
+ char *conf_section,
+ char **db_args)
+{
+ /* NOTE: used only by kadmin */
+ return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code kdb_samba_db_get_age(krb5_context context,
+ char *db_name,
+ time_t *age)
+{
+ /* TODO: returns last modification time of the db */
+
+ /* NOTE: used by and affects only lookaside cache,
+ * defer implementation until needed as samba doesn't keep this
+ * specific value readily available and it would require a full
+ * database search to get it. */
+
+ *age = time(NULL);
+
+ return 0;
+}
+
+static krb5_error_code kdb_samba_db_lock(krb5_context context, int kmode)
+{
+
+ /* NOTE: important only for kadmin */
+ /* NOTE: deferred as samba's DB cannot be easily locked and doesn't
+ * really make sense to do so anyway as the db is shared and support
+ * transactions */
+ return 0;
+}
+
+static krb5_error_code kdb_samba_db_unlock(krb5_context context)
+{
+
+ /* NOTE: important only for kadmin */
+ /* NOTE: deferred as samba's DB cannot be easily locked and doesn't
+ * really make sense to do so anyway as the db is shared and support
+ * transactions */
+ return 0;
+}
+
+static void kdb_samba_db_free_principal_e_data(krb5_context context,
+ krb5_octet *e_data)
+{
+ struct samba_kdc_entry *skdc_entry;
+
+ skdc_entry = talloc_get_type_abort(e_data,
+ struct samba_kdc_entry);
+ skdc_entry->kdc_entry = NULL;
+ TALLOC_FREE(skdc_entry);
+}
+
+kdb_vftabl kdb_function_table = {
+ .maj_ver = KRB5_KDB_DAL_MAJOR_VERSION,
+ .min_ver = KRB5_KDB_DAL_MAJOR_VERSION == 6 ? 1 : 0,
+
+ .init_library = kdb_samba_init_library,
+ .fini_library = kdb_samba_fini_library,
+ .init_module = kdb_samba_init_module,
+ .fini_module = kdb_samba_fini_module,
+
+ .create = kdb_samba_db_create,
+ .destroy = kdb_samba_db_destroy,
+ .get_age = kdb_samba_db_get_age,
+ .lock = kdb_samba_db_lock,
+ .unlock = kdb_samba_db_unlock,
+
+ .get_principal = kdb_samba_db_get_principal,
+ .put_principal = kdb_samba_db_put_principal,
+ .delete_principal = kdb_samba_db_delete_principal,
+
+ .iterate = kdb_samba_db_iterate,
+
+ .fetch_master_key = kdb_samba_fetch_master_key,
+ .fetch_master_key_list = kdb_samba_fetch_master_key_list,
+
+ .change_pwd = kdb_samba_change_pwd,
+
+ .decrypt_key_data = kdb_samba_dbekd_decrypt_key_data,
+ .encrypt_key_data = kdb_samba_dbekd_encrypt_key_data,
+
+ .check_policy_as = kdb_samba_db_check_policy_as,
+ .audit_as_req = kdb_samba_db_audit_as_req,
+ .check_allowed_to_delegate = kdb_samba_db_check_allowed_to_delegate,
+
+ .free_principal_e_data = kdb_samba_db_free_principal_e_data,
+
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
+ .allowed_to_delegate_from = kdb_samba_db_allowed_to_delegate_from,
+ .issue_pac = kdb_samba_db_issue_pac,
+#else
+ .sign_authdata = kdb_samba_db_sign_auth_data,
+#endif
+};
diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h
new file mode 100644
index 0000000..138105e
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba.h
@@ -0,0 +1,184 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2009 Simo Sorce <idra@samba.org>.
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _KDB_SAMBA_H_
+#define _KDB_SAMBA_H_
+
+#include <stdbool.h>
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+
+#define PAC_LOGON_INFO 1
+
+#ifndef discard_const_p
+#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T)
+# define discard_const_p(type, ptr) ((type *)((intptr_t)(ptr)))
+#else
+# define discard_const_p(type, ptr) ((type *)(ptr))
+#endif
+#endif
+
+/* from kdb_samba_common.c */
+
+struct mit_samba_context *ks_get_context(krb5_context kcontext);
+
+krb5_error_code ks_get_principal(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int kflags,
+ krb5_db_entry **kentry);
+
+void ks_free_principal(krb5_context context, krb5_db_entry *entry);
+
+bool ks_data_eq_string(krb5_data d, const char *s);
+
+krb5_data ks_make_data(void *data, unsigned int len);
+
+krb5_boolean ks_is_kadmin(krb5_context context,
+ krb5_const_principal princ);
+
+krb5_boolean ks_is_kadmin_history(krb5_context context,
+ krb5_const_principal princ);
+
+krb5_boolean ks_is_kadmin_changepw(krb5_context context,
+ krb5_const_principal princ);
+
+krb5_boolean ks_is_kadmin_admin(krb5_context context,
+ krb5_const_principal princ);
+
+/* from kdb_samba_principals.c */
+
+krb5_error_code kdb_samba_db_get_principal(krb5_context context,
+ krb5_const_principal princ,
+ unsigned int kflags,
+ krb5_db_entry **kentry);
+
+krb5_error_code kdb_samba_db_put_principal(krb5_context context,
+ krb5_db_entry *entry,
+ char **db_args);
+
+krb5_error_code kdb_samba_db_delete_principal(krb5_context context,
+ krb5_const_principal princ);
+
+krb5_error_code kdb_samba_db_iterate(krb5_context context,
+ char *match_entry,
+ int (*func)(krb5_pointer, krb5_db_entry *),
+ krb5_pointer func_arg,
+ krb5_flags iterflags);
+
+/* from kdb_samba_masterkey.c */
+
+krb5_error_code kdb_samba_fetch_master_key(krb5_context context,
+ krb5_principal name,
+ krb5_keyblock *key,
+ krb5_kvno *kvno,
+ char *db_args);
+
+krb5_error_code kdb_samba_fetch_master_key_list(krb5_context context,
+ krb5_principal mname,
+ const krb5_keyblock *key,
+ krb5_keylist_node **mkeys_list);
+
+/* from kdb_samba_pac.c */
+
+krb5_error_code kdb_samba_dbekd_decrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_key_data *key_data,
+ krb5_keyblock *kkey,
+ krb5_keysalt *keysalt);
+
+krb5_error_code kdb_samba_dbekd_encrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_keyblock *kkey,
+ const krb5_keysalt *keysalt,
+ int keyver,
+ krb5_key_data *key_data);
+
+/* from kdb_samba_policies.c */
+krb5_error_code kdb_samba_db_issue_pac(krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_keyblock *replaced_reply_key,
+ krb5_db_entry *server,
+ krb5_db_entry *signing_krbtgt,
+ krb5_timestamp authtime,
+ krb5_pac old_pac,
+ krb5_pac new_pac,
+ krb5_data ***auth_indicators);
+
+krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_db_entry *local_krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *local_krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ void *authdata_info,
+ krb5_data ***auth_indicators,
+ krb5_authdata ***signed_auth_data);
+
+krb5_error_code kdb_samba_db_check_policy_as(krb5_context context,
+ krb5_kdc_req *kdcreq,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp kdc_time,
+ const char **status,
+ krb5_pa_data ***e_data_out);
+
+krb5_error_code kdb_samba_db_check_allowed_to_delegate(krb5_context context,
+ krb5_const_principal client,
+ const krb5_db_entry *server,
+ krb5_const_principal proxy);
+
+krb5_error_code kdb_samba_db_allowed_to_delegate_from(
+ krb5_context context,
+ krb5_const_principal client,
+ krb5_const_principal server,
+ krb5_pac header_pac,
+ const krb5_db_entry *proxy);
+
+void kdb_samba_db_audit_as_req(krb5_context kcontext,
+ krb5_kdc_req *request,
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code error_code);
+
+/* from kdb_samba_change_pwd.c */
+
+krb5_error_code kdb_samba_change_pwd(krb5_context context,
+ krb5_keyblock *master_key,
+ krb5_key_salt_tuple *ks_tuple,
+ int ks_tuple_count, char *passwd,
+ int new_kvno, krb5_boolean keepold,
+ krb5_db_entry *db_entry);
+
+#endif /* _KDB_SAMBA_H_ */
diff --git a/source4/kdc/mit-kdb/kdb_samba_change_pwd.c b/source4/kdc/mit-kdb/kdb_samba_change_pwd.c
new file mode 100644
index 0000000..ad7bb5d
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_change_pwd.c
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+krb5_error_code kdb_samba_change_pwd(krb5_context context,
+ krb5_keyblock *master_key,
+ krb5_key_salt_tuple *ks_tuple,
+ int ks_tuple_count, char *passwd,
+ int new_kvno, krb5_boolean keepold,
+ krb5_db_entry *db_entry)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_kpasswd_change_password(mit_ctx, passwd, db_entry);
+ if (code != 0) {
+ goto cleanup;
+ }
+
+cleanup:
+
+ return code;
+}
diff --git a/source4/kdc/mit-kdb/kdb_samba_common.c b/source4/kdc/mit-kdb/kdb_samba_common.c
new file mode 100644
index 0000000..1ad1c14
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_common.c
@@ -0,0 +1,114 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+struct mit_samba_context *ks_get_context(krb5_context kcontext)
+{
+ struct mit_samba_context *mit_ctx = NULL;
+ void *db_ctx = NULL;
+ krb5_error_code code;
+
+ code = krb5_db_get_context(kcontext, &db_ctx);
+ if (code != 0) {
+ return NULL;
+ }
+
+ mit_ctx = talloc_get_type_abort(db_ctx, struct mit_samba_context);
+
+ /*
+ * This is nomrally the starting point for Kerberos operations in
+ * MIT KRB5, so reset errno to 0 for possible com_err debug messages.
+ */
+ errno = 0;
+
+ return mit_ctx;
+}
+
+bool ks_data_eq_string(krb5_data d, const char *s)
+{
+ int rc;
+
+ if (d.length != strlen(s) || d.length == 0) {
+ return false;
+ }
+
+ rc = memcmp(d.data, s, d.length);
+ if (rc != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+krb5_data ks_make_data(void *data, unsigned int len)
+{
+ krb5_data d;
+
+ d.magic = KV5M_DATA;
+ d.data = data;
+ d.length = len;
+
+ return d;
+}
+
+krb5_boolean ks_is_kadmin(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) >= 1 &&
+ ks_data_eq_string(princ->data[0], "kadmin");
+}
+
+krb5_boolean ks_is_kadmin_history(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ ks_data_eq_string(princ->data[0], "kadmin") &&
+ ks_data_eq_string(princ->data[1], "history");
+}
+
+krb5_boolean ks_is_kadmin_changepw(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ ks_data_eq_string(princ->data[0], "kadmin") &&
+ ks_data_eq_string(princ->data[1], "changepw");
+}
+
+krb5_boolean ks_is_kadmin_admin(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ ks_data_eq_string(princ->data[0], "kadmin") &&
+ ks_data_eq_string(princ->data[1], "admin");
+}
diff --git a/source4/kdc/mit-kdb/kdb_samba_masterkey.c b/source4/kdc/mit-kdb/kdb_samba_masterkey.c
new file mode 100644
index 0000000..b068d96
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_masterkey.c
@@ -0,0 +1,69 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+krb5_error_code kdb_samba_fetch_master_key(krb5_context context,
+ krb5_principal name,
+ krb5_keyblock *key,
+ krb5_kvno *kvno,
+ char *db_args)
+{
+ return 0;
+}
+
+krb5_error_code kdb_samba_fetch_master_key_list(krb5_context context,
+ krb5_principal mname,
+ const krb5_keyblock *key,
+ krb5_keylist_node **mkeys_list)
+{
+ krb5_keylist_node *mkey;
+
+ /*
+ * NOTE: samba does not support master keys
+ * so just return a dummy key
+ */
+ mkey = calloc(1, sizeof(krb5_keylist_node));
+ if (mkey == NULL) {
+ return ENOMEM;
+ }
+
+ mkey->keyblock.magic = KV5M_KEYBLOCK;
+ mkey->keyblock.enctype = ENCTYPE_UNKNOWN;
+ mkey->kvno = 1;
+
+ *mkeys_list = mkey;
+
+ return 0;
+}
diff --git a/source4/kdc/mit-kdb/kdb_samba_pac.c b/source4/kdc/mit-kdb/kdb_samba_pac.c
new file mode 100644
index 0000000..75b05a6
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_pac.c
@@ -0,0 +1,115 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+krb5_error_code kdb_samba_dbekd_decrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_key_data *key_data,
+ krb5_keyblock *kkey,
+ krb5_keysalt *keysalt)
+{
+ /*
+ * NOTE: Samba doesn't use a master key, so we will just copy
+ * the contents around untouched.
+ */
+ ZERO_STRUCTP(kkey);
+
+ kkey->magic = KV5M_KEYBLOCK;
+ kkey->enctype = key_data->key_data_type[0];
+ kkey->contents = malloc(key_data->key_data_length[0]);
+ if (kkey->contents == NULL) {
+ return ENOMEM;
+ }
+ memcpy(kkey->contents,
+ key_data->key_data_contents[0],
+ key_data->key_data_length[0]);
+ kkey->length = key_data->key_data_length[0];
+
+ if (keysalt != NULL) {
+ keysalt->type = key_data->key_data_type[1];
+ keysalt->data.data = malloc(key_data->key_data_length[1]);
+ if (keysalt->data.data == NULL) {
+ free(kkey->contents);
+ return ENOMEM;
+ }
+ memcpy(keysalt->data.data,
+ key_data->key_data_contents[1],
+ key_data->key_data_length[1]);
+ keysalt->data.length = key_data->key_data_length[1];
+ }
+
+ return 0;
+}
+
+krb5_error_code kdb_samba_dbekd_encrypt_key_data(krb5_context context,
+ const krb5_keyblock *mkey,
+ const krb5_keyblock *kkey,
+ const krb5_keysalt *keysalt,
+ int keyver,
+ krb5_key_data *key_data)
+{
+ /*
+ * NOTE: samba doesn't use a master key, so we will just copy
+ * the contents around untouched.
+ */
+
+ ZERO_STRUCTP(key_data);
+
+ key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ key_data->key_data_kvno = keyver;
+ key_data->key_data_type[0] = kkey->enctype;
+ key_data->key_data_contents[0] = malloc(kkey->length);
+ if (key_data->key_data_contents[0] == NULL) {
+ return ENOMEM;
+ }
+ memcpy(key_data->key_data_contents[0],
+ kkey->contents,
+ kkey->length);
+ key_data->key_data_length[0] = kkey->length;
+
+ if (keysalt != NULL) {
+ key_data->key_data_type[1] = keysalt->type;
+ key_data->key_data_contents[1] = malloc(keysalt->data.length);
+ if (key_data->key_data_contents[1] == NULL) {
+ free(key_data->key_data_contents[0]);
+ return ENOMEM;
+ }
+ memcpy(key_data->key_data_contents[1],
+ keysalt->data.data,
+ keysalt->data.length);
+ key_data->key_data_length[1] = keysalt->data.length;
+ }
+
+ return 0;
+}
diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c
new file mode 100644
index 0000000..0004854
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c
@@ -0,0 +1,769 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014-2021 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/replace/replace.h"
+#include "lib/replace/system/kerberos.h"
+#include "lib/util/data_blob.h"
+#include "lib/util/debug.h"
+#include "lib/util/fault.h"
+#include "lib/util/memory.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+/* FIXME: This is a krb5 function which is exported, but in no header */
+extern krb5_error_code decode_krb5_padata_sequence(const krb5_data *output,
+ krb5_pa_data ***rep);
+
+static krb5_error_code ks_get_netbios_name(krb5_address **addrs, char **name)
+{
+ char *nb_name = NULL;
+ int len, i;
+
+ for (i = 0; addrs[i]; i++) {
+ if (addrs[i]->addrtype != ADDRTYPE_NETBIOS) {
+ continue;
+ }
+ len = MIN(addrs[i]->length, 15);
+ nb_name = strndup((const char *)addrs[i]->contents, len);
+ if (!nb_name) {
+ return ENOMEM;
+ }
+ break;
+ }
+
+ if (nb_name) {
+ /* Strip space padding */
+ i = strlen(nb_name) - 1;
+ for (i = strlen(nb_name) - 1;
+ i > 0 && nb_name[i] == ' ';
+ i--) {
+ nb_name[i] = '\0';
+ }
+ }
+
+ *name = nb_name;
+
+ return 0;
+}
+
+krb5_error_code kdb_samba_db_check_policy_as(krb5_context context,
+ krb5_kdc_req *kdcreq,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp kdc_time,
+ const char **status,
+ krb5_pa_data ***e_data_out)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+ char *client_name = NULL;
+ char *server_name = NULL;
+ char *netbios_name = NULL;
+ char *realm = NULL;
+ bool password_change = false;
+ krb5_const_principal client_princ;
+ DATA_BLOB int_data = { NULL, 0 };
+ krb5_data d;
+ krb5_pa_data **e_data;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ /* Prefer canonicalised name from client entry */
+ client_princ = client ? client->princ : kdcreq->client;
+
+ if (client_princ == NULL || ks_is_kadmin(context, client_princ)) {
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ }
+
+ if (krb5_princ_size(context, kdcreq->server) == 2 &&
+ ks_is_kadmin_changepw(context, kdcreq->server)) {
+ code = krb5_get_default_realm(context, &realm);
+ if (code) {
+ goto done;
+ }
+
+ if (ks_data_eq_string(kdcreq->server->realm, realm)) {
+ password_change = true;
+ }
+ }
+
+ code = krb5_unparse_name(context, kdcreq->server, &server_name);
+ if (code) {
+ goto done;
+ }
+
+ code = krb5_unparse_name(context, client_princ, &client_name);
+ if (code) {
+ goto done;
+ }
+
+ if (kdcreq->addresses) {
+ code = ks_get_netbios_name(kdcreq->addresses, &netbios_name);
+ if (code) {
+ goto done;
+ }
+ }
+
+ code = mit_samba_check_client_access(mit_ctx,
+ client,
+ client_name,
+ server,
+ server_name,
+ netbios_name,
+ password_change,
+ &int_data);
+
+ if (int_data.length && int_data.data) {
+
+ /* make sure the mapped return code is returned - gd */
+ int code_tmp;
+
+ d = ks_make_data(int_data.data, int_data.length);
+
+ code_tmp = decode_krb5_padata_sequence(&d, &e_data);
+ if (code_tmp == 0) {
+ *e_data_out = e_data;
+ }
+ }
+done:
+ free(realm);
+ free(server_name);
+ free(client_name);
+ free(netbios_name);
+
+ return code;
+}
+
+static krb5_error_code ks_get_pac(krb5_context context,
+ uint32_t flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_keyblock *client_key,
+ krb5_pac *pac)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_get_pac(mit_ctx,
+ context,
+ flags,
+ client,
+ server,
+ client_key,
+ pac);
+ if (code != 0) {
+ return code;
+ }
+
+ return code;
+}
+
+#if KRB5_KDB_DAL_MAJOR_VERSION < 9
+static krb5_error_code ks_verify_pac(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ krb5_pac *pac)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_authdata **authdata = NULL;
+ krb5_pac ipac = NULL;
+ DATA_BLOB logon_data = { NULL, 0 };
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ /* find the existing PAC, if present */
+ code = krb5_find_authdata(context,
+ tgt_auth_data,
+ NULL,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &authdata);
+ if (code != 0) {
+ return code;
+ }
+
+ /* no pac data */
+ if (authdata == NULL) {
+ return 0;
+ }
+
+ SMB_ASSERT(authdata[0] != NULL);
+
+ if (authdata[1] != NULL) {
+ code = KRB5KDC_ERR_BADOPTION; /* XXX */
+ goto done;
+ }
+
+ code = krb5_pac_parse(context,
+ authdata[0]->contents,
+ authdata[0]->length,
+ &ipac);
+ if (code != 0) {
+ goto done;
+ }
+
+ /* TODO: verify this is correct
+ *
+ * In the constrained delegation case, the PAC is from a service
+ * ticket rather than a TGT; we must verify the server and KDC
+ * signatures to assert that the server did not forge the PAC.
+ */
+ if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+ code = krb5_pac_verify(context,
+ ipac,
+ authtime,
+ client_princ,
+ server_key,
+ krbtgt_key);
+ } else {
+ code = krb5_pac_verify(context,
+ ipac,
+ authtime,
+ client_princ,
+ krbtgt_key,
+ NULL);
+ }
+ if (code != 0) {
+ goto done;
+ }
+
+ /* check and update PAC */
+ code = krb5_pac_parse(context,
+ authdata[0]->contents,
+ authdata[0]->length,
+ pac);
+ if (code != 0) {
+ goto done;
+ }
+
+ code = mit_samba_reget_pac(mit_ctx,
+ context,
+ flags,
+ client_princ,
+ client,
+ server,
+ krbtgt,
+ krbtgt_key,
+ pac);
+
+done:
+ krb5_free_authdata(context, authdata);
+ krb5_pac_free(context, ipac);
+ free(logon_data.data);
+
+ return code;
+}
+
+krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_const_principal server_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_db_entry *local_krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *local_krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ void *authdata_info,
+ krb5_data ***auth_indicators,
+ krb5_authdata ***signed_auth_data)
+{
+ krb5_const_principal ks_client_princ = NULL;
+ krb5_db_entry *client_entry = NULL;
+ krb5_authdata **pac_auth_data = NULL;
+ krb5_authdata **authdata = NULL;
+ krb5_boolean is_as_req;
+ krb5_error_code code;
+ krb5_pac pac = NULL;
+ krb5_data pac_data;
+ bool with_pac = false;
+ bool generate_pac = false;
+ char *client_name = NULL;
+
+
+ krbtgt = krbtgt == NULL ? local_krbtgt : krbtgt;
+ krbtgt_key = krbtgt_key == NULL ? local_krbtgt_key : krbtgt_key;
+
+ /* FIXME: We don't support S4U yet */
+ if (flags & KRB5_KDB_FLAGS_S4U) {
+ return KRB5_KDB_DBTYPE_NOSUP;
+ }
+
+ is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
+
+ /*
+ * When using s4u2proxy client_princ actually refers to the proxied user
+ * while client->princ to the proxy service asking for the TGS on behalf
+ * of the proxied user. So always use client_princ in preference.
+ *
+ * Note that when client principal is not NULL, client entry might be
+ * NULL for cross-realm case, so we need to make sure to not
+ * dereference NULL pointer here.
+ */
+ if (client_princ != NULL) {
+ ks_client_princ = client_princ;
+ if (!is_as_req) {
+ krb5_boolean is_equal = false;
+
+ if (client != NULL && client->princ != NULL) {
+ is_equal =
+ krb5_principal_compare(context,
+ client_princ,
+ client->princ);
+ }
+
+ /*
+ * When client principal is the same as supplied client
+ * entry, don't fetch it.
+ */
+ if (!is_equal) {
+ code = ks_get_principal(context,
+ ks_client_princ,
+ 0,
+ &client_entry);
+ if (code != 0) {
+ (void)krb5_unparse_name(context,
+ ks_client_princ,
+ &client_name);
+
+ DBG_DEBUG("We didn't find the client "
+ "principal [%s] in our "
+ "database.\n",
+ client_name);
+ SAFE_FREE(client_name);
+
+ /*
+ * If we didn't find client_princ in our
+ * database it might be from another
+ * realm.
+ */
+ client_entry = NULL;
+ }
+ }
+ }
+ } else {
+ if (client == NULL) {
+ *signed_auth_data = NULL;
+ return 0;
+ }
+ ks_client_princ = client->princ;
+ }
+
+ if (client_entry == NULL) {
+ client_entry = client;
+ }
+
+ if (is_as_req) {
+ with_pac = mit_samba_princ_needs_pac(client_entry);
+ } else {
+ with_pac = mit_samba_princ_needs_pac(server);
+ }
+
+ code = krb5_unparse_name(context,
+ client_princ,
+ &client_name);
+ if (code != 0) {
+ goto done;
+ }
+
+ if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC) != 0) {
+ generate_pac = true;
+ }
+
+ DBG_DEBUG("*** Sign data for client principal: %s [%s %s%s]\n",
+ client_name,
+ is_as_req ? "AS-REQ" : "TGS_REQ",
+ with_pac ? is_as_req ? "WITH_PAC" : "FIND_PAC" : "NO_PAC",
+ generate_pac ? " GENERATE_PAC" : "");
+
+ /*
+ * Generate PAC for the AS-REQ or check or generate one for the TGS if
+ * needed.
+ */
+ if (with_pac && generate_pac) {
+ DBG_DEBUG("Generate PAC for AS-REQ [%s]\n", client_name);
+
+ code = krb5_pac_init(context, &pac);
+ if (code != 0) {
+ goto done;
+ }
+
+ code = ks_get_pac(context,
+ flags,
+ client_entry,
+ server,
+ NULL,
+ &pac);
+ if (code != 0) {
+ goto done;
+ }
+ } else if (with_pac && !is_as_req) {
+ /*
+ * Find the PAC in the TGS, if one exists.
+ */
+ code = krb5_find_authdata(context,
+ tgt_auth_data,
+ NULL,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &pac_auth_data);
+ if (code != 0) {
+ DBG_ERR("krb5_find_authdata failed: %d\n", code);
+ goto done;
+ }
+ DBG_DEBUG("Found PAC data for TGS-REQ [%s]\n", client_name);
+
+ if (pac_auth_data != NULL && pac_auth_data[0] != NULL) {
+ if (pac_auth_data[1] != NULL) {
+ DBG_ERR("Invalid PAC data!\n");
+ code = KRB5KDC_ERR_BADOPTION;
+ goto done;
+ }
+
+ DBG_DEBUG("Verify PAC for TGS [%s]\n",
+ client_name);
+
+ code = ks_verify_pac(context,
+ flags,
+ ks_client_princ,
+ client_entry,
+ server,
+ krbtgt,
+ server_key,
+ krbtgt_key,
+ authtime,
+ tgt_auth_data,
+ &pac);
+ if (code != 0) {
+ goto done;
+ }
+ } else {
+ if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+ DBG_DEBUG("Generate PAC for constrained"
+ "delegation TGS [%s]\n",
+ client_name);
+
+ code = krb5_pac_init(context, &pac);
+ if (code != 0) {
+ goto done;
+ }
+
+ code = ks_get_pac(context,
+ flags,
+ client_entry,
+ server,
+ NULL,
+ &pac);
+ if (code != 0 && code != ENOENT) {
+ goto done;
+ }
+ }
+ }
+ }
+
+ if (pac == NULL) {
+ DBG_DEBUG("No PAC data - we're done [%s]\n", client_name);
+ *signed_auth_data = NULL;
+ code = 0;
+ goto done;
+ }
+
+ DBG_DEBUG("Signing PAC for %s [%s]\n",
+ is_as_req ? "AS-REQ" : "TGS-REQ",
+ client_name);
+ code = krb5_pac_sign(context, pac, authtime, ks_client_princ,
+ server_key, krbtgt_key, &pac_data);
+ if (code != 0) {
+ DBG_ERR("krb5_pac_sign failed: %d\n", code);
+ goto done;
+ }
+
+ authdata = calloc(2, sizeof(krb5_authdata *));
+ if (authdata == NULL) {
+ goto done;
+ }
+
+ authdata[0] = malloc(sizeof(krb5_authdata));
+ if (authdata[0] == NULL) {
+ goto done;
+ }
+
+ /* put in signed data */
+ authdata[0]->magic = KV5M_AUTHDATA;
+ authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+ authdata[0]->contents = (krb5_octet *)pac_data.data;
+ authdata[0]->length = pac_data.length;
+
+ code = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ authdata,
+ signed_auth_data);
+ if (code != 0) {
+ goto done;
+ }
+
+ code = 0;
+
+done:
+ if (client_entry != NULL && client_entry != client) {
+ ks_free_principal(context, client_entry);
+ }
+ SAFE_FREE(client_name);
+ krb5_free_authdata(context, authdata);
+ krb5_pac_free(context, pac);
+
+ return code;
+}
+#else /* KRB5_KDB_DAL_MAJOR_VERSION >= 9 */
+static krb5_error_code ks_update_pac(krb5_context context,
+ int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *signing_krbtgt,
+ krb5_pac old_pac,
+ krb5_pac new_pac)
+{
+ struct mit_samba_context *mit_ctx = NULL;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_update_pac(mit_ctx,
+ context,
+ flags,
+ client,
+ server,
+ signing_krbtgt,
+ old_pac,
+ new_pac);
+ if (code != 0) {
+ return code;
+ }
+
+ return code;
+}
+
+krb5_error_code kdb_samba_db_issue_pac(krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_keyblock *replaced_reply_key,
+ krb5_db_entry *server,
+ krb5_db_entry *signing_krbtgt,
+ krb5_timestamp authtime,
+ krb5_pac old_pac,
+ krb5_pac new_pac,
+ krb5_data ***auth_indicators)
+{
+ char *client_name = NULL;
+ char *server_name = NULL;
+ krb5_error_code code = EINVAL;
+
+ /* The KDC handles both signing and verification for us. */
+
+ if (client != NULL) {
+ code = krb5_unparse_name(context,
+ client->princ,
+ &client_name);
+ if (code != 0) {
+ return code;
+ }
+ }
+
+ if (server != NULL) {
+ code = krb5_unparse_name(context,
+ server->princ,
+ &server_name);
+ if (code != 0) {
+ SAFE_FREE(client_name);
+ return code;
+ }
+ }
+
+ /*
+ * Get a new PAC for AS-REQ or S4U2Self for our realm.
+ *
+ * For a simple cross-realm S4U2Proxy there will be the following TGS
+ * requests after the client realm is identified:
+ *
+ * 1. server@SREALM to SREALM for krbtgt/CREALM@SREALM -- a regular TGS
+ * request with server's normal TGT and no S4U2Self padata.
+ * 2. server@SREALM to CREALM for server@SREALM (expressed as an
+ * enterprise principal), with the TGT from #1 as header ticket and
+ * S4U2Self padata identifying the client.
+ * 3. server@SREALM to SREALM for server@SREALM with S4U2Self padata,
+ * with the referral TGT from #2 as header ticket
+ *
+ * In request 2 the PROTOCOL_TRANSITION and CROSS_REALM flags are set,
+ * and the request is for a local client (so client != NULL) and we
+ * want to make a new PAC.
+ *
+ * In request 3 the PROTOCOL_TRANSITION and CROSS_REALM flags are also
+ * set, but the request is for a non-local client (so client == NULL)
+ * and we want to copy the subject PAC contained in the referral TGT.
+ */
+ if (old_pac == NULL ||
+ (client != NULL && (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION))) {
+ DBG_NOTICE("Generate PAC for AS-REQ [client=%s, flags=%#08x]\n",
+ client_name != NULL ? client_name : "<unknown>",
+ flags);
+
+ code = ks_get_pac(context,
+ flags,
+ client,
+ server,
+ replaced_reply_key,
+ &new_pac);
+ } else {
+ DBG_NOTICE("Update PAC for TGS-REQ [client=%s, server=%s, "
+ "flags=%#08x]\n",
+ client_name != NULL ? client_name : "<unknown>",
+ server_name != NULL ? server_name : "<unknown>",
+ flags);
+
+ code = ks_update_pac(context,
+ flags,
+ client,
+ server,
+ signing_krbtgt,
+ old_pac,
+ new_pac);
+ }
+ SAFE_FREE(client_name);
+ SAFE_FREE(server_name);
+
+ return code;
+}
+#endif /* KRB5_KDB_DAL_MAJOR_VERSION */
+
+krb5_error_code kdb_samba_db_check_allowed_to_delegate(krb5_context context,
+ krb5_const_principal client,
+ const krb5_db_entry *server,
+ krb5_const_principal proxy)
+{
+ struct mit_samba_context *mit_ctx = NULL;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ return mit_samba_check_s4u2proxy(mit_ctx,
+ server,
+ proxy);
+
+}
+
+
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
+krb5_error_code kdb_samba_db_allowed_to_delegate_from(
+ krb5_context context,
+ krb5_const_principal client_principal,
+ krb5_const_principal server_principal,
+ krb5_pac header_pac,
+ const krb5_db_entry *proxy)
+{
+ struct mit_samba_context *mit_ctx = NULL;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_check_allowed_to_delegate_from(mit_ctx,
+ client_principal,
+ server_principal,
+ header_pac,
+ proxy);
+
+ return code;
+}
+#endif
+
+
+static void samba_bad_password_count(krb5_db_entry *client,
+ krb5_error_code error_code)
+{
+ switch (error_code) {
+ case 0:
+ mit_samba_zero_bad_password_count(client);
+ break;
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ mit_samba_update_bad_password_count(client);
+ break;
+ }
+}
+
+void kdb_samba_db_audit_as_req(krb5_context context,
+ krb5_kdc_req *request,
+ const krb5_address *local_addr,
+ const krb5_address *remote_addr,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code error_code)
+{
+ /*
+ * FIXME: This segfaulted with a FAST test
+ * FIND_FAST: <unknown client> for <unknown server>, Unknown FAST armor type 0
+ */
+ if (client == NULL) {
+ return;
+ }
+
+ samba_bad_password_count(client, error_code);
+
+ /* TODO: perform proper audit logging for addresses */
+}
diff --git a/source4/kdc/mit-kdb/kdb_samba_principals.c b/source4/kdc/mit-kdb/kdb_samba_principals.c
new file mode 100644
index 0000000..2726018
--- /dev/null
+++ b/source4/kdc/mit-kdb/kdb_samba_principals.c
@@ -0,0 +1,397 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Samba KDB plugin for MIT Kerberos
+
+ Copyright (c) 2010 Simo Sorce <idra@samba.org>.
+ Copyright (c) 2014 Andreas Schneider <asn@samba.org>
+
+ 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 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+
+#include "system/kerberos.h"
+
+#include <profile.h>
+#include <kdb.h>
+
+#include "kdc/samba_kdc.h"
+#include "kdc/mit_samba.h"
+#include "kdb_samba.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_KERBEROS
+
+#define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+
+krb5_error_code ks_get_principal(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int kflags,
+ krb5_db_entry **kentry)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_get_principal(mit_ctx,
+ principal,
+ kflags,
+ kentry);
+ if (code != 0) {
+ goto cleanup;
+ }
+
+cleanup:
+
+ return code;
+}
+
+static void ks_free_principal_e_data(krb5_context context, krb5_octet *e_data)
+{
+ struct samba_kdc_entry *skdc_entry;
+
+ skdc_entry = talloc_get_type_abort(e_data,
+ struct samba_kdc_entry);
+ skdc_entry->kdc_entry = NULL;
+ TALLOC_FREE(skdc_entry);
+}
+
+void ks_free_principal(krb5_context context, krb5_db_entry *entry)
+{
+ krb5_tl_data *tl_data_next = NULL;
+ krb5_tl_data *tl_data = NULL;
+ size_t i, j;
+
+ if (entry != NULL) {
+ krb5_free_principal(context, entry->princ);
+
+ for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
+ tl_data_next = tl_data->tl_data_next;
+ if (tl_data->tl_data_contents != NULL) {
+ free(tl_data->tl_data_contents);
+ }
+ free(tl_data);
+ }
+
+ if (entry->key_data != NULL) {
+ for (i = 0; i < entry->n_key_data; i++) {
+ for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
+ if (entry->key_data[i].key_data_length[j] != 0) {
+ if (entry->key_data[i].key_data_contents[j] != NULL) {
+ memset(entry->key_data[i].key_data_contents[j], 0, entry->key_data[i].key_data_length[j]);
+ free(entry->key_data[i].key_data_contents[j]);
+ }
+ }
+ entry->key_data[i].key_data_contents[j] = NULL;
+ entry->key_data[i].key_data_length[j] = 0;
+ entry->key_data[i].key_data_type[j] = 0;
+ }
+ }
+ free(entry->key_data);
+ }
+
+ if (entry->e_data) {
+ ks_free_principal_e_data(context, entry->e_data);
+ }
+
+ free(entry);
+ }
+}
+
+static krb5_boolean ks_is_master_key_principal(krb5_context context,
+ krb5_const_principal princ)
+{
+ return krb5_princ_size(context, princ) == 2 &&
+ ks_data_eq_string(princ->data[0], "K") &&
+ ks_data_eq_string(princ->data[1], "M");
+}
+
+static krb5_error_code ks_get_master_key_principal(krb5_context context,
+ krb5_const_principal princ,
+ krb5_db_entry **kentry_ptr)
+{
+ krb5_error_code code;
+ krb5_key_data *key_data;
+ krb5_timestamp now;
+ krb5_db_entry *kentry;
+
+ *kentry_ptr = NULL;
+
+ kentry = calloc(1, sizeof(krb5_db_entry));
+ if (kentry == NULL) {
+ return ENOMEM;
+ }
+
+ kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+ kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+ kentry->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+
+ if (princ == NULL) {
+ code = krb5_parse_name(context, KRB5_KDB_M_NAME, &kentry->princ);
+ } else {
+ code = krb5_copy_principal(context, princ, &kentry->princ);
+ }
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ now = time(NULL);
+
+ code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ /* Return a dummy key */
+ kentry->n_key_data = 1;
+ kentry->key_data = calloc(1, sizeof(krb5_key_data));
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ key_data = &kentry->key_data[0];
+
+ key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ key_data->key_data_kvno = 1;
+ key_data->key_data_type[0] = ENCTYPE_UNKNOWN;
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ *kentry_ptr = kentry;
+
+ return 0;
+}
+
+static krb5_error_code ks_create_principal(krb5_context context,
+ krb5_const_principal princ,
+ int attributes,
+ int max_life,
+ const char *password,
+ krb5_db_entry **kentry_ptr)
+{
+ krb5_error_code code;
+ krb5_key_data *key_data;
+ krb5_timestamp now;
+ krb5_db_entry *kentry;
+ krb5_keyblock key;
+ krb5_data salt;
+ krb5_data pwd;
+ int enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
+ int sts = KRB5_KDB_SALTTYPE_SPECIAL;
+
+ if (princ == NULL) {
+ return KRB5_KDB_NOENTRY;
+ }
+
+ *kentry_ptr = NULL;
+
+ kentry = calloc(1, sizeof(krb5_db_entry));
+ if (kentry == NULL) {
+ return ENOMEM;
+ }
+
+ kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+ kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+
+ if (attributes > 0) {
+ kentry->attributes = attributes;
+ }
+
+ if (max_life > 0) {
+ kentry->max_life = max_life;
+ }
+
+ code = krb5_copy_principal(context, princ, &kentry->princ);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ now = time(NULL);
+
+ code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ code = mit_samba_generate_salt(&salt);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ if (password != NULL) {
+ pwd.data = strdup(password);
+ pwd.length = strlen(password);
+ } else {
+ /* create a random password */
+ code = mit_samba_generate_random_password(&pwd);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+ }
+
+ code = krb5_c_string_to_key(context, enctype, &pwd, &salt, &key);
+ SAFE_FREE(pwd.data);
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ kentry->n_key_data = 1;
+ kentry->key_data = calloc(1, sizeof(krb5_key_data));
+ if (code != 0) {
+ krb5_db_free_principal(context, kentry);
+ return code;
+ }
+
+ key_data = &kentry->key_data[0];
+
+ key_data->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+ key_data->key_data_kvno = 1;
+ key_data->key_data_type[0] = key.enctype;
+ key_data->key_data_length[0] = key.length;
+ key_data->key_data_contents[0] = key.contents;
+ key_data->key_data_type[1] = sts;
+ key_data->key_data_length[1] = salt.length;
+ key_data->key_data_contents[1] = (krb5_octet*)salt.data;
+
+ *kentry_ptr = kentry;
+
+ return 0;
+}
+
+static krb5_error_code ks_get_admin_principal(krb5_context context,
+ krb5_const_principal princ,
+ krb5_db_entry **kentry_ptr)
+{
+ krb5_error_code code = EINVAL;
+
+ code = ks_create_principal(context,
+ princ,
+ KRB5_KDB_DISALLOW_TGT_BASED,
+ ADMIN_LIFETIME,
+ NULL,
+ kentry_ptr);
+
+ return code;
+}
+
+krb5_error_code kdb_samba_db_get_principal(krb5_context context,
+ krb5_const_principal princ,
+ unsigned int kflags,
+ krb5_db_entry **kentry)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_error_code code;
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ if (ks_is_master_key_principal(context, princ)) {
+ return ks_get_master_key_principal(context, princ, kentry);
+ }
+
+ /*
+ * Fake a kadmin/admin and kadmin/history principal so that kadmindd can
+ * start
+ */
+ if (ks_is_kadmin_admin(context, princ) ||
+ ks_is_kadmin_history(context, princ)) {
+ return ks_get_admin_principal(context, princ, kentry);
+ }
+
+ code = ks_get_principal(context, princ, kflags, kentry);
+
+ /*
+ * This restricts the changepw account so it isn't able to request a
+ * service ticket. It also marks the principal as the changepw service.
+ */
+ if (ks_is_kadmin_changepw(context, princ)) {
+ /* FIXME: shouldn't we also set KRB5_KDB_DISALLOW_TGT_BASED ?
+ * testing showed that setpw kpasswd command fails then on the
+ * server though... */
+ (*kentry)->attributes |= KRB5_KDB_PWCHANGE_SERVICE;
+ (*kentry)->max_life = CHANGEPW_LIFETIME;
+ }
+
+ return code;
+}
+
+krb5_error_code kdb_samba_db_put_principal(krb5_context context,
+ krb5_db_entry *entry,
+ char **db_args)
+{
+
+ /* NOTE: deferred, samba does not allow the KDC to store
+ * principals for now. We should not return KRB5_KDB_DB_INUSE as this
+ * would result in confusing error messages after password changes. */
+ return 0;
+}
+
+krb5_error_code kdb_samba_db_delete_principal(krb5_context context,
+ krb5_const_principal princ)
+{
+
+ /* NOTE: deferred, samba does not allow the KDC to delete
+ * principals for now */
+ return KRB5_KDB_DB_INUSE;
+}
+
+krb5_error_code kdb_samba_db_iterate(krb5_context context,
+ char *match_entry,
+ int (*func)(krb5_pointer, krb5_db_entry *),
+ krb5_pointer func_arg,
+ krb5_flags iterflags)
+{
+ struct mit_samba_context *mit_ctx;
+ krb5_db_entry *kentry = NULL;
+ krb5_error_code code;
+
+
+ mit_ctx = ks_get_context(context);
+ if (mit_ctx == NULL) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ code = mit_samba_get_firstkey(mit_ctx, &kentry);
+ while (code == 0) {
+ code = (*func)(func_arg, kentry);
+ if (code != 0) {
+ break;
+ }
+
+ code = mit_samba_get_nextkey(mit_ctx, &kentry);
+ }
+
+ if (code == KRB5_KDB_NOENTRY) {
+ code = 0;
+ }
+
+ return code;
+}
diff --git a/source4/kdc/mit-kdb/wscript_build b/source4/kdc/mit-kdb/wscript_build
new file mode 100644
index 0000000..82cea4a
--- /dev/null
+++ b/source4/kdc/mit-kdb/wscript_build
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('mit-kdb-samba',
+ source='''
+ kdb_samba.c
+ kdb_samba_common.c
+ kdb_samba_masterkey.c
+ kdb_samba_pac.c
+ kdb_samba_policies.c
+ kdb_samba_principals.c
+ kdb_samba_change_pwd.c
+ ''',
+ private_library=True,
+ realname='samba.so',
+ install_path='${LIBDIR}/krb5/plugins/kdb',
+ deps='''
+ MIT_SAMBA
+ com_err
+ krb5
+ kdb5
+ ''',
+ enabled=bld.CONFIG_SET('HAVE_KDB_H'))