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/>.
24 #include "python/modules.h"
25 #include "python/py3compat.h"
26 #include "rpc_client/rpc_client.h"
27 #include <sys/socket.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"
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
;
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
;
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
) {
82 c
= talloc_zero(mem_ctx
, struct net_context
);
85 werr
= libnet_init_JoinCtx(mem_ctx
, &r
);
86 if (!W_ERROR_IS_OK(werr
)) {
91 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|sssssszpp:Join",
92 discard_const_p(char *, kwnames
),
98 &r
->in
.os_servicepack
,
99 &r
->in
.machine_password
,
102 talloc_free(mem_ctx
);
103 PyErr_FromString(_("Invalid arguments\n"));
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
);
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_account
= cli_credentials_get_username(self
->creds
);
122 r
->in
.admin_password
= cli_credentials_get_password(self
->creds
);
123 r
->in
.use_kerberos
= cli_credentials_get_kerberos_state(self
->creds
);
124 r
->in
.modify_config
= modify_config
;
125 r
->in
.join_flags
= WKSSVC_JOIN_FLAGS_JOIN_TYPE
|
126 WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE
|
127 WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED
;
128 r
->in
.msg_ctx
= cmdline_messaging_context(get_dyn_CONFIGFILE());
130 c
->opt_user_name
= r
->in
.admin_account
;
131 c
->opt_password
= r
->in
.admin_password
;
132 c
->opt_kerberos
= r
->in
.use_kerberos
;
134 werr
= libnet_Join(mem_ctx
, r
);
135 if (W_ERROR_EQUAL(werr
, WERR_NERR_DCNOTFOUND
)) {
136 r
->in
.domain_name
= lpcfg_workgroup(self
->lp_ctx
);
137 r
->in
.domain_name_type
= JoinDomNameTypeNBT
;
138 werr
= libnet_Join(mem_ctx
, r
);
140 if (!W_ERROR_IS_OK(werr
)) {
141 PyErr_SetWERROR_and_string(werr
,
143 ? r
->out
.error_string
144 : get_friendly_werror_msg(werr
));
145 talloc_free(mem_ctx
);
150 * Check the short name of the domain
153 if (!modify_config
&& !strequal(lpcfg_workgroup(self
->lp_ctx
), r
->out
.netbios_domain_name
)) {
154 d_printf(_("The workgroup in %s does not match the short\n"
155 "domain name obtained from the server.\n"
156 "Using the name [%s] from the server.\n"
157 "You should set \"workgroup = %s\" in %s.\n"),
158 get_dyn_CONFIGFILE(), r
->out
.netbios_domain_name
,
159 r
->out
.netbios_domain_name
, get_dyn_CONFIGFILE());
163 * We try doing the dns update (if it was compiled in
164 * and if it was not disabled on the command line).
165 * If the dns update fails, we still consider the join
166 * operation as succeeded if we came this far.
168 if (!no_dns_updates
) {
169 net_ads_join_dns_updates(c
, mem_ctx
, r
);
172 result
= Py_BuildValue("ss", dom_sid_string(mem_ctx
, r
->out
.domain_sid
),
173 r
->out
.dns_domain_name
);
175 talloc_free(mem_ctx
);
180 static const char py_net_join_member_doc
[] = "join_member(dnshostname, createupn, createcomputer, osName, osVer, osServicePack, machinepass) -> (domain_sid, domain_name)\n\n" \
181 "Join the domain with the specified name.";
183 static PyObject
*py_net_leave(py_net_Object
*self
, PyObject
*args
, PyObject
*kwargs
)
185 struct libnet_UnjoinCtx
*r
= NULL
;
188 int keep_account
= false, debug
= false;
189 const char *kwnames
[] = { "keepAccount", "debug", NULL
};
191 mem_ctx
= talloc_new(self
->mem_ctx
);
192 if (mem_ctx
== NULL
) {
197 if (!*lpcfg_realm(self
->lp_ctx
)) {
198 PyErr_FromString(_("No realm set, are we joined ?\n"));
202 werr
= libnet_init_UnjoinCtx(mem_ctx
, &r
);
203 if (!W_ERROR_IS_OK(werr
)) {
204 PyErr_SetWERROR_and_string(werr
,
205 _("Could not initialise unjoin context.\n"));
209 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "|pp:Leave",
210 discard_const_p(char *, kwnames
),
211 &keep_account
, &debug
)) {
212 talloc_free(mem_ctx
);
213 PyErr_FromString(_("Invalid arguments\n"));
217 r
->in
.use_kerberos
= cli_credentials_get_kerberos_state(self
->creds
);
218 r
->in
.dc_name
= self
->server_address
;
219 r
->in
.domain_name
= lpcfg_realm(self
->lp_ctx
);
220 r
->in
.admin_account
= cli_credentials_get_username(self
->creds
);
221 r
->in
.admin_password
= cli_credentials_get_password(self
->creds
);
222 r
->in
.modify_config
= lp_config_backend_is_registry();
226 * Try to delete it, but if that fails, disable it. The
227 * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable"
229 r
->in
.unjoin_flags
= WKSSVC_JOIN_FLAGS_JOIN_TYPE
|
230 WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE
;
232 r
->in
.delete_machine_account
= false;
234 r
->in
.delete_machine_account
= true;
237 r
->in
.msg_ctx
= cmdline_messaging_context(get_dyn_CONFIGFILE());
239 werr
= libnet_Unjoin(mem_ctx
, r
);
240 if (!W_ERROR_IS_OK(werr
)) {
241 PyErr_SetWERROR_and_string(werr
,
243 ? r
->out
.error_string
244 : get_friendly_werror_msg(werr
));
248 if (r
->out
.deleted_machine_account
) {
249 d_printf(_("Deleted account for '%s' in realm '%s'\n"),
250 r
->in
.machine_name
, r
->out
.dns_domain_name
);
254 if (r
->out
.disabled_machine_account
) {
255 d_printf(_("Disabled account for '%s' in realm '%s'\n"),
256 r
->in
.machine_name
, r
->out
.dns_domain_name
);
262 * Based on what we requested, we shouldn't get here, but if
263 * we did, it means the secrets were removed, and therefore
264 * we have left the domain.
266 d_fprintf(stderr
, _("Machine '%s' Left domain '%s'\n"),
267 r
->in
.machine_name
, r
->out
.dns_domain_name
);
271 static const char py_net_leave_doc
[] = "leave(keepAccount) -> success\n\n" \
272 "Leave the joined domain.";
274 static PyMethodDef net_obj_methods
[] = {
276 .ml_name
= "join_member",
277 .ml_meth
= PY_DISCARD_FUNC_SIG(PyCFunction
,
279 .ml_flags
= METH_VARARGS
|METH_KEYWORDS
,
280 .ml_doc
= py_net_join_member_doc
284 .ml_meth
= PY_DISCARD_FUNC_SIG(PyCFunction
,
286 .ml_flags
= METH_VARARGS
|METH_KEYWORDS
,
287 .ml_doc
= py_net_leave_doc
292 static void py_net_dealloc(py_net_Object
*self
)
294 talloc_free(self
->mem_ctx
);
298 static PyObject
*net_obj_new(PyTypeObject
*type
, PyObject
*args
, PyObject
*kwargs
)
300 PyObject
*py_creds
, *py_lp
= Py_None
;
301 const char *kwnames
[] = { "creds", "lp", "server", NULL
};
303 const char *server_address
= NULL
;
305 if (!PyArg_ParseTupleAndKeywords(args
, kwargs
, "O|Oz",
306 discard_const_p(char *, kwnames
), &py_creds
, &py_lp
,
308 PyErr_FromString(_("Invalid arguments\n"));
312 ret
= PyObject_New(py_net_Object
, type
);
317 ret
->ev
= samba_tevent_context_init(NULL
);
318 ret
->mem_ctx
= talloc_stackframe();
320 ret
->lp_ctx
= lpcfg_from_py_object(ret
->mem_ctx
, py_lp
);
321 if (ret
->lp_ctx
== NULL
) {
326 ret
->server_address
= server_address
;
328 ret
->creds
= cli_credentials_from_py_object(py_creds
);
329 if (ret
->creds
== NULL
) {
330 PyErr_SetString(PyExc_TypeError
, "Expected credentials object");
335 return (PyObject
*)ret
;
339 PyTypeObject py_net_Type
= {
340 PyVarObject_HEAD_INIT(NULL
, 0)
341 .tp_name
= "net_s3.Net",
342 .tp_basicsize
= sizeof(py_net_Object
),
343 .tp_dealloc
= (destructor
)py_net_dealloc
,
344 .tp_methods
= net_obj_methods
,
345 .tp_new
= net_obj_new
,
348 static struct PyModuleDef moduledef
= {
349 PyModuleDef_HEAD_INIT
,
354 MODULE_INIT_FUNC(net_s3
)
358 if (PyType_Ready(&py_net_Type
) < 0)
361 m
= PyModule_Create(&moduledef
);
365 Py_INCREF(&py_net_Type
);
366 PyModule_AddObject(m
, "Net", (PyObject
*)&py_net_Type
);