LCOV - code coverage report
Current view: top level - source3/utils - py_net.c (source / functions) Hit Total Coverage
Test: coverage report for master 98b443d9 Lines: 100 163 61.3 %
Date: 2024-05-31 13:13:24 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Samba python bindings to s3 libnet library
       4             : 
       5             :    Copyright (C) David Mulder <dmulder@samba.org>
       6             : 
       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.
      11             : 
      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.
      16             : 
      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/>.
      19             : */
      20             : 
      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"
      40             : 
      41           8 : static WERROR check_ads_config(struct loadparm_context *lp_ctx)
      42             : {
      43           8 :         if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) {
      44           0 :                 d_printf(_("Host is not configured as a member server.\n"));
      45           0 :                 return WERR_INVALID_DOMAIN_ROLE;
      46             :         }
      47             : 
      48           8 :         if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) {
      49           0 :                 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           0 :                          (unsigned int)strlen(lpcfg_netbios_name(lp_ctx)));
      52           0 :                 return WERR_INVALID_COMPUTERNAME;
      53             :         }
      54             : 
      55           8 :         if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) {
      56           0 :                 d_fprintf(stderr, _("realm must be set in %s for ADS "
      57             :                           "join to succeed.\n"), get_dyn_CONFIGFILE());
      58           0 :                 return WERR_INVALID_PARAMETER;
      59             :         }
      60             : 
      61           8 :         return WERR_OK;
      62             : }
      63             : 
      64           8 : static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
      65             : {
      66           8 :         struct libnet_JoinCtx *r = NULL;
      67           0 :         struct net_context *c;
      68           0 :         WERROR werr;
      69           0 :         PyObject *result;
      70           0 :         TALLOC_CTX *mem_ctx;
      71           8 :         int no_dns_updates = false, debug = false;
      72           8 :         bool modify_config = lp_config_backend_is_registry();
      73           8 :         const char *kwnames[] = { "dnshostname", "createupn", "createcomputer",
      74             :                                   "osName", "osVer", "osServicePack",
      75             :                                   "machinepass", "debug", "noDnsUpdates", NULL };
      76             : 
      77           8 :         mem_ctx = talloc_new(self->mem_ctx);
      78           8 :         if (mem_ctx == NULL) {
      79           0 :                 PyErr_NoMemory();
      80           0 :                 return NULL;
      81             :         }
      82           8 :         c = talloc_zero(mem_ctx, struct net_context);
      83           8 :         c->msg_ctx = mem_ctx;
      84             : 
      85           8 :         werr = libnet_init_JoinCtx(mem_ctx, &r);
      86           8 :         if (!W_ERROR_IS_OK(werr)) {
      87           0 :                 PyErr_NoMemory();
      88           0 :                 return NULL;
      89             :         }
      90             : 
      91           8 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssszpp:Join",
      92             :                                          discard_const_p(char *, kwnames),
      93           8 :                                          &r->in.dnshostname,
      94           8 :                                          &r->in.upn,
      95           8 :                                          &r->in.account_ou,
      96           8 :                                          &r->in.os_name,
      97           8 :                                          &r->in.os_version,
      98           8 :                                          &r->in.os_servicepack,
      99           8 :                                          &r->in.machine_password,
     100             :                                          &debug,
     101             :                                          &no_dns_updates)) {
     102           0 :                 talloc_free(mem_ctx);
     103           0 :                 PyErr_FromString(_("Invalid arguments\n"));
     104           0 :                 return NULL;
     105             :         }
     106             : 
     107           8 :         if (!modify_config) {
     108           8 :                 werr = check_ads_config(self->lp_ctx);
     109           8 :                 if (!W_ERROR_IS_OK(werr)) {
     110           0 :                         PyErr_SetWERROR_and_string(werr,
     111             :                                 _("Invalid configuration.  Exiting....\n"));
     112           0 :                         talloc_free(mem_ctx);
     113           0 :                         return NULL;
     114             :                 }
     115             :         }
     116             : 
     117           8 :         r->in.domain_name    = lpcfg_realm(self->lp_ctx);
     118           8 :         r->in.domain_name_type       = JoinDomNameTypeDNS;
     119           8 :         r->in.create_upn     = r->in.upn != NULL ? true : false;
     120           8 :         r->in.dc_name                = self->server_address;
     121           8 :         r->in.admin_credentials      = self->creds;
     122           8 :         r->in.modify_config  = modify_config;
     123           8 :         r->in.join_flags     = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
     124             :                                   WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
     125             :                                   WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
     126           8 :         r->in.msg_ctx                = cmdline_messaging_context(get_dyn_CONFIGFILE());
     127           8 :         r->in.debug          = debug;
     128             : 
     129           8 :         c->creds = self->creds;
     130           8 :         c->explicit_credentials = true;
     131             : 
     132           8 :         werr = libnet_Join(mem_ctx, r);
     133           8 :         if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) {
     134           0 :                 r->in.domain_name = lpcfg_workgroup(self->lp_ctx);
     135           0 :                 r->in.domain_name_type = JoinDomNameTypeNBT;
     136           0 :                 werr = libnet_Join(mem_ctx, r);
     137             :         }
     138           8 :         if (!W_ERROR_IS_OK(werr)) {
     139           0 :                 PyErr_SetWERROR_and_string(werr,
     140             :                                            r->out.error_string
     141             :                                            ? r->out.error_string
     142             :                                            : get_friendly_werror_msg(werr));
     143           0 :                 talloc_free(mem_ctx);
     144           0 :                 return NULL;
     145             :         }
     146             : 
     147             :         /*
     148             :          * Check the short name of the domain
     149             :          */
     150             : 
     151           8 :         if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) {
     152           0 :                 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           0 :                          get_dyn_CONFIGFILE(), r->out.netbios_domain_name,
     157           0 :                          r->out.netbios_domain_name, get_dyn_CONFIGFILE());
     158             :         }
     159             : 
     160             :         /*
     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.
     165             :          */
     166           8 :         if (!no_dns_updates) {
     167           8 :                 net_ads_join_dns_updates(c, mem_ctx, r);
     168             :         }
     169             : 
     170           8 :         result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid),
     171           8 :                                r->out.dns_domain_name);
     172             : 
     173           8 :         talloc_free(mem_ctx);
     174             : 
     175           8 :         return result;
     176             : }
     177             : 
     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.";
     180             : 
     181           2 : static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs)
     182             : {
     183           2 :         struct libnet_UnjoinCtx *r = NULL;
     184           0 :         WERROR werr;
     185           0 :         TALLOC_CTX *mem_ctx;
     186           2 :         int keep_account = false, debug = false;
     187           2 :         const char *kwnames[] = { "keepAccount", "debug", NULL };
     188             : 
     189           2 :         mem_ctx = talloc_new(self->mem_ctx);
     190           2 :         if (mem_ctx == NULL) {
     191           0 :                 PyErr_NoMemory();
     192           0 :                 return NULL;
     193             :         }
     194             : 
     195           2 :         if (!*lpcfg_realm(self->lp_ctx)) {
     196           0 :                 PyErr_FromString(_("No realm set, are we joined ?\n"));
     197           0 :                 return NULL;
     198             :         }
     199             : 
     200           2 :         werr = libnet_init_UnjoinCtx(mem_ctx, &r);
     201           2 :         if (!W_ERROR_IS_OK(werr)) {
     202           0 :                 PyErr_SetWERROR_and_string(werr,
     203             :                         _("Could not initialise unjoin context.\n"));
     204           0 :                 return NULL;
     205             :         }
     206             : 
     207           2 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave",
     208             :                                          discard_const_p(char *, kwnames),
     209             :                                          &keep_account, &debug)) {
     210           0 :                 talloc_free(mem_ctx);
     211           0 :                 PyErr_FromString(_("Invalid arguments\n"));
     212           0 :                 return NULL;
     213             :         }
     214             : 
     215           2 :         r->in.dc_name                = self->server_address;
     216           2 :         r->in.domain_name    = lpcfg_realm(self->lp_ctx);
     217           2 :         r->in.admin_credentials      = self->creds;
     218           2 :         r->in.modify_config  = lp_config_backend_is_registry();
     219           2 :         r->in.debug          = debug;
     220             : 
     221             :         /*
     222             :          * Try to delete it, but if that fails, disable it.  The
     223             :          * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable"
     224             :          */
     225           2 :         r->in.unjoin_flags   = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
     226             :                                   WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
     227           2 :         if (keep_account) {
     228           0 :                 r->in.delete_machine_account = false;
     229             :         } else {
     230           2 :                 r->in.delete_machine_account = true;
     231             :         }
     232             : 
     233           2 :         r->in.msg_ctx                = cmdline_messaging_context(get_dyn_CONFIGFILE());
     234             : 
     235           2 :         werr = libnet_Unjoin(mem_ctx, r);
     236           2 :         if (!W_ERROR_IS_OK(werr)) {
     237           0 :                 PyErr_SetWERROR_and_string(werr,
     238             :                                            r->out.error_string
     239             :                                            ? r->out.error_string
     240             :                                            : get_friendly_werror_msg(werr));
     241           0 :                 Py_RETURN_FALSE;
     242             :         }
     243             : 
     244           2 :         if (r->out.deleted_machine_account) {
     245           2 :                 d_printf(_("Deleted account for '%s' in realm '%s'\n"),
     246           2 :                         r->in.machine_name, r->out.dns_domain_name);
     247           2 :                 Py_RETURN_TRUE;
     248             :         }
     249             : 
     250           0 :         if (r->out.disabled_machine_account) {
     251           0 :                 d_printf(_("Disabled account for '%s' in realm '%s'\n"),
     252           0 :                         r->in.machine_name, r->out.dns_domain_name);
     253           0 :                 werr = WERR_OK;
     254           0 :                 Py_RETURN_TRUE;
     255             :         }
     256             : 
     257             :         /*
     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.
     261             :          */
     262           0 :         d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
     263           0 :                   r->in.machine_name, r->out.dns_domain_name);
     264           0 :         Py_RETURN_TRUE;
     265             : }
     266             : 
     267             : static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \
     268             : "Leave the joined domain.";
     269             : 
     270             : static PyMethodDef net_obj_methods[] = {
     271             :         {
     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
     277             :         },
     278             :         {
     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
     284             :         },
     285             :         { .ml_name = NULL }
     286             : };
     287             : 
     288           8 : static void py_net_dealloc(py_net_Object *self)
     289             : {
     290           8 :         talloc_free(self->mem_ctx);
     291           8 :         PyObject_Del(self);
     292           8 : }
     293             : 
     294           8 : static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
     295             : {
     296           8 :         PyObject *py_creds, *py_lp = Py_None;
     297           8 :         const char *kwnames[] = { "creds", "lp", "server", NULL };
     298           0 :         py_net_Object *ret;
     299           8 :         const char *server_address = NULL;
     300             : 
     301           8 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
     302             :                                          discard_const_p(char *, kwnames), &py_creds, &py_lp,
     303             :                                          &server_address)) {
     304           0 :                 PyErr_FromString(_("Invalid arguments\n"));
     305           0 :                 return NULL;
     306             :         }
     307             : 
     308           8 :         ret = PyObject_New(py_net_Object, type);
     309           8 :         if (ret == NULL) {
     310           0 :                 return NULL;
     311             :         }
     312             : 
     313           8 :         ret->ev = samba_tevent_context_init(NULL);
     314           8 :         ret->mem_ctx = talloc_stackframe();
     315             : 
     316           8 :         ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp);
     317           8 :         if (ret->lp_ctx == NULL) {
     318           0 :                 Py_DECREF(ret);
     319           0 :                 return NULL;
     320             :         }
     321             : 
     322           8 :         ret->server_address = server_address;
     323             : 
     324           8 :         ret->creds = cli_credentials_from_py_object(py_creds);
     325           8 :         if (ret->creds == NULL) {
     326           0 :                 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
     327           0 :                 Py_DECREF(ret);
     328           0 :                 return NULL;
     329             :         }
     330             : 
     331           8 :         return (PyObject *)ret;
     332             : }
     333             : 
     334             : 
     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,
     342             : };
     343             : 
     344             : static struct PyModuleDef moduledef = {
     345             :         PyModuleDef_HEAD_INIT,
     346             :         .m_name = "net",
     347             :         .m_size = -1,
     348             : };
     349             : 
     350         788 : MODULE_INIT_FUNC(net_s3)
     351             : {
     352          34 :         PyObject *m;
     353             : 
     354         788 :         if (PyType_Ready(&py_net_Type) < 0)
     355           0 :                 return NULL;
     356             : 
     357         788 :         m = PyModule_Create(&moduledef);
     358         788 :         if (m == NULL)
     359           0 :                 return NULL;
     360             : 
     361         592 :         Py_INCREF(&py_net_Type);
     362         788 :         PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
     363             : 
     364         788 :         return m;
     365             : }

Generated by: LCOV version 1.14