s4:smbtorture: Fix samba3.smb.dir on btrfs
[samba4-gss.git] / source3 / utils / py_net.c
blob95cc59f33b485acaf5daf39b06afd91fe6f2197d
1 /*
2 Unix SMB/CIFS implementation.
3 Samba python bindings to s3 libnet library
5 Copyright (C) David Mulder <dmulder@samba.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "lib/replace/system/python.h"
22 #include "includes.h"
23 #include <pytalloc.h>
24 #include "python/modules.h"
25 #include "python/py3compat.h"
26 #include "rpc_client/rpc_client.h"
27 #include <sys/socket.h>
28 #include "net.h"
29 #include "auth/credentials/credentials.h"
30 #include "auth/credentials/pycredentials.h"
31 #include "lib/cmdline_contexts.h"
32 #include "param/loadparm.h"
33 #include "param/s3_param.h"
34 #include "param/pyparam.h"
35 #include "py_net.h"
36 #include "librpc/gen_ndr/libnet_join.h"
37 #include "libnet/libnet_join.h"
38 #include "libcli/security/dom_sid.h"
39 #include "dynconfig/dynconfig.h"
41 static WERROR check_ads_config(struct loadparm_context *lp_ctx)
43 if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) {
44 d_printf(_("Host is not configured as a member server.\n"));
45 return WERR_INVALID_DOMAIN_ROLE;
48 if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) {
49 d_printf(_("Our netbios name can be at most 15 chars long, "
50 "\"%s\" is %u chars long\n"), lpcfg_netbios_name(lp_ctx),
51 (unsigned int)strlen(lpcfg_netbios_name(lp_ctx)));
52 return WERR_INVALID_COMPUTERNAME;
55 if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) {
56 d_fprintf(stderr, _("realm must be set in %s for ADS "
57 "join to succeed.\n"), get_dyn_CONFIGFILE());
58 return WERR_INVALID_PARAMETER;
61 return WERR_OK;
64 static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
66 struct libnet_JoinCtx *r = NULL;
67 struct net_context *c;
68 WERROR werr;
69 PyObject *result;
70 TALLOC_CTX *mem_ctx;
71 int no_dns_updates = false, debug = false;
72 bool modify_config = lp_config_backend_is_registry();
73 const char *kwnames[] = { "dnshostname", "createupn", "createcomputer",
74 "osName", "osVer", "osServicePack",
75 "machinepass", "debug", "noDnsUpdates", NULL };
77 mem_ctx = talloc_new(self->mem_ctx);
78 if (mem_ctx == NULL) {
79 PyErr_NoMemory();
80 return NULL;
82 c = talloc_zero(mem_ctx, struct net_context);
83 c->msg_ctx = mem_ctx;
85 werr = libnet_init_JoinCtx(mem_ctx, &r);
86 if (!W_ERROR_IS_OK(werr)) {
87 PyErr_NoMemory();
88 return NULL;
91 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssszpp:Join",
92 discard_const_p(char *, kwnames),
93 &r->in.dnshostname,
94 &r->in.upn,
95 &r->in.account_ou,
96 &r->in.os_name,
97 &r->in.os_version,
98 &r->in.os_servicepack,
99 &r->in.machine_password,
100 &debug,
101 &no_dns_updates)) {
102 talloc_free(mem_ctx);
103 PyErr_FromString(_("Invalid arguments\n"));
104 return NULL;
107 if (!modify_config) {
108 werr = check_ads_config(self->lp_ctx);
109 if (!W_ERROR_IS_OK(werr)) {
110 PyErr_SetWERROR_and_string(werr,
111 _("Invalid configuration. Exiting....\n"));
112 talloc_free(mem_ctx);
113 return NULL;
117 r->in.domain_name = lpcfg_realm(self->lp_ctx);
118 r->in.domain_name_type = JoinDomNameTypeDNS;
119 r->in.create_upn = r->in.upn != NULL ? true : false;
120 r->in.dc_name = self->server_address;
121 r->in.admin_credentials = self->creds;
122 r->in.modify_config = modify_config;
123 r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
124 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
125 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
126 r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
127 r->in.debug = debug;
129 c->creds = self->creds;
130 c->explicit_credentials = true;
132 werr = libnet_Join(mem_ctx, r);
133 if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) {
134 r->in.domain_name = lpcfg_workgroup(self->lp_ctx);
135 r->in.domain_name_type = JoinDomNameTypeNBT;
136 werr = libnet_Join(mem_ctx, r);
138 if (!W_ERROR_IS_OK(werr)) {
139 PyErr_SetWERROR_and_string(werr,
140 r->out.error_string
141 ? r->out.error_string
142 : get_friendly_werror_msg(werr));
143 talloc_free(mem_ctx);
144 return NULL;
148 * Check the short name of the domain
151 if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) {
152 d_printf(_("The workgroup in %s does not match the short\n"
153 "domain name obtained from the server.\n"
154 "Using the name [%s] from the server.\n"
155 "You should set \"workgroup = %s\" in %s.\n"),
156 get_dyn_CONFIGFILE(), r->out.netbios_domain_name,
157 r->out.netbios_domain_name, get_dyn_CONFIGFILE());
161 * We try doing the dns update (if it was compiled in
162 * and if it was not disabled on the command line).
163 * If the dns update fails, we still consider the join
164 * operation as succeeded if we came this far.
166 if (!no_dns_updates) {
167 net_ads_join_dns_updates(c, mem_ctx, r);
170 result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid),
171 r->out.dns_domain_name);
173 talloc_free(mem_ctx);
175 return result;
178 static const char py_net_join_member_doc[] = "join_member(dnshostname, createupn, createcomputer, osName, osVer, osServicePack, machinepass) -> (domain_sid, domain_name)\n\n" \
179 "Join the domain with the specified name.";
181 static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs)
183 struct libnet_UnjoinCtx *r = NULL;
184 WERROR werr;
185 TALLOC_CTX *mem_ctx;
186 int keep_account = false, debug = false;
187 const char *kwnames[] = { "keepAccount", "debug", NULL };
189 mem_ctx = talloc_new(self->mem_ctx);
190 if (mem_ctx == NULL) {
191 PyErr_NoMemory();
192 return NULL;
195 if (!*lpcfg_realm(self->lp_ctx)) {
196 PyErr_FromString(_("No realm set, are we joined ?\n"));
197 return NULL;
200 werr = libnet_init_UnjoinCtx(mem_ctx, &r);
201 if (!W_ERROR_IS_OK(werr)) {
202 PyErr_SetWERROR_and_string(werr,
203 _("Could not initialise unjoin context.\n"));
204 return NULL;
207 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave",
208 discard_const_p(char *, kwnames),
209 &keep_account, &debug)) {
210 talloc_free(mem_ctx);
211 PyErr_FromString(_("Invalid arguments\n"));
212 return NULL;
215 r->in.dc_name = self->server_address;
216 r->in.domain_name = lpcfg_realm(self->lp_ctx);
217 r->in.admin_credentials = self->creds;
218 r->in.modify_config = lp_config_backend_is_registry();
219 r->in.debug = debug;
222 * Try to delete it, but if that fails, disable it. The
223 * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable"
225 r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
226 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
227 if (keep_account) {
228 r->in.delete_machine_account = false;
229 } else {
230 r->in.delete_machine_account = true;
233 r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
235 werr = libnet_Unjoin(mem_ctx, r);
236 if (!W_ERROR_IS_OK(werr)) {
237 PyErr_SetWERROR_and_string(werr,
238 r->out.error_string
239 ? r->out.error_string
240 : get_friendly_werror_msg(werr));
241 Py_RETURN_FALSE;
244 if (r->out.deleted_machine_account) {
245 d_printf(_("Deleted account for '%s' in realm '%s'\n"),
246 r->in.machine_name, r->out.dns_domain_name);
247 Py_RETURN_TRUE;
250 if (r->out.disabled_machine_account) {
251 d_printf(_("Disabled account for '%s' in realm '%s'\n"),
252 r->in.machine_name, r->out.dns_domain_name);
253 werr = WERR_OK;
254 Py_RETURN_TRUE;
258 * Based on what we requested, we shouldn't get here, but if
259 * we did, it means the secrets were removed, and therefore
260 * we have left the domain.
262 d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
263 r->in.machine_name, r->out.dns_domain_name);
264 Py_RETURN_TRUE;
267 static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \
268 "Leave the joined domain.";
270 static PyMethodDef net_obj_methods[] = {
272 .ml_name = "join_member",
273 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
274 py_net_join_member),
275 .ml_flags = METH_VARARGS|METH_KEYWORDS,
276 .ml_doc = py_net_join_member_doc
279 .ml_name = "leave",
280 .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
281 py_net_leave),
282 .ml_flags = METH_VARARGS|METH_KEYWORDS,
283 .ml_doc = py_net_leave_doc
285 { .ml_name = NULL }
288 static void py_net_dealloc(py_net_Object *self)
290 talloc_free(self->mem_ctx);
291 PyObject_Del(self);
294 static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
296 PyObject *py_creds, *py_lp = Py_None;
297 const char *kwnames[] = { "creds", "lp", "server", NULL };
298 py_net_Object *ret;
299 const char *server_address = NULL;
301 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
302 discard_const_p(char *, kwnames), &py_creds, &py_lp,
303 &server_address)) {
304 PyErr_FromString(_("Invalid arguments\n"));
305 return NULL;
308 ret = PyObject_New(py_net_Object, type);
309 if (ret == NULL) {
310 return NULL;
313 ret->ev = samba_tevent_context_init(NULL);
314 ret->mem_ctx = talloc_stackframe();
316 ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp);
317 if (ret->lp_ctx == NULL) {
318 Py_DECREF(ret);
319 return NULL;
322 ret->server_address = server_address;
324 ret->creds = cli_credentials_from_py_object(py_creds);
325 if (ret->creds == NULL) {
326 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
327 Py_DECREF(ret);
328 return NULL;
331 return (PyObject *)ret;
335 PyTypeObject py_net_Type = {
336 PyVarObject_HEAD_INIT(NULL, 0)
337 .tp_name = "net_s3.Net",
338 .tp_basicsize = sizeof(py_net_Object),
339 .tp_dealloc = (destructor)py_net_dealloc,
340 .tp_methods = net_obj_methods,
341 .tp_new = net_obj_new,
344 static struct PyModuleDef moduledef = {
345 PyModuleDef_HEAD_INIT,
346 .m_name = "net",
347 .m_size = -1,
350 MODULE_INIT_FUNC(net_s3)
352 PyObject *m;
354 if (PyType_Ready(&py_net_Type) < 0)
355 return NULL;
357 m = PyModule_Create(&moduledef);
358 if (m == NULL)
359 return NULL;
361 Py_INCREF(&py_net_Type);
362 PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
364 return m;