From 4f5791ebd03eaec1c7da0865a383175b05102712 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 5 May 2024 19:47:29 +0200 Subject: Adding upstream version 2:4.17.12+dfsg. Signed-off-by: Daniel Baumann --- source4/libnet/py_net.c | 945 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 945 insertions(+) create mode 100644 source4/libnet/py_net.c (limited to 'source4/libnet/py_net.c') diff --git a/source4/libnet/py_net.c b/source4/libnet/py_net.c new file mode 100644 index 0000000..fe5979e --- /dev/null +++ b/source4/libnet/py_net.c @@ -0,0 +1,945 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + + Copyright (C) Jelmer Vernooij 2008-2010 + Copyright (C) Kamen Mazdrashki 2009 + + 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 +#include "python/py3compat.h" +#include "includes.h" +#include "python/modules.h" +#include +#include +#include "libnet.h" +#include "auth/credentials/pycredentials.h" +#include "libcli/security/security.h" +#include "lib/events/events.h" +#include "param/pyparam.h" +#include "auth/gensec/gensec.h" +#include "librpc/rpc/pyrpc_util.h" +#include "libcli/resolve/resolve.h" +#include "libcli/finddc.h" +#include "dsdb/samdb/samdb.h" +#include "py_net.h" +#include "librpc/rpc/pyrpc_util.h" +#include "libcli/drsuapi/drsuapi.h" + +static void PyErr_SetDsExtendedError(enum drsuapi_DsExtendedError ext_err, const char *error_description) +{ + PyObject *mod = NULL; + PyObject *error = NULL; + mod = PyImport_ImportModule("samba"); + if (mod) { + error = PyObject_GetAttrString(mod, "DsExtendedError"); + } + if (error_description == NULL) { + switch (ext_err) { + /* Copied out of ndr_drsuapi.c:ndr_print_drsuapi_DsExtendedError() */ + case DRSUAPI_EXOP_ERR_NONE: + error_description = "DRSUAPI_EXOP_ERR_NONE"; + break; + case DRSUAPI_EXOP_ERR_SUCCESS: + error_description = "DRSUAPI_EXOP_ERR_SUCCESS"; + break; + case DRSUAPI_EXOP_ERR_UNKNOWN_OP: + error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_OP"; + break; + case DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER: + error_description = "DRSUAPI_EXOP_ERR_FSMO_NOT_OWNER"; + break; + case DRSUAPI_EXOP_ERR_UPDATE_ERR: + error_description = "DRSUAPI_EXOP_ERR_UPDATE_ERR"; + break; + case DRSUAPI_EXOP_ERR_EXCEPTION: + error_description = "DRSUAPI_EXOP_ERR_EXCEPTION"; + break; + case DRSUAPI_EXOP_ERR_UNKNOWN_CALLER: + error_description = "DRSUAPI_EXOP_ERR_UNKNOWN_CALLER"; + break; + case DRSUAPI_EXOP_ERR_RID_ALLOC: + error_description = "DRSUAPI_EXOP_ERR_RID_ALLOC"; + break; + case DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED: + error_description = "DRSUAPI_EXOP_ERR_FSMO_OWNER_DELETED"; + break; + case DRSUAPI_EXOP_ERR_FMSO_PENDING_OP: + error_description = "DRSUAPI_EXOP_ERR_FMSO_PENDING_OP"; + break; + case DRSUAPI_EXOP_ERR_MISMATCH: + error_description = "DRSUAPI_EXOP_ERR_MISMATCH"; + break; + case DRSUAPI_EXOP_ERR_COULDNT_CONTACT: + error_description = "DRSUAPI_EXOP_ERR_COULDNT_CONTACT"; + break; + case DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES: + error_description = "DRSUAPI_EXOP_ERR_FSMO_REFUSING_ROLES"; + break; + case DRSUAPI_EXOP_ERR_DIR_ERROR: + error_description = "DRSUAPI_EXOP_ERR_DIR_ERROR"; + break; + case DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS: + error_description = "DRSUAPI_EXOP_ERR_FSMO_MISSING_SETTINGS"; + break; + case DRSUAPI_EXOP_ERR_ACCESS_DENIED: + error_description = "DRSUAPI_EXOP_ERR_ACCESS_DENIED"; + break; + case DRSUAPI_EXOP_ERR_PARAM_ERROR: + error_description = "DRSUAPI_EXOP_ERR_PARAM_ERROR"; + break; + } + } + if (error) { + PyObject *value = + Py_BuildValue(discard_const_p(char, "(i,s)"), + ext_err, + error_description); + PyErr_SetObject(error, value); + if (value) { + Py_DECREF(value); + } + Py_DECREF(error); + } +} + +static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + struct libnet_Join_member r; + int _level = 0; + NTSTATUS status; + PyObject *result; + TALLOC_CTX *mem_ctx; + const char *kwnames[] = { "domain_name", "netbios_name", "level", "machinepass", NULL }; + + ZERO_STRUCT(r); + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "ssi|z:Join", discard_const_p(char *, kwnames), + &r.in.domain_name, &r.in.netbios_name, + &_level, + &r.in.account_pass)) { + return NULL; + } + r.in.level = _level; + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + status = libnet_Join_member(self->libnet_ctx, mem_ctx, &r); + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.out.error_string + ? r.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + result = Py_BuildValue("sss", r.out.join_password, + dom_sid_string(mem_ctx, r.out.domain_sid), + r.out.domain_name); + + talloc_free(mem_ctx); + + return result; +} + +static const char py_net_join_member_doc[] = "join_member(domain_name, netbios_name, level) -> (join_password, domain_sid, domain_name)\n\n" \ +"Join the domain with the specified name."; + +static PyObject *py_net_change_password(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + union libnet_ChangePassword r; + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + struct tevent_context *ev = NULL; + const char *kwnames[] = { "newpassword", "oldpassword", "domain", "username", NULL }; + const char *newpass = NULL; + const char *oldpass = NULL; + ZERO_STRUCT(r); + if (!PyArg_ParseTupleAndKeywords(args, kwargs, PYARG_STR_UNI + "|"PYARG_STR_UNI"ss:change_password", + discard_const_p(char *, kwnames), + "utf8", + &newpass, + "utf8", + &oldpass, + &r.generic.in.domain_name, + &r.generic.in.account_name)) { + return NULL; + } + + r.generic.in.newpassword = newpass; + r.generic.in.oldpassword = oldpass; + + r.generic.level = LIBNET_CHANGE_PASSWORD_GENERIC; + if (r.generic.in.account_name == NULL) { + r.generic.in.account_name + = cli_credentials_get_username(self->libnet_ctx->cred); + } + if (r.generic.in.domain_name == NULL) { + r.generic.in.domain_name + = cli_credentials_get_domain(self->libnet_ctx->cred); + } + if (r.generic.in.oldpassword == NULL) { + r.generic.in.oldpassword + = cli_credentials_get_password(self->libnet_ctx->cred); + } + + /* FIXME: we really need to get a context from the caller or we may end + * up with 2 event contexts */ + ev = s4_event_context_init(NULL); + + mem_ctx = talloc_new(ev); + if (mem_ctx == NULL) { + PyMem_Free(discard_const_p(char, newpass)); + PyMem_Free(discard_const_p(char, oldpass)); + PyErr_NoMemory(); + return NULL; + } + + status = libnet_ChangePassword(self->libnet_ctx, mem_ctx, &r); + + PyMem_Free(discard_const_p(char, newpass)); + PyMem_Free(discard_const_p(char, oldpass)); + + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.generic.out.error_string + ? r.generic.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + talloc_free(mem_ctx); + Py_RETURN_NONE; +} + +static const char py_net_change_password_doc[] = "change_password(newpassword) -> True\n\n" \ +"Change password for a user. You must supply credential with enough rights to do this.\n\n" \ +"Sample usage is:\n" \ +"net.change_password(newpassword=)\n"; + + +static PyObject *py_net_set_password(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + union libnet_SetPassword r; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct tevent_context *ev; + const char *kwnames[] = { "account_name", "domain_name", "newpassword", "force_samr_18", NULL }; + PyObject *py_force_samr_18 = Py_False; + + ZERO_STRUCT(r); + + r.generic.level = LIBNET_SET_PASSWORD_GENERIC; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sss|O:set_password", + discard_const_p(char *, kwnames), + &r.generic.in.account_name, + &r.generic.in.domain_name, + &r.generic.in.newpassword, + &py_force_samr_18)) { + return NULL; + } + + if (py_force_samr_18) { + if (!PyBool_Check(py_force_samr_18)) { + PyErr_SetString(PyExc_TypeError, "Expected boolean force_samr_18"); + return NULL; + } + if (py_force_samr_18 == Py_True) { + r.generic.samr_level = LIBNET_SET_PASSWORD_SAMR_HANDLE_18; + } + } + + /* FIXME: we really need to get a context from the caller or we may end + * up with 2 event contexts */ + ev = s4_event_context_init(NULL); + + mem_ctx = talloc_new(ev); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + status = libnet_SetPassword(self->libnet_ctx, mem_ctx, &r); + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.generic.out.error_string + ? r.generic.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + talloc_free(mem_ctx); + + Py_RETURN_NONE; +} + +static const char py_net_set_password_doc[] = "set_password(account_name, domain_name, newpassword) -> True\n\n" \ +"Set password for a user. You must supply credential with enough rights to do this.\n\n" \ +"Sample usage is:\n" \ +"net.set_password(account_name=account_name, domain_name=domain_name, newpassword=new_pass)\n"; + + +static PyObject *py_net_time(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "server_name", NULL }; + union libnet_RemoteTOD r; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + char timestr[64]; + PyObject *ret; + struct tm *tm; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", + discard_const_p(char *, kwnames), &r.generic.in.server_name)) + return NULL; + + r.generic.level = LIBNET_REMOTE_TOD_GENERIC; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + status = libnet_RemoteTOD(self->libnet_ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.generic.out.error_string + ? r.generic.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + ZERO_STRUCT(timestr); + tm = localtime(&r.generic.out.time); + strftime(timestr, sizeof(timestr)-1, "%c %Z",tm); + + ret = PyUnicode_FromString(timestr); + + talloc_free(mem_ctx); + + return ret; +} + +static const char py_net_time_doc[] = "time(server_name) -> timestr\n" +"Retrieve the remote time on a server"; + +static PyObject *py_net_user_create(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "username", NULL }; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_CreateUser r; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), + &r.in.user_name)) + return NULL; + + r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred); + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + status = libnet_CreateUser(self->libnet_ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.out.error_string + ? r.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + talloc_free(mem_ctx); + + Py_RETURN_NONE; +} + +static const char py_net_create_user_doc[] = "create_user(username)\n" +"Create a new user."; + +static PyObject *py_net_user_delete(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "username", NULL }; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct libnet_DeleteUser r; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", discard_const_p(char *, kwnames), + &r.in.user_name)) + return NULL; + + r.in.domain_name = cli_credentials_get_domain(self->libnet_ctx->cred); + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + status = libnet_DeleteUser(self->libnet_ctx, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS_and_string(status, + r.out.error_string + ? r.out.error_string + : nt_errstr(status)); + talloc_free(mem_ctx); + return NULL; + } + + talloc_free(mem_ctx); + + Py_RETURN_NONE; +} + +static const char py_net_delete_user_doc[] = "delete_user(username)\n" +"Delete a user."; + +struct replicate_state { + void *vampire_state; + dcerpc_InterfaceObject *drs_pipe; + struct libnet_BecomeDC_StoreChunk chunk; + DATA_BLOB gensec_skey; + struct libnet_BecomeDC_Partition partition; + struct libnet_BecomeDC_Forest forest; + struct libnet_BecomeDC_DestDSA dest_dsa; +}; + +/* + setup for replicate_chunk() calls + */ +static PyObject *py_net_replicate_init(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "samdb", "lp", "drspipe", "invocation_id", NULL }; + PyObject *py_ldb, *py_lp, *py_drspipe, *py_invocation_id; + struct ldb_context *samdb; + struct loadparm_context *lp; + struct replicate_state *s; + NTSTATUS status; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOOO", + discard_const_p(char *, kwnames), + &py_ldb, &py_lp, &py_drspipe, + &py_invocation_id)) { + return NULL; + } + + s = talloc_zero(NULL, struct replicate_state); + if (!s) return NULL; + + lp = lpcfg_from_py_object(s, py_lp); + if (lp == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected lp object"); + talloc_free(s); + return NULL; + } + + samdb = pyldb_Ldb_AsLdbContext(py_ldb); + if (samdb == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected ldb object"); + talloc_free(s); + return NULL; + } + if (!py_check_dcerpc_type(py_invocation_id, "samba.dcerpc.misc", "GUID")) { + + talloc_free(s); + return NULL; + } + s->dest_dsa.invocation_id = *pytalloc_get_type(py_invocation_id, struct GUID); + + s->drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe); + + s->vampire_state = libnet_vampire_replicate_init(s, samdb, lp); + if (s->vampire_state == NULL) { + PyErr_SetString(PyExc_TypeError, "Failed to initialise vampire_state"); + talloc_free(s); + return NULL; + } + + status = gensec_session_key(s->drs_pipe->pipe->conn->security_state.generic_state, + s, + &s->gensec_skey); + if (!NT_STATUS_IS_OK(status)) { + char *error_string = talloc_asprintf(s, + "Unable to get session key from drspipe: %s", + nt_errstr(status)); + PyErr_SetNTSTATUS_and_string(status, error_string); + talloc_free(s); + return NULL; + } + + s->forest.dns_name = samdb_dn_to_dns_domain(s, ldb_get_root_basedn(samdb)); + s->forest.root_dn_str = ldb_dn_get_linearized(ldb_get_root_basedn(samdb)); + s->forest.config_dn_str = ldb_dn_get_linearized(ldb_get_config_basedn(samdb)); + s->forest.schema_dn_str = ldb_dn_get_linearized(ldb_get_schema_basedn(samdb)); + + s->chunk.gensec_skey = &s->gensec_skey; + s->chunk.partition = &s->partition; + s->chunk.forest = &s->forest; + s->chunk.dest_dsa = &s->dest_dsa; + + return pytalloc_GenericObject_steal(s); +} + + +/* + process one replication chunk + */ +static PyObject *py_net_replicate_chunk(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "state", "level", "ctr", + "schema", "req_level", "req", + NULL }; + PyObject *py_state, *py_ctr, *py_schema = Py_None, *py_req = Py_None; + struct replicate_state *s; + unsigned level; + unsigned req_level = 0; + WERROR (*chunk_handler)(void *private_data, const struct libnet_BecomeDC_StoreChunk *c); + WERROR werr; + enum drsuapi_DsExtendedError extended_ret = DRSUAPI_EXOP_ERR_NONE; + enum drsuapi_DsExtendedOperation exop = DRSUAPI_EXOP_NONE; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OIO|OIO", + discard_const_p(char *, kwnames), + &py_state, &level, &py_ctr, + &py_schema, &req_level, &py_req)) { + return NULL; + } + + s = pytalloc_get_type(py_state, struct replicate_state); + if (!s) { + return NULL; + } + + switch (level) { + case 1: + if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr1")) { + return NULL; + } + s->chunk.ctr1 = pytalloc_get_ptr(py_ctr); + if (s->chunk.ctr1->naming_context != NULL) { + s->partition.nc = *s->chunk.ctr1->naming_context; + } + extended_ret = s->chunk.ctr1->extended_ret; + s->partition.more_data = s->chunk.ctr1->more_data; + s->partition.source_dsa_guid = s->chunk.ctr1->source_dsa_guid; + s->partition.source_dsa_invocation_id = s->chunk.ctr1->source_dsa_invocation_id; + s->partition.highwatermark = s->chunk.ctr1->new_highwatermark; + break; + case 6: + if (!py_check_dcerpc_type(py_ctr, "samba.dcerpc.drsuapi", "DsGetNCChangesCtr6")) { + return NULL; + } + s->chunk.ctr6 = pytalloc_get_ptr(py_ctr); + if (s->chunk.ctr6->naming_context != NULL) { + s->partition.nc = *s->chunk.ctr6->naming_context; + } + extended_ret = s->chunk.ctr6->extended_ret; + s->partition.more_data = s->chunk.ctr6->more_data; + s->partition.source_dsa_guid = s->chunk.ctr6->source_dsa_guid; + s->partition.source_dsa_invocation_id = s->chunk.ctr6->source_dsa_invocation_id; + s->partition.highwatermark = s->chunk.ctr6->new_highwatermark; + break; + default: + PyErr_Format(PyExc_TypeError, "Bad level %u in replicate_chunk", level); + return NULL; + } + + s->chunk.req5 = NULL; + s->chunk.req8 = NULL; + s->chunk.req10 = NULL; + if (py_req != Py_None) { + switch (req_level) { + case 0: + break; + case 5: + if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest5")) { + return NULL; + } + + s->chunk.req5 = pytalloc_get_ptr(py_req); + exop = s->chunk.req5->extended_op; + break; + case 8: + if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest8")) { + return NULL; + } + + s->chunk.req8 = pytalloc_get_ptr(py_req); + exop = s->chunk.req8->extended_op; + break; + case 10: + if (!py_check_dcerpc_type(py_req, "samba.dcerpc.drsuapi", "DsGetNCChangesRequest10")) { + return NULL; + } + + s->chunk.req10 = pytalloc_get_ptr(py_req); + exop = s->chunk.req10->extended_op; + break; + default: + PyErr_Format(PyExc_TypeError, "Bad req_level %u in replicate_chunk", req_level); + return NULL; + } + } + + if (exop != DRSUAPI_EXOP_NONE && extended_ret != DRSUAPI_EXOP_ERR_SUCCESS) { + PyErr_SetDsExtendedError(extended_ret, NULL); + return NULL; + } + + s->chunk.req_level = req_level; + + chunk_handler = libnet_vampire_cb_store_chunk; + if (py_schema) { + if (!PyBool_Check(py_schema)) { + PyErr_SetString(PyExc_TypeError, "Expected boolean schema"); + return NULL; + } + if (py_schema == Py_True) { + chunk_handler = libnet_vampire_cb_schema_chunk; + } + } + + s->chunk.ctr_level = level; + + werr = chunk_handler(s->vampire_state, &s->chunk); + if (!W_ERROR_IS_OK(werr)) { + char *error_string + = talloc_asprintf(NULL, + "Failed to process 'chunk' of DRS replicated objects: %s", + win_errstr(werr)); + PyErr_SetWERROR_and_string(werr, error_string); + TALLOC_FREE(error_string); + return NULL; + } + + Py_RETURN_NONE; +} + + +/* + just do the decryption of a DRS replicated attribute + */ +static PyObject *py_net_replicate_decrypt(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *kwnames[] = { "drspipe", "attribute", "rid", NULL }; + PyObject *py_drspipe, *py_attribute; + NTSTATUS status; + dcerpc_InterfaceObject *drs_pipe; + TALLOC_CTX *frame; + TALLOC_CTX *context; + DATA_BLOB gensec_skey; + unsigned int rid; + struct drsuapi_DsReplicaAttribute *attribute; + WERROR werr; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOI", + discard_const_p(char *, kwnames), + &py_drspipe, + &py_attribute, &rid)) { + return NULL; + } + + frame = talloc_stackframe(); + + if (!py_check_dcerpc_type(py_drspipe, + "samba.dcerpc.base", + "ClientConnection")) { + return NULL; + } + drs_pipe = (dcerpc_InterfaceObject *)(py_drspipe); + + status = gensec_session_key(drs_pipe->pipe->conn->security_state.generic_state, + frame, + &gensec_skey); + if (!NT_STATUS_IS_OK(status)) { + char *error_string + = talloc_asprintf(frame, + "Unable to get session key from drspipe: %s", + nt_errstr(status)); + PyErr_SetNTSTATUS_and_string(status, error_string); + talloc_free(frame); + return NULL; + } + + if (!py_check_dcerpc_type(py_attribute, "samba.dcerpc.drsuapi", + "DsReplicaAttribute")) { + return NULL; + } + + attribute = pytalloc_get_ptr(py_attribute); + context = pytalloc_get_mem_ctx(py_attribute); + werr = drsuapi_decrypt_attribute(context, &gensec_skey, + rid, 0, attribute); + if (!W_ERROR_IS_OK(werr)) { + char *error_string = talloc_asprintf(frame, + "Unable to get decrypt attribute: %s", + win_errstr(werr)); + PyErr_SetWERROR_and_string(werr, error_string); + talloc_free(frame); + return NULL; + } + + talloc_free(frame); + + Py_RETURN_NONE; + +} + +/* + find a DC given a domain name and server type + */ +static PyObject *py_net_finddc(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + const char *domain = NULL, *address = NULL; + unsigned server_type; + NTSTATUS status; + struct finddcs *io; + TALLOC_CTX *mem_ctx; + PyObject *ret; + const char * const kwnames[] = { "flags", "domain", "address", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "I|zz", + discard_const_p(char *, kwnames), + &server_type, &domain, &address)) { + return NULL; + } + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + io = talloc_zero(mem_ctx, struct finddcs); + if (io == NULL) { + TALLOC_FREE(mem_ctx); + PyErr_NoMemory(); + return NULL; + } + + if (domain != NULL) { + io->in.domain_name = domain; + } + if (address != NULL) { + io->in.server_address = address; + } + io->in.minimum_dc_flags = server_type; + + status = finddcs_cldap(io, io, + lpcfg_resolve_context(self->libnet_ctx->lp_ctx), self->ev); + if (NT_STATUS_IS_ERR(status)) { + PyErr_SetNTSTATUS(status); + talloc_free(mem_ctx); + return NULL; + } + + ret = py_return_ndr_struct("samba.dcerpc.nbt", "NETLOGON_SAM_LOGON_RESPONSE_EX", + io, &io->out.netlogon.data.nt5_ex); + talloc_free(mem_ctx); + + return ret; +} + + +static const char py_net_replicate_init_doc[] = "replicate_init(samdb, lp, drspipe)\n" + "Setup for replicate_chunk calls."; + +static const char py_net_replicate_chunk_doc[] = "replicate_chunk(state, level, ctr, schema)\n" + "Process replication for one chunk"; + +static const char py_net_replicate_decrypt_doc[] = "replicate_decrypt(drs, attribute, rid)\n" + "Decrypt (in place) a DsReplicaAttribute replicated with drs.GetNCChanges()"; + +static const char py_net_finddc_doc[] = "finddc(flags=server_type, domain=None, address=None)\n" + "Find a DC with the specified 'server_type' bits. The 'domain' and/or 'address' have to be used as additional search criteria. Returns the whole netlogon struct"; + +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 = "change_password", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_change_password), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_change_password_doc + }, + { + .ml_name = "set_password", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_set_password), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_set_password_doc + }, + { + .ml_name = "time", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, py_net_time), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_time_doc + }, + { + .ml_name = "create_user", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_user_create), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_create_user_doc + }, + { + .ml_name = "delete_user", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_user_delete), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_delete_user_doc + }, + { + .ml_name = "replicate_init", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_replicate_init), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_replicate_init_doc + }, + { + .ml_name = "replicate_chunk", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_replicate_chunk), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_replicate_chunk_doc + }, + { + .ml_name = "replicate_decrypt", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_replicate_decrypt), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_replicate_decrypt_doc + }, + { + .ml_name = "finddc", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_finddc), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_finddc_doc + }, + { .ml_name = NULL } +}; + +static void py_net_dealloc(py_net_Object *self) +{ + talloc_free(self->ev); + 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; + struct loadparm_context *lp; + const char *server_address = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz", + discard_const_p(char *, kwnames), &py_creds, &py_lp, + &server_address)) + return NULL; + + ret = PyObject_New(py_net_Object, type); + if (ret == NULL) { + return NULL; + } + + /* FIXME: we really need to get a context from the caller or we may end + * up with 2 event contexts */ + ret->ev = s4_event_context_init(NULL); + ret->mem_ctx = talloc_new(ret->ev); + + lp = lpcfg_from_py_object(ret->mem_ctx, py_lp); + if (lp == NULL) { + Py_DECREF(ret); + return NULL; + } + + ret->libnet_ctx = libnet_context_init(ret->ev, lp); + if (ret->libnet_ctx == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Unable to initialize net"); + Py_DECREF(ret); + return NULL; + } + + ret->libnet_ctx->server_address = server_address; + + ret->libnet_ctx->cred = cli_credentials_from_py_object(py_creds); + if (ret->libnet_ctx->cred == 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.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) +{ + 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); + PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_AUTOMATIC", LIBNET_JOINDOMAIN_AUTOMATIC); + PyModule_AddIntConstant(m, "LIBNET_JOINDOMAIN_SPECIFIED", LIBNET_JOINDOMAIN_SPECIFIED); + PyModule_AddIntConstant(m, "LIBNET_JOIN_AUTOMATIC", LIBNET_JOIN_AUTOMATIC); + PyModule_AddIntConstant(m, "LIBNET_JOIN_SPECIFIED", LIBNET_JOIN_SPECIFIED); + + return m; +} -- cgit v1.2.3