/*
   Unix SMB/CIFS implementation.
   Authentication utility functions
   Copyright (C) Volker Lendecke 2010

   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 "../librpc/gen_ndr/netlogon.h"
#include "../libcli/security/security.h"
#include "rpc_client/util_netlogon.h"

#define COPY_LSA_STRING(mem_ctx, in, out, name) do { \
	if (in->name.string) { \
		out->name.string = talloc_strdup(mem_ctx, in->name.string); \
		NT_STATUS_HAVE_NO_MEMORY(out->name.string); \
	} \
} while (0)

NTSTATUS copy_netr_SamBaseInfo(TALLOC_CTX *mem_ctx,
			       const struct netr_SamBaseInfo *in,
			       struct netr_SamBaseInfo *out)
{
	/* first copy all, then realloc pointers */
	*out = *in;

	COPY_LSA_STRING(mem_ctx, in, out, account_name);
	COPY_LSA_STRING(mem_ctx, in, out, full_name);
	COPY_LSA_STRING(mem_ctx, in, out, logon_script);
	COPY_LSA_STRING(mem_ctx, in, out, profile_path);
	COPY_LSA_STRING(mem_ctx, in, out, home_directory);
	COPY_LSA_STRING(mem_ctx, in, out, home_drive);

	if (in->groups.count) {
		out->groups.rids = (struct samr_RidWithAttribute *)
			talloc_memdup(mem_ctx, in->groups.rids,
				(sizeof(struct samr_RidWithAttribute) *
					in->groups.count));
		NT_STATUS_HAVE_NO_MEMORY(out->groups.rids);
	}

	COPY_LSA_STRING(mem_ctx, in, out, logon_server);
	COPY_LSA_STRING(mem_ctx, in, out, logon_domain);

	if (in->domain_sid) {
		out->domain_sid = dom_sid_dup(mem_ctx, in->domain_sid);
		NT_STATUS_HAVE_NO_MEMORY(out->domain_sid);
	}

	return NT_STATUS_OK;
}

NTSTATUS copy_netr_SamInfo3(TALLOC_CTX *mem_ctx,
			    const struct netr_SamInfo3 *in,
			    struct netr_SamInfo3 **pout)
{
	struct netr_SamInfo3 *info3 = NULL;
	unsigned int i;
	NTSTATUS status;

	info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
	if (info3 == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	status = copy_netr_SamBaseInfo(info3, &in->base, &info3->base);
	if (!NT_STATUS_IS_OK(status)) {
		goto out;
	}

	if (in->sidcount) {
		info3->sidcount = in->sidcount;
		info3->sids = talloc_array(info3, struct netr_SidAttr,
					   in->sidcount);
		if (info3->sids == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}

		for (i = 0; i < in->sidcount; i++) {
			info3->sids[i].sid = dom_sid_dup(info3->sids,
							 in->sids[i].sid);
			if (info3->sids[i].sid == NULL) {
				status = NT_STATUS_NO_MEMORY;
				goto out;
			}
			info3->sids[i].attributes = in->sids[i].attributes;
		}
	}

	*pout = info3;
	info3 = NULL;

	status = NT_STATUS_OK;
out:
	TALLOC_FREE(info3);
	return status;
}

NTSTATUS map_validation_to_info3(TALLOC_CTX *mem_ctx,
				 uint16_t validation_level,
				 union netr_Validation *validation,
				 struct netr_SamInfo3 **info3_p)
{
	struct netr_SamInfo3 *info3 = NULL;
	struct netr_SamInfo6 *info6 = NULL;
	NTSTATUS status;

	if (validation == NULL) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	switch (validation_level) {
	case 3:
		if (validation->sam3 == NULL) {
			return NT_STATUS_INVALID_PARAMETER;
		}

		status = copy_netr_SamInfo3(mem_ctx,
					    validation->sam3,
					    &info3);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}

		break;
	case 6:
		if (validation->sam6 == NULL) {
			return NT_STATUS_INVALID_PARAMETER;
		}
		info6 = validation->sam6;

		info3 = talloc_zero(mem_ctx, struct netr_SamInfo3);
		if (info3 == NULL) {
			return NT_STATUS_NO_MEMORY;
		}

