diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/kdc/mit-kdb | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.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.c | 182 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba.h | 184 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_change_pwd.c | 59 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_common.c | 114 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_masterkey.c | 69 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_pac.c | 115 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_policies.c | 769 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/kdb_samba_principals.c | 397 | ||||
-rw-r--r-- | source4/kdc/mit-kdb/wscript_build | 22 |
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')) |