LCOV - code coverage report
Current view: top level - source3/utils - py_net.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 105 168 62.5 %
Date: 2023-11-21 12:31:41 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_account  = cli_credentials_get_username(self->creds);
     122           8 :         r->in.admin_password = cli_credentials_get_password(self->creds);
     123           8 :         r->in.use_kerberos   = cli_credentials_get_kerberos_state(self->creds);
     124           8 :         r->in.modify_config  = modify_config;
     125           8 :         r->in.join_flags     = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
     126             :                                   WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
     127             :                                   WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
     128           8 :         r->in.msg_ctx                = cmdline_messaging_context(get_dyn_CONFIGFILE());
     129           8 :         r->in.debug          = debug;
     130           8 :         c->opt_user_name = r->in.admin_account;
     131           8 :         c->opt_password = r->in.admin_password;
     132           8 :         c->opt_kerberos = r->in.use_kerberos;
     133             : 
     134           8 :         werr = libnet_Join(mem_ctx, r);
     135           8 :         if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) {
     136           0 :                 r->in.domain_name = lpcfg_workgroup(self->lp_ctx);
     137           0 :                 r->in.domain_name_type = JoinDomNameTypeNBT;
     138           0 :                 werr = libnet_Join(mem_ctx, r);
     139             :         }
     140           8 :         if (!W_ERROR_IS_OK(werr)) {
     141           0 :                 PyErr_SetWERROR_and_string(werr,
     142             :                                            r->out.error_string
     143             :                                            ? r->out.error_string
     144             :                                            : get_friendly_werror_msg(werr));
     145           0 :                 talloc_free(mem_ctx);
     146           0 :                 return NULL;
     147             :         }
     148             : 
     149             :         /*
     150             :          * Check the short name of the domain
     151             :          */
     152             : 
     153           8 :         if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) {
     154           0 :                 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           0 :                          get_dyn_CONFIGFILE(), r->out.netbios_domain_name,
     159           0 :                          r->out.netbios_domain_name, get_dyn_CONFIGFILE());
     160             :         }
     161             : 
     162             :         /*
     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.
     167             :          */
     168           8 :         if (!no_dns_updates) {
     169           8 :                 net_ads_join_dns_updates(c, mem_ctx, r);
     170             :         }
     171             : 
     172           8 :         result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid),
     173           8 :                                r->out.dns_domain_name);
     174             : 
     175           8 :         talloc_free(mem_ctx);
     176             : 
     177           8 :         return result;
     178             : }
     179             : 
     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.";
     182             : 
     183           2 : static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs)
     184             : {
     185           2 :         struct libnet_UnjoinCtx *r = NULL;
     186           0 :         WERROR werr;
     187           0 :         TALLOC_CTX *mem_ctx;
     188           2 :         int keep_account = false, debug = false;
     189           2 :         const char *kwnames[] = { "keepAccount", "debug", NULL };
     190             : 
     191           2 :         mem_ctx = talloc_new(self->mem_ctx);
     192           2 :         if (mem_ctx == NULL) {
     193           0 :                 PyErr_NoMemory();
     194           0 :                 return NULL;
     195             :         }
     196             : 
     197           2 :         if (!*lpcfg_realm(self->lp_ctx)) {
     198           0 :                 PyErr_FromString(_("No realm set, are we joined ?\n"));
     199           0 :                 return NULL;
     200             :         }
     201             : 
     202           2 :         werr = libnet_init_UnjoinCtx(mem_ctx, &r);
     203           2 :         if (!W_ERROR_IS_OK(werr)) {
     204           0 :                 PyErr_SetWERROR_and_string(werr,
     205             :                         _("Could not initialise unjoin context.\n"));
     206           0 :                 return NULL;
     207             :         }
     208             : 
     209           2 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave",
     210             :                                          discard_const_p(char *, kwnames),
     211             :                                          &keep_account, &debug)) {
     212           0 :                 talloc_free(mem_ctx);
     213           0 :                 PyErr_FromString(_("Invalid arguments\n"));
     214           0 :                 return NULL;
     215             :         }
     216             : 
     217           2 :         r->in.use_kerberos   = cli_credentials_get_kerberos_state(self->creds);
     218           2 :         r->in.dc_name                = self->server_address;
     219           2 :         r->in.domain_name    = lpcfg_realm(self->lp_ctx);
     220           2 :         r->in.admin_account  = cli_credentials_get_username(self->creds);
     221           2 :         r->in.admin_password = cli_credentials_get_password(self->creds);
     222           2 :         r->in.modify_config  = lp_config_backend_is_registry();
     223           2 :         r->in.debug          = debug;
     224             : 
     225             :         /*
     226             :          * Try to delete it, but if that fails, disable it.  The
     227             :          * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable"
     228             :          */
     229           2 :         r->in.unjoin_flags   = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
     230             :                                   WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
     231           2 :         if (keep_account) {
     232           0 :                 r->in.delete_machine_account = false;
     233             :         } else {
     234           2 :                 r->in.delete_machine_account = true;
     235             :         }
     236             : 
     237           2 :         r->in.msg_ctx                = cmdline_messaging_context(get_dyn_CONFIGFILE());
     238             : 
     239           2 :         werr = libnet_Unjoin(mem_ctx, r);
     240           2 :         if (!W_ERROR_IS_OK(werr)) {
     241           0 :                 PyErr_SetWERROR_and_string(werr,
     242             :                                            r->out.error_string
     243             :                                            ? r->out.error_string
     244             :                                            : get_friendly_werror_msg(werr));
     245           0 :                 Py_RETURN_FALSE;
     246             :         }
     247             : 
     248           2 :         if (r->out.deleted_machine_account) {
     249           2 :                 d_printf(_("Deleted account for '%s' in realm '%s'\n"),
     250           2 :                         r->in.machine_name, r->out.dns_domain_name);
     251           2 :                 Py_RETURN_TRUE;
     252             :         }
     253             : 
     254           0 :         if (r->out.disabled_machine_account) {
     255           0 :                 d_printf(_("Disabled account for '%s' in realm '%s'\n"),
     256           0 :                         r->in.machine_name, r->out.dns_domain_name);
     257           0 :                 werr = WERR_OK;
     258           0 :                 Py_RETURN_TRUE;
     259             :         }
     260             : 
     261             :         /*
     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.
     265             :          */
     266           0 :         d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
     267           0 :                   r->in.machine_name, r->out.dns_domain_name);
     268           0 :         Py_RETURN_TRUE;
     269             : }
     270             : 
     271             : static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \
     272             : "Leave the joined domain.";
     273             : 
     274             : static PyMethodDef net_obj_methods[] = {
     275             :         {
     276             :                 .ml_name  = "join_member",
     277             :                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction,
     278             :                                 py_net_join_member),
     279             :                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
     280             :                 .ml_doc   = py_net_join_member_doc
     281             :         },
     282             :         {
     283             :                 .ml_name  = "leave",
     284             :                 .ml_meth  = PY_DISCARD_FUNC_SIG(PyCFunction,
     285             :                                 py_net_leave),
     286             :                 .ml_flags = METH_VARARGS|METH_KEYWORDS,
     287             :                 .ml_doc   = py_net_leave_doc
     288             :         },
     289             :         { .ml_name = NULL }
     290             : };
     291             : 
     292           8 : static void py_net_dealloc(py_net_Object *self)
     293             : {
     294           8 :         talloc_free(self->mem_ctx);
     295           8 :         PyObject_Del(self);
     296           8 : }
     297             : 
     298           8 : static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
     299             : {
     300           8 :         PyObject *py_creds, *py_lp = Py_None;
     301           8 :         const char *kwnames[] = { "creds", "lp", "server", NULL };
     302           0 :         py_net_Object *ret;
     303           8 :         const char *server_address = NULL;
     304             : 
     305           8 :         if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
     306             :                                          discard_const_p(char *, kwnames), &py_creds, &py_lp,
     307             :                                          &server_address)) {
     308           0 :                 PyErr_FromString(_("Invalid arguments\n"));
     309           0 :                 return NULL;
     310             :         }
     311             : 
     312           8 :         ret = PyObject_New(py_net_Object, type);
     313           8 :         if (ret == NULL) {
     314           0 :                 return NULL;
     315             :         }
     316             : 
     317           8 :         ret->ev = samba_tevent_context_init(NULL);
     318           8 :         ret->mem_ctx = talloc_stackframe();
     319             : 
     320           8 :         ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp);
     321           8 :         if (ret->lp_ctx == NULL) {
     322           0 :                 Py_DECREF(ret);
     323           0 :                 return NULL;
     324             :         }
     325             : 
     326           8 :         ret->server_address = server_address;
     327             : 
     328           8 :         ret->creds = cli_credentials_from_py_object(py_creds);
     329           8 :         if (ret->creds == NULL) {
     330           0 :                 PyErr_SetString(PyExc_TypeError, "Expected credentials object");
     331           0 :                 Py_DECREF(ret);
     332           0 :                 return NULL;
     333             :         }
     334             : 
     335           8 :         return (PyObject *)ret;
     336             : }
     337             : 
     338             : 
     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,
     346             : };
     347             : 
     348             : static struct PyModuleDef moduledef = {
     349             :         PyModuleDef_HEAD_INIT,
     350             :         .m_name = "net",
     351             :         .m_size = -1,
     352             : };
     353             : 
     354        1845 : MODULE_INIT_FUNC(net_s3)
     355             : {
     356          67 :         PyObject *m;
     357             : 
     358        1845 :         if (PyType_Ready(&py_net_Type) < 0)
     359           0 :                 return NULL;
     360             : 
     361        1845 :         m = PyModule_Create(&moduledef);
     362        1845 :         if (m == NULL)
     363           0 :                 return NULL;
     364             : 
     365        1308 :         Py_INCREF(&py_net_Type);
     366        1845 :         PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
     367             : 
     368        1845 :         return m;
     369             : }

Generated by: LCOV version 1.14