From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/utils/py_net.c | 369 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) create mode 100644 source3/utils/py_net.c (limited to 'source3/utils/py_net.c') diff --git a/source3/utils/py_net.c b/source3/utils/py_net.c new file mode 100644 index 0000000..5e81b8f --- /dev/null +++ b/source3/utils/py_net.c @@ -0,0 +1,369 @@ +/* + Unix SMB/CIFS implementation. + Samba python bindings to s3 libnet library + + Copyright (C) David Mulder + + 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 "lib/replace/system/python.h" +#include "includes.h" +#include +#include "python/modules.h" +#include "python/py3compat.h" +#include "rpc_client/rpc_client.h" +#include +#include "net.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/pycredentials.h" +#include "lib/cmdline_contexts.h" +#include "param/loadparm.h" +#include "param/s3_param.h" +#include "param/pyparam.h" +#include "py_net.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "libcli/security/dom_sid.h" +#include "dynconfig/dynconfig.h" + +static WERROR check_ads_config(struct loadparm_context *lp_ctx) +{ + if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) { + d_printf(_("Host is not configured as a member server.\n")); + return WERR_INVALID_DOMAIN_ROLE; + } + + if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) { + d_printf(_("Our netbios name can be at most 15 chars long, " + "\"%s\" is %u chars long\n"), lpcfg_netbios_name(lp_ctx), + (unsigned int)strlen(lpcfg_netbios_name(lp_ctx))); + return WERR_INVALID_COMPUTERNAME; + } + + if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) { + d_fprintf(stderr, _("realm must be set in %s for ADS " + "join to succeed.\n"), get_dyn_CONFIGFILE()); + return WERR_INVALID_PARAMETER; + } + + return WERR_OK; +} + +static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + struct libnet_JoinCtx *r = NULL; + struct net_context *c; + WERROR werr; + PyObject *result; + TALLOC_CTX *mem_ctx; + int no_dns_updates = false, debug = false; + bool modify_config = lp_config_backend_is_registry(); + const char *kwnames[] = { "dnshostname", "createupn", "createcomputer", + "osName", "osVer", "osServicePack", + "machinepass", "debug", "noDnsUpdates", NULL }; + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + c = talloc_zero(mem_ctx, struct net_context); + c->msg_ctx = mem_ctx; + + werr = libnet_init_JoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_NoMemory(); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssszpp:Join", + discard_const_p(char *, kwnames), + &r->in.dnshostname, + &r->in.upn, + &r->in.account_ou, + &r->in.os_name, + &r->in.os_version, + &r->in.os_servicepack, + &r->in.machine_password, + &debug, + &no_dns_updates)) { + talloc_free(mem_ctx); + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + if (!modify_config) { + werr = check_ads_config(self->lp_ctx); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + _("Invalid configuration. Exiting....\n")); + talloc_free(mem_ctx); + return NULL; + } + } + + r->in.domain_name = lpcfg_realm(self->lp_ctx); + r->in.domain_name_type = JoinDomNameTypeDNS; + r->in.create_upn = r->in.upn != NULL ? true : false; + r->in.dc_name = self->server_address; + r->in.admin_account = cli_credentials_get_username(self->creds); + r->in.admin_password = cli_credentials_get_password(self->creds); + r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds); + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + r->in.debug = debug; + c->opt_user_name = r->in.admin_account; + c->opt_password = r->in.admin_password; + c->opt_kerberos = r->in.use_kerberos; + + werr = libnet_Join(mem_ctx, r); + if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) { + r->in.domain_name = lpcfg_workgroup(self->lp_ctx); + r->in.domain_name_type = JoinDomNameTypeNBT; + werr = libnet_Join(mem_ctx, r); + } + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + r->out.error_string + ? r->out.error_string + : get_friendly_werror_msg(werr)); + talloc_free(mem_ctx); + return NULL; + } + + /* + * Check the short name of the domain + */ + + if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) { + d_printf(_("The workgroup in %s does not match the short\n" + "domain name obtained from the server.\n" + "Using the name [%s] from the server.\n" + "You should set \"workgroup = %s\" in %s.\n"), + get_dyn_CONFIGFILE(), r->out.netbios_domain_name, + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + /* + * We try doing the dns update (if it was compiled in + * and if it was not disabled on the command line). + * If the dns update fails, we still consider the join + * operation as succeeded if we came this far. + */ + if (!no_dns_updates) { + net_ads_join_dns_updates(c, mem_ctx, r); + } + + result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid), + r->out.dns_domain_name); + + talloc_free(mem_ctx); + + return result; +} + +static const char py_net_join_member_doc[] = "join_member(dnshostname, createupn, createcomputer, osName, osVer, osServicePack, machinepass) -> (domain_sid, domain_name)\n\n" \ +"Join the domain with the specified name."; + +static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + struct libnet_UnjoinCtx *r = NULL; + WERROR werr; + TALLOC_CTX *mem_ctx; + int keep_account = false, debug = false; + const char *kwnames[] = { "keepAccount", "debug", NULL }; + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if (!*lpcfg_realm(self->lp_ctx)) { + PyErr_FromString(_("No realm set, are we joined ?\n")); + return NULL; + } + + werr = libnet_init_UnjoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + _("Could not initialise unjoin context.\n")); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave", + discard_const_p(char *, kwnames), + &keep_account, &debug)) { + talloc_free(mem_ctx); + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds); + r->in.dc_name = self->server_address; + r->in.domain_name = lpcfg_realm(self->lp_ctx); + r->in.admin_account = cli_credentials_get_username(self->creds); + r->in.admin_password = cli_credentials_get_password(self->creds); + r->in.modify_config = lp_config_backend_is_registry(); + r->in.debug = debug; + + /* + * Try to delete it, but if that fails, disable it. The + * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable" + */ + r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE; + if (keep_account) { + r->in.delete_machine_account = false; + } else { + r->in.delete_machine_account = true; + } + + r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + + werr = libnet_Unjoin(mem_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + r->out.error_string + ? r->out.error_string + : get_friendly_werror_msg(werr)); + Py_RETURN_FALSE; + } + + if (r->out.deleted_machine_account) { + d_printf(_("Deleted account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + Py_RETURN_TRUE; + } + + if (r->out.disabled_machine_account) { + d_printf(_("Disabled account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + werr = WERR_OK; + Py_RETURN_TRUE; + } + + /* + * Based on what we requested, we shouldn't get here, but if + * we did, it means the secrets were removed, and therefore + * we have left the domain. + */ + d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + Py_RETURN_TRUE; +} + +static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \ +"Leave the joined domain."; + +static PyMethodDef net_obj_methods[] = { + { + .ml_name = "join_member", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_join_member), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_join_member_doc + }, + { + .ml_name = "leave", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_leave), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_leave_doc + }, + { .ml_name = NULL } +}; + +static void py_net_dealloc(py_net_Object *self) +{ + talloc_free(self->mem_ctx); + PyObject_Del(self); +} + +static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *py_creds, *py_lp = Py_None; + const char *kwnames[] = { "creds", "lp", "server", NULL }; + py_net_Object *ret; + const char *server_address = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz", + discard_const_p(char *, kwnames), &py_creds, &py_lp, + &server_address)) { + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + ret = PyObject_New(py_net_Object, type); + if (ret == NULL) { + return NULL; + } + + ret->ev = samba_tevent_context_init(NULL); + ret->mem_ctx = talloc_stackframe(); + + ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp); + if (ret->lp_ctx == NULL) { + Py_DECREF(ret); + return NULL; + } + + ret->server_address = server_address; + + ret->creds = cli_credentials_from_py_object(py_creds); + if (ret->creds == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected credentials object"); + Py_DECREF(ret); + return NULL; + } + + return (PyObject *)ret; +} + + +PyTypeObject py_net_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "net_s3.Net", + .tp_basicsize = sizeof(py_net_Object), + .tp_dealloc = (destructor)py_net_dealloc, + .tp_methods = net_obj_methods, + .tp_new = net_obj_new, +}; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "net", + .m_size = -1, +}; + +MODULE_INIT_FUNC(net_s3) +{ + PyObject *m; + + if (PyType_Ready(&py_net_Type) < 0) + return NULL; + + m = PyModule_Create(&moduledef); + if (m == NULL) + return NULL; + + Py_INCREF(&py_net_Type); + PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type); + + return m; +} -- cgit v1.2.3