From ade958e20b561b702e2fec86a28659144dbe4a9e Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 12 May 2014 10:49:24 +0200 Subject: [PATCH] mit-kdb: Add initial MIT KDB Samba driver Signed-off-by: Andreas Schneider Signed-off-by: Simo Sorce Reviewed-by: Andrew Bartlett Pair-Programmed-With: Simo Sorce Reviewed-by: Sumit Bose Reviewed-by: Guenther Deschner Reviewed-by: Andrew Bartlett --- source4/kdc/mit-kdb/kdb_samba.c | 185 +++++++++ source4/kdc/mit-kdb/kdb_samba.h | 137 +++++++ source4/kdc/mit-kdb/kdb_samba_common.c | 119 ++++++ source4/kdc/mit-kdb/kdb_samba_masterkey.c | 66 ++++ source4/kdc/mit-kdb/kdb_samba_pac.c | 112 ++++++ source4/kdc/mit-kdb/kdb_samba_policies.c | 437 +++++++++++++++++++++ source4/kdc/mit-kdb/kdb_samba_principals.c | 227 +++++++++++ source4/kdc/mit-kdb/wscript_build | 21 + source4/kdc/wscript_build | 2 + 9 files changed, 1306 insertions(+) create mode 100644 source4/kdc/mit-kdb/kdb_samba.c create mode 100644 source4/kdc/mit-kdb/kdb_samba.h create mode 100644 source4/kdc/mit-kdb/kdb_samba_common.c create mode 100644 source4/kdc/mit-kdb/kdb_samba_masterkey.c create mode 100644 source4/kdc/mit-kdb/kdb_samba_pac.c create mode 100644 source4/kdc/mit-kdb/kdb_samba_policies.c create mode 100644 source4/kdc/mit-kdb/kdb_samba_principals.c create mode 100644 source4/kdc/mit-kdb/wscript_build diff --git a/source4/kdc/mit-kdb/kdb_samba.c b/source4/kdc/mit-kdb/kdb_samba.c new file mode 100644 index 00000000000..1f3fb7300ae --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba.c @@ -0,0 +1,185 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#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_alloc(krb5_context context, void *ptr, size_t size) +{ + return realloc(ptr, size); +} + +static void kdb_samba_db_free(krb5_context context, void *ptr) +{ + free(ptr); +} + +kdb_vftabl kdb_function_table = { + KRB5_KDB_DAL_MAJOR_VERSION, /* major version number */ + 0, /* minor version number */ + kdb_samba_init_library, /* init_library */ + kdb_samba_fini_library, /* fini_library */ + kdb_samba_init_module, /* init_module */ + kdb_samba_fini_module, /* fini_module */ + + kdb_samba_db_create, /* db_create */ + kdb_samba_db_destroy, /* db_destroy */ + kdb_samba_db_get_age, /* db_get_age */ + kdb_samba_db_lock, /* db_lock */ + kdb_samba_db_unlock, /* db_unlock */ + + kdb_samba_db_get_principal, /* db_get_principal */ + kdb_samba_db_free_principal, /* db_free_principal */ + kdb_samba_db_put_principal, /* db_put_principal */ + kdb_samba_db_delete_principal, /* db_delete_principal */ + kdb_samba_db_iterate, /* db_iterate */ + + NULL, /* create_policy */ + NULL, /* get_policy */ + NULL, /* put_policy */ + NULL, /* iter_policy */ + NULL, /* delete_policy */ + NULL, /* free_policy */ + + kdb_samba_db_alloc, /* db_alloc */ + kdb_samba_db_free, /* db_free */ + + kdb_samba_fetch_master_key, /* fetch_master_key */ + kdb_samba_fetch_master_key_list, /* fetch_master_key_list */ + NULL, /* store_master_key_list */ + NULL, /* dbe_search_enctype */ + NULL, /* change_pwd */ + NULL, /* promote_db */ + kdb_samba_dbekd_decrypt_key_data, /* decrypt_key_data */ + kdb_samba_dbekd_encrypt_key_data, /* encrypt_key_data */ + + kdb_samba_db_sign_auth_data, /* sign_authdata */ + NULL, /* check_transited_realms */ + kdb_samba_db_check_policy_as, /* check_policy_as */ + NULL, /* check_policy_tgs */ + NULL, /* audit_as_req */ + NULL, /* refresh_config */ + kdb_samba_db_check_allowed_to_delegate +}; diff --git a/source4/kdc/mit-kdb/kdb_samba.h b/source4/kdc/mit-kdb/kdb_samba.h new file mode 100644 index 00000000000..89c628fff63 --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba.h @@ -0,0 +1,137 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * plugins/kdb/samba/kdb_samba.h + * + * Copyright (c) 2009, Simo Sorce + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#ifndef _KDB_SAMBA_H_ +#define _KDB_SAMBA_H_ + +#include + +#include +#include + +#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); + +void ks_free_krb5_db_entry(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); + +/* 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); + +void kdb_samba_db_free_principal(krb5_context context, + krb5_db_entry *entry); + +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); + +/* 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_sign_auth_data(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 *client_key, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_keyblock *session_key, + krb5_timestamp authtime, + krb5_authdata **tgt_auth_data, + 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); + +#endif /* _KDB_SAMBA_H_ */ 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 00000000000..de94f03719b --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba_common.c @@ -0,0 +1,119 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#include "kdc/mit_samba.h" +#include "kdb_samba.h" + +struct mit_samba_context *ks_get_context(krb5_context kcontext) +{ + void *db_ctx; + krb5_error_code code; + + code = krb5_db_get_context(kcontext, &db_ctx); + if (code != 0) { + return NULL; + } + + return (struct mit_samba_context *)db_ctx; +} + +void ks_free_krb5_db_entry(krb5_context context, + krb5_db_entry *entry) +{ + krb5_tl_data *tl_data_next = NULL; + krb5_tl_data *tl_data = NULL; + int i, j; + + if (entry == NULL) { + return; + } + +#if 0 /* TODO FIXME do we have something to free? */ + if (entry->e_data != NULL) { + /* FREE ME! */ + } +#endif + + 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); + } + + free(entry); +} + +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; +} 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 00000000000..3de3868cac8 --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba_masterkey.c @@ -0,0 +1,66 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#include "kdc/mit_samba.h" +#include "kdb_samba.h" + +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 = malloc(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 00000000000..0ab8c372e0e --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba_pac.c @@ -0,0 +1,112 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#include "kdc/mit_samba.h" +#include "kdb_samba.h" + +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]) { + 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 00000000000..75c8c837a27 --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba_policies.c @@ -0,0 +1,437 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#include "kdc/mit_samba.h" +#include "kdb_samba.h" + +/* 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; + 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; + } + + 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, kdcreq->client, &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 (code) { + goto done; + } + + d = ks_make_data(int_data.data, int_data.length); + + code = decode_krb5_padata_sequence(&d, &e_data); + if (code == 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, + krb5_db_entry *client, + krb5_pac *pac) +{ + struct mit_samba_context *mit_ctx; + DATA_BLOB pac_data; + krb5_data data; + krb5_error_code code; + + mit_ctx = ks_get_context(context); + if (mit_ctx == NULL) { + return KRB5_KDB_DBNOTINITED; + } + + code = mit_samba_get_pac_data(mit_ctx, + client, + &pac_data); + if (code != 0) { + return code; + } + + code = krb5_pac_init(context, pac); + if (code != 0) { + goto done; + } + + data = ks_make_data(pac_data.data, pac_data.length); + + code = krb5_pac_add_buffer(context, *pac, PAC_LOGON_INFO, &data); + if (code != 0) { + goto done; + } + +done: + free(pac_data.data); + return code; +} + +static krb5_error_code ks_verify_pac(krb5_context context, + unsigned int flags, + krb5_const_principal client_princ, + krb5_db_entry *client, + 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 pac_data = { NULL, 0 }; + DATA_BLOB logon_data = { NULL, 0 }; + krb5_data data; + 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 */ + pac_data.data = authdata[0]->contents; + pac_data.length = authdata[0]->length; + + code = mit_samba_update_pac_data(mit_ctx, + client, + &pac_data, + &logon_data); + if (code != 0) { + goto done; + } + + code = krb5_pac_init(context, pac); + if (code != 0) { + goto done; + } + + data = ks_make_data(logon_data.data, logon_data.length); + + code = krb5_pac_add_buffer(context, *pac, PAC_LOGON_INFO, &data); + if (code != 0) { + goto done; + } + +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_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_keyblock *session_key, + krb5_timestamp authtime, + krb5_authdata **tgt_auth_data, + krb5_authdata ***signed_auth_data) +{ + krb5_const_principal ks_client_princ; + krb5_authdata **authdata = NULL; + krb5_boolean is_as_req; + krb5_error_code code; + krb5_pac pac = NULL; + krb5_data pac_data; + + /* Prefer canonicalised name from client entry */ + if (client != NULL) { + ks_client_princ = client->princ; + } else { + ks_client_princ = client_princ; + } + + is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0); + + if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) { + + code = ks_get_pac(context, client, &pac); + if (code != 0) { + goto done; + } + } + + if (!is_as_req) { + code = ks_verify_pac(context, flags, ks_client_princ, client, + server_key, krbtgt_key, authtime, + tgt_auth_data, &pac); + if (code != 0) { + goto done; + } + } + + if (pac == NULL && client != NULL) { + + code = ks_get_pac(context, client, &pac); + if (code != 0) { + goto done; + } + } + + if (pac == NULL) { + code = KRB5_KDB_DBTYPE_NOSUP; + goto done; + } + + code = krb5_pac_sign(context, pac, authtime, ks_client_princ, + server_key, krbtgt_key, &pac_data); + if (code != 0) { + goto done; + } + + authdata = malloc(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: + krb5_pac_free(context, pac); + krb5_free_authdata(context, authdata); + + return code; +} + +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; + + /* + * Names are quite odd and confusing in the current implementation. + * The following mappings should help understanding what is what. + * client -> client to impersonate + * server; -> delegating service + * proxy; -> target principal + */ + krb5_db_entry *delegating_service = discard_const_p(krb5_db_entry, server); + + char *target_name = NULL; + bool is_enterprise; + krb5_error_code code; + + mit_ctx = ks_get_context(context); + if (mit_ctx == NULL) { + return KRB5_KDB_DBNOTINITED; + } + + code = krb5_unparse_name(context, proxy, &target_name); + if (code) { + goto done; + } + + is_enterprise = (proxy->type == KRB5_NT_ENTERPRISE_PRINCIPAL); + + code = mit_samba_check_s4u2proxy(mit_ctx, + delegating_service, + target_name, + is_enterprise); + +done: + free(target_name); + return code; +} 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 00000000000..50f17776563 --- /dev/null +++ b/source4/kdc/mit-kdb/kdb_samba_principals.c @@ -0,0 +1,227 @@ +/* + Unix SMB/CIFS implementation. + + Samba KDB plugin for MIT Kerberos + + Copyright (c) 2010 Simo Sorce . + Copyright (c) 2014 Andreas Schneider + + 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 . +*/ + +#include "includes.h" + +#include "system/kerberos.h" + +#include +#include + +#include "kdc/mit_samba.h" +#include "kdb_samba.h" + +static 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 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 = malloc(sizeof(krb5_db_entry)); + if (kentry == NULL) { + return ENOMEM; + } + + ZERO_STRUCTP(kentry); + + 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) { + ks_free_krb5_db_entry(context, kentry); + return code; + } + + now = time(NULL); + + code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ); + if (code != 0) { + ks_free_krb5_db_entry(context, kentry); + return code; + } + + /* Return a dummy key */ + kentry->n_key_data = 1; + kentry->key_data = malloc(sizeof(krb5_key_data)); + if (code != 0) { + ks_free_krb5_db_entry(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) { + ks_free_krb5_db_entry(context, kentry); + return code; + } + + *kentry_ptr = kentry; + + return 0; +} + +static 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_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); + } + + /* FIXME: temporarily fake up kadmin history to let kadmin.local work */ + if (ks_is_kadmin_history(context, princ)) { + return ks_get_dummy_principal(context, princ, kentry); + } + + code = ks_get_principal(context, princ, kflags, kentry); + + return code; +} + +void kdb_samba_db_free_principal(krb5_context context, + krb5_db_entry *entry) +{ + struct mit_samba_context *mit_ctx; + + mit_ctx = ks_get_context(context); + if (mit_ctx == NULL) { + return; + } + + ks_free_krb5_db_entry(context, entry); +} + +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 */ + return KRB5_KDB_DB_INUSE; +} + +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) +{ + 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 00000000000..68f086649c2 --- /dev/null +++ b/source4/kdc/mit-kdb/wscript_build @@ -0,0 +1,21 @@ +#!/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 + ''', + 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')) diff --git a/source4/kdc/wscript_build b/source4/kdc/wscript_build index 89e4fd293d5..3c9c77bba0b 100755 --- a/source4/kdc/wscript_build +++ b/source4/kdc/wscript_build @@ -121,3 +121,5 @@ bld.SAMBA_SUBSYSTEM('MIT_SAMBA', kdb5 ''', enabled=(not bld.CONFIG_SET('SAMBA4_USES_HEIMDAL') and bld.CONFIG_SET('HAVE_KDB_H')) ) + +bld.RECURSE('mit-kdb') -- 2.34.1