		status = copy_netr_SamBaseInfo(info3,
					       &info6->base,
					       &info3->base);
		if (!NT_STATUS_IS_OK(status)) {
			TALLOC_FREE(info3);
			return status;
		}

		if (validation->sam6->sidcount > 0) {
			int i;

			info3->sidcount = info6->sidcount;

			info3->sids = talloc_array(info3,
						   struct netr_SidAttr,
						   info3->sidcount);
			if (info3->sids == NULL) {
				TALLOC_FREE(info3);
				return NT_STATUS_NO_MEMORY;
			}

			for (i = 0; i < info3->sidcount; i++) {
				info3->sids[i].sid = dom_sid_dup(
					info3->sids, info6->sids[i].sid);
				if (info3->sids[i].sid == NULL) {
					TALLOC_FREE(info3);
					return NT_STATUS_NO_MEMORY;
				}
				info3->sids[i].attributes =
					info6->sids[i].attributes;
			}
		}
		break;
	default:
		return NT_STATUS_BAD_VALIDATION_CLASS;
	}

	*info3_p = info3;

	return NT_STATUS_OK;
}

NTSTATUS copy_netr_SamInfo6(TALLOC_CTX *mem_ctx,
			    const struct netr_SamInfo6 *in,
			    struct netr_SamInfo6 **pout)
{
	struct netr_SamInfo6 *info6 = NULL;
	unsigned int i;
	NTSTATUS status;

	info6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
	if (info6 == NULL) {
		status = NT_STATUS_NO_MEMORY;
		goto out;
	}

	status = copy_netr_SamBaseInfo(info6, &in->base, &info6->base);
	if (!NT_STATUS_IS_OK(status)) {
		goto out;
	}

	if (in->sidcount) {
		info6->sidcount = in->sidcount;
		info6->sids = talloc_array(info6, struct netr_SidAttr,
					   in->sidcount);
		if (info6->sids == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}

		for (i = 0; i < in->sidcount; i++) {
			info6->sids[i].sid = dom_sid_dup(info6->sids,
							 in->sids[i].sid);
			if (info6->sids[i].sid == NULL) {
				status = NT_STATUS_NO_MEMORY;
				goto out;
			}
			info6->sids[i].attributes = in->sids[i].attributes;
		}
	}

	if (in->dns_domainname.string != NULL) {
		info6->dns_domainname.string = talloc_strdup(info6,
						in->dns_domainname.string);
		if (info6->dns_domainname.string == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
	}

	if (in->principal_name.string != NULL) {
		info6->principal_name.string = talloc_strdup(info6,
						in->principal_name.string);
		if (info6->principal_name.string == NULL) {
			status = NT_STATUS_NO_MEMORY;
			goto out;
		}
	}

	*pout = info6;
	info6 = NULL;

	status = NT_STATUS_OK;
out:
	TALLOC_FREE(info6);
	return status;
}

NTSTATUS map_validation_to_info6(TALLOC_CTX *mem_ctx,
				 uint16_t validation_level,
				 union netr_Validation *validation,
				 struct netr_SamInfo6 **info6_p)
{
	struct netr_SamInfo3 *info3 = NULL;
	struct netr_SamInfo6 *info6 = NULL;
	NTSTATUS status;

	if (validation == NULL) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	switch (validation_level) {
	case 3:
		if (validation->sam3 == NULL) {
			return NT_STATUS_INVALID_PARAMETER;
		}
		info3 = validation->sam3;

		info6 = talloc_zero(mem_ctx, struct netr_SamInfo6);
		if (info6 == NULL) {
			return NT_STATUS_NO_MEMORY;
		}

		status = copy_netr_SamBaseInfo(info6,
					       &info3->base,
					       &info6->base);
		if (!NT_STATUS_IS_OK(status)) {
			TALLOC_FREE(info6);
			return status;
		}

		if (validation->sam3->sidcount > 0) {
			int i;

			info6->sidcount = info3->sidcount;

			info6->sids = talloc_array(info6,
						   struct netr_SidAttr,
						   info6->sidcount);
			if (info6->sids == NULL) {
				TALLOC_FREE(info6);
				return NT_STATUS_NO_MEMORY;
			}

			for (i = 0; i < info6->sidcount; i++) {
				info6->sids[i].sid = dom_sid_dup(
					info6->sids, info3->sids[i].sid);
				if (info6->sids[i].sid == NULL) {
					TALLOC_FREE(info6);
					return NT_STATUS_NO_MEMORY;
				}
				info6->sids[i].attributes =
					info3->sids[i].attributes;
			}
		}
		break;
	case 6:
		if (validation->sam6 == NULL) {
			return NT_STATUS_INVALID_PARAMETER;
		}

		status = copy_netr_SamInfo6(mem_ctx,
					    validation->sam6,
					    &info6);
		if (!NT_STATUS_IS_OK(status)) {
			return status;
		}

		break;
	default:
		return NT_STATUS_BAD_VALIDATION_CLASS;
	}

