From 70e4bb093ce3985882869574a4bc50f8e0f98fe5 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Wed, 30 Aug 2023 19:59:04 +0200 Subject: [PATCH 01/11] s3:libnetapi: Return error from RequestOfflineJoin The error code must be returned to caller even if the error string is not set. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit e4afb211fe32f2aa92cc903df948874046f60305) --- source3/lib/netapi/joindomain.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index a2c66877b0c..7145ce5d6d1 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -862,8 +862,10 @@ static WERROR NetRequestOfflineDomainJoin_backend(struct libnetapi_ctx *ctx, WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED; werr = libnet_Join(j, j); - if (!W_ERROR_IS_OK(werr) && j->out.error_string) { - libnetapi_set_error_string(ctx, "%s", j->out.error_string); + if (!W_ERROR_IS_OK(werr)) { + if (j->out.error_string != NULL) { + libnetapi_set_error_string(ctx, "%s", j->out.error_string); + } talloc_free(j); return werr; } -- 2.42.0 From cde2aa3fb7f4d98d2130738dae5ed879392ec7ce Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 4 Sep 2023 10:47:06 +0200 Subject: [PATCH 02/11] s3:libnetapi: Add some comments to document ODJ blob charset conversions BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit bdab834dfad55776155915f7ec410b5a192406fa) --- source3/lib/netapi/joindomain.c | 4 ++++ source3/utils/net_offlinejoin.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index 7145ce5d6d1..31e3ac74b1e 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -894,6 +894,10 @@ WERROR NetRequestOfflineDomainJoin_l(struct libnetapi_ctx *ctx, return W_ERROR(NERR_BadOfflineJoinInfo); } + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs. Check for the + * unicode BOM mark and convert back to UNIX charset if necessary. + */ if (r->in.provision_bin_data[0] == 0xff && r->in.provision_bin_data[1] == 0xfe) { ok = convert_string_talloc(ctx, CH_UTF16LE, CH_UNIX, diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c index 0cfd5fdfe23..6a25d70280e 100644 --- a/source3/utils/net_offlinejoin.c +++ b/source3/utils/net_offlinejoin.c @@ -193,11 +193,17 @@ int net_offlinejoin_provision(struct net_context *c, DATA_BLOB ucs2_blob, blob; bool ok; + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs + * so we also do it for compatibility. Someone may provision an + * account for a Windows machine with samba. + */ ok = push_reg_sz(c, &ucs2_blob, provision_text_data); if (!ok) { return -1; } + /* Add the unicode BOM mark */ blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2); blob.data[0] = 0xff; -- 2.42.0 From ec405902c6fee61da7b91a544a96a882c62368e0 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 31 Aug 2023 12:39:04 +0200 Subject: [PATCH 03/11] s3:libnetapi: Add NetComposeOfflineDomainJoin() to IDL BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit 740e704bd68a6b618b62336ba1583c0edeb82d6f) --- source3/librpc/idl/libnetapi.idl | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source3/librpc/idl/libnetapi.idl b/source3/librpc/idl/libnetapi.idl index 15cac524607..4675af6c6fa 100644 --- a/source3/librpc/idl/libnetapi.idl +++ b/source3/librpc/idl/libnetapi.idl @@ -164,6 +164,26 @@ interface libnetapi [in,unique] string *windows_path ); + /*******************************************/ + /* NetComposeOfflineDomainJoin */ + /*******************************************/ + + [nopush,nopull] NET_API_STATUS NetComposeOfflineDomainJoin( + [in,ref] string *dns_domain_name, + [in,ref] string *netbios_domain_name, + [in,ref] domsid *domain_sid, + [in,ref] GUID *domain_guid, + [in,ref] string *forest_name, + [in,ref] string *machine_account_name, + [in,ref] string *machine_account_password, + [in,unique] string *dc_name, + [in,unique] string *dc_address, + [in] boolean8 domain_is_ad, + [in,out,unique] uint8 **compose_bin_data, + [in,out,unique] uint32 *compose_bin_data_size, + [in,out,unique] string **compose_text_data + ); + /*******************************************/ /* NetServerGetInfo */ /*******************************************/ -- 2.42.0 From a937abedc4e10d2c874451ecc544ef6f07b6fcd5 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 31 Aug 2023 12:43:22 +0200 Subject: [PATCH 04/11] s3:libnetapi: Add NetComposeOfflineDomainJoin() boilerplate BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit 532701e3cce9d15e95166ee7c24cd1e4af51fcc4) --- source3/lib/netapi/joindomain.c | 18 +++++++++ source3/lib/netapi/libnetapi.c | 67 +++++++++++++++++++++++++++++++++ source3/lib/netapi/libnetapi.h | 17 +++++++++ 3 files changed, 102 insertions(+) diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index 31e3ac74b1e..22457158728 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -947,3 +947,21 @@ WERROR NetRequestOfflineDomainJoin_l(struct libnetapi_ctx *ctx, return W_ERROR(NERR_JoinPerformedMustRestart); } + +/**************************************************************** +****************************************************************/ + +WERROR NetComposeOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r) +{ + return WERR_NOT_SUPPORTED; +} + +/**************************************************************** +****************************************************************/ + +WERROR NetComposeOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r) +{ + return WERR_NOT_SUPPORTED; +} diff --git a/source3/lib/netapi/libnetapi.c b/source3/lib/netapi/libnetapi.c index 2fd97bba75b..29073168ef8 100644 --- a/source3/lib/netapi/libnetapi.c +++ b/source3/lib/netapi/libnetapi.c @@ -392,6 +392,73 @@ NET_API_STATUS NetRequestOfflineDomainJoin(uint8_t *provision_bin_data /* [in] [ return (NET_API_STATUS)r.out.result; } +/**************************************************************** + NetComposeOfflineDomainJoin +****************************************************************/ +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **compose_bin_data /* [in,out] [unique] */, + uint32_t *compose_bin_data_size /* [in,out] [unique] */, + const char * *compose_text_data /* [in,out] [unique] */) +{ + struct NetComposeOfflineDomainJoin r; + struct libnetapi_ctx *ctx = NULL; + NET_API_STATUS status; + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + + ZERO_STRUCT(r); + + status = libnetapi_getctx(&ctx); + if (status != 0) { + TALLOC_FREE(frame); + return status; + } + + /* In parameters */ + r.in.dns_domain_name = dns_domain_name; + r.in.netbios_domain_name = netbios_domain_name; + r.in.domain_sid = domain_sid; + r.in.domain_guid = domain_guid; + r.in.forest_name = forest_name; + r.in.machine_account_name = machine_account_name; + r.in.machine_account_password = machine_account_password; + r.in.dc_name = dc_name; + r.in.dc_address = dc_address; + r.in.domain_is_ad = domain_is_ad; + r.in.compose_bin_data = compose_bin_data; + r.in.compose_bin_data_size = compose_bin_data_size; + r.in.compose_text_data = compose_text_data; + + /* Out parameters */ + r.out.compose_bin_data = compose_bin_data; + r.out.compose_bin_data_size = compose_bin_data_size; + r.out.compose_text_data = compose_text_data; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_IN_DEBUG(NetComposeOfflineDomainJoin, &r); + } + + werr = NetComposeOfflineDomainJoin_l(ctx, &r); + + r.out.result = W_ERROR_V(werr); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_OUT_DEBUG(NetComposeOfflineDomainJoin, &r); + } + + TALLOC_FREE(frame); + return (NET_API_STATUS)r.out.result; +} + /**************************************************************** NetServerGetInfo ****************************************************************/ diff --git a/source3/lib/netapi/libnetapi.h b/source3/lib/netapi/libnetapi.h index d217656845d..784d467ff55 100644 --- a/source3/lib/netapi/libnetapi.h +++ b/source3/lib/netapi/libnetapi.h @@ -83,6 +83,23 @@ WERROR NetRequestOfflineDomainJoin_r(struct libnetapi_ctx *ctx, struct NetRequestOfflineDomainJoin *r); WERROR NetRequestOfflineDomainJoin_l(struct libnetapi_ctx *ctx, struct NetRequestOfflineDomainJoin *r); +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); +WERROR NetComposeOfflineDomainJoin_r(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r); +WERROR NetComposeOfflineDomainJoin_l(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r); NET_API_STATUS NetServerGetInfo(const char * server_name /* [in] [unique] */, uint32_t level /* [in] */, uint8_t **buffer /* [out] [ref] */); -- 2.42.0 From a8d2fdf47f836dd79034e0344a55b1178916221b Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 31 Aug 2023 12:44:26 +0200 Subject: [PATCH 05/11] s3:libnetapi: Add NetComposeOfflineDomainJoin() to API. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit 7cabbec2eaf5aefd3751c635c12556eca590f506) --- source3/lib/netapi/netapi.h | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/source3/lib/netapi/netapi.h b/source3/lib/netapi/netapi.h index df465aef51b..660a7766bd6 100644 --- a/source3/lib/netapi/netapi.h +++ b/source3/lib/netapi/netapi.h @@ -1673,6 +1673,49 @@ NET_API_STATUS NetRequestOfflineDomainJoin(uint8_t *provision_bin_data /* [in] [ uint32_t options /* [in] */, const char * windows_path /* [in] [unique] */); +/************************************************************//** + * + * NetComposeOfflineDomainJoin + * + * @brief Compose an offline domain join blob + * + * Intended to be used by external applications who provision the computer + * acconut on their own. + * + * + * @param[in] dns_domain_name The domain DNS name + * @param[in] netbios_domain_name The domain NETBIOS name + * @param[in] domain_sid The domain SID + * @param[in] domain_guid The domain GUID + * @param[in] forest_name The forest name + * @param[in] machine_account_name The machine account name + * @param[in] machine_account_password The machine account password + * @param[in] dc_name The domain controller name used to provision the account + * @param[in] dc_address The domain controller address used to provision the account + * @param[in] domain_is_ad True if the domain is AD + * @param[in,out] compose_bin_data The generated binary buffer + * @param[in,out] compose_bin_data_size The generated binary buffer size + * @param[in,out] compose_text_data The generated text data blob + * @return NET_API_STATUS + * + * example join/compose_offline_domain_join.c + * + ***************************************************************/ + +NET_API_STATUS NetComposeOfflineDomainJoin(const char *dns_domain_name /* [in] [ref] */, + const char *netbios_domain_name /* [in] [ref] */, + struct domsid *domain_sid /* [in] [ref] */, + struct GUID *domain_guid /* [in] [ref] */, + const char *forest_name /* [in] [ref] */, + const char *machine_account_name /* [in] [ref] */, + const char *machine_account_password /* [in] [ref] */, + const char *dc_name /* [in] [unique] */, + const char *dc_address /* [in] [unique] */, + int domain_is_ad /* [in] */, + uint8_t **provision_bin_data /* [in,out] [unique] */, + uint32_t *provision_bin_data_size /* [in,out] [unique] */, + const char * *provision_text_data /* [in,out] [unique] */); + /************************************************************//** * * NetServerGetInfo -- 2.42.0 From ef6c26f485a2ce2521a19dad4e63ea675c5416f8 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 31 Aug 2023 12:45:42 +0200 Subject: [PATCH 06/11] s3:libnetapi: Implement NetComposeOfflineDomainJoin_l() BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit a8bd8f22aac2c223e85e318dba7af8b64052b053) --- source3/lib/netapi/joindomain.c | 180 +++++++++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 1 deletion(-) diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index 22457158728..04fc423b41f 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -33,6 +33,7 @@ #include "../librpc/gen_ndr/ndr_ODJ.h" #include "lib/util/base64.h" #include "libnet/libnet_join_offline.h" +#include "libcli/security/dom_sid.h" /**************************************************************** ****************************************************************/ @@ -960,8 +961,185 @@ WERROR NetComposeOfflineDomainJoin_r(struct libnetapi_ctx *ctx, /**************************************************************** ****************************************************************/ +static WERROR NetComposeOfflineDomainJoin_backend(struct libnetapi_ctx *ctx, + struct NetComposeOfflineDomainJoin *r, + TALLOC_CTX *mem_ctx, + struct ODJ_PROVISION_DATA **p) +{ + struct libnet_JoinCtx *j = NULL; + WERROR werr; + + werr = libnet_init_JoinCtx(ctx, &j); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + j->in.domain_name = talloc_strdup(j, r->in.dns_domain_name); + if (j->in.domain_name == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + j->in.dc_name = talloc_strdup(j, r->in.dc_name); + W_ERROR_HAVE_NO_MEMORY(j->in.dc_name); + + j->in.machine_password = talloc_strdup(j, r->in.machine_account_password); + W_ERROR_HAVE_NO_MEMORY(j->in.machine_password); + + j->out.account_name = talloc_strdup(j, r->in.machine_account_name); + W_ERROR_HAVE_NO_MEMORY(j->out.account_name); + + j->out.dns_domain_name = talloc_strdup(j, r->in.dns_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dns_domain_name); + + j->out.netbios_domain_name = talloc_strdup(j, r->in.netbios_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.netbios_domain_name); + + j->out.domain_sid = dom_sid_dup(j, (struct dom_sid *)r->in.domain_sid); + W_ERROR_HAVE_NO_MEMORY(j->out.domain_sid); + + j->out.domain_guid = *r->in.domain_guid; + + j->out.forest_name = talloc_strdup(j, r->in.forest_name); + W_ERROR_HAVE_NO_MEMORY(j->out.forest_name); + + j->out.domain_is_ad = r->in.domain_is_ad; + + j->out.dcinfo = talloc_zero(j, struct netr_DsRGetDCNameInfo); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo); + + j->out.dcinfo->dc_unc = talloc_asprintf(j->out.dcinfo, "\\\\%s", r->in.dc_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->dc_unc); + + j->out.dcinfo->dc_address = talloc_asprintf(j->out.dcinfo, "\\\\%s", r->in.dc_address); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->dc_address); + + j->out.dcinfo->dc_address_type = DS_ADDRESS_TYPE_INET; + + j->out.dcinfo->domain_guid = *r->in.domain_guid; + + j->out.dcinfo->domain_name = talloc_strdup(j->out.dcinfo, r->in.dns_domain_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->domain_name); + + j->out.dcinfo->forest_name = talloc_strdup(j->out.dcinfo, r->in.forest_name); + W_ERROR_HAVE_NO_MEMORY(j->out.dcinfo->forest_name); + + werr = libnet_odj_compose_ODJ_PROVISION_DATA(mem_ctx, j, p); + if (!W_ERROR_IS_OK(werr)) { + return werr; + } + + return WERR_OK; +} + WERROR NetComposeOfflineDomainJoin_l(struct libnetapi_ctx *ctx, struct NetComposeOfflineDomainJoin *r) { - return WERR_NOT_SUPPORTED; + WERROR werr; + enum ndr_err_code ndr_err; + const char *b64_bin_data_str; + DATA_BLOB blob; + struct ODJ_PROVISION_DATA_serialized_ptr odj_compose_data; + struct ODJ_PROVISION_DATA *p; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if (r->in.compose_bin_data == NULL && + r->in.compose_text_data == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data != NULL && + r->in.compose_text_data != NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data == NULL && + r->in.compose_bin_data_size != NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + if (r->in.compose_bin_data != NULL && + r->in.compose_bin_data_size == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dns_domain_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.netbios_domain_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.domain_sid == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.domain_guid == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.forest_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.machine_account_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.machine_account_password == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dc_name == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + if (r->in.dc_address == NULL) { + werr = WERR_INVALID_PARAMETER; + goto out; + } + + werr = NetComposeOfflineDomainJoin_backend(ctx, r, tmp_ctx, &p); + if (!W_ERROR_IS_OK(werr)) { + goto out; + } + + ZERO_STRUCT(odj_compose_data); + + odj_compose_data.s.p = p; + + ndr_err = ndr_push_struct_blob(&blob, ctx, &odj_compose_data, + (ndr_push_flags_fn_t)ndr_push_ODJ_PROVISION_DATA_serialized_ptr); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + werr = W_ERROR(NERR_BadOfflineJoinInfo); + goto out; + } + + if (r->out.compose_text_data != NULL) { + b64_bin_data_str = base64_encode_data_blob(ctx, blob); + if (b64_bin_data_str == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + } + *r->out.compose_text_data = b64_bin_data_str; + } + + if (r->out.compose_bin_data != NULL && + r->out.compose_bin_data_size != NULL) { + *r->out.compose_bin_data = blob.data; + *r->out.compose_bin_data_size = blob.length; + } + + werr = WERR_OK; +out: + talloc_free(tmp_ctx); + return werr; } -- 2.42.0 From 0b9e3decf042a3c9253483eaaa38b0399898a43f Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Thu, 31 Aug 2023 12:46:52 +0200 Subject: [PATCH 07/11] s3:net: Add "net offlinejoin composeodj" command BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit 4a1f2071a6028a761bbe7efee20e9654851b51f0) --- source3/utils/net_offlinejoin.c | 262 ++++++++++++++++++++++++++++++++ source3/utils/net_proto.h | 2 + 2 files changed, 264 insertions(+) diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c index 6a25d70280e..9f252e51e90 100644 --- a/source3/utils/net_offlinejoin.c +++ b/source3/utils/net_offlinejoin.c @@ -22,6 +22,8 @@ #include #include "netapi/netapi_net.h" #include "libcli/registry/util_reg.h" +#include "libcli/security/dom_sid.h" +#include "lib/cmdline/cmdline.h" int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv) { @@ -30,6 +32,7 @@ int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv) d_printf(_("Valid commands:\n")); d_printf(_("\tprovision\t\t\tProvision machine account in AD\n")); d_printf(_("\trequestodj\t\t\tRequest offline domain join\n")); + d_printf(_("\tcomposeodj\t\t\tCompose offline domain join blob\n")); net_common_flags_usage(c, argc, argv); return -1; } @@ -79,6 +82,13 @@ int net_offlinejoin(struct net_context *c, int argc, const char **argv) } } + if (strcasecmp_m(argv[0], "composeodj") == 0) { + ret = net_offlinejoin_composeodj(c, argc, argv); + if (ret != 0) { + return ret; + } + } + return 0; } @@ -299,3 +309,255 @@ int net_offlinejoin_requestodj(struct net_context *c, return 0; } + +static int net_offlinejoin_composeodj_usage(struct net_context *c, + int argc, + const char **argv) +{ + d_printf(_("\nnet offlinejoin composeodj [misc. options]\n" + "\tComposes offline domain join blob\n")); + d_printf(_("Valid options:\n")); + d_printf(_("\tdomain_sid=\t\t\tThe domain SID\n")); + d_printf(_("\tdomain_guid=\t\t\tThe domain GUID\n")); + d_printf(_("\tforest_name=\t\t\tThe forest name\n")); + d_printf(_("\tdomain_is_nt4\t\t\t\tThe domain not AD but NT4\n")); + d_printf(_("\tsavefile=\t\t\tFile to store the ODJ data\n")); + d_printf(_("\tprintblob\t\t\t\tPrint the base64 encoded ODJ data on stdout\n")); + net_common_flags_usage(c, argc, argv); + d_printf(_("Example:\n")); + d_printf("\tnet offlinejoin composeodj --realm= " + "--workgroup= domain_sid= domain_guid= " + "forest_name= -S -I " + "--password= printblob\n"); + return -1; +} + +int net_offlinejoin_composeodj(struct net_context *c, + int argc, + const char **argv) +{ + struct cli_credentials *creds = samba_cmdline_get_creds(); + NET_API_STATUS status; + const char *dns_domain_name = NULL; + const char *netbios_domain_name = NULL; + const char *machine_account_name = NULL; + const char *machine_account_password = NULL; + const char *domain_sid_str = NULL; + const char *domain_guid_str = NULL; + struct dom_sid domain_sid; + struct GUID domain_guid; + const char *forest_name = NULL; + const char *dc_name = NULL; + char dc_address[INET6_ADDRSTRLEN] = { 0 }; + bool domain_is_ad = true; + const char *provision_text_data = NULL; + const char *savefile = NULL; + bool printblob = false; + enum credentials_obtained obtained; + bool ok; + NTSTATUS ntstatus; + int i; + + if (c->display_usage || argc < 4) { + return net_offlinejoin_composeodj_usage(c, argc, argv); + } + + dns_domain_name = cli_credentials_get_realm(creds); + netbios_domain_name = cli_credentials_get_domain(creds); + + machine_account_name = cli_credentials_get_username_and_obtained(creds, &obtained); + if (obtained < CRED_CALLBACK_RESULT) { + const char *netbios_name = cli_credentials_get_workstation(creds); + cli_credentials_set_username( + creds, + talloc_asprintf(c, "%s$", netbios_name), + CRED_SPECIFIED); + } + + machine_account_name = cli_credentials_get_username(creds); + machine_account_password = cli_credentials_get_password(creds); + dc_name = c->opt_host; + + if (c->opt_have_ip) { + struct sockaddr_in *in4 = NULL; + struct sockaddr_in6 *in6 = NULL; + const char *p = NULL; + + switch(c->opt_dest_ip.ss_family) { + case AF_INET: + in4 = (struct sockaddr_in *)&c->opt_dest_ip; + p = inet_ntop(AF_INET, &in4->sin_addr, dc_address, sizeof(dc_address)); + break; + case AF_INET6: + in6 = (struct sockaddr_in6 *)&c->opt_dest_ip; + p = inet_ntop(AF_INET6, &in6->sin6_addr, dc_address, sizeof(dc_address)); + break; + default: + d_printf("Unknown IP address family\n"); + return -1; + } + + if (p == NULL) { + d_fprintf(stderr, "Failed to parse IP address: %s\n", strerror(errno)); + return -1; + } + } + + /* process additional command line args */ + + for (i = 0; i < argc; i++) { + if (strnequal(argv[i], "domain_sid", strlen("domain_sid"))) { + domain_sid_str = get_string_param(argv[i]); + if (domain_sid_str == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "domain_guid", strlen("domain_guid"))) { + domain_guid_str = get_string_param(argv[i]); + if (domain_guid_str == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "forest_name", strlen("forest_name"))) { + forest_name = get_string_param(argv[i]); + if (forest_name == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "savefile", strlen("savefile"))) { + savefile = get_string_param(argv[i]); + if (savefile == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "printblob", strlen("printblob"))) { + printblob = true; + } + + if (strnequal(argv[i], "domain_is_nt4", strlen("domain_is_nt4"))) { + domain_is_ad = false; + } + } + + /* Check command line arguments */ + + if (savefile == NULL && !printblob) { + d_printf("Choose either save the blob to a file or print it\n"); + return -1; + } + + if (dns_domain_name == NULL) { + d_printf("Please provide a valid realm parameter (--realm)\n"); + return -1; + } + + if (netbios_domain_name == NULL) { + d_printf("Please provide a valid domain parameter (--workgroup)\n"); + return -1; + } + + if (dc_name == NULL) { + d_printf("Please provide a valid DC name parameter (-S)\n"); + return -1; + } + + if (strlen(dc_address) == 0) { + d_printf("Please provide a valid domain controller address parameter (-I)\n"); + return -1; + } + + if (machine_account_name == NULL) { + d_printf("Please provide a valid netbios name parameter\n"); + return -1; + } + + if (machine_account_password == NULL) { + d_printf("Please provide a valid password parameter\n"); + return -1; + } + + if (domain_sid_str == NULL) { + d_printf("Please provide a valid parameter\n"); + return -1; + } + + if (domain_guid_str == NULL) { + d_printf("Please provide a valid parameter\n"); + return -1; + } + + if (forest_name == NULL) { + d_printf("Please provide a valid parameter\n"); + return -1; + } + + ok = dom_sid_parse(domain_sid_str, &domain_sid); + if (!ok) { + d_fprintf(stderr, _("Failed to parse domain SID\n")); + return -1; + } + + ntstatus = GUID_from_string(domain_guid_str, &domain_guid); + if (NT_STATUS_IS_ERR(ntstatus)) { + d_fprintf(stderr, _("Failed to parse domain GUID\n")); + return -1; + } + + status = NetComposeOfflineDomainJoin(dns_domain_name, + netbios_domain_name, + (struct domsid *)&domain_sid, + &domain_guid, + forest_name, + machine_account_name, + machine_account_password, + dc_name, + dc_address, + domain_is_ad, + NULL, + 0, + &provision_text_data); + if (status != 0) { + d_printf("Failed to compose offline domain join blob: %s\n", + libnetapi_get_error_string(c->netapi_ctx, status)); + return status; + } + + if (savefile != NULL) { + DATA_BLOB ucs2_blob, blob; + + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs + * so we also do it for compatibility. Someone may provision an + * account for a Windows machine with samba. + */ + ok = push_reg_sz(c, &ucs2_blob, provision_text_data); + if (!ok) { + return -1; + } + + /* Add the unicode BOM mark */ + blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2); + + blob.data[0] = 0xff; + blob.data[1] = 0xfe; + + memcpy(blob.data + 2, ucs2_blob.data, ucs2_blob.length); + + ok = file_save(savefile, blob.data, blob.length); + if (!ok) { + d_printf("Failed to save %s: %s\n", savefile, + strerror(errno)); + return -1; + } + } + + if (printblob) { + printf("%s\n", provision_text_data); + } + + return 0; +} diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h index fe6b5dd9b84..b945c220762 100644 --- a/source3/utils/net_proto.h +++ b/source3/utils/net_proto.h @@ -120,6 +120,8 @@ int net_offlinejoin_provision(struct net_context *c, int argc, const char **argv); int net_offlinejoin_requestodj(struct net_context *c, int argc, const char **argv); +int net_offlinejoin_composeodj(struct net_context *c, + int argc, const char **argv); /* The following definitions come from utils/net_lookup.c */ -- 2.42.0 From 7dda9c0ca36b77441f7ef45c8f1d84c7f5876564 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Wed, 30 Aug 2023 20:25:17 +0200 Subject: [PATCH 08/11] s3:net: Load ODJ blob from file only if "loadfile" parameter is present BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit b2399b6994c89404f245e1a97ba1c1cf13d7fc86) --- source3/utils/net_offlinejoin.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c index 9f252e51e90..456f331140c 100644 --- a/source3/utils/net_offlinejoin.c +++ b/source3/utils/net_offlinejoin.c @@ -257,7 +257,6 @@ int net_offlinejoin_requestodj(struct net_context *c, uint8_t *provision_bin_data = NULL; size_t provision_bin_data_size = 0; uint32_t options = NETSETUP_PROVISION_ONLINE_CALLER; - const char *loadfile = NULL; const char *windows_path = NULL; int i; @@ -270,10 +269,23 @@ int net_offlinejoin_requestodj(struct net_context *c, for (i = 0; i < argc; i++) { if (strnequal(argv[i], "loadfile", strlen("loadfile"))) { + const char *loadfile = NULL; + loadfile = get_string_param(argv[i]); if (loadfile == NULL) { return -1; } + + provision_bin_data = + (uint8_t *)file_load(loadfile, + &provision_bin_data_size, + 0, + c); + if (provision_bin_data == NULL) { + d_printf("Failed to read loadfile: %s\n", + loadfile); + return -1; + } } #if 0 if (strnequal(argv[i], "localos", strlen("localos"))) { @@ -282,10 +294,8 @@ int net_offlinejoin_requestodj(struct net_context *c, #endif } - provision_bin_data = - (uint8_t *)file_load(loadfile, &provision_bin_data_size, 0, c); - if (provision_bin_data == NULL) { - d_printf("Failed to read loadfile: %s\n", loadfile); + if (provision_bin_data == NULL || provision_bin_data_size == 0) { + d_printf("Please provide provision data\n"); return -1; } if (provision_bin_data_size > UINT32_MAX) { -- 2.42.0 From 9d93ba782d1d07308d8702e47a3e5c2782c27005 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Wed, 30 Aug 2023 20:53:18 +0200 Subject: [PATCH 09/11] s3:net: Allow to load ODJ blob from stdin BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit c14a4f51443f67bc46a670a342eed8cb9e81f37d) --- source3/utils/net_offlinejoin.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c index 456f331140c..35de74e2df9 100644 --- a/source3/utils/net_offlinejoin.c +++ b/source3/utils/net_offlinejoin.c @@ -244,6 +244,7 @@ static int net_offlinejoin_requestodj_usage(struct net_context *c, int argc, con d_printf(_("\nnet offlinejoin requestodj [misc. options]\n" "\tRequests offline domain join\n")); d_printf(_("Valid options:\n")); + d_printf(_("\t-i\t\t\t\t\tRead ODJ data from STDIN\n")); d_printf(_("\tloadfile=\t\t\tFile that provides the ODJ data\n")); /*d_printf(_("\tlocalos\t\t\t\t\tModify the local machine\n"));*/ net_common_flags_usage(c, argc, argv); @@ -260,7 +261,7 @@ int net_offlinejoin_requestodj(struct net_context *c, const char *windows_path = NULL; int i; - if (c->display_usage || argc == 1) { + if (c->display_usage) { return net_offlinejoin_requestodj_usage(c, argc, argv); } @@ -294,8 +295,29 @@ int net_offlinejoin_requestodj(struct net_context *c, #endif } + if (c->opt_stdin) { + if (isatty(STDIN_FILENO) == 1) { + d_fprintf(stderr, + "hint: stdin waiting for ODJ blob, end " + "with .\n"); + } + provision_bin_data = + (uint8_t *)fd_load(STDIN_FILENO, + &provision_bin_data_size, 0, c); + if (provision_bin_data == NULL) { + d_printf("Failed to read ODJ blob from stdin\n"); + return -1; + } + + /* Strip last newline */ + if (provision_bin_data[provision_bin_data_size - 1] == '\n') { + provision_bin_data[provision_bin_data_size - 1] = '\0'; + } + } + if (provision_bin_data == NULL || provision_bin_data_size == 0) { - d_printf("Please provide provision data\n"); + d_printf("Please provide provision data either from file " + "(using loadfile parameter) of from stdin (-i)\n"); return -1; } if (provision_bin_data_size > UINT32_MAX) { -- 2.42.0 From 7b2dc27920058d0805a9e294eec21f5ac8bc8105 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 4 Sep 2023 16:18:35 +0200 Subject: [PATCH 10/11] testprogs: Cleanup machine account in net offlinejoin tests BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett (cherry picked from commit e92e4b9544231c15eaf0bdbba4505345cd0f6ab5) --- testprogs/blackbox/test_net_offline.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testprogs/blackbox/test_net_offline.sh b/testprogs/blackbox/test_net_offline.sh index b15e77edd95..a3809b59e0a 100755 --- a/testprogs/blackbox/test_net_offline.sh +++ b/testprogs/blackbox/test_net_offline.sh @@ -43,6 +43,8 @@ testit "testjoin" $VALGRIND $net_tool ads testjoin -P --use-kerberos=required || rm -f $ODJFILE +testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) + # 2. Test with dcname testit "provision with dcname" $VALGRIND $net_tool offlinejoin provision domain=$REALM machine_name=$netbios savefile=$ODJFILE dcname=$DC_SERVER -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) @@ -53,6 +55,8 @@ testit "testjoin" $VALGRIND $net_tool ads testjoin -P --use-kerberos=required || rm -f $ODJFILE +testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) + # 3. Test with defpwd testit "provision with dcname and default password" $VALGRIND $net_tool offlinejoin provision domain=$REALM machine_name=$netbios savefile=$ODJFILE dcname=$DC_SERVER defpwd -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) @@ -63,6 +67,8 @@ testit "testjoin" $VALGRIND $net_tool ads testjoin -P --use-kerberos=required || rm -f $ODJFILE +testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) + rm -rf $BASEDIR/$WORKDIR exit $failed -- 2.42.0 From 62d1be05c274d48a78c7b56b94a39123001c12a1 Mon Sep 17 00:00:00 2001 From: Samuel Cabrero Date: Mon, 4 Sep 2023 16:49:52 +0200 Subject: [PATCH 11/11] testprogs: Add net offlinejoin composeodj tests BUG: https://bugzilla.samba.org/show_bug.cgi?id=13577 Signed-off-by: Samuel Cabrero Reviewed-by: Andrew Bartlett Autobuild-User(master): Andrew Bartlett Autobuild-Date(master): Tue Sep 5 22:11:46 UTC 2023 on atb-devel-224 (cherry picked from commit f3c632e74ba100b455eeac66e8914b11d1d9b0a0) --- testprogs/blackbox/test_net_offline.sh | 88 ++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/testprogs/blackbox/test_net_offline.sh b/testprogs/blackbox/test_net_offline.sh index a3809b59e0a..f4a8026df62 100755 --- a/testprogs/blackbox/test_net_offline.sh +++ b/testprogs/blackbox/test_net_offline.sh @@ -27,6 +27,7 @@ cd $RUNDIR failed=0 net_tool="$BINDIR/net --configfile=$BASEDIR/$WORKDIR/client.conf --option=security=ads" +samba_texpect="$BINDIR/texpect" # Load test functions . $(dirname $0)/subunit.sh @@ -69,6 +70,93 @@ rm -f $ODJFILE testit "leave" $VALGRIND $net_tool ads leave -U$DC_USERNAME%$DC_PASSWORD || failed=$(expr $failed + 1) +test_compose_odj() { + local mode=$1 + local composeargv=() + + # Retrieve the necessary information to compose the ODJ blob + # The machine needs to be correctly joined at this point + local netbios_domain_name=$($net_tool ads lookup | awk -F': ' '/^Pre-Win2k Domain/ {print $2}') + local domain_sid=$($net_tool getdomainsid | awk -F': ' "/^SID for domain $netbios_domain_name/ {print \$2}") + local domain_guid=$($net_tool ads lookup | awk -F': ' '/^GUID/ {print $2}') + local forest_name=$($net_tool ads lookup | awk -F': ' '/^Forest/ {print $2}') + local dc_name=$($net_tool ads info | awk -F': ' '/^LDAP server name/ {print $2}') + local dc_address=$($net_tool ads info | awk -F': ' '/^LDAP server:/ {print $2}') + local ret=1 + local out="" + + composeargv=( \ + "domain_sid=${domain_sid}" \ + "domain_guid=${domain_guid}" \ + "forest_name=${forest_name}" \ + "-S ${dc_name}" \ + "-I ${dc_address}" \ + "savefile=${ODJFILE}" + ) + case $mode in + machacct) + cmd='$net_tool offlinejoin composeodj ${composeargv[@]} -P 2>&1' + out=$(eval $cmd) + ret=$? + ;; + stdinfd) + cmd='echo ${netbios} | $net_tool offlinejoin composeodj ${composeargv[@]} -U${netbios^^}\$ 2>&1' + out=$(PASSWD_FD=0 eval $cmd) + ret=$? + ;; + callback) + tmpfile=$BASEDIR/$WORKDIR/composeodj_password_script + cat >$tmpfile <