From 3ff298c37be5daa015006fe5cede3980b26e570e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 06:43:32 +0200 Subject: s4:selftest: add "smb.signing" to anon signing tests metze --- source4/selftest/samba4_tests.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'source4') diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh index 667b21975f..af8c3e94c1 100755 --- a/source4/selftest/samba4_tests.sh +++ b/source4/selftest/samba4_tests.sh @@ -328,9 +328,9 @@ for mech in \ name="smb.signing on with $signoptions" plantest "$name local-creds" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp $signoptions -U"\$NETBIOSNAME/\$USERNAME"%"\$PASSWORD" BASE-XCOPY "$*" done -plantest "--signing=yes anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=yes -U% BASE-XCOPY "$*" -plantest "--signing=required anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=required -U% BASE-XCOPY "$*" -plantest "--signing=no anon" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=no -U% BASE-XCOPY "$*" +plantest "smb.signing --signing=yes anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=yes -U% BASE-XCOPY "$*" +plantest "smb.signing --signing=required anon" dc $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=required -U% BASE-XCOPY "$*" +plantest "smb.signing --signing=no anon" member $VALGRIND $smb4torture //"\$NETBIOSNAME"/tmp -k no --signing=no -U% BASE-XCOPY "$*" NBT_TESTS=`$smb4torture --list | grep "^NBT-" | xargs` -- cgit From abe0af4ab417086d413f02cadf7315a8f801a0f8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 04:59:24 +0200 Subject: dcerpc.idl: add DCERPC_PFC_OFFSET metze --- source4/librpc/idl/dcerpc.idl | 1 + 1 file changed, 1 insertion(+) (limited to 'source4') diff --git a/source4/librpc/idl/dcerpc.idl b/source4/librpc/idl/dcerpc.idl index 1c6574b11b..a78329d990 100644 --- a/source4/librpc/idl/dcerpc.idl +++ b/source4/librpc/idl/dcerpc.idl @@ -261,6 +261,7 @@ interface dcerpc const uint8 DCERPC_PFC_FLAG_OBJECT_UUID = 0x80; /* on valid guid is in the optional object field */ /* these offsets are needed by the signing code */ + const uint8 DCERPC_PFC_OFFSET = 3; const uint8 DCERPC_DREP_OFFSET = 4; const uint8 DCERPC_FRAG_LEN_OFFSET = 8; const uint8 DCERPC_AUTH_LEN_OFFSET = 10; -- cgit From eb1de4f0ed149dd2b348d756f091809d934309a1 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 04:59:56 +0200 Subject: s4:librpc/rpc: correctly sign or seal rpc request with an object guid metze --- source4/librpc/rpc/dcerpc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'source4') diff --git a/source4/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 5bbcc5e91c..c1e3e26ee9 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -334,6 +334,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, DATA_BLOB creds2; size_t payload_length; enum ndr_err_code ndr_err; + size_t hdr_size = DCERPC_REQUEST_LENGTH; /* non-signed packets are simpler */ if (sig_size == 0) { @@ -367,6 +368,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT; + hdr_size += 16; } ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt); @@ -415,7 +417,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_seal_packet(c->security_state.generic_state, mem_ctx, - blob->data + DCERPC_REQUEST_LENGTH, + blob->data + hdr_size, payload_length, blob->data, blob->length, @@ -428,7 +430,7 @@ static NTSTATUS ncacn_push_request_sign(struct dcerpc_connection *c, case DCERPC_AUTH_LEVEL_INTEGRITY: status = gensec_sign_packet(c->security_state.generic_state, mem_ctx, - blob->data + DCERPC_REQUEST_LENGTH, + blob->data + hdr_size, payload_length, blob->data, blob->length, -- cgit From 844b331d257b02c073cc0dc4a6e229e6b9645949 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 05:01:19 +0200 Subject: s4:rpc_server: correctly handle dcerpc requests with object uuids metze --- source4/rpc_server/dcerpc_server.c | 8 ++++---- source4/rpc_server/dcesrv_auth.c | 10 ++++++++-- 2 files changed, 12 insertions(+), 6 deletions(-) (limited to 'source4') diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c index e5f59d0cf9..893055d3b1 100644 --- a/source4/rpc_server/dcerpc_server.c +++ b/source4/rpc_server/dcerpc_server.c @@ -870,10 +870,6 @@ static NTSTATUS dcesrv_request(struct dcesrv_call_state *call) call->context = context; call->ndr_pull = pull; - if (call->pkt.pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { - pull->flags |= LIBNDR_FLAG_OBJECT_PRESENT; - } - if (!(call->pkt.drep[0] & DCERPC_DREP_LE)) { pull->flags |= LIBNDR_FLAG_BIGENDIAN; } @@ -1112,6 +1108,10 @@ NTSTATUS dcesrv_input_process(struct dcesrv_connection *dce_conn) ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } + if (CVAL(blob.data, DCERPC_PFC_OFFSET) & DCERPC_PFC_FLAG_OBJECT_UUID) { + ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT; + } + ndr_err = ndr_pull_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, &call->pkt); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(dce_conn->partial_input.data); diff --git a/source4/rpc_server/dcesrv_auth.c b/source4/rpc_server/dcesrv_auth.c index 16bf4eb7ed..52d5631cfd 100644 --- a/source4/rpc_server/dcesrv_auth.c +++ b/source4/rpc_server/dcesrv_auth.c @@ -287,6 +287,7 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet) struct ndr_pull *ndr; NTSTATUS status; enum ndr_err_code ndr_err; + size_t hdr_size = DCERPC_REQUEST_LENGTH; if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.gensec_security) { @@ -335,6 +336,11 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet) ndr->flags |= LIBNDR_FLAG_BIGENDIAN; } + if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) { + ndr->flags |= LIBNDR_FLAG_OBJECT_PRESENT; + hdr_size += 16; + } + ndr_err = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { talloc_free(ndr); @@ -346,13 +352,13 @@ bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet) case DCERPC_AUTH_LEVEL_PRIVACY: status = gensec_unseal_packet(dce_conn->auth_state.gensec_security, call, - full_packet->data + DCERPC_REQUEST_LENGTH, + full_packet->data + hdr_size, pkt->u.request.stub_and_verifier.length, full_packet->data, full_packet->length-auth.credentials.length, &auth.credentials); memcpy(pkt->u.request.stub_and_verifier.data, - full_packet->data + DCERPC_REQUEST_LENGTH, + full_packet->data + hdr_size, pkt->u.request.stub_and_verifier.length); break; -- cgit From 40e7769959e29eccf1f0f14142828c9d0b7880b6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 06:40:15 +0200 Subject: s4:torture/rpc: add RPC-OBJECTUUID This test shows that object uuids should be ignored if they're present in a dcerpc request. metze --- source4/torture/config.mk | 2 +- source4/torture/rpc/object_uuid.c | 87 +++++++++++++++++++++++++++++++++++++++ source4/torture/rpc/rpc.c | 1 + 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 source4/torture/rpc/object_uuid.c (limited to 'source4') diff --git a/source4/torture/config.mk b/source4/torture/config.mk index 96da10b5df..211d09756d 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -116,7 +116,7 @@ torture_rpc_OBJ_FILES = $(addprefix $(torturesrcdir)/rpc/, \ eventlog.o epmapper.o winreg.o initshutdown.o oxidresolve.o remact.o mgmt.o \ scanner.o autoidl.o countcalls.o testjoin.o schannel.o netlogon.o remote_pac.o samlogon.o \ samsync.o bind.o dssetup.o alter_context.o bench.o samba3rpc.o rpc.o async_bind.o \ - handles.o frsapi.o) + handles.o frsapi.o object_uuid.o) $(eval $(call proto_header_template,$(torturesrcdir)/rpc/proto.h,$(torture_rpc_OBJ_FILES:.o=.c))) diff --git a/source4/torture/rpc/object_uuid.c b/source4/torture/rpc/object_uuid.c new file mode 100644 index 0000000000..5a77bd1c29 --- /dev/null +++ b/source4/torture/rpc/object_uuid.c @@ -0,0 +1,87 @@ +/* + Unix SMB/CIFS implementation. + + test suite for behaviour of object uuids in rpc requests + + Copyright (C) Stefan Metzmacher 2008 + + 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 "torture/torture.h" +#include "librpc/gen_ndr/ndr_dssetup.h" +#include "librpc/gen_ndr/ndr_dssetup_c.h" +#include "librpc/gen_ndr/ndr_lsa.h" +#include "librpc/gen_ndr/ndr_lsa_c.h" +#include "torture/rpc/rpc.h" + +/* + this tests the send object uuids in the dcerpc request +*/ + +static bool test_random_uuid(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p1, *p2; + struct rpc_request *req; + struct GUID uuid; + struct dssetup_DsRoleGetPrimaryDomainInformation r1; + struct lsa_GetUserName r2; + struct lsa_StringPointer authority_name_p; + + torture_comment(torture, "RPC-OBJECTUUID-RANDOM\n"); + + status = torture_rpc_connection(torture, &p1, &ndr_table_dssetup); + torture_assert_ntstatus_ok(torture, status, "opening dsetup pipe1"); + + status = torture_rpc_connection(torture, &p2, &ndr_table_lsarpc); + torture_assert_ntstatus_ok(torture, status, "opening lsa pipe1"); + + uuid = GUID_random(); + + r1.in.level = DS_ROLE_BASIC_INFORMATION; + req = dcerpc_ndr_request_send(p1, &uuid, + &ndr_table_dssetup, + NDR_DSSETUP_DSROLEGETPRIMARYDOMAININFORMATION, + torture, &r1); + status = dcerpc_ndr_request_recv(req); + torture_assert_ntstatus_ok(torture, status, "DsRoleGetPrimaryDomainInformation failed"); + torture_assert_werr_ok(torture, r1.out.result, "DsRoleGetPrimaryDomainInformation failed"); + + uuid = GUID_random(); + + r2.in.system_name = "\\"; + r2.in.account_name = NULL; + r2.in.authority_name = &authority_name_p; + authority_name_p.string = NULL; + + req = dcerpc_ndr_request_send(p2, &uuid, + &ndr_table_lsarpc, + NDR_LSA_GETUSERNAME, + torture, &r2); + status = dcerpc_ndr_request_recv(req); + torture_assert_ntstatus_ok(torture, status, "lsaClose failed"); + torture_assert_ntstatus_ok(torture, r2.out.result, "lsaClose failed"); + + return true; +} + +struct torture_suite *torture_rpc_object_uuid(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite; + suite = torture_suite_create(mem_ctx, "OBJECTUUID"); + torture_suite_add_simple_test(suite, "random-uuid", test_random_uuid); + return suite; +} diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c index 85f7bde16c..c35f93e3f9 100644 --- a/source4/torture/rpc/rpc.c +++ b/source4/torture/rpc/rpc.c @@ -387,6 +387,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_suite(suite, torture_rpc_atsvc(suite)); torture_suite_add_suite(suite, torture_rpc_wkssvc(suite)); torture_suite_add_suite(suite, torture_rpc_handles(suite)); + torture_suite_add_suite(suite, torture_rpc_object_uuid(suite)); torture_suite_add_suite(suite, torture_rpc_winreg(suite)); torture_suite_add_simple_test(suite, "SPOOLSS", torture_rpc_spoolss); torture_suite_add_suite(suite, torture_rpc_spoolss_notify(suite)); -- cgit From 45645426c2ae541fc2c3a112cde5a7f1d857938b Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 06:42:53 +0200 Subject: s4:selftest: make sure we test RPC-OBJECTUUID with seal metze --- source4/selftest/samba4_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh index af8c3e94c1..bfe386af69 100755 --- a/source4/selftest/samba4_tests.sh +++ b/source4/selftest/samba4_tests.sh @@ -91,7 +91,7 @@ plantest "ldb" none TEST_DATA_PREFIX=\$PREFIX $LDBDIR/tests/test-tdb.sh # that they stay passing ncacn_np_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-SAMSYNC RPC-SAMBA3SESSIONKEY RPC-SAMBA3-GETUSERNAME RPC-SAMBA3-LSA RPC-BINDSAMBA3 RPC-NETLOGSAMBA3 RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" ncalrpc_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-DRSUAPI RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" -ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-DSSYNC RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT" +ncacn_ip_tcp_tests="RPC-SCHANNEL RPC-JOIN RPC-LSA RPC-DSSETUP RPC-ALTERCONTEXT RPC-MULTIBIND RPC-NETLOGON RPC-HANDLES RPC-DSSYNC RPC-ASYNCBIND RPC-LSALOOKUP RPC-LSA-GETUSER RPC-SCHANNEL2 RPC-AUTHCONTEXT RPC-OBJECTUUID" slow_ncacn_np_tests="RPC-SAMLOGON RPC-SAMR RPC-SAMR-USERS RPC-SAMR-PASSWORDS" slow_ncalrpc_tests="RPC-SAMR RPC-SAMR-PASSWORDS" slow_ncacn_ip_tcp_tests="RPC-SAMR RPC-SAMR-PASSWORDS RPC-CRACKNAMES" -- cgit From 8dd35fabbb4732cf601a93abbe0373fd53e661a1 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 24 Sep 2008 11:20:45 -0700 Subject: NetApp found that we never set the smb signing flags2. This was found during testing at the CIFS plugfest. No other client seems to honour or use this bit, but it is quite ligitimately required by the spec. Andrew Bartlett --- source4/smb_server/smb/request.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'source4') diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c index c7fa2d7d8a..eb3e100b96 100644 --- a/source4/smb_server/smb/request.c +++ b/source4/smb_server/smb/request.c @@ -140,6 +140,10 @@ void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen) flags2 |= FLAGS2_32_BIT_ERROR_CODES; } + if (req->smb_conn->signing.allow_smb_signing || req->smb_conn->signing.mandatory_signing) { + flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; + } + req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.vwv = req->out.hdr + HDR_VWV; req->out.wct = wct; -- cgit From 6ad78f01a5982666c83025a94ce4ff3729d5db6f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 24 Sep 2008 12:53:10 -0700 Subject: Rename hdb_ldb to hdb_samba4 and load as a plugin into the kdc. This avoids one more custom patch to the Heimdal code, and provides a more standard way to produce hdb plugins in future. I've renamed from hdb_ldb to hdb_samba4 as it really is not generic ldb. Andrew Bartlett --- source4/heimdal/lib/hdb/hdb.c | 42 +- source4/heimdal/lib/hdb/hdb.h | 6 + source4/kdc/config.mk | 8 +- source4/kdc/hdb-ldb.c | 1550 ----------------------------------------- source4/kdc/hdb-samba4.c | 1550 +++++++++++++++++++++++++++++++++++++++++ source4/kdc/kdc.c | 17 +- 6 files changed, 1604 insertions(+), 1569 deletions(-) delete mode 100644 source4/kdc/hdb-ldb.c create mode 100644 source4/kdc/hdb-samba4.c (limited to 'source4') diff --git a/source4/heimdal/lib/hdb/hdb.c b/source4/heimdal/lib/hdb/hdb.c index 3fddabb2d0..19c170767d 100644 --- a/source4/heimdal/lib/hdb/hdb.c +++ b/source4/heimdal/lib/hdb/hdb.c @@ -31,34 +31,27 @@ * SUCH DAMAGE. */ +#include "krb5.h" +#include "krb5_locl.h" #include "hdb_locl.h" - RCSID("$Id$"); #ifdef HAVE_DLFCN_H #include #endif -struct hdb_method { - const char *prefix; - krb5_error_code (*create)(krb5_context, HDB **, const char *filename); -}; - static struct hdb_method methods[] = { #if HAVE_DB1 || HAVE_DB3 - {"db:", hdb_db_create}, + {HDB_INTERFACE_VERSION, "db:", hdb_db_create}, #endif #if HAVE_NDBM - {"ndbm:", hdb_ndbm_create}, + {HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create}, #endif #if defined(OPENLDAP) && !defined(OPENLDAP_MODULE) - {"ldap:", hdb_ldap_create}, - {"ldapi:", hdb_ldapi_create}, -#endif -#ifdef HAVE_LDB /* Used for integrated samba build */ - {"ldb:", hdb_ldb_create}, + {HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create}, + {HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create}, #endif - {NULL, NULL} + {0, NULL, NULL} }; #if HAVE_DB1 || HAVE_DB3 @@ -398,11 +391,32 @@ hdb_create(krb5_context context, HDB **db, const char *filename) { const struct hdb_method *h; const char *residual; + krb5_error_code ret; + struct krb5_plugin *list = NULL, *e; if(filename == NULL) filename = HDB_DEFAULT_DB; krb5_add_et_list(context, initialize_hdb_error_table_r); h = find_method (filename, &residual); + + if (h == NULL) { + ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list); + if(ret == 0 && list != NULL) { + for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { + h = _krb5_plugin_get_symbol(e); + if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0 + && h->interface_version == HDB_INTERFACE_VERSION) { + residual = filename + strlen(h->prefix); + break; + } + } + if (e == NULL) { + h = NULL; + _krb5_plugin_free(list); + } + } + } + #ifdef HAVE_DLOPEN if (h == NULL) h = find_dynamic_method (context, filename, &residual); diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h index bc1b744015..5c2097ea59 100644 --- a/source4/heimdal/lib/hdb/hdb.h +++ b/source4/heimdal/lib/hdb/hdb.h @@ -139,6 +139,12 @@ typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*, hdb_entry_ex*, void*); extern krb5_kt_ops hdb_kt_ops; +struct hdb_method { + int interface_version; + const char *prefix; + krb5_error_code (*create)(krb5_context, HDB **, const char *filename); +}; + #include #endif /* __HDB_H__ */ diff --git a/source4/kdc/config.mk b/source4/kdc/config.mk index b3e5bfdb92..dfd2879bd6 100644 --- a/source4/kdc/config.mk +++ b/source4/kdc/config.mk @@ -6,7 +6,7 @@ INIT_FUNCTION = server_service_kdc_init SUBSYSTEM = samba PRIVATE_DEPENDENCIES = \ - HEIMDAL_KDC HDB_LDB + HEIMDAL_KDC HDB_SAMBA4 # End SUBSYSTEM KDC ####################### @@ -14,7 +14,7 @@ KDC_OBJ_FILES = $(addprefix $(kdcsrcdir)/, kdc.o kpasswdd.o) ####################### # Start SUBSYSTEM KDC -[SUBSYSTEM::HDB_LDB] +[SUBSYSTEM::HDB_SAMBA4] CFLAGS = -Iheimdal/kdc -Iheimdal/lib/hdb PRIVATE_DEPENDENCIES = \ LIBLDB auth_sam auth_sam_reply CREDENTIALS \ @@ -22,5 +22,5 @@ PRIVATE_DEPENDENCIES = \ # End SUBSYSTEM KDC ####################### -HDB_LDB_OBJ_FILES = $(addprefix $(kdcsrcdir)/, hdb-ldb.o pac-glue.o) -$(eval $(call proto_header_template,$(kdcsrcdir)/pac_glue.h,$(HDB_LDB_OBJ_FILES:.o=.c))) +HDB_SAMBA4_OBJ_FILES = $(addprefix $(kdcsrcdir)/, hdb-samba4.o pac-glue.o) +$(eval $(call proto_header_template,$(kdcsrcdir)/pac_glue.h,$(HDB_SAMBA4_OBJ_FILES:.o=.c))) diff --git a/source4/kdc/hdb-ldb.c b/source4/kdc/hdb-ldb.c deleted file mode 100644 index 4fde75cf70..0000000000 --- a/source4/kdc/hdb-ldb.c +++ /dev/null @@ -1,1550 +0,0 @@ -/* - * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd. - * Copyright (c) 2004, Andrew Bartlett . - * Copyright (c) 2004, Stefan Metzmacher - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * 3. Neither the name of PADL Software nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "includes.h" -#include "system/time.h" -#include "dsdb/common/flags.h" -#include "lib/ldb/include/ldb.h" -#include "lib/ldb/include/ldb_errors.h" -#include "librpc/gen_ndr/netlogon.h" -#include "auth/auth.h" -#include "auth/credentials/credentials.h" -#include "auth/auth_sam.h" -#include "util/util_ldb.h" -#include "dsdb/samdb/samdb.h" -#include "librpc/ndr/libndr.h" -#include "librpc/gen_ndr/ndr_drsblobs.h" -#include "librpc/gen_ndr/lsa.h" -#include "libcli/auth/libcli_auth.h" -#include "param/param.h" -#include "events/events.h" -#include "kdc/kdc.h" -#include "../lib/crypto/md4.h" - -enum hdb_ldb_ent_type -{ HDB_LDB_ENT_TYPE_CLIENT, HDB_LDB_ENT_TYPE_SERVER, - HDB_LDB_ENT_TYPE_KRBTGT, HDB_LDB_ENT_TYPE_TRUST, HDB_LDB_ENT_TYPE_ANY }; - -enum trust_direction { - UNKNOWN = 0, - INBOUND = LSA_TRUST_DIRECTION_INBOUND, - OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND -}; - -static const char *realm_ref_attrs[] = { - "nCName", - "dnsRoot", - NULL -}; - -static const char *trust_attrs[] = { - "trustPartner", - "trustAuthIncoming", - "trustAuthOutgoing", - "whenCreated", - "msDS-SupportedEncryptionTypes", - "trustAttributes", - "trustDirection", - "trustType", - NULL -}; - -static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val) -{ - const char *tmp; - const char *gentime; - struct tm tm; - - gentime = ldb_msg_find_attr_as_string(msg, attr, NULL); - if (!gentime) - return default_val; - - tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); - if (tmp == NULL) { - return default_val; - } - - return timegm(&tm); -} - -static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_ldb_ent_type ent_type) -{ - HDBFlags flags = int2HDBFlags(0); - - /* we don't allow kadmin deletes */ - flags.immutable = 1; - - /* mark the principal as invalid to start with */ - flags.invalid = 1; - - flags.renewable = 1; - - /* All accounts are servers, but this may be disabled again in the caller */ - flags.server = 1; - - /* Account types - clear the invalid bit if it turns out to be valid */ - if (userAccountControl & UF_NORMAL_ACCOUNT) { - if (ent_type == HDB_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { - flags.client = 1; - } - flags.invalid = 0; - } - - if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) { - if (ent_type == HDB_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { - flags.client = 1; - } - flags.invalid = 0; - } - if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) { - if (ent_type == HDB_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { - flags.client = 1; - } - flags.invalid = 0; - } - if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) { - if (ent_type == HDB_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { - flags.client = 1; - } - flags.invalid = 0; - } - - /* Not permitted to act as a client if disabled */ - if (userAccountControl & UF_ACCOUNTDISABLE) { - flags.client = 0; - } - if (userAccountControl & UF_LOCKOUT) { - flags.invalid = 1; - } -/* - if (userAccountControl & UF_PASSWORD_NOTREQD) { - flags.invalid = 1; - } -*/ -/* - UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent -*/ - if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) { - flags.invalid = 1; - } - -/* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in LDB_message2entry() */ - -/* - if (userAccountControl & UF_MNS_LOGON_ACCOUNT) { - flags.invalid = 1; - } -*/ - if (userAccountControl & UF_SMARTCARD_REQUIRED) { - flags.require_hwauth = 1; - } - if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) { - flags.ok_as_delegate = 1; - } - if (!(userAccountControl & UF_NOT_DELEGATED)) { - flags.forwardable = 1; - flags.proxiable = 1; - } - - if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) { - flags.require_preauth = 0; - } else { - flags.require_preauth = 1; - - } - return flags; -} - -static int hdb_ldb_destructor(struct hdb_ldb_private *private) -{ - hdb_entry_ex *entry_ex = private->entry_ex; - free_hdb_entry(&entry_ex->entry); - return 0; -} - -static void hdb_ldb_free_entry(krb5_context context, hdb_entry_ex *entry_ex) -{ - talloc_free(entry_ex->ctx); -} - -static krb5_error_code LDB_message2entry_keys(krb5_context context, - struct smb_iconv_convenience *iconv_convenience, - TALLOC_CTX *mem_ctx, - struct ldb_message *msg, - unsigned int userAccountControl, - hdb_entry_ex *entry_ex) -{ - krb5_error_code ret = 0; - enum ndr_err_code ndr_err; - struct samr_Password *hash; - const struct ldb_val *sc_val; - struct supplementalCredentialsBlob scb; - struct supplementalCredentialsPackage *scpk = NULL; - bool newer_keys = false; - struct package_PrimaryKerberosBlob _pkb; - struct package_PrimaryKerberosCtr3 *pkb3 = NULL; - struct package_PrimaryKerberosCtr4 *pkb4 = NULL; - uint32_t i; - uint32_t allocated_keys = 0; - - entry_ex->entry.keys.val = NULL; - entry_ex->entry.keys.len = 0; - - entry_ex->entry.kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0); - - /* Get keys from the db */ - - hash = samdb_result_hash(mem_ctx, msg, "unicodePwd"); - sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials"); - - /* unicodePwd for enctype 0x17 (23) if present */ - if (hash) { - allocated_keys++; - } - - /* supplementalCredentials if present */ - if (sc_val) { - ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, iconv_convenience, &scb, - (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - dump_data(0, sc_val->data, sc_val->length); - ret = EINVAL; - goto out; - } - - if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) { - NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb); - ret = EINVAL; - goto out; - } - - for (i=0; i < scb.sub.num_packages; i++) { - if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) { - scpk = &scb.sub.packages[i]; - if (!scpk->data || !scpk->data[0]) { - scpk = NULL; - continue; - } - newer_keys = true; - break; - } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) { - scpk = &scb.sub.packages[i]; - if (!scpk->data || !scpk->data[0]) { - scpk = NULL; - } - /* - * we don't break here in hope to find - * a Kerberos-Newer-Keys package - */ - } - } - } - /* - * Primary:Kerberos-Newer-Keys or Primary:Kerberos element - * of supplementalCredentials - */ - if (scpk) { - DATA_BLOB blob; - - blob = strhex_to_data_blob(scpk->data); - if (!blob.data) { - ret = ENOMEM; - goto out; - } - talloc_steal(mem_ctx, blob.data); - - /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */ - ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, iconv_convenience, &_pkb, - (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - krb5_set_error_string(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob"); - krb5_warnx(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob"); - ret = EINVAL; - goto out; - } - - if (newer_keys && _pkb.version != 4) { - krb5_set_error_string(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); - krb5_warnx(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); - ret = EINVAL; - goto out; - } - - if (!newer_keys && _pkb.version != 3) { - krb5_set_error_string(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3"); - krb5_warnx(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3"); - ret = EINVAL; - goto out; - } - - if (_pkb.version == 4) { - pkb4 = &_pkb.ctr.ctr4; - allocated_keys += pkb4->num_keys; - } else if (_pkb.version == 3) { - pkb3 = &_pkb.ctr.ctr3; - allocated_keys += pkb3->num_keys; - } - } - - if (allocated_keys == 0) { - /* oh, no password. Apparently (comment in - * hdb-ldap.c) this violates the ASN.1, but this - * allows an entry with no keys (yet). */ - return 0; - } - - /* allocate space to decode into */ - entry_ex->entry.keys.len = 0; - entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key)); - if (entry_ex->entry.keys.val == NULL) { - ret = ENOMEM; - goto out; - } - - if (hash && !(userAccountControl & UF_USE_DES_KEY_ONLY)) { - Key key; - - key.mkvno = 0; - key.salt = NULL; /* No salt for this enc type */ - - ret = krb5_keyblock_init(context, - ENCTYPE_ARCFOUR_HMAC_MD5, - hash->hash, sizeof(hash->hash), - &key.key); - if (ret) { - goto out; - } - - entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; - entry_ex->entry.keys.len++; - } - - if (pkb4) { - for (i=0; i < pkb4->num_keys; i++) { - bool use = true; - Key key; - - if (!pkb4->keys[i].value) continue; - - if (userAccountControl & UF_USE_DES_KEY_ONLY) { - switch (pkb4->keys[i].keytype) { - case ENCTYPE_DES_CBC_CRC: - case ENCTYPE_DES_CBC_MD5: - break; - default: - use = false; - break; - } - } - - if (!use) continue; - - key.mkvno = 0; - key.salt = NULL; - - if (pkb4->salt.string) { - DATA_BLOB salt; - - salt = data_blob_string_const(pkb4->salt.string); - - key.salt = calloc(1, sizeof(*key.salt)); - if (key.salt == NULL) { - ret = ENOMEM; - goto out; - } - - key.salt->type = hdb_pw_salt; - - ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); - if (ret) { - free(key.salt); - key.salt = NULL; - goto out; - } - } - - /* TODO: maybe pass the iteration_count somehow... */ - - ret = krb5_keyblock_init(context, - pkb4->keys[i].keytype, - pkb4->keys[i].value->data, - pkb4->keys[i].value->length, - &key.key); - if (ret) { - if (key.salt) { - free_Salt(key.salt); - free(key.salt); - key.salt = NULL; - } - goto out; - } - - entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; - entry_ex->entry.keys.len++; - } - } else if (pkb3) { - for (i=0; i < pkb3->num_keys; i++) { - bool use = true; - Key key; - - if (!pkb3->keys[i].value) continue; - - if (userAccountControl & UF_USE_DES_KEY_ONLY) { - switch (pkb3->keys[i].keytype) { - case ENCTYPE_DES_CBC_CRC: - case ENCTYPE_DES_CBC_MD5: - break; - default: - use = false; - break; - } - } - - if (!use) continue; - - key.mkvno = 0; - key.salt = NULL; - - if (pkb3->salt.string) { - DATA_BLOB salt; - - salt = data_blob_string_const(pkb3->salt.string); - - key.salt = calloc(1, sizeof(*key.salt)); - if (key.salt == NULL) { - ret = ENOMEM; - goto out; - } - - key.salt->type = hdb_pw_salt; - - ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); - if (ret) { - free(key.salt); - key.salt = NULL; - goto out; - } - } - - ret = krb5_keyblock_init(context, - pkb3->keys[i].keytype, - pkb3->keys[i].value->data, - pkb3->keys[i].value->length, - &key.key); - if (ret) { - if (key.salt) { - free_Salt(key.salt); - free(key.salt); - key.salt = NULL; - } - goto out; - } - - entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; - entry_ex->entry.keys.len++; - } - } - -out: - if (ret != 0) { - entry_ex->entry.keys.len = 0; - } - if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) { - free(entry_ex->entry.keys.val); - entry_ex->entry.keys.val = NULL; - } - return ret; -} - -/* - * Construct an hdb_entry from a directory entry. - */ -static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, - TALLOC_CTX *mem_ctx, krb5_const_principal principal, - enum hdb_ldb_ent_type ent_type, - struct ldb_message *msg, - struct ldb_message *realm_ref_msg, - hdb_entry_ex *entry_ex) -{ - unsigned int userAccountControl; - int i; - krb5_error_code ret = 0; - krb5_boolean is_computer = FALSE; - const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg, "dnsRoot", NULL); - char *realm = strupper_talloc(mem_ctx, dnsdomain); - struct loadparm_context *lp_ctx = ldb_get_opaque((struct ldb_context *)db->hdb_db, "loadparm"); - struct ldb_dn *domain_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, - mem_ctx, - realm_ref_msg, - "nCName", - ldb_dn_new(mem_ctx, (struct ldb_context *)db->hdb_db, NULL)); - - struct hdb_ldb_private *private; - NTTIME acct_expiry; - - struct ldb_message_element *objectclasses; - struct ldb_val computer_val; - computer_val.data = discard_const_p(uint8_t,"computer"); - computer_val.length = strlen((const char *)computer_val.data); - - objectclasses = ldb_msg_find_element(msg, "objectClass"); - - if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) { - is_computer = TRUE; - } - - memset(entry_ex, 0, sizeof(*entry_ex)); - - if (!realm) { - krb5_set_error_string(context, "talloc_strdup: out of memory"); - ret = ENOMEM; - goto out; - } - - private = talloc(mem_ctx, struct hdb_ldb_private); - if (!private) { - ret = ENOMEM; - goto out; - } - - private->entry_ex = entry_ex; - private->iconv_convenience = lp_iconv_convenience(lp_ctx); - private->netbios_name = lp_netbios_name(lp_ctx); - - talloc_set_destructor(private, hdb_ldb_destructor); - - entry_ex->ctx = private; - entry_ex->free_entry = hdb_ldb_free_entry; - - userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0); - - - entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); - if (ent_type == HDB_LDB_ENT_TYPE_ANY && principal == NULL) { - const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); - if (!samAccountName) { - krb5_set_error_string(context, "LDB_message2entry: no samAccountName present"); - ret = ENOENT; - goto out; - } - samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); - krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL); - } else { - char *strdup_realm; - ret = copy_Principal(principal, entry_ex->entry.principal); - if (ret) { - krb5_clear_error_string(context); - goto out; - } - - /* While we have copied the client principal, tests - * show that Win2k3 returns the 'corrected' realm, not - * the client-specified realm. This code attempts to - * replace the client principal's realm with the one - * we determine from our records */ - - /* this has to be with malloc() */ - strdup_realm = strdup(realm); - if (!strdup_realm) { - ret = ENOMEM; - krb5_clear_error_string(context); - goto out; - } - free(*krb5_princ_realm(context, entry_ex->entry.principal)); - krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm); - } - - entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type); - - if (ent_type == HDB_LDB_ENT_TYPE_KRBTGT) { - entry_ex->entry.flags.invalid = 0; - entry_ex->entry.flags.server = 1; - entry_ex->entry.flags.forwardable = 1; - entry_ex->entry.flags.ok_as_delegate = 1; - } - - if (lp_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) { - if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) { - entry_ex->entry.flags.server = 0; - } - } - - /* use 'whenCreated' */ - entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); - /* use '???' */ - entry_ex->entry.created_by.principal = NULL; - - entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event)); - if (entry_ex->entry.modified_by == NULL) { - krb5_set_error_string(context, "malloc: out of memory"); - ret = ENOMEM; - goto out; - } - - /* use 'whenChanged' */ - entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0); - /* use '???' */ - entry_ex->entry.modified_by->principal = NULL; - - entry_ex->entry.valid_start = NULL; - - acct_expiry = samdb_result_account_expires(msg); - if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) { - entry_ex->entry.valid_end = NULL; - } else { - entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end)); - if (entry_ex->entry.valid_end == NULL) { - ret = ENOMEM; - goto out; - } - *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry); - } - - if (ent_type != HDB_LDB_ENT_TYPE_KRBTGT) { - NTTIME must_change_time - = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx, - domain_dn, msg); - if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) { - entry_ex->entry.pw_end = NULL; - } else { - entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end)); - if (entry_ex->entry.pw_end == NULL) { - ret = ENOMEM; - goto out; - } - *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time); - } - } else { - entry_ex->entry.pw_end = NULL; - } - - entry_ex->entry.max_life = NULL; - - entry_ex->entry.max_renew = NULL; - - entry_ex->entry.generation = NULL; - - /* Get keys from the db */ - ret = LDB_message2entry_keys(context, private->iconv_convenience, private, msg, userAccountControl, entry_ex); - if (ret) { - /* Could be bougus data in the entry, or out of memory */ - goto out; - } - - entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); - if (entry_ex->entry.etypes == NULL) { - krb5_clear_error_string(context); - ret = ENOMEM; - goto out; - } - entry_ex->entry.etypes->len = entry_ex->entry.keys.len; - entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); - if (entry_ex->entry.etypes->val == NULL) { - krb5_clear_error_string(context); - ret = ENOMEM; - goto out; - } - for (i=0; i < entry_ex->entry.etypes->len; i++) { - entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; - } - - - private->msg = talloc_steal(private, msg); - private->realm_ref_msg = talloc_steal(private, realm_ref_msg); - private->samdb = (struct ldb_context *)db->hdb_db; - -out: - if (ret != 0) { - /* This doesn't free ent itself, that is for the eventual caller to do */ - hdb_free_entry(context, entry_ex); - } else { - talloc_steal(db, entry_ex->ctx); - } - - return ret; -} - -/* - * Construct an hdb_entry from a directory entry. - */ -static krb5_error_code LDB_trust_message2entry(krb5_context context, HDB *db, - struct loadparm_context *lp_ctx, - TALLOC_CTX *mem_ctx, krb5_const_principal principal, - enum trust_direction direction, - struct ldb_message *msg, - hdb_entry_ex *entry_ex) -{ - - const char *dnsdomain; - char *realm; - char *strdup_realm; - DATA_BLOB password_utf16; - struct samr_Password password_hash; - const struct ldb_val *password_val; - struct trustAuthInOutBlob password_blob; - struct hdb_ldb_private *private; - - enum ndr_err_code ndr_err; - int i, ret, trust_direction_flags; - - private = talloc(mem_ctx, struct hdb_ldb_private); - if (!private) { - ret = ENOMEM; - goto out; - } - - private->entry_ex = entry_ex; - private->iconv_convenience = lp_iconv_convenience(lp_ctx); - private->netbios_name = lp_netbios_name(lp_ctx); - - talloc_set_destructor(private, hdb_ldb_destructor); - - entry_ex->ctx = private; - entry_ex->free_entry = hdb_ldb_free_entry; - - /* use 'whenCreated' */ - entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); - /* use '???' */ - entry_ex->entry.created_by.principal = NULL; - - entry_ex->entry.valid_start = NULL; - - trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0); - - if (direction == INBOUND) { - realm = strupper_talloc(mem_ctx, lp_realm(lp_ctx)); - password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming"); - - } else { /* OUTBOUND */ - dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL); - realm = strupper_talloc(mem_ctx, dnsdomain); - password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing"); - } - - if (!password_val || !(trust_direction_flags & direction)) { - ret = ENOENT; - goto out; - } - - ndr_err = ndr_pull_struct_blob_all(password_val, mem_ctx, private->iconv_convenience, &password_blob, - (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - ret = EINVAL; - goto out; - } - - for (i=0; i < password_blob.count; i++) { - if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) { - password_utf16 = data_blob_const(password_blob.current->array[i].AuthInfo.clear.password, - password_blob.current->array[i].AuthInfo.clear.size); - /* In the future, generate all sorts of - * hashes, but for now we can't safely convert - * the random strings windows uses into - * utf8 */ - - /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */ - mdfour(password_hash.hash, password_utf16.data, password_utf16.length); - break; - } else if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { - password_hash = password_blob.current->array[i].AuthInfo.nt4owf.password; - break; - } - } - entry_ex->entry.keys.len = 0; - entry_ex->entry.keys.val = NULL; - - if (i < password_blob.count) { - Key key; - /* Must have found a cleartext or MD4 password */ - entry_ex->entry.keys.val = calloc(1, sizeof(Key)); - - key.mkvno = 0; - key.salt = NULL; /* No salt for this enc type */ - - if (entry_ex->entry.keys.val == NULL) { - ret = ENOMEM; - goto out; - } - - ret = krb5_keyblock_init(context, - ENCTYPE_ARCFOUR_HMAC_MD5, - password_hash.hash, sizeof(password_hash.hash), - &key.key); - - entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; - entry_ex->entry.keys.len++; - } - - ret = copy_Principal(principal, entry_ex->entry.principal); - if (ret) { - krb5_clear_error_string(context); - goto out; - } - - /* While we have copied the client principal, tests - * show that Win2k3 returns the 'corrected' realm, not - * the client-specified realm. This code attempts to - * replace the client principal's realm with the one - * we determine from our records */ - - /* this has to be with malloc() */ - strdup_realm = strdup(realm); - if (!strdup_realm) { - ret = ENOMEM; - krb5_clear_error_string(context); - goto out; - } - free(*krb5_princ_realm(context, entry_ex->entry.principal)); - krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm); - - entry_ex->entry.flags = int2HDBFlags(0); - entry_ex->entry.flags.immutable = 1; - entry_ex->entry.flags.invalid = 0; - entry_ex->entry.flags.server = 1; - entry_ex->entry.flags.require_preauth = 1; - - entry_ex->entry.pw_end = NULL; - - entry_ex->entry.max_life = NULL; - - entry_ex->entry.max_renew = NULL; - - entry_ex->entry.generation = NULL; - - entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); - if (entry_ex->entry.etypes == NULL) { - krb5_clear_error_string(context); - ret = ENOMEM; - goto out; - } - entry_ex->entry.etypes->len = entry_ex->entry.keys.len; - entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); - if (entry_ex->entry.etypes->val == NULL) { - krb5_clear_error_string(context); - ret = ENOMEM; - goto out; - } - for (i=0; i < entry_ex->entry.etypes->len; i++) { - entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; - } - - - private->msg = talloc_steal(private, msg); - private->realm_ref_msg = NULL; - private->samdb = (struct ldb_context *)db->hdb_db; - -out: - if (ret != 0) { - /* This doesn't free ent itself, that is for the eventual caller to do */ - hdb_free_entry(context, entry_ex); - } else { - talloc_steal(db, entry_ex->ctx); - } - - return ret; - -} - -static krb5_error_code LDB_lookup_principal(krb5_context context, struct ldb_context *ldb_ctx, - TALLOC_CTX *mem_ctx, - krb5_const_principal principal, - enum hdb_ldb_ent_type ent_type, - struct ldb_dn *realm_dn, - struct ldb_message ***pmsg) -{ - krb5_error_code ret; - int lret; - char *filter = NULL; - const char * const *princ_attrs = user_attrs; - - char *short_princ; - char *short_princ_talloc; - - struct ldb_result *res = NULL; - - ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ); - - if (ret != 0) { - krb5_set_error_string(context, "LDB_lookup_principal: could not parse principal"); - krb5_warnx(context, "LDB_lookup_principal: could not parse principal"); - return ret; - } - - short_princ_talloc = talloc_strdup(mem_ctx, short_princ); - free(short_princ); - if (!short_princ_talloc) { - krb5_set_error_string(context, "LDB_lookup_principal: talloc_strdup() failed!"); - return ENOMEM; - } - - switch (ent_type) { - case HDB_LDB_ENT_TYPE_CLIENT: - case HDB_LDB_ENT_TYPE_TRUST: - case HDB_LDB_ENT_TYPE_ANY: - /* Can't happen */ - return EINVAL; - case HDB_LDB_ENT_TYPE_KRBTGT: - filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", - KRB5_TGS_NAME); - break; - case HDB_LDB_ENT_TYPE_SERVER: - filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", - short_princ_talloc); - break; - } - - if (!filter) { - krb5_set_error_string(context, "talloc_asprintf: out of memory"); - return ENOMEM; - } - - lret = ldb_search(ldb_ctx, mem_ctx, &res, realm_dn, - LDB_SCOPE_SUBTREE, princ_attrs, "%s", filter); - if (lret != LDB_SUCCESS) { - DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx))); - return HDB_ERR_NOENTRY; - } else if (res->count == 0 || res->count > 1) { - DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count)); - talloc_free(res); - return HDB_ERR_NOENTRY; - } - talloc_steal(mem_ctx, res->msgs); - *pmsg = res->msgs; - talloc_free(res); - return 0; -} - -static krb5_error_code LDB_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx, - TALLOC_CTX *mem_ctx, - const char *realm, - struct ldb_dn *realm_dn, - struct ldb_message ***pmsg) -{ - int lret; - char *filter = NULL; - const char * const *attrs = trust_attrs; - - struct ldb_result *res = NULL; - filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", realm, realm); - - if (!filter) { - krb5_set_error_string(context, "talloc_asprintf: out of memory"); - return ENOMEM; - } - - lret = ldb_search(ldb_ctx, mem_ctx, &res, - ldb_get_default_basedn(ldb_ctx), - LDB_SCOPE_SUBTREE, attrs, "%s", filter); - if (lret != LDB_SUCCESS) { - DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx))); - return HDB_ERR_NOENTRY; - } else if (res->count == 0 || res->count > 1) { - DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count)); - talloc_free(res); - return HDB_ERR_NOENTRY; - } - talloc_steal(mem_ctx, res->msgs); - *pmsg = res->msgs; - talloc_free(res); - return 0; -} - -static krb5_error_code LDB_lookup_realm(krb5_context context, struct ldb_context *ldb_ctx, - TALLOC_CTX *mem_ctx, - const char *realm, - struct ldb_message ***pmsg) -{ - int ret; - struct ldb_result *cross_ref_res; - struct ldb_dn *partitions_basedn = samdb_partitions_dn(ldb_ctx, mem_ctx); - - ret = ldb_search(ldb_ctx, mem_ctx, &cross_ref_res, - partitions_basedn, LDB_SCOPE_SUBTREE, realm_ref_attrs, - "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", - realm, realm); - - if (ret != LDB_SUCCESS) { - DEBUG(3, ("Failed to search to lookup realm(%s): %s\n", realm, ldb_errstring(ldb_ctx))); - talloc_free(cross_ref_res); - return HDB_ERR_NOENTRY; - } else if (cross_ref_res->count == 0 || cross_ref_res->count > 1) { - DEBUG(3, ("Failed find a single entry for realm %s: got %d\n", realm, cross_ref_res->count)); - talloc_free(cross_ref_res); - return HDB_ERR_NOENTRY; - } - - if (pmsg) { - *pmsg = cross_ref_res->msgs; - talloc_steal(mem_ctx, cross_ref_res->msgs); - } - talloc_free(cross_ref_res); - - return 0; -} - - -static krb5_error_code LDB_open(krb5_context context, HDB *db, int flags, mode_t mode) -{ - if (db->hdb_master_key_set) { - krb5_warnx(context, "LDB_open: use of a master key incompatible with LDB\n"); - krb5_set_error_string(context, "LDB_open: use of a master key incompatible with LDB\n"); - return HDB_ERR_NOENTRY; - } - - return 0; -} - -static krb5_error_code LDB_close(krb5_context context, HDB *db) -{ - return 0; -} - -static krb5_error_code LDB_lock(krb5_context context, HDB *db, int operation) -{ - return 0; -} - -static krb5_error_code LDB_unlock(krb5_context context, HDB *db) -{ - return 0; -} - -static krb5_error_code LDB_rename(krb5_context context, HDB *db, const char *new_name) -{ - return HDB_ERR_DB_INUSE; -} - -static krb5_error_code LDB_fetch_client(krb5_context context, HDB *db, - TALLOC_CTX *mem_ctx, - krb5_const_principal principal, - unsigned flags, - hdb_entry_ex *entry_ex) { - NTSTATUS nt_status; - char *principal_string; - krb5_error_code ret; - struct ldb_message **msg = NULL; - struct ldb_message **realm_ref_msg = NULL; - - ret = krb5_unparse_name(context, principal, &principal_string); - - if (ret != 0) { - return ret; - } - - nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db, - mem_ctx, principal_string, - &msg, &realm_ref_msg); - free(principal_string); - if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) { - return HDB_ERR_NOENTRY; - } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) { - return ENOMEM; - } else if (!NT_STATUS_IS_OK(nt_status)) { - return EINVAL; - } - - ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_CLIENT, - msg[0], realm_ref_msg[0], entry_ex); - return ret; -} - -static krb5_error_code LDB_fetch_krbtgt(krb5_context context, HDB *db, - TALLOC_CTX *mem_ctx, - krb5_const_principal principal, - unsigned flags, - hdb_entry_ex *entry_ex) -{ - krb5_error_code ret; - struct ldb_message **msg = NULL; - struct ldb_message **realm_ref_msg_1 = NULL; - struct ldb_message **realm_ref_msg_2 = NULL; - struct ldb_dn *realm_dn; - const char *realm; - - krb5_principal alloc_principal = NULL; - if (principal->name.name_string.len != 2 - || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) { - /* Not a krbtgt */ - return HDB_ERR_NOENTRY; - } - - /* krbtgt case. Either us or a trusted realm */ - - if ((LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, - mem_ctx, principal->realm, &realm_ref_msg_1) == 0) - && (LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, - mem_ctx, principal->name.name_string.val[1], &realm_ref_msg_2) == 0) - && (ldb_dn_compare(realm_ref_msg_1[0]->dn, realm_ref_msg_1[0]->dn) == 0)) { - /* us */ - /* Cludge, cludge cludge. If the realm part of krbtgt/realm, - * is in our db, then direct the caller at our primary - * krbtgt */ - - const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg_1[0], "dnsRoot", NULL); - char *realm_fixed = strupper_talloc(mem_ctx, dnsdomain); - if (!realm_fixed) { - krb5_set_error_string(context, "strupper_talloc: out of memory"); - return ENOMEM; - } - - ret = krb5_copy_principal(context, principal, &alloc_principal); - if (ret) { - return ret; - } - - free(alloc_principal->name.name_string.val[1]); - alloc_principal->name.name_string.val[1] = strdup(realm_fixed); - talloc_free(realm_fixed); - if (!alloc_principal->name.name_string.val[1]) { - krb5_set_error_string(context, "LDB_fetch: strdup() failed!"); - return ENOMEM; - } - principal = alloc_principal; - realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg_1[0], "nCName", NULL); - - ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, - mem_ctx, - principal, HDB_LDB_ENT_TYPE_KRBTGT, realm_dn, &msg); - - if (ret != 0) { - krb5_warnx(context, "LDB_fetch: could not find principal in DB"); - krb5_set_error_string(context, "LDB_fetch: could not find principal in DB"); - return ret; - } - - ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_KRBTGT, - msg[0], realm_ref_msg_1[0], entry_ex); - if (ret != 0) { - krb5_warnx(context, "LDB_fetch: message2entry failed"); - } - return ret; - - } else { - enum trust_direction direction = UNKNOWN; - - struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(db->hdb_db, "loadparm"), struct loadparm_context); - /* Either an inbound or outbound trust */ - - if (strcasecmp(lp_realm(lp_ctx), principal->realm) == 0) { - /* look for inbound trust */ - direction = INBOUND; - realm = principal->name.name_string.val[1]; - } - - if (strcasecmp(lp_realm(lp_ctx), principal->name.name_string.val[1]) == 0) { - /* look for outbound trust */ - direction = OUTBOUND; - realm = principal->realm; - } - - /* Trusted domains are under CN=system */ - - ret = LDB_lookup_trust(context, (struct ldb_context *)db->hdb_db, - mem_ctx, - realm, realm_dn, &msg); - - if (ret != 0) { - krb5_warnx(context, "LDB_fetch: could not find principal in DB"); - krb5_set_error_string(context, "LDB_fetch: could not find principal in DB"); - return ret; - } - - ret = LDB_trust_message2entry(context, db, lp_ctx, mem_ctx, - principal, direction, - msg[0], entry_ex); - if (ret != 0) { - krb5_warnx(context, "LDB_fetch: message2entry failed"); - } - return ret; - - - /* we should lookup trusted domains */ - return HDB_ERR_NOENTRY; - } - -} - -static krb5_error_code LDB_fetch_server(krb5_context context, HDB *db, - TALLOC_CTX *mem_ctx, - krb5_const_principal principal, - unsigned flags, - hdb_entry_ex *entry_ex) -{ - krb5_error_code ret; - const char *realm; - struct ldb_message **msg = NULL; - struct ldb_message **realm_ref_msg = NULL; - struct ldb_dn *partitions_basedn = samdb_partitions_dn(db->hdb_db, mem_ctx); - if (principal->name.name_string.len >= 2) { - /* 'normal server' case */ - int ldb_ret; - NTSTATUS nt_status; - struct ldb_dn *user_dn, *domain_dn; - char *principal_string; - - ret = krb5_unparse_name_flags(context, principal, - KRB5_PRINCIPAL_UNPARSE_NO_REALM, - &principal_string); - if (ret != 0) { - return ret; - } - - /* At this point we may find the host is known to be - * in a different realm, so we should generate a - * referral instead */ - nt_status = crack_service_principal_name((struct ldb_context *)db->hdb_db, - mem_ctx, principal_string, - &user_dn, &domain_dn); - free(principal_string); - - if (!NT_STATUS_IS_OK(nt_status)) { - return HDB_ERR_NOENTRY; - } - - ldb_ret = gendb_search_dn((struct ldb_context *)db->hdb_db, - mem_ctx, user_dn, &msg, user_attrs); - - if (ldb_ret != 1) { - return HDB_ERR_NOENTRY; - } - - ldb_ret = gendb_search((struct ldb_context *)db->hdb_db, - mem_ctx, partitions_basedn, &realm_ref_msg, realm_ref_attrs, - "ncName=%s", ldb_dn_get_linearized(domain_dn)); - - if (ldb_ret != 1) { - return HDB_ERR_NOENTRY; - } - - } else { - struct ldb_dn *realm_dn; - /* server as client principal case, but we must not lookup userPrincipalNames */ - - realm = krb5_principal_get_realm(context, principal); - - ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, - mem_ctx, realm, &realm_ref_msg); - if (ret != 0) { - return HDB_ERR_NOENTRY; - } - - realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg[0], "nCName", NULL); - - ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, - mem_ctx, - principal, HDB_LDB_ENT_TYPE_SERVER, realm_dn, &msg); - - if (ret != 0) { - return ret; - } - } - - ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_SERVER, - msg[0], realm_ref_msg[0], entry_ex); - if (ret != 0) { - krb5_warnx(context, "LDB_fetch: message2entry failed"); - } - - return ret; -} - -static krb5_error_code LDB_fetch(krb5_context context, HDB *db, - krb5_const_principal principal, - unsigned flags, - hdb_entry_ex *entry_ex) -{ - krb5_error_code ret = HDB_ERR_NOENTRY; - - TALLOC_CTX *mem_ctx = talloc_named(db, 0, "LDB_fetch context"); - - if (!mem_ctx) { - krb5_set_error_string(context, "LDB_fetch: talloc_named() failed!"); - return ENOMEM; - } - - if (flags & HDB_F_GET_CLIENT) { - ret = LDB_fetch_client(context, db, mem_ctx, principal, flags, entry_ex); - if (ret != HDB_ERR_NOENTRY) goto done; - } - if (flags & HDB_F_GET_SERVER) { - /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */ - ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex); - if (ret != HDB_ERR_NOENTRY) goto done; - - /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */ - ret = LDB_fetch_server(context, db, mem_ctx, principal, flags, entry_ex); - if (ret != HDB_ERR_NOENTRY) goto done; - } - if (flags & HDB_F_GET_KRBTGT) { - ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex); - if (ret != HDB_ERR_NOENTRY) goto done; - } - -done: - talloc_free(mem_ctx); - return ret; -} - -static krb5_error_code LDB_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) -{ - return HDB_ERR_DB_INUSE; -} - -static krb5_error_code LDB_remove(krb5_context context, HDB *db, krb5_const_principal principal) -{ - return HDB_ERR_DB_INUSE; -} - -struct hdb_ldb_seq { - struct ldb_context *ctx; - int index; - int count; - struct ldb_message **msgs; - struct ldb_message **realm_ref_msgs; -}; - -static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) -{ - krb5_error_code ret; - struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc; - TALLOC_CTX *mem_ctx; - hdb_entry_ex entry_ex; - memset(&entry_ex, '\0', sizeof(entry_ex)); - - if (!priv) { - return HDB_ERR_NOENTRY; - } - - mem_ctx = talloc_named(priv, 0, "LDB_seq context"); - - if (!mem_ctx) { - krb5_set_error_string(context, "LDB_seq: talloc_named() failed!"); - return ENOMEM; - } - - if (priv->index < priv->count) { - ret = LDB_message2entry(context, db, mem_ctx, - NULL, HDB_LDB_ENT_TYPE_ANY, - priv->msgs[priv->index++], - priv->realm_ref_msgs[0], entry); - } else { - ret = HDB_ERR_NOENTRY; - } - - if (ret != 0) { - talloc_free(priv); - db->hdb_dbc = NULL; - } else { - talloc_free(mem_ctx); - } - - return ret; -} - -static krb5_error_code LDB_firstkey(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) -{ - struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db; - struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc; - char *realm; - struct ldb_dn *realm_dn = NULL; - struct ldb_result *res = NULL; - struct ldb_message **realm_ref_msgs = NULL; - krb5_error_code ret; - TALLOC_CTX *mem_ctx; - int lret; - - if (priv) { - talloc_free(priv); - db->hdb_dbc = NULL; - } - - priv = (struct hdb_ldb_seq *) talloc(db, struct hdb_ldb_seq); - if (!priv) { - krb5_set_error_string(context, "talloc: out of memory"); - return ENOMEM; - } - - priv->ctx = ldb_ctx; - priv->index = 0; - priv->msgs = NULL; - priv->realm_ref_msgs = NULL; - priv->count = 0; - - mem_ctx = talloc_named(priv, 0, "LDB_firstkey context"); - - if (!mem_ctx) { - krb5_set_error_string(context, "LDB_firstkey: talloc_named() failed!"); - return ENOMEM; - } - - ret = krb5_get_default_realm(context, &realm); - if (ret != 0) { - talloc_free(priv); - return ret; - } - - ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, - mem_ctx, realm, &realm_ref_msgs); - - free(realm); - - if (ret != 0) { - talloc_free(priv); - krb5_warnx(context, "LDB_firstkey: could not find realm\n"); - return HDB_ERR_NOENTRY; - } - - realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msgs[0], "nCName", NULL); - - priv->realm_ref_msgs = talloc_steal(priv, realm_ref_msgs); - - lret = ldb_search(ldb_ctx, priv, &res, - realm_dn, LDB_SCOPE_SUBTREE, user_attrs, - "(objectClass=user)"); - - if (lret != LDB_SUCCESS) { - talloc_free(priv); - return HDB_ERR_NOENTRY; - } - - priv->count = res->count; - priv->msgs = talloc_steal(priv, res->msgs); - talloc_free(res); - - db->hdb_dbc = priv; - - ret = LDB_seq(context, db, flags, entry); - - if (ret != 0) { - talloc_free(priv); - db->hdb_dbc = NULL; - } else { - talloc_free(mem_ctx); - } - return ret; -} - -static krb5_error_code LDB_nextkey(krb5_context context, HDB *db, unsigned flags, - hdb_entry_ex *entry) -{ - return LDB_seq(context, db, flags, entry); -} - -static krb5_error_code LDB_destroy(krb5_context context, HDB *db) -{ - talloc_free(db); - return 0; -} - -/* This interface is to be called by the KDC, which is expecting Samba - * calling conventions. It is also called by a wrapper - * (hdb_ldb_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb - * code */ - -NTSTATUS kdc_hdb_ldb_create(TALLOC_CTX *mem_ctx, - struct event_context *ev_ctx, - struct loadparm_context *lp_ctx, - krb5_context context, struct HDB **db, const char *arg) -{ - NTSTATUS nt_status; - struct auth_session_info *session_info; - *db = talloc(mem_ctx, HDB); - if (!*db) { - krb5_set_error_string(context, "malloc: out of memory"); - return NT_STATUS_NO_MEMORY; - } - - (*db)->hdb_master_key_set = 0; - (*db)->hdb_db = NULL; - - nt_status = auth_system_session_info(*db, lp_ctx, &session_info); - if (!NT_STATUS_IS_OK(nt_status)) { - return nt_status; - } - - /* The idea here is very simple. Using Kerberos to - * authenticate the KDC to the LDAP server is higly likely to - * be circular. - * - * In future we may set this up to use EXERNAL and SSL - * certificates, for now it will almost certainly be NTLMSSP - */ - - cli_credentials_set_kerberos_state(session_info->credentials, - CRED_DONT_USE_KERBEROS); - - /* Setup the link to LDB */ - (*db)->hdb_db = samdb_connect(*db, ev_ctx, lp_ctx, session_info); - if ((*db)->hdb_db == NULL) { - DEBUG(1, ("hdb_ldb_create: Cannot open samdb for KDC backend!")); - return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; - } - - (*db)->hdb_dbc = NULL; - (*db)->hdb_open = LDB_open; - (*db)->hdb_close = LDB_close; - (*db)->hdb_fetch = LDB_fetch; - (*db)->hdb_store = LDB_store; - (*db)->hdb_remove = LDB_remove; - (*db)->hdb_firstkey = LDB_firstkey; - (*db)->hdb_nextkey = LDB_nextkey; - (*db)->hdb_lock = LDB_lock; - (*db)->hdb_unlock = LDB_unlock; - (*db)->hdb_rename = LDB_rename; - /* we don't implement these, as we are not a lockable database */ - (*db)->hdb__get = NULL; - (*db)->hdb__put = NULL; - /* kadmin should not be used for deletes - use other tools instead */ - (*db)->hdb__del = NULL; - (*db)->hdb_destroy = LDB_destroy; - - return NT_STATUS_OK; -} - -krb5_error_code hdb_ldb_create(krb5_context context, struct HDB **db, const char *arg) -{ - NTSTATUS nt_status; - /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */ - nt_status = kdc_hdb_ldb_create(kdc_mem_ctx, event_context_find(kdc_mem_ctx), kdc_lp_ctx, - context, db, arg); - - if (NT_STATUS_IS_OK(nt_status)) { - return 0; - } - return EINVAL; -} diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c new file mode 100644 index 0000000000..d7317f17d4 --- /dev/null +++ b/source4/kdc/hdb-samba4.c @@ -0,0 +1,1550 @@ +/* + * Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd. + * Copyright (c) 2004, Andrew Bartlett . + * Copyright (c) 2004, Stefan Metzmacher + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of PADL Software nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "includes.h" +#include "system/time.h" +#include "dsdb/common/flags.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "librpc/gen_ndr/netlogon.h" +#include "auth/auth.h" +#include "auth/credentials/credentials.h" +#include "auth/auth_sam.h" +#include "util/util_ldb.h" +#include "dsdb/samdb/samdb.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "librpc/gen_ndr/lsa.h" +#include "libcli/auth/libcli_auth.h" +#include "param/param.h" +#include "events/events.h" +#include "kdc/kdc.h" +#include "../lib/crypto/md4.h" + +enum hdb_ldb_ent_type +{ HDB_SAMBA4_ENT_TYPE_CLIENT, HDB_SAMBA4_ENT_TYPE_SERVER, + HDB_SAMBA4_ENT_TYPE_KRBTGT, HDB_SAMBA4_ENT_TYPE_TRUST, HDB_SAMBA4_ENT_TYPE_ANY }; + +enum trust_direction { + UNKNOWN = 0, + INBOUND = LSA_TRUST_DIRECTION_INBOUND, + OUTBOUND = LSA_TRUST_DIRECTION_OUTBOUND +}; + +static const char *realm_ref_attrs[] = { + "nCName", + "dnsRoot", + NULL +}; + +static const char *trust_attrs[] = { + "trustPartner", + "trustAuthIncoming", + "trustAuthOutgoing", + "whenCreated", + "msDS-SupportedEncryptionTypes", + "trustAttributes", + "trustDirection", + "trustType", + NULL +}; + +static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val) +{ + const char *tmp; + const char *gentime; + struct tm tm; + + gentime = ldb_msg_find_attr_as_string(msg, attr, NULL); + if (!gentime) + return default_val; + + tmp = strptime(gentime, "%Y%m%d%H%M%SZ", &tm); + if (tmp == NULL) { + return default_val; + } + + return timegm(&tm); +} + +static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum hdb_ldb_ent_type ent_type) +{ + HDBFlags flags = int2HDBFlags(0); + + /* we don't allow kadmin deletes */ + flags.immutable = 1; + + /* mark the principal as invalid to start with */ + flags.invalid = 1; + + flags.renewable = 1; + + /* All accounts are servers, but this may be disabled again in the caller */ + flags.server = 1; + + /* Account types - clear the invalid bit if it turns out to be valid */ + if (userAccountControl & UF_NORMAL_ACCOUNT) { + if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { + flags.client = 1; + } + flags.invalid = 0; + } + + if (userAccountControl & UF_INTERDOMAIN_TRUST_ACCOUNT) { + if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { + flags.client = 1; + } + flags.invalid = 0; + } + if (userAccountControl & UF_WORKSTATION_TRUST_ACCOUNT) { + if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { + flags.client = 1; + } + flags.invalid = 0; + } + if (userAccountControl & UF_SERVER_TRUST_ACCOUNT) { + if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { + flags.client = 1; + } + flags.invalid = 0; + } + + /* Not permitted to act as a client if disabled */ + if (userAccountControl & UF_ACCOUNTDISABLE) { + flags.client = 0; + } + if (userAccountControl & UF_LOCKOUT) { + flags.invalid = 1; + } +/* + if (userAccountControl & UF_PASSWORD_NOTREQD) { + flags.invalid = 1; + } +*/ +/* + UF_PASSWORD_CANT_CHANGE and UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED are irrelevent +*/ + if (userAccountControl & UF_TEMP_DUPLICATE_ACCOUNT) { + flags.invalid = 1; + } + +/* UF_DONT_EXPIRE_PASSWD and UF_USE_DES_KEY_ONLY handled in LDB_message2entry() */ + +/* + if (userAccountControl & UF_MNS_LOGON_ACCOUNT) { + flags.invalid = 1; + } +*/ + if (userAccountControl & UF_SMARTCARD_REQUIRED) { + flags.require_hwauth = 1; + } + if (userAccountControl & UF_TRUSTED_FOR_DELEGATION) { + flags.ok_as_delegate = 1; + } + if (!(userAccountControl & UF_NOT_DELEGATED)) { + flags.forwardable = 1; + flags.proxiable = 1; + } + + if (userAccountControl & UF_DONT_REQUIRE_PREAUTH) { + flags.require_preauth = 0; + } else { + flags.require_preauth = 1; + + } + return flags; +} + +static int hdb_ldb_destructor(struct hdb_ldb_private *private) +{ + hdb_entry_ex *entry_ex = private->entry_ex; + free_hdb_entry(&entry_ex->entry); + return 0; +} + +static void hdb_ldb_free_entry(krb5_context context, hdb_entry_ex *entry_ex) +{ + talloc_free(entry_ex->ctx); +} + +static krb5_error_code LDB_message2entry_keys(krb5_context context, + struct smb_iconv_convenience *iconv_convenience, + TALLOC_CTX *mem_ctx, + struct ldb_message *msg, + unsigned int userAccountControl, + hdb_entry_ex *entry_ex) +{ + krb5_error_code ret = 0; + enum ndr_err_code ndr_err; + struct samr_Password *hash; + const struct ldb_val *sc_val; + struct supplementalCredentialsBlob scb; + struct supplementalCredentialsPackage *scpk = NULL; + bool newer_keys = false; + struct package_PrimaryKerberosBlob _pkb; + struct package_PrimaryKerberosCtr3 *pkb3 = NULL; + struct package_PrimaryKerberosCtr4 *pkb4 = NULL; + uint32_t i; + uint32_t allocated_keys = 0; + + entry_ex->entry.keys.val = NULL; + entry_ex->entry.keys.len = 0; + + entry_ex->entry.kvno = ldb_msg_find_attr_as_int(msg, "msDS-KeyVersionNumber", 0); + + /* Get keys from the db */ + + hash = samdb_result_hash(mem_ctx, msg, "unicodePwd"); + sc_val = ldb_msg_find_ldb_val(msg, "supplementalCredentials"); + + /* unicodePwd for enctype 0x17 (23) if present */ + if (hash) { + allocated_keys++; + } + + /* supplementalCredentials if present */ + if (sc_val) { + ndr_err = ndr_pull_struct_blob_all(sc_val, mem_ctx, iconv_convenience, &scb, + (ndr_pull_flags_fn_t)ndr_pull_supplementalCredentialsBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dump_data(0, sc_val->data, sc_val->length); + ret = EINVAL; + goto out; + } + + if (scb.sub.signature != SUPPLEMENTAL_CREDENTIALS_SIGNATURE) { + NDR_PRINT_DEBUG(supplementalCredentialsBlob, &scb); + ret = EINVAL; + goto out; + } + + for (i=0; i < scb.sub.num_packages; i++) { + if (strcmp("Primary:Kerberos-Newer-Keys", scb.sub.packages[i].name) == 0) { + scpk = &scb.sub.packages[i]; + if (!scpk->data || !scpk->data[0]) { + scpk = NULL; + continue; + } + newer_keys = true; + break; + } else if (strcmp("Primary:Kerberos", scb.sub.packages[i].name) == 0) { + scpk = &scb.sub.packages[i]; + if (!scpk->data || !scpk->data[0]) { + scpk = NULL; + } + /* + * we don't break here in hope to find + * a Kerberos-Newer-Keys package + */ + } + } + } + /* + * Primary:Kerberos-Newer-Keys or Primary:Kerberos element + * of supplementalCredentials + */ + if (scpk) { + DATA_BLOB blob; + + blob = strhex_to_data_blob(scpk->data); + if (!blob.data) { + ret = ENOMEM; + goto out; + } + talloc_steal(mem_ctx, blob.data); + + /* we cannot use ndr_pull_struct_blob_all() here, as w2k and w2k3 add padding bytes */ + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, iconv_convenience, &_pkb, + (ndr_pull_flags_fn_t)ndr_pull_package_PrimaryKerberosBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + krb5_set_error_string(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob"); + krb5_warnx(context, "LDB_message2entry_keys: could not parse package_PrimaryKerberosBlob"); + ret = EINVAL; + goto out; + } + + if (newer_keys && _pkb.version != 4) { + krb5_set_error_string(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); + krb5_warnx(context, "LDB_message2entry_keys: Primary:Kerberos-Newer-Keys not version 4"); + ret = EINVAL; + goto out; + } + + if (!newer_keys && _pkb.version != 3) { + krb5_set_error_string(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3"); + krb5_warnx(context, "LDB_message2entry_keys: could not parse Primary:Kerberos not version 3"); + ret = EINVAL; + goto out; + } + + if (_pkb.version == 4) { + pkb4 = &_pkb.ctr.ctr4; + allocated_keys += pkb4->num_keys; + } else if (_pkb.version == 3) { + pkb3 = &_pkb.ctr.ctr3; + allocated_keys += pkb3->num_keys; + } + } + + if (allocated_keys == 0) { + /* oh, no password. Apparently (comment in + * hdb-ldap.c) this violates the ASN.1, but this + * allows an entry with no keys (yet). */ + return 0; + } + + /* allocate space to decode into */ + entry_ex->entry.keys.len = 0; + entry_ex->entry.keys.val = calloc(allocated_keys, sizeof(Key)); + if (entry_ex->entry.keys.val == NULL) { + ret = ENOMEM; + goto out; + } + + if (hash && !(userAccountControl & UF_USE_DES_KEY_ONLY)) { + Key key; + + key.mkvno = 0; + key.salt = NULL; /* No salt for this enc type */ + + ret = krb5_keyblock_init(context, + ENCTYPE_ARCFOUR_HMAC_MD5, + hash->hash, sizeof(hash->hash), + &key.key); + if (ret) { + goto out; + } + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + + if (pkb4) { + for (i=0; i < pkb4->num_keys; i++) { + bool use = true; + Key key; + + if (!pkb4->keys[i].value) continue; + + if (userAccountControl & UF_USE_DES_KEY_ONLY) { + switch (pkb4->keys[i].keytype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD5: + break; + default: + use = false; + break; + } + } + + if (!use) continue; + + key.mkvno = 0; + key.salt = NULL; + + if (pkb4->salt.string) { + DATA_BLOB salt; + + salt = data_blob_string_const(pkb4->salt.string); + + key.salt = calloc(1, sizeof(*key.salt)); + if (key.salt == NULL) { + ret = ENOMEM; + goto out; + } + + key.salt->type = hdb_pw_salt; + + ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); + if (ret) { + free(key.salt); + key.salt = NULL; + goto out; + } + } + + /* TODO: maybe pass the iteration_count somehow... */ + + ret = krb5_keyblock_init(context, + pkb4->keys[i].keytype, + pkb4->keys[i].value->data, + pkb4->keys[i].value->length, + &key.key); + if (ret) { + if (key.salt) { + free_Salt(key.salt); + free(key.salt); + key.salt = NULL; + } + goto out; + } + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + } else if (pkb3) { + for (i=0; i < pkb3->num_keys; i++) { + bool use = true; + Key key; + + if (!pkb3->keys[i].value) continue; + + if (userAccountControl & UF_USE_DES_KEY_ONLY) { + switch (pkb3->keys[i].keytype) { + case ENCTYPE_DES_CBC_CRC: + case ENCTYPE_DES_CBC_MD5: + break; + default: + use = false; + break; + } + } + + if (!use) continue; + + key.mkvno = 0; + key.salt = NULL; + + if (pkb3->salt.string) { + DATA_BLOB salt; + + salt = data_blob_string_const(pkb3->salt.string); + + key.salt = calloc(1, sizeof(*key.salt)); + if (key.salt == NULL) { + ret = ENOMEM; + goto out; + } + + key.salt->type = hdb_pw_salt; + + ret = krb5_data_copy(&key.salt->salt, salt.data, salt.length); + if (ret) { + free(key.salt); + key.salt = NULL; + goto out; + } + } + + ret = krb5_keyblock_init(context, + pkb3->keys[i].keytype, + pkb3->keys[i].value->data, + pkb3->keys[i].value->length, + &key.key); + if (ret) { + if (key.salt) { + free_Salt(key.salt); + free(key.salt); + key.salt = NULL; + } + goto out; + } + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + } + +out: + if (ret != 0) { + entry_ex->entry.keys.len = 0; + } + if (entry_ex->entry.keys.len == 0 && entry_ex->entry.keys.val) { + free(entry_ex->entry.keys.val); + entry_ex->entry.keys.val = NULL; + } + return ret; +} + +/* + * Construct an hdb_entry from a directory entry. + */ +static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, + TALLOC_CTX *mem_ctx, krb5_const_principal principal, + enum hdb_ldb_ent_type ent_type, + struct ldb_message *msg, + struct ldb_message *realm_ref_msg, + hdb_entry_ex *entry_ex) +{ + unsigned int userAccountControl; + int i; + krb5_error_code ret = 0; + krb5_boolean is_computer = FALSE; + const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg, "dnsRoot", NULL); + char *realm = strupper_talloc(mem_ctx, dnsdomain); + struct loadparm_context *lp_ctx = ldb_get_opaque((struct ldb_context *)db->hdb_db, "loadparm"); + struct ldb_dn *domain_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, + mem_ctx, + realm_ref_msg, + "nCName", + ldb_dn_new(mem_ctx, (struct ldb_context *)db->hdb_db, NULL)); + + struct hdb_ldb_private *private; + NTTIME acct_expiry; + + struct ldb_message_element *objectclasses; + struct ldb_val computer_val; + computer_val.data = discard_const_p(uint8_t,"computer"); + computer_val.length = strlen((const char *)computer_val.data); + + objectclasses = ldb_msg_find_element(msg, "objectClass"); + + if (objectclasses && ldb_msg_find_val(objectclasses, &computer_val)) { + is_computer = TRUE; + } + + memset(entry_ex, 0, sizeof(*entry_ex)); + + if (!realm) { + krb5_set_error_string(context, "talloc_strdup: out of memory"); + ret = ENOMEM; + goto out; + } + + private = talloc(mem_ctx, struct hdb_ldb_private); + if (!private) { + ret = ENOMEM; + goto out; + } + + private->entry_ex = entry_ex; + private->iconv_convenience = lp_iconv_convenience(lp_ctx); + private->netbios_name = lp_netbios_name(lp_ctx); + + talloc_set_destructor(private, hdb_ldb_destructor); + + entry_ex->ctx = private; + entry_ex->free_entry = hdb_ldb_free_entry; + + userAccountControl = ldb_msg_find_attr_as_uint(msg, "userAccountControl", 0); + + + entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); + if (ent_type == HDB_SAMBA4_ENT_TYPE_ANY && principal == NULL) { + const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); + if (!samAccountName) { + krb5_set_error_string(context, "LDB_message2entry: no samAccountName present"); + ret = ENOENT; + goto out; + } + samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL); + krb5_make_principal(context, &entry_ex->entry.principal, realm, samAccountName, NULL); + } else { + char *strdup_realm; + ret = copy_Principal(principal, entry_ex->entry.principal); + if (ret) { + krb5_clear_error_string(context); + goto out; + } + + /* While we have copied the client principal, tests + * show that Win2k3 returns the 'corrected' realm, not + * the client-specified realm. This code attempts to + * replace the client principal's realm with the one + * we determine from our records */ + + /* this has to be with malloc() */ + strdup_realm = strdup(realm); + if (!strdup_realm) { + ret = ENOMEM; + krb5_clear_error_string(context); + goto out; + } + free(*krb5_princ_realm(context, entry_ex->entry.principal)); + krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm); + } + + entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type); + + if (ent_type == HDB_SAMBA4_ENT_TYPE_KRBTGT) { + entry_ex->entry.flags.invalid = 0; + entry_ex->entry.flags.server = 1; + entry_ex->entry.flags.forwardable = 1; + entry_ex->entry.flags.ok_as_delegate = 1; + } + + if (lp_parm_bool(lp_ctx, NULL, "kdc", "require spn for service", true)) { + if (!is_computer && !ldb_msg_find_attr_as_string(msg, "servicePrincipalName", NULL)) { + entry_ex->entry.flags.server = 0; + } + } + + /* use 'whenCreated' */ + entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); + /* use '???' */ + entry_ex->entry.created_by.principal = NULL; + + entry_ex->entry.modified_by = (Event *) malloc(sizeof(Event)); + if (entry_ex->entry.modified_by == NULL) { + krb5_set_error_string(context, "malloc: out of memory"); + ret = ENOMEM; + goto out; + } + + /* use 'whenChanged' */ + entry_ex->entry.modified_by->time = ldb_msg_find_krb5time_ldap_time(msg, "whenChanged", 0); + /* use '???' */ + entry_ex->entry.modified_by->principal = NULL; + + entry_ex->entry.valid_start = NULL; + + acct_expiry = samdb_result_account_expires(msg); + if (acct_expiry == 0x7FFFFFFFFFFFFFFFULL) { + entry_ex->entry.valid_end = NULL; + } else { + entry_ex->entry.valid_end = malloc(sizeof(*entry_ex->entry.valid_end)); + if (entry_ex->entry.valid_end == NULL) { + ret = ENOMEM; + goto out; + } + *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry); + } + + if (ent_type != HDB_SAMBA4_ENT_TYPE_KRBTGT) { + NTTIME must_change_time + = samdb_result_force_password_change((struct ldb_context *)db->hdb_db, mem_ctx, + domain_dn, msg); + if (must_change_time == 0x7FFFFFFFFFFFFFFFULL) { + entry_ex->entry.pw_end = NULL; + } else { + entry_ex->entry.pw_end = malloc(sizeof(*entry_ex->entry.pw_end)); + if (entry_ex->entry.pw_end == NULL) { + ret = ENOMEM; + goto out; + } + *entry_ex->entry.pw_end = nt_time_to_unix(must_change_time); + } + } else { + entry_ex->entry.pw_end = NULL; + } + + entry_ex->entry.max_life = NULL; + + entry_ex->entry.max_renew = NULL; + + entry_ex->entry.generation = NULL; + + /* Get keys from the db */ + ret = LDB_message2entry_keys(context, private->iconv_convenience, private, msg, userAccountControl, entry_ex); + if (ret) { + /* Could be bougus data in the entry, or out of memory */ + goto out; + } + + entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); + if (entry_ex->entry.etypes == NULL) { + krb5_clear_error_string(context); + ret = ENOMEM; + goto out; + } + entry_ex->entry.etypes->len = entry_ex->entry.keys.len; + entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); + if (entry_ex->entry.etypes->val == NULL) { + krb5_clear_error_string(context); + ret = ENOMEM; + goto out; + } + for (i=0; i < entry_ex->entry.etypes->len; i++) { + entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; + } + + + private->msg = talloc_steal(private, msg); + private->realm_ref_msg = talloc_steal(private, realm_ref_msg); + private->samdb = (struct ldb_context *)db->hdb_db; + +out: + if (ret != 0) { + /* This doesn't free ent itself, that is for the eventual caller to do */ + hdb_free_entry(context, entry_ex); + } else { + talloc_steal(db, entry_ex->ctx); + } + + return ret; +} + +/* + * Construct an hdb_entry from a directory entry. + */ +static krb5_error_code LDB_trust_message2entry(krb5_context context, HDB *db, + struct loadparm_context *lp_ctx, + TALLOC_CTX *mem_ctx, krb5_const_principal principal, + enum trust_direction direction, + struct ldb_message *msg, + hdb_entry_ex *entry_ex) +{ + + const char *dnsdomain; + char *realm; + char *strdup_realm; + DATA_BLOB password_utf16; + struct samr_Password password_hash; + const struct ldb_val *password_val; + struct trustAuthInOutBlob password_blob; + struct hdb_ldb_private *private; + + enum ndr_err_code ndr_err; + int i, ret, trust_direction_flags; + + private = talloc(mem_ctx, struct hdb_ldb_private); + if (!private) { + ret = ENOMEM; + goto out; + } + + private->entry_ex = entry_ex; + private->iconv_convenience = lp_iconv_convenience(lp_ctx); + private->netbios_name = lp_netbios_name(lp_ctx); + + talloc_set_destructor(private, hdb_ldb_destructor); + + entry_ex->ctx = private; + entry_ex->free_entry = hdb_ldb_free_entry; + + /* use 'whenCreated' */ + entry_ex->entry.created_by.time = ldb_msg_find_krb5time_ldap_time(msg, "whenCreated", 0); + /* use '???' */ + entry_ex->entry.created_by.principal = NULL; + + entry_ex->entry.valid_start = NULL; + + trust_direction_flags = ldb_msg_find_attr_as_int(msg, "trustDirection", 0); + + if (direction == INBOUND) { + realm = strupper_talloc(mem_ctx, lp_realm(lp_ctx)); + password_val = ldb_msg_find_ldb_val(msg, "trustAuthIncoming"); + + } else { /* OUTBOUND */ + dnsdomain = ldb_msg_find_attr_as_string(msg, "trustPartner", NULL); + realm = strupper_talloc(mem_ctx, dnsdomain); + password_val = ldb_msg_find_ldb_val(msg, "trustAuthOutgoing"); + } + + if (!password_val || !(trust_direction_flags & direction)) { + ret = ENOENT; + goto out; + } + + ndr_err = ndr_pull_struct_blob_all(password_val, mem_ctx, private->iconv_convenience, &password_blob, + (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + ret = EINVAL; + goto out; + } + + for (i=0; i < password_blob.count; i++) { + if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) { + password_utf16 = data_blob_const(password_blob.current->array[i].AuthInfo.clear.password, + password_blob.current->array[i].AuthInfo.clear.size); + /* In the future, generate all sorts of + * hashes, but for now we can't safely convert + * the random strings windows uses into + * utf8 */ + + /* but as it is utf16 already, we can get the NT password/arcfour-hmac-md5 key */ + mdfour(password_hash.hash, password_utf16.data, password_utf16.length); + break; + } else if (password_blob.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { + password_hash = password_blob.current->array[i].AuthInfo.nt4owf.password; + break; + } + } + entry_ex->entry.keys.len = 0; + entry_ex->entry.keys.val = NULL; + + if (i < password_blob.count) { + Key key; + /* Must have found a cleartext or MD4 password */ + entry_ex->entry.keys.val = calloc(1, sizeof(Key)); + + key.mkvno = 0; + key.salt = NULL; /* No salt for this enc type */ + + if (entry_ex->entry.keys.val == NULL) { + ret = ENOMEM; + goto out; + } + + ret = krb5_keyblock_init(context, + ENCTYPE_ARCFOUR_HMAC_MD5, + password_hash.hash, sizeof(password_hash.hash), + &key.key); + + entry_ex->entry.keys.val[entry_ex->entry.keys.len] = key; + entry_ex->entry.keys.len++; + } + + ret = copy_Principal(principal, entry_ex->entry.principal); + if (ret) { + krb5_clear_error_string(context); + goto out; + } + + /* While we have copied the client principal, tests + * show that Win2k3 returns the 'corrected' realm, not + * the client-specified realm. This code attempts to + * replace the client principal's realm with the one + * we determine from our records */ + + /* this has to be with malloc() */ + strdup_realm = strdup(realm); + if (!strdup_realm) { + ret = ENOMEM; + krb5_clear_error_string(context); + goto out; + } + free(*krb5_princ_realm(context, entry_ex->entry.principal)); + krb5_princ_set_realm(context, entry_ex->entry.principal, &strdup_realm); + + entry_ex->entry.flags = int2HDBFlags(0); + entry_ex->entry.flags.immutable = 1; + entry_ex->entry.flags.invalid = 0; + entry_ex->entry.flags.server = 1; + entry_ex->entry.flags.require_preauth = 1; + + entry_ex->entry.pw_end = NULL; + + entry_ex->entry.max_life = NULL; + + entry_ex->entry.max_renew = NULL; + + entry_ex->entry.generation = NULL; + + entry_ex->entry.etypes = malloc(sizeof(*(entry_ex->entry.etypes))); + if (entry_ex->entry.etypes == NULL) { + krb5_clear_error_string(context); + ret = ENOMEM; + goto out; + } + entry_ex->entry.etypes->len = entry_ex->entry.keys.len; + entry_ex->entry.etypes->val = calloc(entry_ex->entry.etypes->len, sizeof(int)); + if (entry_ex->entry.etypes->val == NULL) { + krb5_clear_error_string(context); + ret = ENOMEM; + goto out; + } + for (i=0; i < entry_ex->entry.etypes->len; i++) { + entry_ex->entry.etypes->val[i] = entry_ex->entry.keys.val[i].key.keytype; + } + + + private->msg = talloc_steal(private, msg); + private->realm_ref_msg = NULL; + private->samdb = (struct ldb_context *)db->hdb_db; + +out: + if (ret != 0) { + /* This doesn't free ent itself, that is for the eventual caller to do */ + hdb_free_entry(context, entry_ex); + } else { + talloc_steal(db, entry_ex->ctx); + } + + return ret; + +} + +static krb5_error_code LDB_lookup_principal(krb5_context context, struct ldb_context *ldb_ctx, + TALLOC_CTX *mem_ctx, + krb5_const_principal principal, + enum hdb_ldb_ent_type ent_type, + struct ldb_dn *realm_dn, + struct ldb_message ***pmsg) +{ + krb5_error_code ret; + int lret; + char *filter = NULL; + const char * const *princ_attrs = user_attrs; + + char *short_princ; + char *short_princ_talloc; + + struct ldb_result *res = NULL; + + ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &short_princ); + + if (ret != 0) { + krb5_set_error_string(context, "LDB_lookup_principal: could not parse principal"); + krb5_warnx(context, "LDB_lookup_principal: could not parse principal"); + return ret; + } + + short_princ_talloc = talloc_strdup(mem_ctx, short_princ); + free(short_princ); + if (!short_princ_talloc) { + krb5_set_error_string(context, "LDB_lookup_principal: talloc_strdup() failed!"); + return ENOMEM; + } + + switch (ent_type) { + case HDB_SAMBA4_ENT_TYPE_CLIENT: + case HDB_SAMBA4_ENT_TYPE_TRUST: + case HDB_SAMBA4_ENT_TYPE_ANY: + /* Can't happen */ + return EINVAL; + case HDB_SAMBA4_ENT_TYPE_KRBTGT: + filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", + KRB5_TGS_NAME); + break; + case HDB_SAMBA4_ENT_TYPE_SERVER: + filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", + short_princ_talloc); + break; + } + + if (!filter) { + krb5_set_error_string(context, "talloc_asprintf: out of memory"); + return ENOMEM; + } + + lret = ldb_search(ldb_ctx, mem_ctx, &res, realm_dn, + LDB_SCOPE_SUBTREE, princ_attrs, "%s", filter); + if (lret != LDB_SUCCESS) { + DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx))); + return HDB_ERR_NOENTRY; + } else if (res->count == 0 || res->count > 1) { + DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count)); + talloc_free(res); + return HDB_ERR_NOENTRY; + } + talloc_steal(mem_ctx, res->msgs); + *pmsg = res->msgs; + talloc_free(res); + return 0; +} + +static krb5_error_code LDB_lookup_trust(krb5_context context, struct ldb_context *ldb_ctx, + TALLOC_CTX *mem_ctx, + const char *realm, + struct ldb_dn *realm_dn, + struct ldb_message ***pmsg) +{ + int lret; + char *filter = NULL; + const char * const *attrs = trust_attrs; + + struct ldb_result *res = NULL; + filter = talloc_asprintf(mem_ctx, "(&(objectClass=trustedDomain)(|(flatname=%s)(trustPartner=%s)))", realm, realm); + + if (!filter) { + krb5_set_error_string(context, "talloc_asprintf: out of memory"); + return ENOMEM; + } + + lret = ldb_search(ldb_ctx, mem_ctx, &res, + ldb_get_default_basedn(ldb_ctx), + LDB_SCOPE_SUBTREE, attrs, "%s", filter); + if (lret != LDB_SUCCESS) { + DEBUG(3, ("Failed to search for %s: %s\n", filter, ldb_errstring(ldb_ctx))); + return HDB_ERR_NOENTRY; + } else if (res->count == 0 || res->count > 1) { + DEBUG(3, ("Failed find a single entry for %s: got %d\n", filter, res->count)); + talloc_free(res); + return HDB_ERR_NOENTRY; + } + talloc_steal(mem_ctx, res->msgs); + *pmsg = res->msgs; + talloc_free(res); + return 0; +} + +static krb5_error_code LDB_lookup_realm(krb5_context context, struct ldb_context *ldb_ctx, + TALLOC_CTX *mem_ctx, + const char *realm, + struct ldb_message ***pmsg) +{ + int ret; + struct ldb_result *cross_ref_res; + struct ldb_dn *partitions_basedn = samdb_partitions_dn(ldb_ctx, mem_ctx); + + ret = ldb_search(ldb_ctx, mem_ctx, &cross_ref_res, + partitions_basedn, LDB_SCOPE_SUBTREE, realm_ref_attrs, + "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", + realm, realm); + + if (ret != LDB_SUCCESS) { + DEBUG(3, ("Failed to search to lookup realm(%s): %s\n", realm, ldb_errstring(ldb_ctx))); + talloc_free(cross_ref_res); + return HDB_ERR_NOENTRY; + } else if (cross_ref_res->count == 0 || cross_ref_res->count > 1) { + DEBUG(3, ("Failed find a single entry for realm %s: got %d\n", realm, cross_ref_res->count)); + talloc_free(cross_ref_res); + return HDB_ERR_NOENTRY; + } + + if (pmsg) { + *pmsg = cross_ref_res->msgs; + talloc_steal(mem_ctx, cross_ref_res->msgs); + } + talloc_free(cross_ref_res); + + return 0; +} + + +static krb5_error_code LDB_open(krb5_context context, HDB *db, int flags, mode_t mode) +{ + if (db->hdb_master_key_set) { + krb5_warnx(context, "LDB_open: use of a master key incompatible with LDB\n"); + krb5_set_error_string(context, "LDB_open: use of a master key incompatible with LDB\n"); + return HDB_ERR_NOENTRY; + } + + return 0; +} + +static krb5_error_code LDB_close(krb5_context context, HDB *db) +{ + return 0; +} + +static krb5_error_code LDB_lock(krb5_context context, HDB *db, int operation) +{ + return 0; +} + +static krb5_error_code LDB_unlock(krb5_context context, HDB *db) +{ + return 0; +} + +static krb5_error_code LDB_rename(krb5_context context, HDB *db, const char *new_name) +{ + return HDB_ERR_DB_INUSE; +} + +static krb5_error_code LDB_fetch_client(krb5_context context, HDB *db, + TALLOC_CTX *mem_ctx, + krb5_const_principal principal, + unsigned flags, + hdb_entry_ex *entry_ex) { + NTSTATUS nt_status; + char *principal_string; + krb5_error_code ret; + struct ldb_message **msg = NULL; + struct ldb_message **realm_ref_msg = NULL; + + ret = krb5_unparse_name(context, principal, &principal_string); + + if (ret != 0) { + return ret; + } + + nt_status = sam_get_results_principal((struct ldb_context *)db->hdb_db, + mem_ctx, principal_string, + &msg, &realm_ref_msg); + free(principal_string); + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER)) { + return HDB_ERR_NOENTRY; + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_MEMORY)) { + return ENOMEM; + } else if (!NT_STATUS_IS_OK(nt_status)) { + return EINVAL; + } + + ret = LDB_message2entry(context, db, mem_ctx, + principal, HDB_SAMBA4_ENT_TYPE_CLIENT, + msg[0], realm_ref_msg[0], entry_ex); + return ret; +} + +static krb5_error_code LDB_fetch_krbtgt(krb5_context context, HDB *db, + TALLOC_CTX *mem_ctx, + krb5_const_principal principal, + unsigned flags, + hdb_entry_ex *entry_ex) +{ + krb5_error_code ret; + struct ldb_message **msg = NULL; + struct ldb_message **realm_ref_msg_1 = NULL; + struct ldb_message **realm_ref_msg_2 = NULL; + struct ldb_dn *realm_dn; + const char *realm; + + krb5_principal alloc_principal = NULL; + if (principal->name.name_string.len != 2 + || (strcmp(principal->name.name_string.val[0], KRB5_TGS_NAME) != 0)) { + /* Not a krbtgt */ + return HDB_ERR_NOENTRY; + } + + /* krbtgt case. Either us or a trusted realm */ + + if ((LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, + mem_ctx, principal->realm, &realm_ref_msg_1) == 0) + && (LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, + mem_ctx, principal->name.name_string.val[1], &realm_ref_msg_2) == 0) + && (ldb_dn_compare(realm_ref_msg_1[0]->dn, realm_ref_msg_1[0]->dn) == 0)) { + /* us */ + /* Cludge, cludge cludge. If the realm part of krbtgt/realm, + * is in our db, then direct the caller at our primary + * krbtgt */ + + const char *dnsdomain = ldb_msg_find_attr_as_string(realm_ref_msg_1[0], "dnsRoot", NULL); + char *realm_fixed = strupper_talloc(mem_ctx, dnsdomain); + if (!realm_fixed) { + krb5_set_error_string(context, "strupper_talloc: out of memory"); + return ENOMEM; + } + + ret = krb5_copy_principal(context, principal, &alloc_principal); + if (ret) { + return ret; + } + + free(alloc_principal->name.name_string.val[1]); + alloc_principal->name.name_string.val[1] = strdup(realm_fixed); + talloc_free(realm_fixed); + if (!alloc_principal->name.name_string.val[1]) { + krb5_set_error_string(context, "LDB_fetch: strdup() failed!"); + return ENOMEM; + } + principal = alloc_principal; + realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg_1[0], "nCName", NULL); + + ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, + mem_ctx, + principal, HDB_SAMBA4_ENT_TYPE_KRBTGT, realm_dn, &msg); + + if (ret != 0) { + krb5_warnx(context, "LDB_fetch: could not find principal in DB"); + krb5_set_error_string(context, "LDB_fetch: could not find principal in DB"); + return ret; + } + + ret = LDB_message2entry(context, db, mem_ctx, + principal, HDB_SAMBA4_ENT_TYPE_KRBTGT, + msg[0], realm_ref_msg_1[0], entry_ex); + if (ret != 0) { + krb5_warnx(context, "LDB_fetch: message2entry failed"); + } + return ret; + + } else { + enum trust_direction direction = UNKNOWN; + + struct loadparm_context *lp_ctx = talloc_get_type(ldb_get_opaque(db->hdb_db, "loadparm"), struct loadparm_context); + /* Either an inbound or outbound trust */ + + if (strcasecmp(lp_realm(lp_ctx), principal->realm) == 0) { + /* look for inbound trust */ + direction = INBOUND; + realm = principal->name.name_string.val[1]; + } + + if (strcasecmp(lp_realm(lp_ctx), principal->name.name_string.val[1]) == 0) { + /* look for outbound trust */ + direction = OUTBOUND; + realm = principal->realm; + } + + /* Trusted domains are under CN=system */ + + ret = LDB_lookup_trust(context, (struct ldb_context *)db->hdb_db, + mem_ctx, + realm, realm_dn, &msg); + + if (ret != 0) { + krb5_warnx(context, "LDB_fetch: could not find principal in DB"); + krb5_set_error_string(context, "LDB_fetch: could not find principal in DB"); + return ret; + } + + ret = LDB_trust_message2entry(context, db, lp_ctx, mem_ctx, + principal, direction, + msg[0], entry_ex); + if (ret != 0) { + krb5_warnx(context, "LDB_fetch: message2entry failed"); + } + return ret; + + + /* we should lookup trusted domains */ + return HDB_ERR_NOENTRY; + } + +} + +static krb5_error_code LDB_fetch_server(krb5_context context, HDB *db, + TALLOC_CTX *mem_ctx, + krb5_const_principal principal, + unsigned flags, + hdb_entry_ex *entry_ex) +{ + krb5_error_code ret; + const char *realm; + struct ldb_message **msg = NULL; + struct ldb_message **realm_ref_msg = NULL; + struct ldb_dn *partitions_basedn = samdb_partitions_dn(db->hdb_db, mem_ctx); + if (principal->name.name_string.len >= 2) { + /* 'normal server' case */ + int ldb_ret; + NTSTATUS nt_status; + struct ldb_dn *user_dn, *domain_dn; + char *principal_string; + + ret = krb5_unparse_name_flags(context, principal, + KRB5_PRINCIPAL_UNPARSE_NO_REALM, + &principal_string); + if (ret != 0) { + return ret; + } + + /* At this point we may find the host is known to be + * in a different realm, so we should generate a + * referral instead */ + nt_status = crack_service_principal_name((struct ldb_context *)db->hdb_db, + mem_ctx, principal_string, + &user_dn, &domain_dn); + free(principal_string); + + if (!NT_STATUS_IS_OK(nt_status)) { + return HDB_ERR_NOENTRY; + } + + ldb_ret = gendb_search_dn((struct ldb_context *)db->hdb_db, + mem_ctx, user_dn, &msg, user_attrs); + + if (ldb_ret != 1) { + return HDB_ERR_NOENTRY; + } + + ldb_ret = gendb_search((struct ldb_context *)db->hdb_db, + mem_ctx, partitions_basedn, &realm_ref_msg, realm_ref_attrs, + "ncName=%s", ldb_dn_get_linearized(domain_dn)); + + if (ldb_ret != 1) { + return HDB_ERR_NOENTRY; + } + + } else { + struct ldb_dn *realm_dn; + /* server as client principal case, but we must not lookup userPrincipalNames */ + + realm = krb5_principal_get_realm(context, principal); + + ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, + mem_ctx, realm, &realm_ref_msg); + if (ret != 0) { + return HDB_ERR_NOENTRY; + } + + realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msg[0], "nCName", NULL); + + ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, + mem_ctx, + principal, HDB_SAMBA4_ENT_TYPE_SERVER, realm_dn, &msg); + + if (ret != 0) { + return ret; + } + } + + ret = LDB_message2entry(context, db, mem_ctx, + principal, HDB_SAMBA4_ENT_TYPE_SERVER, + msg[0], realm_ref_msg[0], entry_ex); + if (ret != 0) { + krb5_warnx(context, "LDB_fetch: message2entry failed"); + } + + return ret; +} + +static krb5_error_code LDB_fetch(krb5_context context, HDB *db, + krb5_const_principal principal, + unsigned flags, + hdb_entry_ex *entry_ex) +{ + krb5_error_code ret = HDB_ERR_NOENTRY; + + TALLOC_CTX *mem_ctx = talloc_named(db, 0, "LDB_fetch context"); + + if (!mem_ctx) { + krb5_set_error_string(context, "LDB_fetch: talloc_named() failed!"); + return ENOMEM; + } + + if (flags & HDB_F_GET_CLIENT) { + ret = LDB_fetch_client(context, db, mem_ctx, principal, flags, entry_ex); + if (ret != HDB_ERR_NOENTRY) goto done; + } + if (flags & HDB_F_GET_SERVER) { + /* krbtgt fits into this situation for trusted realms, and for resolving different versions of our own realm name */ + ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex); + if (ret != HDB_ERR_NOENTRY) goto done; + + /* We return 'no entry' if it does not start with krbtgt/, so move to the common case quickly */ + ret = LDB_fetch_server(context, db, mem_ctx, principal, flags, entry_ex); + if (ret != HDB_ERR_NOENTRY) goto done; + } + if (flags & HDB_F_GET_KRBTGT) { + ret = LDB_fetch_krbtgt(context, db, mem_ctx, principal, flags, entry_ex); + if (ret != HDB_ERR_NOENTRY) goto done; + } + +done: + talloc_free(mem_ctx); + return ret; +} + +static krb5_error_code LDB_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +{ + return HDB_ERR_DB_INUSE; +} + +static krb5_error_code LDB_remove(krb5_context context, HDB *db, krb5_const_principal principal) +{ + return HDB_ERR_DB_INUSE; +} + +struct hdb_ldb_seq { + struct ldb_context *ctx; + int index; + int count; + struct ldb_message **msgs; + struct ldb_message **realm_ref_msgs; +}; + +static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry) +{ + krb5_error_code ret; + struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc; + TALLOC_CTX *mem_ctx; + hdb_entry_ex entry_ex; + memset(&entry_ex, '\0', sizeof(entry_ex)); + + if (!priv) { + return HDB_ERR_NOENTRY; + } + + mem_ctx = talloc_named(priv, 0, "LDB_seq context"); + + if (!mem_ctx) { + krb5_set_error_string(context, "LDB_seq: talloc_named() failed!"); + return ENOMEM; + } + + if (priv->index < priv->count) { + ret = LDB_message2entry(context, db, mem_ctx, + NULL, HDB_SAMBA4_ENT_TYPE_ANY, + priv->msgs[priv->index++], + priv->realm_ref_msgs[0], entry); + } else { + ret = HDB_ERR_NOENTRY; + } + + if (ret != 0) { + talloc_free(priv); + db->hdb_dbc = NULL; + } else { + talloc_free(mem_ctx); + } + + return ret; +} + +static krb5_error_code LDB_firstkey(krb5_context context, HDB *db, unsigned flags, + hdb_entry_ex *entry) +{ + struct ldb_context *ldb_ctx = (struct ldb_context *)db->hdb_db; + struct hdb_ldb_seq *priv = (struct hdb_ldb_seq *)db->hdb_dbc; + char *realm; + struct ldb_dn *realm_dn = NULL; + struct ldb_result *res = NULL; + struct ldb_message **realm_ref_msgs = NULL; + krb5_error_code ret; + TALLOC_CTX *mem_ctx; + int lret; + + if (priv) { + talloc_free(priv); + db->hdb_dbc = NULL; + } + + priv = (struct hdb_ldb_seq *) talloc(db, struct hdb_ldb_seq); + if (!priv) { + krb5_set_error_string(context, "talloc: out of memory"); + return ENOMEM; + } + + priv->ctx = ldb_ctx; + priv->index = 0; + priv->msgs = NULL; + priv->realm_ref_msgs = NULL; + priv->count = 0; + + mem_ctx = talloc_named(priv, 0, "LDB_firstkey context"); + + if (!mem_ctx) { + krb5_set_error_string(context, "LDB_firstkey: talloc_named() failed!"); + return ENOMEM; + } + + ret = krb5_get_default_realm(context, &realm); + if (ret != 0) { + talloc_free(priv); + return ret; + } + + ret = LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db, + mem_ctx, realm, &realm_ref_msgs); + + free(realm); + + if (ret != 0) { + talloc_free(priv); + krb5_warnx(context, "LDB_firstkey: could not find realm\n"); + return HDB_ERR_NOENTRY; + } + + realm_dn = samdb_result_dn((struct ldb_context *)db->hdb_db, mem_ctx, realm_ref_msgs[0], "nCName", NULL); + + priv->realm_ref_msgs = talloc_steal(priv, realm_ref_msgs); + + lret = ldb_search(ldb_ctx, priv, &res, + realm_dn, LDB_SCOPE_SUBTREE, user_attrs, + "(objectClass=user)"); + + if (lret != LDB_SUCCESS) { + talloc_free(priv); + return HDB_ERR_NOENTRY; + } + + priv->count = res->count; + priv->msgs = talloc_steal(priv, res->msgs); + talloc_free(res); + + db->hdb_dbc = priv; + + ret = LDB_seq(context, db, flags, entry); + + if (ret != 0) { + talloc_free(priv); + db->hdb_dbc = NULL; + } else { + talloc_free(mem_ctx); + } + return ret; +} + +static krb5_error_code LDB_nextkey(krb5_context context, HDB *db, unsigned flags, + hdb_entry_ex *entry) +{ + return LDB_seq(context, db, flags, entry); +} + +static krb5_error_code LDB_destroy(krb5_context context, HDB *db) +{ + talloc_free(db); + return 0; +} + +/* This interface is to be called by the KDC, which is expecting Samba + * calling conventions. It is also called by a wrapper + * (hdb_ldb_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb + * code */ + +NTSTATUS kdc_hdb_samba4_create(TALLOC_CTX *mem_ctx, + struct event_context *ev_ctx, + struct loadparm_context *lp_ctx, + krb5_context context, struct HDB **db, const char *arg) +{ + NTSTATUS nt_status; + struct auth_session_info *session_info; + *db = talloc(mem_ctx, HDB); + if (!*db) { + krb5_set_error_string(context, "malloc: out of memory"); + return NT_STATUS_NO_MEMORY; + } + + (*db)->hdb_master_key_set = 0; + (*db)->hdb_db = NULL; + + nt_status = auth_system_session_info(*db, lp_ctx, &session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + /* The idea here is very simple. Using Kerberos to + * authenticate the KDC to the LDAP server is higly likely to + * be circular. + * + * In future we may set this up to use EXERNAL and SSL + * certificates, for now it will almost certainly be NTLMSSP + */ + + cli_credentials_set_kerberos_state(session_info->credentials, + CRED_DONT_USE_KERBEROS); + + /* Setup the link to LDB */ + (*db)->hdb_db = samdb_connect(*db, ev_ctx, lp_ctx, session_info); + if ((*db)->hdb_db == NULL) { + DEBUG(1, ("hdb_ldb_create: Cannot open samdb for KDC backend!")); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + (*db)->hdb_dbc = NULL; + (*db)->hdb_open = LDB_open; + (*db)->hdb_close = LDB_close; + (*db)->hdb_fetch = LDB_fetch; + (*db)->hdb_store = LDB_store; + (*db)->hdb_remove = LDB_remove; + (*db)->hdb_firstkey = LDB_firstkey; + (*db)->hdb_nextkey = LDB_nextkey; + (*db)->hdb_lock = LDB_lock; + (*db)->hdb_unlock = LDB_unlock; + (*db)->hdb_rename = LDB_rename; + /* we don't implement these, as we are not a lockable database */ + (*db)->hdb__get = NULL; + (*db)->hdb__put = NULL; + /* kadmin should not be used for deletes - use other tools instead */ + (*db)->hdb__del = NULL; + (*db)->hdb_destroy = LDB_destroy; + + return NT_STATUS_OK; +} + +krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db, const char *arg) +{ + NTSTATUS nt_status; + /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */ + nt_status = kdc_hdb_samba4_create(kdc_mem_ctx, event_context_find(kdc_mem_ctx), kdc_lp_ctx, + context, db, arg); + + if (NT_STATUS_IS_OK(nt_status)) { + return 0; + } + return EINVAL; +} diff --git a/source4/kdc/kdc.c b/source4/kdc/kdc.c index 030eb23c10..83c6f1c2ee 100644 --- a/source4/kdc/kdc.c +++ b/source4/kdc/kdc.c @@ -667,6 +667,11 @@ static void kdc_task_init(struct task_server *task) NTSTATUS status; krb5_error_code ret; struct interface *ifaces; + struct hdb_method hdb_samba4 = { + .interface_version = HDB_INTERFACE_VERSION, + .prefix = "samba4:", + .create = hdb_samba4_create + }; switch (lp_server_role(task->lp_ctx)) { case ROLE_STANDALONE: @@ -724,7 +729,7 @@ static void kdc_task_init(struct task_server *task) } kdc->config->num_db = 1; - status = kdc_hdb_ldb_create(kdc, task->event_ctx, task->lp_ctx, + status = kdc_hdb_samba4_create(kdc, task->event_ctx, task->lp_ctx, kdc->smb_krb5_context->krb5_context, &kdc->config->db[0], NULL); if (!NT_STATUS_IS_OK(status)) { @@ -732,6 +737,16 @@ static void kdc_task_init(struct task_server *task) return; } + + /* Register hdb-samba4 hooks */ + ret = krb5_plugin_register(kdc->smb_krb5_context->krb5_context, + PLUGIN_TYPE_DATA, "hdb", + &hdb_samba4); + if(ret) { + task_server_terminate(task, "kdc: failed to register hdb keytab"); + return; + } + ret = krb5_kt_register(kdc->smb_krb5_context->krb5_context, &hdb_kt_ops); if(ret) { task_server_terminate(task, "kdc: failed to register hdb keytab"); -- cgit From 15b686198120cca0aaa305edc0a5e3242b4fa869 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 24 Sep 2008 12:53:40 -0700 Subject: Use the new 'samba4' name for our internal hdb plugin. --- source4/setup/secrets_dc.ldif | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source4') diff --git a/source4/setup/secrets_dc.ldif b/source4/setup/secrets_dc.ldif index abc5860cf7..8ae5578e6b 100644 --- a/source4/setup/secrets_dc.ldif +++ b/source4/setup/secrets_dc.ldif @@ -22,7 +22,7 @@ realm: ${REALM} sAMAccountName: krbtgt objectSid: ${DOMAINSID} servicePrincipalName: kadmin/changepw -krb5Keytab: HDB:ldb:${SAM_LDB}: +krb5Keytab: HDB:samba4:${SAM_LDB}: #The trailing : here is a HACK, but it matches the Heimdal format. # A hook from our credentials system into HDB, as we must be on a KDC, -- cgit From 64195b72be6c251412500984c2a5c103e376d3c6 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Sep 2008 21:36:21 -0700 Subject: Fix parsing of the trust passwords in LSA CreateTrustedDomainEx* --- source4/libcli/config.mk | 8 -- source4/libcli/drsblobs.c | 179 ------------------------------ source4/librpc/config.mk | 4 +- source4/librpc/idl/drsblobs.idl | 25 +++-- source4/librpc/ndr/ndr_drsblobs.c | 213 ++++++++++++++++++++++++++++++++++++ source4/rpc_server/lsa/dcesrv_lsa.c | 8 +- source4/torture/rpc/lsa.c | 6 +- 7 files changed, 239 insertions(+), 204 deletions(-) delete mode 100644 source4/libcli/drsblobs.c create mode 100644 source4/librpc/ndr/ndr_drsblobs.c (limited to 'source4') diff --git a/source4/libcli/config.mk b/source4/libcli/config.mk index 2f81d7cff0..d68a2a2ce3 100644 --- a/source4/libcli/config.mk +++ b/source4/libcli/config.mk @@ -67,14 +67,6 @@ PUBLIC_DEPENDENCIES = LIBSAMBA-UTIL LIBCLI_NDR_NETLOGON LIBCLI_NETLOGON_OBJ_FILES = $(addprefix $(libclinbtsrcdir)/, \ netlogon.o) -[SUBSYSTEM::LIBCLI_DRSBLOBS] -PUBLIC_DEPENDENCIES = LIBNDR - -LIBCLI_DRSBLOBS_OBJ_FILES = $(addprefix $(libclisrcdir)/, \ - drsblobs.o) - -$(eval $(call proto_header_template,$(libclisrcdir)/drsblobs_proto.h,$(LIBCLI_DRSBLOBS_OBJ_FILES:.o=.c))) - [PYTHON::python_netbios] LIBRARY_REALNAME = samba/netbios.$(SHLIBEXT) PUBLIC_DEPENDENCIES = LIBCLI_NBT DYNCONFIG LIBSAMBA-HOSTCONFIG diff --git a/source4/libcli/drsblobs.c b/source4/libcli/drsblobs.c deleted file mode 100644 index 126f2ccc40..0000000000 --- a/source4/libcli/drsblobs.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Manually parsed structures found in the DRS protocol - - Copyright (C) Andrew Bartlett 2008 - - 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 "libcli/drsblobs.h" - -/* parser auto-generated by pidl, then hand-modified by abartlet */ - -/* Modified to have 'count' specified */ -static enum ndr_err_code ndr_push_AuthenticationInformationArray_with_count(struct ndr_push *ndr, int ndr_flags, int count, - const struct AuthenticationInformationArray *r) -{ - uint32_t cntr_array_0; - if (ndr_flags & NDR_SCALARS) { - NDR_CHECK(ndr_push_align(ndr, 4)); - for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { - NDR_CHECK(ndr_push_AuthenticationInformation(ndr, NDR_SCALARS, &r->array[cntr_array_0])); - } - } - if (ndr_flags & NDR_BUFFERS) { - for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { - NDR_CHECK(ndr_push_AuthenticationInformation(ndr, NDR_BUFFERS, &r->array[cntr_array_0])); - } - } - return NDR_ERR_SUCCESS; -} - -/* Modified to have 'count' specified, and to allocate the array */ -static enum ndr_err_code ndr_pull_AuthenticationInformationArray_with_count(struct ndr_pull *ndr, int ndr_flags, int count, struct AuthenticationInformationArray *r) -{ - uint32_t cntr_array_0; - TALLOC_CTX *_mem_save_array_0; - if (ndr_flags & NDR_SCALARS) { - NDR_CHECK(ndr_pull_align(ndr, 4)); - NDR_PULL_ALLOC_N(ndr, r->array, count); - _mem_save_array_0 = NDR_PULL_GET_MEM_CTX(ndr); - NDR_PULL_SET_MEM_CTX(ndr, r->array, 0); - for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { - NDR_CHECK(ndr_pull_AuthenticationInformation(ndr, NDR_SCALARS, &r->array[cntr_array_0])); - } - NDR_PULL_SET_MEM_CTX(ndr, _mem_save_array_0, 0); - } - if (ndr_flags & NDR_BUFFERS) { - for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { - NDR_CHECK(ndr_pull_AuthenticationInformation(ndr, NDR_BUFFERS, &r->array[cntr_array_0])); - } - } - return NDR_ERR_SUCCESS; -} - -/* Modified to have 'count' specified */ -_PUBLIC_ void ndr_print_AuthenticationInformationArray_with_count(struct ndr_print *ndr, const char *name, int count, const struct AuthenticationInformationArray *r) -{ - uint32_t cntr_array_0; - ndr_print_struct(ndr, name, "AuthenticationInformationArray"); - ndr->depth++; - ndr->print(ndr, "%s: ARRAY(%d)", "array", (int)1); - ndr->depth++; - for (cntr_array_0=0;cntr_array_0array[cntr_array_0]); - free(idx_0); - } - } - ndr->depth--; - ndr->depth--; -} - -/* Modified to call AuthenticationInformationArray with 'count' specified */ -_PUBLIC_ enum ndr_err_code ndr_push_trustAuthInOutBlob(struct ndr_push *ndr, int ndr_flags, const struct trustAuthInOutBlob *r) -{ - if (ndr_flags & NDR_SCALARS) { - NDR_CHECK(ndr_push_align(ndr, 4)); - NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->count)); - NDR_CHECK(ndr_push_relative_ptr1(ndr, r->current)); - NDR_CHECK(ndr_push_relative_ptr1(ndr, r->previous)); - } - if (ndr_flags & NDR_BUFFERS) { - if (r->current) { - NDR_CHECK(ndr_push_relative_ptr2(ndr, r->current)); - NDR_CHECK(ndr_push_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->current)); - } - if (r->previous) { - NDR_CHECK(ndr_push_relative_ptr2(ndr, r->previous)); - NDR_CHECK(ndr_push_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->previous)); - } - } - return NDR_ERR_SUCCESS; -} - -_PUBLIC_ enum ndr_err_code ndr_pull_trustAuthInOutBlob(struct ndr_pull *ndr, int ndr_flags, struct trustAuthInOutBlob *r) -{ - uint32_t _ptr_current; - TALLOC_CTX *_mem_save_current_0; - uint32_t _ptr_previous; - TALLOC_CTX *_mem_save_previous_0; - if (ndr_flags & NDR_SCALARS) { - NDR_CHECK(ndr_pull_align(ndr, 4)); - NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->count)); - NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_current)); - if (_ptr_current) { - NDR_PULL_ALLOC(ndr, r->current); - NDR_CHECK(ndr_pull_relative_ptr1(ndr, r->current, _ptr_current)); - } else { - r->current = NULL; - } - NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_previous)); - if (_ptr_previous) { - NDR_PULL_ALLOC(ndr, r->previous); - NDR_CHECK(ndr_pull_relative_ptr1(ndr, r->previous, _ptr_previous)); - } else { - r->previous = NULL; - } - } - if (ndr_flags & NDR_BUFFERS) { - if (r->current) { - uint32_t _relative_save_offset; - _relative_save_offset = ndr->offset; - NDR_CHECK(ndr_pull_relative_ptr2(ndr, r->current)); - _mem_save_current_0 = NDR_PULL_GET_MEM_CTX(ndr); - NDR_PULL_SET_MEM_CTX(ndr, r->current, 0); - NDR_CHECK(ndr_pull_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->current)); - NDR_PULL_SET_MEM_CTX(ndr, _mem_save_current_0, 0); - ndr->offset = _relative_save_offset; - } - if (r->previous) { - uint32_t _relative_save_offset; - _relative_save_offset = ndr->offset; - NDR_CHECK(ndr_pull_relative_ptr2(ndr, r->previous)); - _mem_save_previous_0 = NDR_PULL_GET_MEM_CTX(ndr); - NDR_PULL_SET_MEM_CTX(ndr, r->previous, 0); - NDR_CHECK(ndr_pull_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->previous)); - NDR_PULL_SET_MEM_CTX(ndr, _mem_save_previous_0, 0); - ndr->offset = _relative_save_offset; - } - } - return NDR_ERR_SUCCESS; -} - -_PUBLIC_ void ndr_print_trustAuthInOutBlob(struct ndr_print *ndr, const char *name, const struct trustAuthInOutBlob *r) -{ - ndr_print_struct(ndr, name, "trustAuthInOutBlob"); - ndr->depth++; - ndr_print_uint32(ndr, "count", r->count); - ndr_print_ptr(ndr, "current", r->current); - ndr->depth++; - if (r->current) { - ndr_print_AuthenticationInformationArray_with_count(ndr, "current", r->count, r->current); - } - ndr->depth--; - ndr_print_ptr(ndr, "previous", r->previous); - ndr->depth++; - if (r->previous) { - ndr_print_AuthenticationInformationArray_with_count(ndr, "previous", r->count, r->previous); - } - ndr->depth--; - ndr->depth--; -} - - diff --git a/source4/librpc/config.mk b/source4/librpc/config.mk index cf41f9884a..41dd17e428 100644 --- a/source4/librpc/config.mk +++ b/source4/librpc/config.mk @@ -144,9 +144,9 @@ PUBLIC_DEPENDENCIES = LIBNDR NDR_COMPRESSION NDR_SECURITY NDR_SAMR ASN1_UTIL NDR_DRSUAPI_OBJ_FILES = $(gen_ndrsrcdir)/ndr_drsuapi.o $(ndrsrcdir)/ndr_drsuapi.o [SUBSYSTEM::NDR_DRSBLOBS] -PUBLIC_DEPENDENCIES = LIBNDR NDR_MISC NDR_DRSUAPI LIBCLI_DRSBLOBS +PUBLIC_DEPENDENCIES = LIBNDR NDR_MISC NDR_DRSUAPI -NDR_DRSBLOBS_OBJ_FILES = $(gen_ndrsrcdir)/ndr_drsblobs.o +NDR_DRSBLOBS_OBJ_FILES = $(gen_ndrsrcdir)/ndr_drsblobs.o $(ndrsrcdir)/ndr_drsblobs.o [SUBSYSTEM::NDR_SASL_HELPERS] PUBLIC_DEPENDENCIES = LIBNDR diff --git a/source4/librpc/idl/drsblobs.idl b/source4/librpc/idl/drsblobs.idl index 6b1f649ff5..31fe8a359e 100644 --- a/source4/librpc/idl/drsblobs.idl +++ b/source4/librpc/idl/drsblobs.idl @@ -415,18 +415,27 @@ interface drsblobs { [relative] AuthenticationInformationArray *previous; } trustAuthInOutBlob; - typedef [public] struct { - uint8 confounder[512]; - trustAuthInOutBlob outgoing; - trustAuthInOutBlob incoming; - [value(ndr_size_trustAuthInOutBlob(&outgoing, ndr->flags))] uint32 outgoing_size; - [value(ndr_size_trustAuthInOutBlob(&incoming, ndr->flags))] uint32 incoming_size; - } trustAuthInAndOutBlob; - + typedef [public,gensize] struct { + uint32 count; + [relative] AuthenticationInformation *current[count]; + } trustCurrentPasswords; + void decode_trustAuthInOut( [in] trustAuthInOutBlob blob ); + typedef [public,nopull] struct { + uint8 confounder[512]; + [subcontext(0),subcontext_size(outgoing_size)] trustCurrentPasswords outgoing; + [subcontext(0),subcontext_size(incoming_size)] trustCurrentPasswords incoming; + [value(ndr_size_trustCurrentPasswords(&outgoing, ndr->flags))] uint32 outgoing_size; + [value(ndr_size_trustCurrentPasswords(&incoming, ndr->flags))] uint32 incoming_size; + } trustDomainPasswords; + + void decode_trustDomainPasswords( + [in] trustDomainPasswords blob + ); + typedef [public] struct { uint32 marker; DATA_BLOB data; diff --git a/source4/librpc/ndr/ndr_drsblobs.c b/source4/librpc/ndr/ndr_drsblobs.c new file mode 100644 index 0000000000..47a6bb60c9 --- /dev/null +++ b/source4/librpc/ndr/ndr_drsblobs.c @@ -0,0 +1,213 @@ +/* + Unix SMB/CIFS implementation. + + Manually parsed structures found in the DRS protocol + + Copyright (C) Andrew Bartlett 2008 + + 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 "libcli/drsblobs.h" + +/* parser auto-generated by pidl, then hand-modified by abartlet */ + +/* Modified to have 'count' specified */ +static enum ndr_err_code ndr_push_AuthenticationInformationArray_with_count(struct ndr_push *ndr, int ndr_flags, int count, + const struct AuthenticationInformationArray *r) +{ + uint32_t cntr_array_0; + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_push_align(ndr, 4)); + for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { + NDR_CHECK(ndr_push_AuthenticationInformation(ndr, NDR_SCALARS, &r->array[cntr_array_0])); + } + } + if (ndr_flags & NDR_BUFFERS) { + for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { + NDR_CHECK(ndr_push_AuthenticationInformation(ndr, NDR_BUFFERS, &r->array[cntr_array_0])); + } + } + return NDR_ERR_SUCCESS; +} + +/* Modified to have 'count' specified, and to allocate the array */ +static enum ndr_err_code ndr_pull_AuthenticationInformationArray_with_count(struct ndr_pull *ndr, int ndr_flags, int count, struct AuthenticationInformationArray *r) +{ + uint32_t cntr_array_0; + TALLOC_CTX *_mem_save_array_0; + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_pull_align(ndr, 4)); + NDR_PULL_ALLOC_N(ndr, r->array, count); + _mem_save_array_0 = NDR_PULL_GET_MEM_CTX(ndr); + NDR_PULL_SET_MEM_CTX(ndr, r->array, 0); + for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { + NDR_CHECK(ndr_pull_AuthenticationInformation(ndr, NDR_SCALARS, &r->array[cntr_array_0])); + } + NDR_PULL_SET_MEM_CTX(ndr, _mem_save_array_0, 0); + } + if (ndr_flags & NDR_BUFFERS) { + for (cntr_array_0 = 0; cntr_array_0 < count; cntr_array_0++) { + NDR_CHECK(ndr_pull_AuthenticationInformation(ndr, NDR_BUFFERS, &r->array[cntr_array_0])); + } + } + return NDR_ERR_SUCCESS; +} + +/* Modified to have 'count' specified */ +_PUBLIC_ void ndr_print_AuthenticationInformationArray_with_count(struct ndr_print *ndr, const char *name, int count, const struct AuthenticationInformationArray *r) +{ + uint32_t cntr_array_0; + ndr_print_struct(ndr, name, "AuthenticationInformationArray"); + ndr->depth++; + ndr->print(ndr, "%s: ARRAY(%d)", "array", (int)1); + ndr->depth++; + for (cntr_array_0=0;cntr_array_0array[cntr_array_0]); + free(idx_0); + } + } + ndr->depth--; + ndr->depth--; +} + +/* Modified to call AuthenticationInformationArray with 'count' specified */ +_PUBLIC_ enum ndr_err_code ndr_push_trustAuthInOutBlob(struct ndr_push *ndr, int ndr_flags, const struct trustAuthInOutBlob *r) +{ + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_push_align(ndr, 4)); + NDR_CHECK(ndr_push_uint32(ndr, NDR_SCALARS, r->count)); + NDR_CHECK(ndr_push_relative_ptr1(ndr, r->current)); + NDR_CHECK(ndr_push_relative_ptr1(ndr, r->previous)); + } + if (ndr_flags & NDR_BUFFERS) { + if (r->current) { + NDR_CHECK(ndr_push_relative_ptr2(ndr, r->current)); + NDR_CHECK(ndr_push_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->current)); + } + if (r->previous) { + NDR_CHECK(ndr_push_relative_ptr2(ndr, r->previous)); + NDR_CHECK(ndr_push_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->previous)); + } + } + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ enum ndr_err_code ndr_pull_trustAuthInOutBlob(struct ndr_pull *ndr, int ndr_flags, struct trustAuthInOutBlob *r) +{ + uint32_t _ptr_current; + TALLOC_CTX *_mem_save_current_0; + uint32_t _ptr_previous; + TALLOC_CTX *_mem_save_previous_0; + if (ndr_flags & NDR_SCALARS) { + NDR_CHECK(ndr_pull_align(ndr, 4)); + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->count)); + NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_current)); + if (_ptr_current) { + NDR_PULL_ALLOC(ndr, r->current); + NDR_CHECK(ndr_pull_relative_ptr1(ndr, r->current, _ptr_current)); + } else { + r->current = NULL; + } + NDR_CHECK(ndr_pull_generic_ptr(ndr, &_ptr_previous)); + if (_ptr_previous) { + NDR_PULL_ALLOC(ndr, r->previous); + NDR_CHECK(ndr_pull_relative_ptr1(ndr, r->previous, _ptr_previous)); + } else { + r->previous = NULL; + } + } + if (ndr_flags & NDR_BUFFERS) { + if (r->current) { + uint32_t _relative_save_offset; + _relative_save_offset = ndr->offset; + NDR_CHECK(ndr_pull_relative_ptr2(ndr, r->current)); + _mem_save_current_0 = NDR_PULL_GET_MEM_CTX(ndr); + NDR_PULL_SET_MEM_CTX(ndr, r->current, 0); + NDR_CHECK(ndr_pull_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->current)); + NDR_PULL_SET_MEM_CTX(ndr, _mem_save_current_0, 0); + ndr->offset = _relative_save_offset; + } + if (r->previous) { + uint32_t _relative_save_offset; + _relative_save_offset = ndr->offset; + NDR_CHECK(ndr_pull_relative_ptr2(ndr, r->previous)); + _mem_save_previous_0 = NDR_PULL_GET_MEM_CTX(ndr); + NDR_PULL_SET_MEM_CTX(ndr, r->previous, 0); + NDR_CHECK(ndr_pull_AuthenticationInformationArray_with_count(ndr, NDR_SCALARS|NDR_BUFFERS, r->count, r->previous)); + NDR_PULL_SET_MEM_CTX(ndr, _mem_save_previous_0, 0); + ndr->offset = _relative_save_offset; + } + } + return NDR_ERR_SUCCESS; +} + +_PUBLIC_ void ndr_print_trustAuthInOutBlob(struct ndr_print *ndr, const char *name, const struct trustAuthInOutBlob *r) +{ + ndr_print_struct(ndr, name, "trustAuthInOutBlob"); + ndr->depth++; + ndr_print_uint32(ndr, "count", r->count); + ndr_print_ptr(ndr, "current", r->current); + ndr->depth++; + if (r->current) { + ndr_print_AuthenticationInformationArray_with_count(ndr, "current", r->count, r->current); + } + ndr->depth--; + ndr_print_ptr(ndr, "previous", r->previous); + ndr->depth++; + if (r->previous) { + ndr_print_AuthenticationInformationArray_with_count(ndr, "previous", r->count, r->previous); + } + ndr->depth--; + ndr->depth--; +} + +_PUBLIC_ enum ndr_err_code ndr_pull_trustDomainPasswords(struct ndr_pull *ndr, int ndr_flags, struct trustDomainPasswords *r) +{ + if (ndr_flags & NDR_SCALARS) { + uint32_t offset; + NDR_PULL_ALIGN(ndr, 4); + NDR_PULL_NEED_BYTES(ndr, 8); + + offset = ndr->offset; + ndr->offset = ndr->data_size - 8; + + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->outgoing_size)); + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->incoming_size)); + + ndr->offset = offset; + NDR_CHECK(ndr_pull_array_uint8(ndr, NDR_SCALARS, r->confounder, 512)); + { + struct ndr_pull *_ndr_outgoing; + NDR_CHECK(ndr_pull_subcontext_start(ndr, &_ndr_outgoing, 0, r->outgoing_size)); + NDR_CHECK(ndr_pull_trustCurrentPasswords(_ndr_outgoing, NDR_SCALARS|NDR_BUFFERS, &r->outgoing)); + NDR_CHECK(ndr_pull_subcontext_end(ndr, _ndr_outgoing, 0, r->outgoing_size)); + } + { + struct ndr_pull *_ndr_incoming; + NDR_CHECK(ndr_pull_subcontext_start(ndr, &_ndr_incoming, 0, r->incoming_size)); + NDR_CHECK(ndr_pull_trustCurrentPasswords(_ndr_incoming, NDR_SCALARS|NDR_BUFFERS, &r->incoming)); + NDR_CHECK(ndr_pull_subcontext_end(ndr, _ndr_incoming, 0, r->incoming_size)); + } + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->outgoing_size)); + NDR_CHECK(ndr_pull_uint32(ndr, NDR_SCALARS, &r->incoming_size)); + } + if (ndr_flags & NDR_BUFFERS) { + } + return NDR_ERR_SUCCESS; +} + diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c index 3b70f3e934..4c3c708d4a 100644 --- a/source4/rpc_server/lsa/dcesrv_lsa.c +++ b/source4/rpc_server/lsa/dcesrv_lsa.c @@ -626,7 +626,7 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc const char *name; DATA_BLOB session_key = data_blob(NULL, 0); DATA_BLOB trustAuthIncoming, trustAuthOutgoing, auth_blob; - struct trustAuthInAndOutBlob auth_struct; + struct trustDomainPasswords auth_struct; int ret; NTSTATUS nt_status; enum ndr_err_code ndr_err; @@ -679,7 +679,7 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc ndr_err = ndr_pull_struct_blob(&auth_blob, mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), &auth_struct, - (ndr_pull_flags_fn_t)ndr_pull_trustAuthInAndOutBlob); + (ndr_pull_flags_fn_t)ndr_pull_trustDomainPasswords); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return NT_STATUS_INVALID_PARAMETER; } @@ -689,7 +689,7 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc ndr_err = ndr_push_struct_blob(&trustAuthIncoming, mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), &auth_struct.incoming, - (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob); + (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return NT_STATUS_INVALID_PARAMETER; } @@ -701,7 +701,7 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc ndr_err = ndr_push_struct_blob(&trustAuthOutgoing, mem_ctx, lp_iconv_convenience(dce_call->conn->dce_ctx->lp_ctx), &auth_struct.outgoing, - (ndr_push_flags_fn_t)ndr_push_trustAuthInOutBlob); + (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { return NT_STATUS_INVALID_PARAMETER; } diff --git a/source4/torture/rpc/lsa.c b/source4/torture/rpc/lsa.c index 69bf33352b..af5ee4f6e1 100644 --- a/source4/torture/rpc/lsa.c +++ b/source4/torture/rpc/lsa.c @@ -2077,7 +2077,7 @@ static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p, struct lsa_CreateTrustedDomainEx2 r; struct lsa_TrustDomainInfoInfoEx trustinfo; struct lsa_TrustDomainInfoAuthInfoInternal authinfo; - struct trustAuthInAndOutBlob auth_struct; + struct trustDomainPasswords auth_struct; DATA_BLOB auth_blob; struct dom_sid *domsid[12]; struct policy_handle trustdom_handle[12]; @@ -2125,9 +2125,9 @@ static bool test_CreateTrustedDomainEx2(struct dcerpc_pipe *p, auth_struct.incoming.count = 0; ndr_err = ndr_push_struct_blob(&auth_blob, mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &auth_struct, - (ndr_push_flags_fn_t)ndr_push_trustAuthInAndOutBlob); + (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - printf("ndr_push_struct_blob of trustAuthInAndOutBlob structure failed"); + printf("ndr_push_struct_blob of trustDomainPasswords structure failed"); ret = false; } -- cgit From 04edf11bee9f248241c46ce809870163a16d3ba0 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 29 Sep 2008 22:34:30 -0700 Subject: Rework to match new trustDomainPasswords IDL --- source4/rpc_server/lsa/dcesrv_lsa.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'source4') diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c index 4c3c708d4a..7b15241b96 100644 --- a/source4/rpc_server/lsa/dcesrv_lsa.c +++ b/source4/rpc_server/lsa/dcesrv_lsa.c @@ -859,11 +859,11 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc if (auth_struct.incoming.count) { int i; for (i=0; i < auth_struct.incoming.count; i++ ) { - if (auth_struct.incoming.current->array[i].AuthType == TRUST_AUTH_TYPE_NT4OWF) { + if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_NT4OWF) { samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb, mem_ctx, msg_user, "unicodePwd", - &auth_struct.incoming.current->array[i].AuthInfo.nt4owf.password); - } else if (auth_struct.incoming.current->array[i].AuthType == TRUST_AUTH_TYPE_CLEAR) { + &auth_struct.incoming.current[i]->AuthInfo.nt4owf.password); + } else if (auth_struct.incoming.current[i]->AuthType == TRUST_AUTH_TYPE_CLEAR) { struct samr_Password hash; /* . We cannot do this, as windows chooses to send in random passwords here, that won't convert to UTF8 @@ -871,8 +871,8 @@ static NTSTATUS dcesrv_lsa_CreateTrustedDomain_base(struct dcesrv_call_state *dc mem_ctx, msg_user, "userPassword", auth_struct.incoming.current->array[i].AuthInfo.clear.password); */ - mdfour(hash.hash, auth_struct.incoming.current->array[i].AuthInfo.clear.password, - auth_struct.incoming.current->array[i].AuthInfo.clear.size); + mdfour(hash.hash, auth_struct.incoming.current[i]->AuthInfo.clear.password, + auth_struct.incoming.current[i]->AuthInfo.clear.size); samdb_msg_add_hash(trusted_domain_state->policy->sam_ldb, mem_ctx, msg_user, "unicodePwd", &hash); -- cgit From e622325155a13fd9f6f85a767d345344f18eca41 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 08:41:16 +0200 Subject: s4:smb_server/smb: FLAGS2_SMB_SECURITY_SIGNATURES should only be echoed, not set always metze --- source4/smb_server/smb/request.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'source4') diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c index eb3e100b96..241c262857 100644 --- a/source4/smb_server/smb/request.c +++ b/source4/smb_server/smb/request.c @@ -135,15 +135,16 @@ void smbsrv_setup_reply(struct smbsrv_request *req, uint_t wct, size_t buflen) flags2 = FLAGS2_LONG_PATH_COMPONENTS | FLAGS2_EXTENDED_ATTRIBUTES | FLAGS2_IS_LONG_NAME; - flags2 |= (req->flags2 & (FLAGS2_UNICODE_STRINGS|FLAGS2_EXTENDED_SECURITY)); +#define _SMB_FLAGS2_ECHOED_FLAGS ( \ + FLAGS2_UNICODE_STRINGS | \ + FLAGS2_EXTENDED_SECURITY | \ + FLAGS2_SMB_SECURITY_SIGNATURES \ +) + flags2 |= (req->flags2 & _SMB_FLAGS2_ECHOED_FLAGS); if (req->smb_conn->negotiate.client_caps & CAP_STATUS32) { flags2 |= FLAGS2_32_BIT_ERROR_CODES; } - if (req->smb_conn->signing.allow_smb_signing || req->smb_conn->signing.mandatory_signing) { - flags2 |= FLAGS2_SMB_SECURITY_SIGNATURES; - } - req->out.hdr = req->out.buffer + NBT_HDR_SIZE; req->out.vwv = req->out.hdr + HDR_VWV; req->out.wct = wct; -- cgit From 394f24b374e1fda8dc8222b71ce164a1a79efde6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Tue, 30 Sep 2008 09:04:52 +0200 Subject: s4:drsblob: fix the build metze --- source4/libcli/drsblobs.h | 28 ---------------------------- source4/librpc/ndr/ndr_drsblobs.c | 2 +- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 source4/libcli/drsblobs.h (limited to 'source4') diff --git a/source4/libcli/drsblobs.h b/source4/libcli/drsblobs.h deleted file mode 100644 index 8fee4114be..0000000000 --- a/source4/libcli/drsblobs.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - Manually parsed structures found in the DRS protocol - - Copyright (C) Andrew Bartlett 2008 - - 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 . -*/ - -#ifndef __LIBCLI_DRSBLOBS_H__ -#define __LIBCLI_DRSBLOBS_H__ - -#include "librpc/gen_ndr/ndr_drsblobs.h" - -#include "libcli/drsblobs_proto.h" -#endif /* __CLDAP_SERVER_PROTO_H__ */ diff --git a/source4/librpc/ndr/ndr_drsblobs.c b/source4/librpc/ndr/ndr_drsblobs.c index 47a6bb60c9..aecb0c3b16 100644 --- a/source4/librpc/ndr/ndr_drsblobs.c +++ b/source4/librpc/ndr/ndr_drsblobs.c @@ -20,7 +20,7 @@ */ #include "includes.h" -#include "libcli/drsblobs.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" /* parser auto-generated by pidl, then hand-modified by abartlet */ -- cgit