diff options
Diffstat (limited to 'source4')
-rw-r--r-- | source4/heimdal/lib/hdb/hdb.c | 42 | ||||
-rw-r--r-- | source4/heimdal/lib/hdb/hdb.h | 6 | ||||
-rw-r--r-- | source4/kdc/config.mk | 8 | ||||
-rw-r--r-- | source4/kdc/hdb-samba4.c (renamed from source4/kdc/hdb-ldb.c) | 48 | ||||
-rw-r--r-- | source4/kdc/kdc.c | 17 | ||||
-rw-r--r-- | source4/libcli/config.mk | 8 | ||||
-rw-r--r-- | source4/libcli/drsblobs.h | 28 | ||||
-rw-r--r-- | source4/librpc/config.mk | 4 | ||||
-rw-r--r-- | source4/librpc/idl/dcerpc.idl | 1 | ||||
-rw-r--r-- | source4/librpc/idl/drsblobs.idl | 25 | ||||
-rw-r--r-- | source4/librpc/ndr/ndr_drsblobs.c (renamed from source4/libcli/drsblobs.c) | 36 | ||||
-rw-r--r-- | source4/librpc/rpc/dcerpc.c | 6 | ||||
-rw-r--r-- | source4/rpc_server/dcerpc_server.c | 8 | ||||
-rw-r--r-- | source4/rpc_server/dcesrv_auth.c | 10 | ||||
-rw-r--r-- | source4/rpc_server/lsa/dcesrv_lsa.c | 18 | ||||
-rwxr-xr-x | source4/selftest/samba4_tests.sh | 8 | ||||
-rw-r--r-- | source4/setup/secrets_dc.ldif | 2 | ||||
-rw-r--r-- | source4/smb_server/smb/request.c | 7 | ||||
-rw-r--r-- | source4/torture/config.mk | 2 | ||||
-rw-r--r-- | source4/torture/rpc/lsa.c | 6 | ||||
-rw-r--r-- | source4/torture/rpc/object_uuid.c | 87 | ||||
-rw-r--r-- | source4/torture/rpc/rpc.c | 1 |
22 files changed, 261 insertions, 117 deletions
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 <dlfcn.h> #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 <hdb-protos.h> #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-samba4.c index 4fde75cf70..d7317f17d4 100644 --- a/source4/kdc/hdb-ldb.c +++ b/source4/kdc/hdb-samba4.c @@ -53,8 +53,8 @@ #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 }; +{ 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, @@ -115,26 +115,26 @@ static HDBFlags uf2HDBFlags(krb5_context context, int userAccountControl, enum h /* 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) { + 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_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { + 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_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { + 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_LDB_ENT_TYPE_CLIENT || ent_type == HDB_LDB_ENT_TYPE_ANY) { + if (ent_type == HDB_SAMBA4_ENT_TYPE_CLIENT || ent_type == HDB_SAMBA4_ENT_TYPE_ANY) { flags.client = 1; } flags.invalid = 0; @@ -551,7 +551,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, entry_ex->entry.principal = malloc(sizeof(*(entry_ex->entry.principal))); - if (ent_type == HDB_LDB_ENT_TYPE_ANY && principal == NULL) { + 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"); @@ -587,7 +587,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, entry_ex->entry.flags = uf2HDBFlags(context, userAccountControl, ent_type); - if (ent_type == HDB_LDB_ENT_TYPE_KRBTGT) { + 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; @@ -631,7 +631,7 @@ static krb5_error_code LDB_message2entry(krb5_context context, HDB *db, *entry_ex->entry.valid_end = nt_time_to_unix(acct_expiry); } - if (ent_type != HDB_LDB_ENT_TYPE_KRBTGT) { + 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); @@ -909,16 +909,16 @@ static krb5_error_code LDB_lookup_principal(krb5_context context, struct ldb_con } switch (ent_type) { - case HDB_LDB_ENT_TYPE_CLIENT: - case HDB_LDB_ENT_TYPE_TRUST: - case HDB_LDB_ENT_TYPE_ANY: + 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_LDB_ENT_TYPE_KRBTGT: + case HDB_SAMBA4_ENT_TYPE_KRBTGT: filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", KRB5_TGS_NAME); break; - case HDB_LDB_ENT_TYPE_SERVER: + case HDB_SAMBA4_ENT_TYPE_SERVER: filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", short_princ_talloc); break; @@ -1075,7 +1075,7 @@ static krb5_error_code LDB_fetch_client(krb5_context context, HDB *db, } ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_CLIENT, + principal, HDB_SAMBA4_ENT_TYPE_CLIENT, msg[0], realm_ref_msg[0], entry_ex); return ret; } @@ -1136,7 +1136,7 @@ static krb5_error_code LDB_fetch_krbtgt(krb5_context context, HDB *db, ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_KRBTGT, realm_dn, &msg); + principal, HDB_SAMBA4_ENT_TYPE_KRBTGT, realm_dn, &msg); if (ret != 0) { krb5_warnx(context, "LDB_fetch: could not find principal in DB"); @@ -1145,7 +1145,7 @@ static krb5_error_code LDB_fetch_krbtgt(krb5_context context, HDB *db, } ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_KRBTGT, + 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"); @@ -1265,7 +1265,7 @@ static krb5_error_code LDB_fetch_server(krb5_context context, HDB *db, ret = LDB_lookup_principal(context, (struct ldb_context *)db->hdb_db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_SERVER, realm_dn, &msg); + principal, HDB_SAMBA4_ENT_TYPE_SERVER, realm_dn, &msg); if (ret != 0) { return ret; @@ -1273,7 +1273,7 @@ static krb5_error_code LDB_fetch_server(krb5_context context, HDB *db, } ret = LDB_message2entry(context, db, mem_ctx, - principal, HDB_LDB_ENT_TYPE_SERVER, + principal, HDB_SAMBA4_ENT_TYPE_SERVER, msg[0], realm_ref_msg[0], entry_ex); if (ret != 0) { krb5_warnx(context, "LDB_fetch: message2entry failed"); @@ -1358,7 +1358,7 @@ static krb5_error_code LDB_seq(krb5_context context, HDB *db, unsigned flags, hd if (priv->index < priv->count) { ret = LDB_message2entry(context, db, mem_ctx, - NULL, HDB_LDB_ENT_TYPE_ANY, + NULL, HDB_SAMBA4_ENT_TYPE_ANY, priv->msgs[priv->index++], priv->realm_ref_msgs[0], entry); } else { @@ -1476,7 +1476,7 @@ static krb5_error_code LDB_destroy(krb5_context context, HDB *db) * (hdb_ldb_create) from the kpasswdd -> krb5 -> keytab_hdb -> hdb * code */ -NTSTATUS kdc_hdb_ldb_create(TALLOC_CTX *mem_ctx, +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) @@ -1536,12 +1536,12 @@ NTSTATUS kdc_hdb_ldb_create(TALLOC_CTX *mem_ctx, return NT_STATUS_OK; } -krb5_error_code hdb_ldb_create(krb5_context context, struct HDB **db, const char *arg) +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_ldb_create(kdc_mem_ctx, event_context_find(kdc_mem_ctx), kdc_lp_ctx, - context, db, arg); + 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; 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"); 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.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 <abartlet@samba.org> 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 <http://www.gnu.org/licenses/>. -*/ - -#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/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/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; 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/libcli/drsblobs.c b/source4/librpc/ndr/ndr_drsblobs.c index 126f2ccc40..aecb0c3b16 100644 --- a/source4/libcli/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 */ @@ -176,4 +176,38 @@ _PUBLIC_ void ndr_print_trustAuthInOutBlob(struct ndr_print *ndr, const char *na 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/librpc/rpc/dcerpc.c b/source4/librpc/rpc/dcerpc.c index 5cee9f27ad..4e07cc7b57 100644 --- a/source4/librpc/rpc/dcerpc.c +++ b/source4/librpc/rpc/dcerpc.c @@ -332,6 +332,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) { @@ -365,6 +366,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); @@ -413,7 +415,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, @@ -426,7 +428,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, 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; diff --git a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c index 3b70f3e934..7b15241b96 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; } @@ -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); diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh index 667b21975f..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" @@ -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` 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, diff --git a/source4/smb_server/smb/request.c b/source4/smb_server/smb/request.c index c7fa2d7d8a..241c262857 100644 --- a/source4/smb_server/smb/request.c +++ b/source4/smb_server/smb/request.c @@ -135,7 +135,12 @@ 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; } 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/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; } 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 <http://www.gnu.org/licenses/>. +*/ + +#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)); |