	*info6_p = info6;

	return NT_STATUS_OK;
}

NTSTATUS map_info3_to_validation(TALLOC_CTX *mem_ctx,
				 struct netr_SamInfo3 *info3,
				 uint16_t *_validation_level,
				 union netr_Validation **_validation)
{
	union netr_Validation *validation = NULL;
	NTSTATUS status;

	validation = talloc_zero(mem_ctx, union netr_Validation);
	if (validation == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	status = copy_netr_SamInfo3(mem_ctx,
				    info3,
				    &validation->sam3);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(validation);
		return status;
	}

	* _validation_level = 3;
	*_validation = validation;
	return NT_STATUS_OK;
}

NTSTATUS map_info6_to_validation(TALLOC_CTX *mem_ctx,
				 const struct netr_SamInfo6 *info6,
				 uint16_t *_validation_level,
				 union netr_Validation **_validation)
{
	union netr_Validation *validation = NULL;
	NTSTATUS status;

	validation = talloc_zero(mem_ctx, union netr_Validation);
	if (validation == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	status = copy_netr_SamInfo6(validation,
				    info6,
				    &validation->sam6);
	if (!NT_STATUS_IS_OK(status)) {
		TALLOC_FREE(validation);
		return status;
	}

	* _validation_level = 6;
	*_validation = validation;
	return NT_STATUS_OK;
}

/****************************************************************
****************************************************************/

NTSTATUS copy_netr_DsRGetDCNameInfo(TALLOC_CTX *mem_ctx,
				    const struct netr_DsRGetDCNameInfo *in,
				    struct netr_DsRGetDCNameInfo **pout)
{
	struct netr_DsRGetDCNameInfo *r;

	r = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo);
	if (r == NULL) {
		return NT_STATUS_NO_MEMORY;
	}

	r->dc_unc = talloc_strdup(r, in->dc_unc);
	if (r->dc_unc == NULL) {
		talloc_free(r);
		return NT_STATUS_NO_MEMORY;
	}
	r->dc_address = talloc_strdup(r, in->dc_address);
	if (r->dc_address == NULL) {
		talloc_free(r);
		return NT_STATUS_NO_MEMORY;
	}
	r->dc_address_type = in->dc_address_type;
	r->domain_guid = in->domain_guid;
	r->domain_name = talloc_strdup(r, in->domain_name);
	if (r->domain_name == NULL) {
		talloc_free(r);
		return NT_STATUS_NO_MEMORY;
	}
	/* forest could be empty */
	if (in->forest_name != NULL) {
		r->forest_name = talloc_strdup(r, in->forest_name);
		if (r->forest_name == NULL) {
			talloc_free(r);
			return NT_STATUS_NO_MEMORY;
		}
	}
	r->dc_flags = in->dc_flags;
	if (in->dc_site_name != NULL) {
		r->dc_site_name = talloc_strdup(r, in->dc_site_name);
		if (r->dc_site_name == NULL) {
			talloc_free(r);
			return NT_STATUS_NO_MEMORY;
		}
	}
	if (in->client_site_name != NULL) {
		r->client_site_name = talloc_strdup(r, in->client_site_name);
		if (r->client_site_name == NULL) {
			talloc_free(r);
			return NT_STATUS_NO_MEMORY;
		}
	}

	*pout = r;

	return NT_STATUS_OK;
}
